Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.Services.Authentication Prototype/Service/Provider/HeuristicLabMembershipProvider.cs @ 4005

Last change on this file since 4005 was 4005, checked in by jhaider, 14 years ago

added implementation (#1080)

File size: 18.3 KB
Line 
1using System;
2using System.Collections.Specialized;
3using System.Configuration;
4using System.Configuration.Provider;
5using System.IO;
6using System.Linq;
7using System.Security.Cryptography;
8using System.Text;
9using System.Web.Configuration;
10using System.Web.Security;
11using Persistence;
12
13
14namespace Service.Provider {
15  class HeuristicLabMembershipProvider : MembershipProvider {
16
17    #region variables
18
19    private string pApplicationName;
20    private bool pEnablePasswordReset;
21    private bool pEnablePasswordRetrieval;
22    private bool pRequiresQuestionAndAnswer;
23    private bool pRequiresUniqueEmail;
24    private int pMaxInvalidPasswordAttempts;
25    private int pPasswordAttemptWindow;
26    private int pMinRequiredPasswordLength = 5;
27    private MembershipPasswordFormat pPasswordFormat = MembershipPasswordFormat.Clear;
28
29    #endregion
30
31    #region properties
32
33    public override string ApplicationName {
34      get { return pApplicationName; }
35      set { pApplicationName = value; }
36    }
37
38    public override bool EnablePasswordReset {
39      get { return pEnablePasswordReset; }
40    }
41
42    public override bool EnablePasswordRetrieval {
43      get { return pEnablePasswordRetrieval; }
44    }
45
46    public override int MaxInvalidPasswordAttempts {
47      get { return pMaxInvalidPasswordAttempts; }
48    }
49
50    public override int MinRequiredNonAlphanumericCharacters {
51      get { return 0; }
52    }
53
54    public override int MinRequiredPasswordLength {
55      get { return pMinRequiredPasswordLength; }
56    }
57
58    public override int PasswordAttemptWindow {
59      get { return pPasswordAttemptWindow; }
60    }
61
62    public override MembershipPasswordFormat PasswordFormat {
63      get { return pPasswordFormat; }
64    }
65
66    public override string PasswordStrengthRegularExpression {
67      get { return string.Empty; }
68    }
69
70    public override bool RequiresQuestionAndAnswer {
71      get { return pRequiresQuestionAndAnswer; }
72    }
73
74    public override bool RequiresUniqueEmail {
75      get { return pRequiresUniqueEmail; }
76    }
77
78    #endregion
79
80    public override void Initialize(string name, NameValueCollection config) {
81      //
82      // Initialize values from web.config.
83      //
84
85      if (config == null)
86        throw new ArgumentNullException("config");
87
88      if (string.IsNullOrEmpty(name) || name.Length == 0)
89        name = "HeuristicLabMembershipProvider";
90
91      if (String.IsNullOrEmpty(config["description"])) {
92        config.Remove("description");
93        config.Add("description", "Heuristic Lab Membership provider");
94      }
95
96      // Initialize the abstract base class.
97      base.Initialize(name, config);
98
99      pApplicationName = GetConfigValue(config["applicationName"], System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
100      pMaxInvalidPasswordAttempts = Convert.ToInt32(GetConfigValue(config["maxInvalidPasswordAttempts"], "5"));
101      pPasswordAttemptWindow = Convert.ToInt32(GetConfigValue(config["passwordAttemptWindow"], "10"));
102      pMinRequiredPasswordLength = Convert.ToInt32(GetConfigValue(config["minRequiredPasswordLength"], "7"));
103      pEnablePasswordReset = Convert.ToBoolean(GetConfigValue(config["enablePasswordReset"], "true"));
104      pEnablePasswordRetrieval = Convert.ToBoolean(GetConfigValue(config["enablePasswordRetrieval"], "true"));
105      pRequiresQuestionAndAnswer = Convert.ToBoolean(GetConfigValue(config["requiresQuestionAndAnswer"], "false"));
106      pRequiresUniqueEmail = Convert.ToBoolean(GetConfigValue(config["requiresUniqueEmail"], "true"));
107
108      string tempFormat = config["passwordFormat"];
109      if (tempFormat == null) {
110        tempFormat = "Clear";
111      }
112
113      switch (tempFormat) {
114        case "Hashed":
115          pPasswordFormat = MembershipPasswordFormat.Hashed;
116          break;
117        case "Encrypted":
118          pPasswordFormat = MembershipPasswordFormat.Encrypted;
119          break;
120        case "Clear":
121          pPasswordFormat = MembershipPasswordFormat.Clear;
122          break;
123        default:
124          throw new ProviderException("Password format not supported.");
125      }
126    }
127
128
129
130    public override bool ChangePassword(string username, string oldPassword, string newPassword) {
131      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
132        // check database connection
133        if (db == null) {
134          return false;
135        }
136
137        if (newPassword.Length < MinRequiredPasswordLength) {
138          return false;
139        }
140
141        try {
142          // try to get user
143          HeuristicLabUser u = db.HeuristicLabUsers.Single(x => x.UserName == username);
144
145          if (u.Password == EncodePassword(oldPassword) && newPassword.Length > 0) {
146            u.Password = EncodePassword(newPassword);
147            db.SubmitChanges();
148            return true;
149          }
150          return false;
151
152
153        }
154        catch (Exception) {
155          return false;
156        }
157      }
158    }
159
160    public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer) {
161      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
162
163        if (db.HeuristicLabUsers.Count(x => x.UserName == username) > 0 && newPasswordQuestion.Length > 0 && newPasswordAnswer.Length > 0) {
164          HeuristicLabUser u = db.HeuristicLabUsers.Single(x => x.UserName == username);
165          u.PasswordAnswer = newPasswordAnswer;
166          u.PasswordQuestion = newPasswordQuestion;
167          db.SubmitChanges();
168          return true;
169        }
170        return false;
171      }
172    }
173
174    public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) {
175      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
176        // check database connection
177        if (db == null) {
178          status = MembershipCreateStatus.ProviderError;
179          return null;
180        }
181        try {
182          // check for duplicate entries
183          if (db.HeuristicLabUsers.Count(x => x.UserName == username) > 0) {
184            status = MembershipCreateStatus.DuplicateUserName;
185            return null;
186          }
187          if (db.HeuristicLabUsers.Count(x => x.Email == email) > 0) {
188            status = MembershipCreateStatus.DuplicateEmail;
189            return null;
190          }
191          if (password.Length < MinRequiredPasswordLength) {
192            status = MembershipCreateStatus.InvalidPassword;
193            return null;
194          }
195
196          // create new user
197          HeuristicLabUser u = new HeuristicLabUser(username, email, passwordQuestion, "");
198          password = EncodePassword(password);
199          u.Password = password;
200          u.PasswordAnswer = passwordAnswer;
201          u.PasswordQuestion = passwordQuestion;
202          // save user into database
203          db.HeuristicLabUsers.InsertOnSubmit(u);
204          db.SubmitChanges();
205
206          // success
207          status = MembershipCreateStatus.Success;
208          return u.getMembershipUser(this.Name);
209        }
210        catch (Exception) {
211          // error
212          status = MembershipCreateStatus.ProviderError;
213          return null;
214        }
215      }
216    }
217
218    public override bool DeleteUser(string username, bool deleteAllRelatedData) {
219      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
220        // check database connection
221        if (db == null) {
222          return false;
223        }
224        try {
225          // try to get user
226          HeuristicLabUser u =
227            db.HeuristicLabUsers.Single<HeuristicLabUser>(x => x.UserName == username);
228
229          // optionally delete related data
230          if (deleteAllRelatedData) {
231            db.HeuristicLabUserRole.DeleteAllOnSubmit<HeuristicLabUserRole>(u.HeuristicLabUserRoles);
232          }
233
234          // delete user
235          db.HeuristicLabUsers.DeleteOnSubmit(u);
236          db.SubmitChanges();
237          return true;
238        }
239        catch (Exception) {
240          return false;
241        }
242      }
243    }
244
245
246
247    public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords) {
248      throw new NotImplementedException();
249    }
250
251    public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords) {
252      throw new NotImplementedException();
253    }
254
255    // not for production use - fab and dkhan are currently working on that
256    public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords) {
257      totalRecords = 0;
258      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
259        if (db == null) {
260          totalRecords = 0;
261          return new MembershipUserCollection();
262        }
263
264        // bail out if there are no records
265        if (0 == (totalRecords = db.HeuristicLabUsers.Count<HeuristicLabUser>())) return new MembershipUserCollection();
266
267        MembershipUserCollection userCollection = new MembershipUserCollection();
268        int skip = (pageIndex == 0) ? 0 : (pageIndex * pageSize) - 1;
269
270        var users = from u in db.HeuristicLabUsers select u;
271
272        foreach (HeuristicLabUser u in users) {
273
274          // this leads to a npe
275          if (u != null) {
276            userCollection.Add(u.getMembershipUser(this.Name));
277          }
278        }
279        return userCollection;
280      }
281    }
282    /// <summary>
283    /// not jet implemented returns 0 as default
284    /// </summary>
285    /// <returns></returns>
286    public override int GetNumberOfUsersOnline() {
287      return 0;
288    }
289
290    public override string GetPassword(string username, string answer) {
291      if (PasswordFormat == MembershipPasswordFormat.Hashed)
292        throw new NotSupportedException("Passwort is hashed");
293      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
294        HeuristicLabUser u =
295          db.HeuristicLabUsers.Single<HeuristicLabUser>(x => x.UserName == username);
296        if (EnablePasswordRetrieval && RequiresQuestionAndAnswer) {
297          if (u.PasswordAnswer.Equals(answer)) {
298            return u.Password;
299          } else {
300            throw new MembershipPasswordException();
301          }
302        } else throw new NotSupportedException("PasswortRetrival or RequiresQuestionAndAnswer not set");
303      }
304    }
305
306    public override MembershipUser GetUser(string username, bool userIsOnline) {
307      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
308        HeuristicLabUser u =
309            db.HeuristicLabUsers.Single<HeuristicLabUser>(x => x.UserName == username);
310        if (u != null)
311          return u.getMembershipUser(this.Name);
312        else
313          return null;
314      }
315    }
316
317    public override MembershipUser GetUser(object providerUserKey, bool userIsOnline) {
318      long ID = providerUserKey is long ? (long)providerUserKey : -1;
319      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
320        HeuristicLabUser u =
321            db.HeuristicLabUsers.Single<HeuristicLabUser>(x => x.ID == ID);
322        if (u != null)
323          return u.getMembershipUser(this.Name);
324        else
325          return null;
326      }
327    }
328
329    public override string GetUserNameByEmail(string email) {
330      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
331        HeuristicLabUser u =
332          db.HeuristicLabUsers.Single<HeuristicLabUser>(x => x.Email == email);
333        if (u != null)
334          return u.UserName;
335        else
336          return null;
337      }
338    }
339
340    public override string ResetPassword(string username, string answer) {
341      throw new NotSupportedException("Restet password not avaliable");
342    }
343
344    public bool LockUser(string userName) {
345      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
346        // check database connection
347        if (db == null) {
348          return false;
349        }
350        try {
351          // try to get user
352          HeuristicLabUser u =
353            db.HeuristicLabUsers.Single<HeuristicLabUser>(x => x.UserName == userName);
354
355          // unlock user
356          u.Locked = true;
357          db.SubmitChanges();
358          return true;
359        }
360        catch (Exception) {
361          return false;
362        }
363      }
364    }
365
366    public override bool UnlockUser(string userName) {
367      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
368        // check database connection
369        if (db == null) {
370          return false;
371        }
372        try {
373          // try to get user
374          HeuristicLabUser u =
375            db.HeuristicLabUsers.Single<HeuristicLabUser>(x => x.UserName == userName);
376
377          // unlock user
378          u.Locked = false;
379          db.SubmitChanges();
380          return true;
381        }
382        catch (Exception) {
383          return false;
384        }
385      }
386    }
387
388    public override void UpdateUser(MembershipUser user) {
389      throw new NotImplementedException();
390    }
391    /// <summary>
392    /// Validates a user
393    /// </summary>
394    /// <param name="username"></param>
395    /// <param name="password"></param>
396    /// <returns></returns>
397    public override bool ValidateUser(string username, string password) {
398      bool isValid = false;
399      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
400        if (db == null) {
401          return false;
402        }
403        HeuristicLabUser u = db.HeuristicLabUsers.Single(x => x.UserName == username);
404        isValid = CheckPassword(password, u.Password);
405      }
406      return isValid;
407    }
408
409    /// <summary>
410    /// compaiers to passwords
411    /// </summary>
412    /// <param name="password"></param>
413    /// <param name="dbpassword"></param>
414    /// <returns></returns>
415    private bool CheckPassword(string password, string dbpassword) {
416      string pass1 = password;
417      string pass2 = dbpassword;
418
419      switch (PasswordFormat) {
420        case MembershipPasswordFormat.Encrypted:
421          pass2 = DecodePassword(dbpassword);
422          break;
423        case MembershipPasswordFormat.Hashed:
424          pass1 = EncodePassword(password);
425          break;
426        default:
427          break;
428      }
429
430      if (pass1 == pass2) {
431        return true;
432      }
433
434      return false;
435    }
436
437
438    /// <summary>
439    /// Encodes a password.
440    /// Public because needed for unit testing.
441    /// </summary>
442    /// <param name="password"></param>
443    /// <returns></returns>
444    public string EncodePassword(string password) {
445      string encodedPassword = password;
446
447      switch (PasswordFormat) {
448        case MembershipPasswordFormat.Clear:
449          break;
450        case MembershipPasswordFormat.Encrypted:
451          encodedPassword =
452            Convert.ToBase64String(EncryptPassword(Encoding.Unicode.GetBytes(password)));
453          break;
454        case MembershipPasswordFormat.Hashed:
455          SHA512 sha512 = SHA512.Create();
456          ASCIIEncoding encoder = new ASCIIEncoding();
457          byte[] combined = encoder.GetBytes(password);
458          sha512.ComputeHash(combined);
459          encodedPassword = Convert.ToBase64String(sha512.Hash);
460          break;
461        default:
462          throw new ProviderException("Unsupported password format.");
463      }
464
465      return encodedPassword;
466    }
467
468    private readonly byte[] _rgbKey = new byte[]
469                           {
470                             182, 140, 37, 101, 52, 157, 80, 17, 65, 35, 130, 208, 101, 68, 161, 45, 197, 102, 112, 190,
471                             187, 177, 37, 76, 63, 38, 190, 117, 247, 122, 94, 17
472                           };
473    private readonly byte[] _rgbIv = new byte[] { 60, 121, 178, 142, 50, 160, 226, 84, 41, 66, 158, 180, 26, 232, 42, 113 };
474
475    protected override byte[] EncryptPassword(byte[] password) {
476      SymmetricAlgorithm sa = Aes.Create();
477      MemoryStream msEncrypt = new MemoryStream();
478      CryptoStream csEncrypt = new CryptoStream(msEncrypt, sa.CreateEncryptor(_rgbKey, _rgbIv), CryptoStreamMode.Write);
479      csEncrypt.Write(password, 0, password.Length);
480      csEncrypt.Close();
481      byte[] encryptedTextBytes = msEncrypt.ToArray();
482      msEncrypt.Close();
483      return encryptedTextBytes;
484    }
485
486    protected override byte[] DecryptPassword(byte[] encodedPassword) {
487      SymmetricAlgorithm sa = Aes.Create();
488      MemoryStream msDecrypt = new MemoryStream(encodedPassword);
489      CryptoStream csDecrypt = new CryptoStream(msDecrypt, sa.CreateDecryptor(_rgbKey, _rgbIv), CryptoStreamMode.Read);
490      byte[] decryptedTextBytes = new Byte[encodedPassword.Length];
491      csDecrypt.Read(decryptedTextBytes, 0, encodedPassword.Length);
492      csDecrypt.Close();
493      msDecrypt.Close();
494      return decryptedTextBytes;
495    }
496
497    /// <summary>
498    /// Decodes a encoded Password
499    /// </summary>
500    /// <param name="encodedPassword"></param>
501    /// <returns></returns>
502    private string DecodePassword(string encodedPassword) {
503      string password = encodedPassword;
504
505      switch (PasswordFormat) {
506        case MembershipPasswordFormat.Clear:
507          break;
508        case MembershipPasswordFormat.Encrypted:
509          password =
510            Encoding.Unicode.GetString(DecryptPassword(Convert.FromBase64String(password))).TrimEnd('\0');
511
512
513          break;
514        case MembershipPasswordFormat.Hashed:
515          throw new ProviderException("Cannot unencode a hashed password.");
516        default:
517          throw new ProviderException("Unsupported password format.");
518      }
519
520      return password;
521    }
522
523    /// <summary>
524    /// returns the configuration string, if the value is null or empty the default value is returned
525    /// </summary>
526    /// <param name="configValue"></param>
527    /// <param name="defaultValue"></param>
528    /// <returns></returns>
529    private string GetConfigValue(string configValue, string defaultValue) {
530      if (String.IsNullOrEmpty(configValue))
531        return defaultValue;
532
533      return configValue;
534    }
535  }
536}
Note: See TracBrowser for help on using the repository browser.