Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 4007 was 4007, checked in by dkahn, 14 years ago

#1061 Moved a paging into a dedicated method to prevent redundancy

File size: 19.0 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    public override bool ChangePassword(string username, string oldPassword, string newPassword) {
129      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
130        // check database connection
131        if (db == null) {
132          return false;
133        }
134
135        if (newPassword.Length < MinRequiredPasswordLength) {
136          return false;
137        }
138
139        try {
140          // try to get user
141          HeuristicLabUser u = db.HeuristicLabUsers.Single(x => x.UserName == username);
142
143          if (u.Password == EncodePassword(oldPassword) && newPassword.Length > 0) {
144            u.Password = EncodePassword(newPassword);
145            db.SubmitChanges();
146            return true;
147          }
148          return false;
149
150
151        }
152        catch (Exception) {
153          return false;
154        }
155      }
156    }
157
158    public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer) {
159      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
160
161        if (db.HeuristicLabUsers.Count(x => x.UserName == username) > 0 && newPasswordQuestion.Length > 0 && newPasswordAnswer.Length > 0) {
162          HeuristicLabUser u = db.HeuristicLabUsers.Single(x => x.UserName == username);
163          u.PasswordAnswer = newPasswordAnswer;
164          u.PasswordQuestion = newPasswordQuestion;
165          db.SubmitChanges();
166          return true;
167        }
168        return false;
169      }
170    }
171
172    public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) {
173      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
174        // check database connection
175        if (db == null) {
176          status = MembershipCreateStatus.ProviderError;
177          return null;
178        }
179        try {
180          // check for duplicate entries
181          if (db.HeuristicLabUsers.Count(x => x.UserName == username) > 0) {
182            status = MembershipCreateStatus.DuplicateUserName;
183            return null;
184          }
185          if (db.HeuristicLabUsers.Count(x => x.Email == email) > 0) {
186            status = MembershipCreateStatus.DuplicateEmail;
187            return null;
188          }
189          if (password.Length < MinRequiredPasswordLength) {
190            status = MembershipCreateStatus.InvalidPassword;
191            return null;
192          }
193
194          // create new user
195          HeuristicLabUser u = new HeuristicLabUser(username, email, passwordQuestion, "");
196          password = EncodePassword(password);
197          u.Password = password;
198          u.PasswordAnswer = passwordAnswer;
199          u.PasswordQuestion = passwordQuestion;
200          // save user into database
201          db.HeuristicLabUsers.InsertOnSubmit(u);
202          db.SubmitChanges();
203
204          // success
205          status = MembershipCreateStatus.Success;
206          return u.getMembershipUser(this.Name);
207        }
208        catch (Exception) {
209          // error
210          status = MembershipCreateStatus.ProviderError;
211          return null;
212        }
213      }
214    }
215
216    public override bool DeleteUser(string username, bool deleteAllRelatedData) {
217      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
218        // check database connection
219        if (db == null) {
220          return false;
221        }
222        try {
223          // try to get user
224          HeuristicLabUser u =
225            db.HeuristicLabUsers.Single<HeuristicLabUser>(x => x.UserName == username);
226
227          // optionally delete related data
228          if (deleteAllRelatedData) {
229            db.HeuristicLabUserRole.DeleteAllOnSubmit<HeuristicLabUserRole>(u.HeuristicLabUserRoles);
230          }
231
232          // delete user
233          db.HeuristicLabUsers.DeleteOnSubmit(u);
234          db.SubmitChanges();
235          return true;
236        }
237        catch (Exception) {
238          return false;
239        }
240      }
241    }
242
243
244
245    public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords) {
246      throw new NotImplementedException();
247      totalRecords = 0;
248      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
249        // don't test db - throw an exception instead if something wents wrong
250        // bail out if there are no records
251        if (0 == (totalRecords = db.HeuristicLabUsers.Count<HeuristicLabUser>())) return new MembershipUserCollection();
252
253        MembershipUserCollection userCollection = new MembershipUserCollection();
254
255      }
256    }
257
258
259    public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords) {
260      throw new NotImplementedException();
261    }
262
263    public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords) {
264      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
265        // orderby u.UserName
266        // skipped ordering for now as the default comparator seems to be wrong
267        IQueryable<HeuristicLabUser> users = (from u in db.HeuristicLabUsers select u);
268        return PagedCollection(users, pageIndex, pageSize, out totalRecords);
269      }
270    }
271
272    /// <summary>
273    /// Helper method that takes an IQueriable object and returns a paged part of it
274    /// </summary>
275    /// <param name="querySource"></param>
276    /// <param name="pageIndex"></param>
277    /// <param name="pageSize"></param>
278    /// <param name="totalRecords"></param>
279    /// <returns></returns>
280    private MembershipUserCollection PagedCollection(IQueryable<HeuristicLabUser> querySource, int pageIndex, int pageSize, out int totalRecords) {
281      totalRecords = querySource.Count();
282      MembershipUserCollection userCollection = new MembershipUserCollection();
283      int skip = (pageIndex == 0) ? 0 : (pageIndex * pageSize);
284      foreach (HeuristicLabUser u in querySource.Skip(skip).Take(pageSize)) {
285        userCollection.Add(u.getMembershipUser(this.Name));
286      }
287      return userCollection;
288    }
289
290    /// <summary>
291    /// not jet implemented returns 0 as default
292    /// </summary>
293    /// <returns></returns>
294    public override int GetNumberOfUsersOnline() {
295      return 0;
296    }
297
298    public override string GetPassword(string username, string answer) {
299      if (PasswordFormat == MembershipPasswordFormat.Hashed)
300        throw new NotSupportedException("Passwort is hashed");
301      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
302        HeuristicLabUser u =
303          db.HeuristicLabUsers.Single<HeuristicLabUser>(x => x.UserName == username);
304        if (EnablePasswordRetrieval && RequiresQuestionAndAnswer) {
305          if (u.PasswordAnswer.Equals(answer)) {
306            return u.Password;
307          } else {
308            throw new MembershipPasswordException();
309          }
310        } else throw new NotSupportedException("PasswortRetrival or RequiresQuestionAndAnswer not set");
311      }
312    }
313
314    public override MembershipUser GetUser(string username, bool userIsOnline) {
315      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
316        HeuristicLabUser u =
317            db.HeuristicLabUsers.Single<HeuristicLabUser>(x => x.UserName == username);
318        if (u != null)
319          return u.getMembershipUser(this.Name);
320        else
321          return null;
322      }
323    }
324
325    public override MembershipUser GetUser(object providerUserKey, bool userIsOnline) {
326      long ID = providerUserKey is long ? (long)providerUserKey : -1;
327      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
328        HeuristicLabUser u =
329            db.HeuristicLabUsers.Single<HeuristicLabUser>(x => x.ID == ID);
330        if (u != null)
331          return u.getMembershipUser(this.Name);
332        else
333          return null;
334      }
335    }
336
337    public override string GetUserNameByEmail(string email) {
338      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
339        HeuristicLabUser u =
340          db.HeuristicLabUsers.Single<HeuristicLabUser>(x => x.Email == email);
341        if (u != null)
342          return u.UserName;
343        else
344          return null;
345      }
346    }
347
348    public override string ResetPassword(string username, string answer) {
349      throw new NotSupportedException("Restet password not avaliable");
350    }
351
352    public bool LockUser(string userName) {
353      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
354        // check database connection
355        if (db == null) {
356          return false;
357        }
358        try {
359          // try to get user
360          HeuristicLabUser u =
361            db.HeuristicLabUsers.Single<HeuristicLabUser>(x => x.UserName == userName);
362
363          // unlock user
364          u.Locked = true;
365          db.SubmitChanges();
366          return true;
367        }
368        catch (Exception) {
369          return false;
370        }
371      }
372    }
373
374    public override bool UnlockUser(string userName) {
375      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
376        // check database connection
377        if (db == null) {
378          return false;
379        }
380        try {
381          // try to get user
382          HeuristicLabUser u =
383            db.HeuristicLabUsers.Single<HeuristicLabUser>(x => x.UserName == userName);
384
385          // unlock user
386          u.Locked = false;
387          db.SubmitChanges();
388          return true;
389        }
390        catch (Exception) {
391          return false;
392        }
393      }
394    }
395
396    public override void UpdateUser(MembershipUser user) {
397      throw new NotImplementedException();
398    }
399    /// <summary>
400    /// Validates a user
401    /// </summary>
402    /// <param name="username"></param>
403    /// <param name="password"></param>
404    /// <returns></returns>
405    public override bool ValidateUser(string username, string password) {
406      bool isValid = false;
407      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
408        if (db == null) {
409          return false;
410        }
411        HeuristicLabUser u = db.HeuristicLabUsers.Single(x => x.UserName == username);
412        isValid = CheckPassword(password, u.Password);
413      }
414      return isValid;
415    }
416
417    /// <summary>
418    /// compaiers to passwords
419    /// </summary>
420    /// <param name="password"></param>
421    /// <param name="dbpassword"></param>
422    /// <returns></returns>
423    private bool CheckPassword(string password, string dbpassword) {
424      string pass1 = password;
425      string pass2 = dbpassword;
426
427      switch (PasswordFormat) {
428        case MembershipPasswordFormat.Encrypted:
429          pass2 = DecodePassword(dbpassword);
430          break;
431        case MembershipPasswordFormat.Hashed:
432          pass1 = EncodePassword(password);
433          break;
434        default:
435          break;
436      }
437
438      if (pass1 == pass2) {
439        return true;
440      }
441
442      return false;
443    }
444
445
446    /// <summary>
447    /// Encodes a password.
448    /// Public because needed for unit testing.
449    /// </summary>
450    /// <param name="password"></param>
451    /// <returns></returns>
452    public string EncodePassword(string password) {
453      string encodedPassword = password;
454
455      switch (PasswordFormat) {
456        case MembershipPasswordFormat.Clear:
457          break;
458        case MembershipPasswordFormat.Encrypted:
459          encodedPassword =
460            Convert.ToBase64String(EncryptPassword(Encoding.Unicode.GetBytes(password)));
461          break;
462        case MembershipPasswordFormat.Hashed:
463          SHA512 sha512 = SHA512.Create();
464          ASCIIEncoding encoder = new ASCIIEncoding();
465          byte[] combined = encoder.GetBytes(password);
466          sha512.ComputeHash(combined);
467          encodedPassword = Convert.ToBase64String(sha512.Hash);
468          break;
469        default:
470          throw new ProviderException("Unsupported password format.");
471      }
472
473      return encodedPassword;
474    }
475
476    private readonly byte[] _rgbKey = new byte[]
477                           {
478                             182, 140, 37, 101, 52, 157, 80, 17, 65, 35, 130, 208, 101, 68, 161, 45, 197, 102, 112, 190,
479                             187, 177, 37, 76, 63, 38, 190, 117, 247, 122, 94, 17
480                           };
481    private readonly byte[] _rgbIv = new byte[] { 60, 121, 178, 142, 50, 160, 226, 84, 41, 66, 158, 180, 26, 232, 42, 113 };
482
483    protected override byte[] EncryptPassword(byte[] password) {
484      SymmetricAlgorithm sa = Aes.Create();
485      MemoryStream msEncrypt = new MemoryStream();
486      CryptoStream csEncrypt = new CryptoStream(msEncrypt, sa.CreateEncryptor(_rgbKey, _rgbIv), CryptoStreamMode.Write);
487      csEncrypt.Write(password, 0, password.Length);
488      csEncrypt.Close();
489      byte[] encryptedTextBytes = msEncrypt.ToArray();
490      msEncrypt.Close();
491      return encryptedTextBytes;
492    }
493
494    protected override byte[] DecryptPassword(byte[] encodedPassword) {
495      SymmetricAlgorithm sa = Aes.Create();
496      MemoryStream msDecrypt = new MemoryStream(encodedPassword);
497      CryptoStream csDecrypt = new CryptoStream(msDecrypt, sa.CreateDecryptor(_rgbKey, _rgbIv), CryptoStreamMode.Read);
498      byte[] decryptedTextBytes = new Byte[encodedPassword.Length];
499      csDecrypt.Read(decryptedTextBytes, 0, encodedPassword.Length);
500      csDecrypt.Close();
501      msDecrypt.Close();
502      return decryptedTextBytes;
503    }
504
505    /// <summary>
506    /// Decodes a encoded Password
507    /// </summary>
508    /// <param name="encodedPassword"></param>
509    /// <returns></returns>
510    private string DecodePassword(string encodedPassword) {
511      string password = encodedPassword;
512
513      switch (PasswordFormat) {
514        case MembershipPasswordFormat.Clear:
515          break;
516        case MembershipPasswordFormat.Encrypted:
517          password =
518            Encoding.Unicode.GetString(DecryptPassword(Convert.FromBase64String(password))).TrimEnd('\0');
519
520
521          break;
522        case MembershipPasswordFormat.Hashed:
523          throw new ProviderException("Cannot unencode a hashed password.");
524        default:
525          throw new ProviderException("Unsupported password format.");
526      }
527
528      return password;
529    }
530
531    /// <summary>
532    /// returns the configuration string, if the value is null or empty the default value is returned
533    /// </summary>
534    /// <param name="configValue"></param>
535    /// <param name="defaultValue"></param>
536    /// <returns></returns>
537    private string GetConfigValue(string configValue, string defaultValue) {
538      if (String.IsNullOrEmpty(configValue))
539        return defaultValue;
540
541      return configValue;
542    }
543  }
544}
Note: See TracBrowser for help on using the repository browser.