Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 4004 was 4004, checked in by bfarka, 14 years ago

fixed min passwordlength test #1046

File size: 16.8 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
292      throw new NotImplementedException();
293    }
294
295    public override MembershipUser GetUser(string username, bool userIsOnline) {
296      throw new NotImplementedException();
297    }
298
299    public override MembershipUser GetUser(object providerUserKey, bool userIsOnline) {
300      throw new NotImplementedException();
301    }
302
303    public override string GetUserNameByEmail(string email) {
304      throw new NotImplementedException();
305    }
306
307
308
309    public override string ResetPassword(string username, string answer) {
310      throw new NotImplementedException();
311    }
312
313    public bool LockUser(string userName) {
314      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
315        // check database connection
316        if (db == null) {
317          return false;
318        }
319        try {
320          // try to get user
321          HeuristicLabUser u =
322            db.HeuristicLabUsers.Single<HeuristicLabUser>(x => x.UserName == userName);
323
324          // unlock user
325          u.Locked = true;
326          db.SubmitChanges();
327          return true;
328        }
329        catch (Exception) {
330          return false;
331        }
332      }
333    }
334
335    public override bool UnlockUser(string userName) {
336      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
337        // check database connection
338        if (db == null) {
339          return false;
340        }
341        try {
342          // try to get user
343          HeuristicLabUser u =
344            db.HeuristicLabUsers.Single<HeuristicLabUser>(x => x.UserName == userName);
345
346          // unlock user
347          u.Locked = false;
348          db.SubmitChanges();
349          return true;
350        }
351        catch (Exception) {
352          return false;
353        }
354      }
355    }
356
357    public override void UpdateUser(MembershipUser user) {
358      throw new NotImplementedException();
359    }
360    /// <summary>
361    /// Validates a user
362    /// </summary>
363    /// <param name="username"></param>
364    /// <param name="password"></param>
365    /// <returns></returns>
366    public override bool ValidateUser(string username, string password) {
367      bool isValid = false;
368      using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
369        if (db == null) {
370          return false;
371        }
372        HeuristicLabUser u = db.HeuristicLabUsers.Single(x => x.UserName == username);
373        isValid = CheckPassword(password, u.Password);
374      }
375      return isValid;
376    }
377
378    /// <summary>
379    /// compaiers to passwords
380    /// </summary>
381    /// <param name="password"></param>
382    /// <param name="dbpassword"></param>
383    /// <returns></returns>
384    private bool CheckPassword(string password, string dbpassword) {
385      string pass1 = password;
386      string pass2 = dbpassword;
387
388      switch (PasswordFormat) {
389        case MembershipPasswordFormat.Encrypted:
390          pass2 = DecodePassword(dbpassword);
391          break;
392        case MembershipPasswordFormat.Hashed:
393          pass1 = EncodePassword(password);
394          break;
395        default:
396          break;
397      }
398
399      if (pass1 == pass2) {
400        return true;
401      }
402
403      return false;
404    }
405
406
407    /// <summary>
408    /// Encodes a password.
409    /// Public because needed for unit testing.
410    /// </summary>
411    /// <param name="password"></param>
412    /// <returns></returns>
413    public string EncodePassword(string password) {
414      string encodedPassword = password;
415
416      switch (PasswordFormat) {
417        case MembershipPasswordFormat.Clear:
418          break;
419        case MembershipPasswordFormat.Encrypted:
420          encodedPassword =
421            Convert.ToBase64String(EncryptPassword(Encoding.Unicode.GetBytes(password)));
422          break;
423        case MembershipPasswordFormat.Hashed:
424          SHA512 sha512 = SHA512.Create();
425          ASCIIEncoding encoder = new ASCIIEncoding();
426          byte[] combined = encoder.GetBytes(password);
427          sha512.ComputeHash(combined);
428          encodedPassword = Convert.ToBase64String(sha512.Hash);
429          break;
430        default:
431          throw new ProviderException("Unsupported password format.");
432      }
433
434      return encodedPassword;
435    }
436
437    private readonly byte[] _rgbKey = new byte[]
438                           {
439                             182, 140, 37, 101, 52, 157, 80, 17, 65, 35, 130, 208, 101, 68, 161, 45, 197, 102, 112, 190,
440                             187, 177, 37, 76, 63, 38, 190, 117, 247, 122, 94, 17
441                           };
442    private readonly byte[] _rgbIv = new byte[] { 60, 121, 178, 142, 50, 160, 226, 84, 41, 66, 158, 180, 26, 232, 42, 113 };
443
444    protected override byte[] EncryptPassword(byte[] password) {
445      SymmetricAlgorithm sa = Aes.Create();
446      MemoryStream msEncrypt = new MemoryStream();
447      CryptoStream csEncrypt = new CryptoStream(msEncrypt, sa.CreateEncryptor(_rgbKey, _rgbIv), CryptoStreamMode.Write);
448      csEncrypt.Write(password, 0, password.Length);
449      csEncrypt.Close();
450      byte[] encryptedTextBytes = msEncrypt.ToArray();
451      msEncrypt.Close();
452      return encryptedTextBytes;
453    }
454
455    protected override byte[] DecryptPassword(byte[] encodedPassword) {
456      SymmetricAlgorithm sa = Aes.Create();
457      MemoryStream msDecrypt = new MemoryStream(encodedPassword);
458      CryptoStream csDecrypt = new CryptoStream(msDecrypt, sa.CreateDecryptor(_rgbKey, _rgbIv), CryptoStreamMode.Read);
459      byte[] decryptedTextBytes = new Byte[encodedPassword.Length];
460      csDecrypt.Read(decryptedTextBytes, 0, encodedPassword.Length);
461      csDecrypt.Close();
462      msDecrypt.Close();
463      return decryptedTextBytes;
464    }
465
466    /// <summary>
467    /// Decodes a encoded Password
468    /// </summary>
469    /// <param name="encodedPassword"></param>
470    /// <returns></returns>
471    private string DecodePassword(string encodedPassword) {
472      string password = encodedPassword;
473
474      switch (PasswordFormat) {
475        case MembershipPasswordFormat.Clear:
476          break;
477        case MembershipPasswordFormat.Encrypted:
478          password =
479            Encoding.Unicode.GetString(DecryptPassword(Convert.FromBase64String(password))).TrimEnd('\0');
480
481
482          break;
483        case MembershipPasswordFormat.Hashed:
484          throw new ProviderException("Cannot unencode a hashed password.");
485        default:
486          throw new ProviderException("Unsupported password format.");
487      }
488
489      return password;
490    }
491
492    /// <summary>
493    /// returns the configuration string, if the value is null or empty the default value is returned
494    /// </summary>
495    /// <param name="configValue"></param>
496    /// <param name="defaultValue"></param>
497    /// <returns></returns>
498    private string GetConfigValue(string configValue, string defaultValue) {
499      if (String.IsNullOrEmpty(configValue))
500        return defaultValue;
501
502      return configValue;
503    }
504  }
505}
Note: See TracBrowser for help on using the repository browser.