UI tweaks, Code style amends.
parent
3b27d9be75
commit
9ef52f1757
|
@ -4,15 +4,15 @@ namespace BuildFeed
|
|||
{
|
||||
internal static class MongoConfig
|
||||
{
|
||||
public static string Host { get; private set; }
|
||||
public static int Port { get; private set; }
|
||||
public static string Database { get; private set; }
|
||||
public static string Host { get; }
|
||||
public static int Port { get; }
|
||||
public static string Database { get; }
|
||||
|
||||
static MongoConfig()
|
||||
{
|
||||
Host = !string.IsNullOrEmpty(ConfigurationManager.AppSettings["data:MongoHost"])
|
||||
? ConfigurationManager.AppSettings["data:MongoHost"]
|
||||
: "localhost";
|
||||
Host = !string.IsNullOrEmpty(ConfigurationManager.AppSettings["data:MongoHost"]) ?
|
||||
ConfigurationManager.AppSettings["data:MongoHost"] :
|
||||
"localhost";
|
||||
|
||||
int _port;
|
||||
bool success = int.TryParse(ConfigurationManager.AppSettings["data:MongoPort"], out _port);
|
||||
|
@ -22,9 +22,9 @@ namespace BuildFeed
|
|||
}
|
||||
Port = _port;
|
||||
|
||||
Database = !string.IsNullOrEmpty(ConfigurationManager.AppSettings["data:MongoDB"])
|
||||
? ConfigurationManager.AppSettings["data:MongoDB"]
|
||||
: "MongoAuth";
|
||||
Database = !string.IsNullOrEmpty(ConfigurationManager.AppSettings["data:MongoDB"]) ?
|
||||
ConfigurationManager.AppSettings["data:MongoDB"] :
|
||||
"MongoAuth";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,16 +9,18 @@ namespace BuildFeed
|
|||
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
|
||||
{
|
||||
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
|
||||
|
||||
DateTime retValue;
|
||||
bool success = DateTime.TryParse(value.AttemptedValue, CultureInfo.CurrentUICulture.DateTimeFormat, DateTimeStyles.AllowWhiteSpaces, out retValue);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
success = DateTime.TryParseExact(value.AttemptedValue, "yyMMdd-HHmm", CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out retValue);
|
||||
}
|
||||
|
||||
return success
|
||||
? retValue as DateTime?
|
||||
: null;
|
||||
return success ?
|
||||
retValue as DateTime? :
|
||||
null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@ namespace BuildFeed.Code
|
|||
CultureInfo.CurrentCulture = ci;
|
||||
CultureInfo.CurrentUICulture = ci;
|
||||
}
|
||||
catch(CultureNotFoundException cnex)
|
||||
catch(CultureNotFoundException)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
@using System.Globalization;
|
||||
@{
|
||||
bool IsRTL = System.Globalization.CultureInfo.CurrentUICulture.TextInfo.IsRightToLeft;
|
||||
bool IsRTL = CultureInfo.CurrentUICulture.TextInfo.IsRightToLeft;
|
||||
}
|
||||
<!DOCTYPE html>
|
||||
<html dir="@(IsRTL ? "rtl" : "ltr")">
|
||||
<html dir="@(IsRTL ? "rtl" : "ltr")" lang="@CultureInfo.CurrentUICulture.TwoLetterISOLanguageName">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<link href="//maxcdn.bootstrapcdn.com/bootswatch/3.3.5/slate/bootstrap.min.css" rel="stylesheet" />
|
||||
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css" rel="stylesheet" />
|
||||
<link href="//fonts.googleapis.com/css?family=Hind:300,700,400&subset=latin,latin-ext" rel="stylesheet" type="text/css" />
|
||||
<link href="//fonts.googleapis.com/css?family=Rajdhani:400,700&subset=latin,latin-ext" rel="stylesheet" type="text/css" />
|
||||
<link rel="shortcut icon" href="~/favicon.ico" />
|
||||
<link rel="icon" href="~/favicon.ico" />
|
||||
<link rel="canonical" href="@Url.Action()" />
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<link href="//maxcdn.bootstrapcdn.com/bootswatch/3.3.5/slate/bootstrap.min.css" rel="stylesheet" />
|
||||
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css" rel="stylesheet" />
|
||||
<link href="//fonts.googleapis.com/css?family=Hind:300,700,400" rel="stylesheet" type="text/css" />
|
||||
<link href="//fonts.googleapis.com/css?family=Rajdhani:400,700&subset=latin,latin-ext" rel="stylesheet" type="text/css" />
|
||||
<link rel="shortcut icon" href="~/favicon.ico" />
|
||||
<link rel="icon" href="~/favicon.ico" />
|
||||
<meta name="application-name" content="@BuildFeed.Local.Common.SiteName" />
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
Html.EnableUnobtrusiveJavaScript();
|
||||
}
|
||||
|
||||
<h2>@BuildFeed.Local.Support.Login</h2>
|
||||
<h1>@BuildFeed.Local.Support.Login</h1>
|
||||
|
||||
@using (Html.BeginForm())
|
||||
{
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
Html.EnableUnobtrusiveJavaScript();
|
||||
}
|
||||
|
||||
<h2>@BuildFeed.Local.Support.ChangePassword</h2>
|
||||
<h1>@BuildFeed.Local.Support.ChangePassword</h1>
|
||||
|
||||
@using (Html.BeginForm())
|
||||
{
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
Html.EnableUnobtrusiveJavaScript();
|
||||
}
|
||||
|
||||
<h2>@BuildFeed.Local.Support.Register</h2>
|
||||
<h1>@BuildFeed.Local.Support.Register</h1>
|
||||
|
||||
@using (Html.BeginForm())
|
||||
{
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
ViewBag.Title = string.Format("{0} | {1}", BuildFeed.Local.Common.RssFeeds, BuildFeed.Local.Common.SiteName);
|
||||
}
|
||||
|
||||
<h2>@BuildFeed.Local.Common.RssFeeds</h2>
|
||||
<h1>@BuildFeed.Local.Common.RssFeeds</h1>
|
||||
|
||||
<ul>
|
||||
<li><a href="@Url.Action("index", new { controller = "rss" })" title="@BuildFeed.Local.Support.RecentlyCompiled"><i class="fa fa-sm fa-rss"></i> @BuildFeed.Local.Support.RecentlyCompiled</a></li>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
ViewBag.Title = string.Format("{0} | {1}", BuildFeed.Local.Common.Sitemap, BuildFeed.Local.Common.SiteName);
|
||||
}
|
||||
|
||||
<h2>@BuildFeed.Local.Common.Sitemap</h2>
|
||||
<h1>@BuildFeed.Local.Common.Sitemap</h1>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
ViewBag.Title = string.Format("{0} | {1}", BuildFeed.Local.Common.Statistics, BuildFeed.Local.Common.SiteName);
|
||||
}
|
||||
|
||||
<h2>@BuildFeed.Local.Common.Statistics</h2>
|
||||
<h1>@BuildFeed.Local.Common.Statistics</h1>
|
||||
|
||||
<h4>@BuildFeed.Local.Support.StatsNewAdditions</h4>
|
||||
<canvas id="stats-addition" width="960" height="320"></canvas>
|
||||
|
|
|
@ -3,5 +3,5 @@
|
|||
ViewBag.Title = string.Format("{0} | {1}", BuildFeed.Local.Support.ThanksRegister, BuildFeed.Local.Common.SiteName);
|
||||
}
|
||||
|
||||
<h2>@BuildFeed.Local.Support.ThanksRegister</h2>
|
||||
<h1>@BuildFeed.Local.Support.ThanksRegister</h1>
|
||||
<p>@BuildFeed.Local.Support.AccountValidation</p>
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
body, h1, h2, h3
|
||||
{
|
||||
font-family: 'Hind', sans-serif;
|
||||
font-family: 'Rajdhani', sans-serif;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
h1
|
||||
{
|
||||
font-size: 48px;
|
||||
font-weight: 300;
|
||||
font-size: 56px;
|
||||
}
|
||||
|
||||
h1 a
|
||||
|
@ -27,8 +27,7 @@ h1
|
|||
|
||||
.build-group-title
|
||||
{
|
||||
font-size: 24px;
|
||||
font-weight: 300;
|
||||
font-size: 25px;
|
||||
margin: 0 0 4px 0;
|
||||
}
|
||||
|
||||
|
@ -41,7 +40,6 @@ h1
|
|||
{
|
||||
-ms-text-overflow: ellipsis;
|
||||
-o-text-overflow: ellipsis;
|
||||
-moz-text-overflow: ellipsis;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
@ -58,10 +56,21 @@ h1
|
|||
}
|
||||
|
||||
.form-details label
|
||||
{
|
||||
font-size: 16px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.form-details .form-control-static
|
||||
{
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.form-details .form-control-static a.more-link
|
||||
{
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.credits-list dd + dt
|
||||
{
|
||||
margin-top: 1.5em;
|
||||
|
@ -69,7 +78,7 @@ h1
|
|||
|
||||
#page-footer
|
||||
{
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
margin-top: 3em;
|
||||
}
|
||||
|
||||
|
@ -88,6 +97,11 @@ label, .control-label, .help-block, .checkbox, .radio
|
|||
padding: 9px 0 7px;
|
||||
}
|
||||
|
||||
.btn-xs
|
||||
{
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.table .btn
|
||||
{
|
||||
padding: 4px 9px;
|
||||
|
@ -130,7 +144,6 @@ label, .control-label, .help-block, .checkbox, .radio
|
|||
overflow: hidden;
|
||||
-ms-text-overflow: ellipsis;
|
||||
-o-text-overflow: ellipsis;
|
||||
-moz-text-overflow: ellipsis;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
|
|
@ -2,25 +2,29 @@
|
|||
|
||||
namespace MongoAuth
|
||||
{
|
||||
internal static class DatabaseConfig
|
||||
{
|
||||
public static string Host { get; private set; }
|
||||
public static int Port { get; private set; }
|
||||
public static string Database { get; private set; }
|
||||
internal static class DatabaseConfig
|
||||
{
|
||||
public static string Host { get; }
|
||||
public static int Port { get; }
|
||||
public static string Database { get; }
|
||||
|
||||
static DatabaseConfig()
|
||||
{
|
||||
Host = !string.IsNullOrEmpty(ConfigurationManager.AppSettings["data:MongoHost"]) ? ConfigurationManager.AppSettings["data:MongoHost"] : "localhost";
|
||||
static DatabaseConfig()
|
||||
{
|
||||
Host = !string.IsNullOrEmpty(ConfigurationManager.AppSettings["data:MongoHost"]) ?
|
||||
ConfigurationManager.AppSettings["data:MongoHost"] :
|
||||
"localhost";
|
||||
|
||||
int _port;
|
||||
bool success = int.TryParse(ConfigurationManager.AppSettings["data:MongoPort"], out _port);
|
||||
if (!success)
|
||||
{
|
||||
_port = 27017; // mongo default port
|
||||
}
|
||||
Port = _port;
|
||||
int _port;
|
||||
bool success = int.TryParse(ConfigurationManager.AppSettings["data:MongoPort"], out _port);
|
||||
if (!success)
|
||||
{
|
||||
_port = 27017; // mongo default port
|
||||
}
|
||||
Port = _port;
|
||||
|
||||
Database = !string.IsNullOrEmpty(ConfigurationManager.AppSettings["data:MongoDB"]) ? ConfigurationManager.AppSettings["data:MongoDB"] : "MongoAuth";
|
||||
}
|
||||
}
|
||||
Database = !string.IsNullOrEmpty(ConfigurationManager.AppSettings["data:MongoDB"]) ?
|
||||
ConfigurationManager.AppSettings["data:MongoDB"] :
|
||||
"MongoAuth";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,470 +12,434 @@ using System.Web.Security;
|
|||
|
||||
namespace MongoAuth
|
||||
{
|
||||
public class MongoMembershipProvider : MembershipProvider
|
||||
{
|
||||
private const string _memberCollectionName = "members";
|
||||
public class MongoMembershipProvider : MembershipProvider
|
||||
{
|
||||
private const string _memberCollectionName = "members";
|
||||
|
||||
private bool _enablePasswordReset = true;
|
||||
private int _maxInvalidPasswordAttempts = 5;
|
||||
private int _minRequiredNonAlphanumericCharacters = 1;
|
||||
private int _minRequriedPasswordLength = 12;
|
||||
private int _passwordAttemptWindow = 60;
|
||||
private bool _requiresUniqueEmail = true;
|
||||
private bool _enablePasswordReset = true;
|
||||
private int _maxInvalidPasswordAttempts = 5;
|
||||
private int _minRequiredNonAlphanumericCharacters = 1;
|
||||
private int _minRequriedPasswordLength = 12;
|
||||
private int _passwordAttemptWindow = 60;
|
||||
private bool _requiresUniqueEmail = true;
|
||||
|
||||
private MongoClient _dbClient;
|
||||
private IMongoCollection<MongoMember> _memberCollection;
|
||||
private MongoClient _dbClient;
|
||||
private IMongoCollection<MongoMember> _memberCollection;
|
||||
|
||||
public override string ApplicationName { get; set; }
|
||||
public override string ApplicationName { get; set; }
|
||||
|
||||
public override bool EnablePasswordReset
|
||||
{
|
||||
get { return _enablePasswordReset; }
|
||||
}
|
||||
public override bool EnablePasswordReset => _enablePasswordReset;
|
||||
|
||||
public override bool EnablePasswordRetrieval
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
public override bool EnablePasswordRetrieval => false;
|
||||
|
||||
public override int MaxInvalidPasswordAttempts
|
||||
{
|
||||
get { return _maxInvalidPasswordAttempts; }
|
||||
}
|
||||
public override int MaxInvalidPasswordAttempts => _maxInvalidPasswordAttempts;
|
||||
|
||||
public override int MinRequiredNonAlphanumericCharacters
|
||||
{
|
||||
get { return _minRequiredNonAlphanumericCharacters; }
|
||||
}
|
||||
public override int MinRequiredNonAlphanumericCharacters => _minRequiredNonAlphanumericCharacters;
|
||||
|
||||
public override int MinRequiredPasswordLength
|
||||
{
|
||||
get { return _minRequriedPasswordLength; }
|
||||
}
|
||||
public override int MinRequiredPasswordLength => _minRequriedPasswordLength;
|
||||
|
||||
public override int PasswordAttemptWindow
|
||||
{
|
||||
get { return _passwordAttemptWindow; }
|
||||
}
|
||||
public override int PasswordAttemptWindow => _passwordAttemptWindow;
|
||||
|
||||
public override MembershipPasswordFormat PasswordFormat
|
||||
{
|
||||
get { return MembershipPasswordFormat.Hashed; }
|
||||
}
|
||||
public override MembershipPasswordFormat PasswordFormat => MembershipPasswordFormat.Hashed;
|
||||
|
||||
public override string PasswordStrengthRegularExpression
|
||||
{
|
||||
get { return ""; }
|
||||
}
|
||||
public override string PasswordStrengthRegularExpression => "";
|
||||
|
||||
public override bool RequiresQuestionAndAnswer
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
public override bool RequiresQuestionAndAnswer => false;
|
||||
|
||||
public override bool RequiresUniqueEmail
|
||||
{
|
||||
get { return _requiresUniqueEmail; }
|
||||
}
|
||||
public override bool RequiresUniqueEmail => _requiresUniqueEmail;
|
||||
|
||||
public override void Initialize(string name, NameValueCollection config)
|
||||
{
|
||||
if (config == null)
|
||||
{
|
||||
throw new ArgumentNullException("config");
|
||||
}
|
||||
public override void Initialize(string name, NameValueCollection config)
|
||||
{
|
||||
if (config == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(config));
|
||||
}
|
||||
|
||||
base.Initialize(name, config);
|
||||
base.Initialize(name, config);
|
||||
|
||||
_enablePasswordReset = tryReadBool(config["enablePasswordReset"], _enablePasswordReset);
|
||||
_maxInvalidPasswordAttempts = tryReadInt(config["maxInvalidPasswordAttempts"], _maxInvalidPasswordAttempts);
|
||||
_minRequiredNonAlphanumericCharacters = tryReadInt(config["minRequiredNonAlphanumericCharacters"], _minRequiredNonAlphanumericCharacters);
|
||||
_minRequriedPasswordLength = tryReadInt(config["minRequriedPasswordLength"], _minRequriedPasswordLength);
|
||||
_passwordAttemptWindow = tryReadInt(config["passwordAttemptWindow"], _passwordAttemptWindow);
|
||||
_requiresUniqueEmail = tryReadBool(config["requiresUniqueEmail"], _requiresUniqueEmail);
|
||||
_enablePasswordReset = tryReadBool(config["enablePasswordReset"], _enablePasswordReset);
|
||||
_maxInvalidPasswordAttempts = tryReadInt(config["maxInvalidPasswordAttempts"], _maxInvalidPasswordAttempts);
|
||||
_minRequiredNonAlphanumericCharacters = tryReadInt(config["minRequiredNonAlphanumericCharacters"], _minRequiredNonAlphanumericCharacters);
|
||||
_minRequriedPasswordLength = tryReadInt(config["minRequriedPasswordLength"], _minRequriedPasswordLength);
|
||||
_passwordAttemptWindow = tryReadInt(config["passwordAttemptWindow"], _passwordAttemptWindow);
|
||||
_requiresUniqueEmail = tryReadBool(config["requiresUniqueEmail"], _requiresUniqueEmail);
|
||||
|
||||
_dbClient = new MongoClient(new MongoClientSettings()
|
||||
{
|
||||
Server = new MongoServerAddress(DatabaseConfig.Host, DatabaseConfig.Port)
|
||||
});
|
||||
_memberCollection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection<MongoMember>(_memberCollectionName);
|
||||
}
|
||||
_dbClient = new MongoClient(new MongoClientSettings()
|
||||
{
|
||||
Server = new MongoServerAddress(DatabaseConfig.Host, DatabaseConfig.Port)
|
||||
});
|
||||
_memberCollection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection<MongoMember>(_memberCollectionName);
|
||||
}
|
||||
|
||||
public override bool ChangePassword(string username, string oldPassword, string newPassword)
|
||||
{
|
||||
bool isAuthenticated = ValidateUser(username, oldPassword);
|
||||
public override bool ChangePassword(string username, string oldPassword, string newPassword)
|
||||
{
|
||||
bool isAuthenticated = ValidateUser(username, oldPassword);
|
||||
|
||||
if (isAuthenticated)
|
||||
{
|
||||
var task = _memberCollection
|
||||
.Find(m => m.UserName.ToLower() == username.ToLower())
|
||||
.SingleOrDefaultAsync();
|
||||
task.Wait();
|
||||
var mm = task.Result;
|
||||
|
||||
if (mm == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] salt = new byte[24];
|
||||
byte[] hash = calculateHash(newPassword, ref salt);
|
||||
|
||||
mm.PassSalt = salt;
|
||||
mm.PassHash = hash;
|
||||
|
||||
var replaceTask = _memberCollection
|
||||
.ReplaceOneAsync(m => m.Id == mm.Id, mm);
|
||||
replaceTask.Wait();
|
||||
|
||||
if (replaceTask.IsCompleted)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
|
||||
{
|
||||
if (password.Length < MinRequiredPasswordLength)
|
||||
{
|
||||
status = MembershipCreateStatus.InvalidPassword;
|
||||
return null;
|
||||
}
|
||||
|
||||
MembershipUser mu = null;
|
||||
|
||||
var dupeUsers = _memberCollection
|
||||
if (isAuthenticated)
|
||||
{
|
||||
var task = _memberCollection
|
||||
.Find(m => m.UserName.ToLower() == username.ToLower())
|
||||
.CountAsync();
|
||||
var dupeEmails = _memberCollection
|
||||
.Find(m => m.EmailAddress.ToLower() == email.ToLower())
|
||||
.CountAsync();
|
||||
dupeUsers.Wait();
|
||||
dupeEmails.Wait();
|
||||
|
||||
if (dupeUsers.Result > 0)
|
||||
{
|
||||
status = MembershipCreateStatus.DuplicateUserName;
|
||||
}
|
||||
else if (dupeEmails.Result > 0)
|
||||
{
|
||||
status = MembershipCreateStatus.DuplicateEmail;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] salt = new byte[24];
|
||||
byte[] hash = calculateHash(password, ref salt);
|
||||
|
||||
MongoMember mm = new MongoMember()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserName = username,
|
||||
PassHash = hash,
|
||||
PassSalt = salt,
|
||||
EmailAddress = email,
|
||||
|
||||
IsApproved = false,
|
||||
IsLockedOut = false,
|
||||
|
||||
CreationDate = DateTime.Now,
|
||||
LastLoginDate = DateTime.MinValue,
|
||||
LastActivityDate = DateTime.MinValue,
|
||||
LastLockoutDate = DateTime.MinValue
|
||||
};
|
||||
|
||||
var insertTask = _memberCollection
|
||||
.InsertOneAsync(mm);
|
||||
insertTask.Wait();
|
||||
|
||||
if (insertTask.Status == TaskStatus.RanToCompletion)
|
||||
{
|
||||
|
||||
status = MembershipCreateStatus.Success;
|
||||
mu = new MembershipUser(this.Name, mm.UserName, mm.Id, mm.EmailAddress, "", "", mm.IsApproved, mm.IsLockedOut, mm.CreationDate, mm.LastLoginDate, mm.LastActivityDate, DateTime.MinValue, mm.LastLockoutDate);
|
||||
}
|
||||
else
|
||||
{
|
||||
status = MembershipCreateStatus.ProviderError;
|
||||
}
|
||||
}
|
||||
|
||||
return mu;
|
||||
}
|
||||
|
||||
public override bool DeleteUser(string username, bool deleteAllRelatedData)
|
||||
{
|
||||
var task = _memberCollection
|
||||
.DeleteOneAsync(m => m.UserName.ToLower() == username);
|
||||
task.Wait();
|
||||
|
||||
return task.Result.IsAcknowledged && task.Result.DeletedCount == 1;
|
||||
}
|
||||
|
||||
public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
|
||||
{
|
||||
MembershipUserCollection muc = new MembershipUserCollection();
|
||||
|
||||
var users = _memberCollection
|
||||
.Find(new BsonDocument());
|
||||
|
||||
var totalRecordsTask = users
|
||||
.CountAsync();
|
||||
totalRecordsTask.Wait();
|
||||
totalRecords = Convert.ToInt32(totalRecordsTask.Result);
|
||||
|
||||
users = users
|
||||
.Skip(pageIndex * pageSize)
|
||||
.Limit(pageSize);
|
||||
var pageItemsTask = users.ToListAsync();
|
||||
pageItemsTask.Wait();
|
||||
|
||||
foreach (var mm in pageItemsTask.Result)
|
||||
{
|
||||
muc.Add(new MembershipUser(this.Name, mm.UserName, mm.Id, mm.EmailAddress, "", "", mm.IsApproved, mm.IsLockedOut, mm.CreationDate, mm.LastLoginDate, mm.LastActivityDate, DateTime.MinValue, mm.LastLockoutDate));
|
||||
}
|
||||
|
||||
return muc;
|
||||
}
|
||||
|
||||
public override int GetNumberOfUsersOnline()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override string GetPassword(string username, string answer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override MembershipUser GetUser(string username, bool userIsOnline)
|
||||
{
|
||||
var task = _memberCollection
|
||||
.Find(f => f.UserName.ToLower() == username.ToLower())
|
||||
.FirstOrDefaultAsync();
|
||||
task.Wait();
|
||||
|
||||
var mm = task.Result;
|
||||
|
||||
return mm == null ? null : new MembershipUser(this.Name, mm.UserName, mm.Id, mm.EmailAddress, "", "", mm.IsApproved, mm.IsLockedOut, mm.CreationDate, mm.LastLoginDate, mm.LastActivityDate, DateTime.MinValue, mm.LastLockoutDate);
|
||||
}
|
||||
|
||||
public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
|
||||
{
|
||||
var task = _memberCollection
|
||||
.Find(f => f.Id == (Guid)providerUserKey)
|
||||
.FirstOrDefaultAsync();
|
||||
task.Wait();
|
||||
|
||||
var mm = task.Result;
|
||||
|
||||
return mm == null ? null : new MembershipUser(this.Name, mm.UserName, mm.Id, mm.EmailAddress, "", "", mm.IsApproved, mm.IsLockedOut, mm.CreationDate, mm.LastLoginDate, mm.LastActivityDate, DateTime.MinValue, mm.LastLockoutDate);
|
||||
}
|
||||
|
||||
public override string GetUserNameByEmail(string email)
|
||||
{
|
||||
var task = _memberCollection
|
||||
.Find(f => f.EmailAddress.ToLower() == email.ToLower())
|
||||
.FirstOrDefaultAsync();
|
||||
task.Wait();
|
||||
|
||||
return task.Result.UserName;
|
||||
}
|
||||
|
||||
public override string ResetPassword(string username, string answer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ChangeApproval(Guid Id, bool newStatus)
|
||||
{
|
||||
var task = _memberCollection
|
||||
.UpdateOneAsync(
|
||||
Builders<MongoMember>.Filter.Eq(u => u.Id, Id),
|
||||
Builders<MongoMember>.Update.Set(u => u.IsApproved, newStatus));
|
||||
task.Wait();
|
||||
}
|
||||
|
||||
public void ChangeLockStatus(Guid Id, bool newStatus)
|
||||
{
|
||||
var updateDefinition = new List<UpdateDefinition<MongoMember>>();
|
||||
updateDefinition.Add(Builders<MongoMember>.Update.Set(u => u.IsLockedOut, newStatus));
|
||||
|
||||
if (newStatus)
|
||||
{
|
||||
updateDefinition.Add(Builders<MongoMember>.Update.Set(u => u.LastLockoutDate, DateTime.Now));
|
||||
}
|
||||
else
|
||||
{
|
||||
updateDefinition.Add(Builders<MongoMember>.Update.Set(u => u.LockoutWindowAttempts, 0));
|
||||
updateDefinition.Add(Builders<MongoMember>.Update.Set(u => u.LastLockoutDate, DateTime.MinValue));
|
||||
}
|
||||
|
||||
var task = _memberCollection
|
||||
.UpdateOneAsync(
|
||||
Builders<MongoMember>.Filter.Eq(u => u.Id, Id),
|
||||
Builders<MongoMember>.Update.Combine(updateDefinition));
|
||||
task.Wait();
|
||||
}
|
||||
|
||||
public override bool UnlockUser(string userName)
|
||||
{
|
||||
var task = _memberCollection
|
||||
.UpdateOneAsync(
|
||||
Builders<MongoMember>.Filter.Eq(m => m.UserName.ToLower(), userName.ToLower()),
|
||||
Builders<MongoMember>.Update.Set(m => m.IsLockedOut,
|
||||
false));
|
||||
task.Wait();
|
||||
|
||||
return task.Result.IsAcknowledged && task.Result.ModifiedCount == 1;
|
||||
}
|
||||
|
||||
public override void UpdateUser(MembershipUser user)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override bool ValidateUser(string username, string password)
|
||||
{
|
||||
var task = _memberCollection
|
||||
.Find(f => f.UserName.ToLower() == username.ToLower())
|
||||
.FirstOrDefaultAsync();
|
||||
.SingleOrDefaultAsync();
|
||||
task.Wait();
|
||||
var mm = task.Result;
|
||||
|
||||
if (mm == null || !(mm.IsApproved && !mm.IsLockedOut))
|
||||
if (mm == null)
|
||||
{
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] salt = mm.PassSalt;
|
||||
byte[] salt = new byte[24];
|
||||
byte[] hash = calculateHash(newPassword, ref salt);
|
||||
|
||||
mm.PassSalt = salt;
|
||||
mm.PassHash = hash;
|
||||
|
||||
var replaceTask = _memberCollection
|
||||
.ReplaceOneAsync(m => m.Id == mm.Id, mm);
|
||||
replaceTask.Wait();
|
||||
|
||||
return replaceTask.IsCompleted;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
|
||||
{
|
||||
if (password.Length < MinRequiredPasswordLength)
|
||||
{
|
||||
status = MembershipCreateStatus.InvalidPassword;
|
||||
return null;
|
||||
}
|
||||
|
||||
MembershipUser mu = null;
|
||||
|
||||
var dupeUsers = _memberCollection
|
||||
.Find(m => m.UserName.ToLower() == username.ToLower())
|
||||
.CountAsync();
|
||||
var dupeEmails = _memberCollection
|
||||
.Find(m => m.EmailAddress.ToLower() == email.ToLower())
|
||||
.CountAsync();
|
||||
dupeUsers.Wait();
|
||||
dupeEmails.Wait();
|
||||
|
||||
if (dupeUsers.Result > 0)
|
||||
{
|
||||
status = MembershipCreateStatus.DuplicateUserName;
|
||||
}
|
||||
else if (dupeEmails.Result > 0)
|
||||
{
|
||||
status = MembershipCreateStatus.DuplicateEmail;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] salt = new byte[24];
|
||||
byte[] hash = calculateHash(password, ref salt);
|
||||
|
||||
bool isFail = false;
|
||||
|
||||
for (int i = 0; i > hash.Length; i++)
|
||||
MongoMember mm = new MongoMember()
|
||||
{
|
||||
isFail |= (hash[i] != mm.PassHash[i]);
|
||||
}
|
||||
Id = Guid.NewGuid(),
|
||||
UserName = username,
|
||||
PassHash = hash,
|
||||
PassSalt = salt,
|
||||
EmailAddress = email,
|
||||
|
||||
IsApproved = false,
|
||||
IsLockedOut = false,
|
||||
|
||||
if (isFail)
|
||||
CreationDate = DateTime.Now,
|
||||
LastLoginDate = DateTime.MinValue,
|
||||
LastActivityDate = DateTime.MinValue,
|
||||
LastLockoutDate = DateTime.MinValue
|
||||
};
|
||||
|
||||
var insertTask = _memberCollection
|
||||
.InsertOneAsync(mm);
|
||||
insertTask.Wait();
|
||||
|
||||
if (insertTask.Status == TaskStatus.RanToCompletion)
|
||||
{
|
||||
if (mm.LockoutWindowStart == DateTime.MinValue)
|
||||
{
|
||||
mm.LockoutWindowStart = DateTime.Now;
|
||||
mm.LockoutWindowAttempts = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mm.LockoutWindowStart.AddMinutes(PasswordAttemptWindow) > DateTime.Now)
|
||||
{
|
||||
// still within window
|
||||
mm.LockoutWindowAttempts++;
|
||||
if (mm.LockoutWindowAttempts >= MaxInvalidPasswordAttempts)
|
||||
{
|
||||
mm.IsLockedOut = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// outside of window, reset
|
||||
mm.LockoutWindowStart = DateTime.Now;
|
||||
mm.LockoutWindowAttempts = 1;
|
||||
}
|
||||
}
|
||||
|
||||
status = MembershipCreateStatus.Success;
|
||||
mu = new MembershipUser(this.Name, mm.UserName, mm.Id, mm.EmailAddress, "", "", mm.IsApproved, mm.IsLockedOut, mm.CreationDate, mm.LastLoginDate, mm.LastActivityDate, DateTime.MinValue, mm.LastLockoutDate);
|
||||
}
|
||||
else
|
||||
{
|
||||
mm.LastLoginDate = DateTime.Now;
|
||||
mm.LockoutWindowStart = DateTime.MinValue;
|
||||
mm.LockoutWindowAttempts = 0;
|
||||
status = MembershipCreateStatus.ProviderError;
|
||||
}
|
||||
}
|
||||
|
||||
var updTask = _memberCollection
|
||||
.ReplaceOneAsync(
|
||||
Builders<MongoMember>.Filter.Eq(u => u.Id, mm.Id),
|
||||
mm);
|
||||
updTask.Wait();
|
||||
return mu;
|
||||
}
|
||||
|
||||
return !isFail;
|
||||
}
|
||||
public override bool DeleteUser(string username, bool deleteAllRelatedData)
|
||||
{
|
||||
var task = _memberCollection
|
||||
.DeleteOneAsync(m => m.UserName.ToLower() == username);
|
||||
task.Wait();
|
||||
|
||||
private static byte[] calculateHash(string password, ref byte[] salt)
|
||||
{
|
||||
if (!salt.Any(v => v != 0))
|
||||
return task.Result.IsAcknowledged && task.Result.DeletedCount == 1;
|
||||
}
|
||||
|
||||
public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
|
||||
{
|
||||
MembershipUserCollection muc = new MembershipUserCollection();
|
||||
|
||||
var users = _memberCollection
|
||||
.Find(new BsonDocument());
|
||||
|
||||
var totalRecordsTask = users
|
||||
.CountAsync();
|
||||
totalRecordsTask.Wait();
|
||||
totalRecords = Convert.ToInt32(totalRecordsTask.Result);
|
||||
|
||||
users = users
|
||||
.Skip(pageIndex * pageSize)
|
||||
.Limit(pageSize);
|
||||
var pageItemsTask = users.ToListAsync();
|
||||
pageItemsTask.Wait();
|
||||
|
||||
foreach (var mm in pageItemsTask.Result)
|
||||
{
|
||||
muc.Add(new MembershipUser(this.Name, mm.UserName, mm.Id, mm.EmailAddress, "", "", mm.IsApproved, mm.IsLockedOut, mm.CreationDate, mm.LastLoginDate, mm.LastActivityDate, DateTime.MinValue, mm.LastLockoutDate));
|
||||
}
|
||||
|
||||
return muc;
|
||||
}
|
||||
|
||||
public override int GetNumberOfUsersOnline()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override string GetPassword(string username, string answer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override MembershipUser GetUser(string username, bool userIsOnline)
|
||||
{
|
||||
var task = _memberCollection
|
||||
.Find(f => f.UserName.ToLower() == username.ToLower())
|
||||
.FirstOrDefaultAsync();
|
||||
task.Wait();
|
||||
|
||||
var mm = task.Result;
|
||||
|
||||
return mm == null ? null : new MembershipUser(this.Name, mm.UserName, mm.Id, mm.EmailAddress, "", "", mm.IsApproved, mm.IsLockedOut, mm.CreationDate, mm.LastLoginDate, mm.LastActivityDate, DateTime.MinValue, mm.LastLockoutDate);
|
||||
}
|
||||
|
||||
public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
|
||||
{
|
||||
var task = _memberCollection
|
||||
.Find(f => f.Id == (Guid)providerUserKey)
|
||||
.FirstOrDefaultAsync();
|
||||
task.Wait();
|
||||
|
||||
var mm = task.Result;
|
||||
|
||||
return mm == null ? null : new MembershipUser(this.Name, mm.UserName, mm.Id, mm.EmailAddress, "", "", mm.IsApproved, mm.IsLockedOut, mm.CreationDate, mm.LastLoginDate, mm.LastActivityDate, DateTime.MinValue, mm.LastLockoutDate);
|
||||
}
|
||||
|
||||
public override string GetUserNameByEmail(string email)
|
||||
{
|
||||
var task = _memberCollection
|
||||
.Find(f => f.EmailAddress.ToLower() == email.ToLower())
|
||||
.FirstOrDefaultAsync();
|
||||
task.Wait();
|
||||
|
||||
return task.Result.UserName;
|
||||
}
|
||||
|
||||
public override string ResetPassword(string username, string answer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ChangeApproval(Guid Id, bool newStatus)
|
||||
{
|
||||
var task = _memberCollection
|
||||
.UpdateOneAsync(
|
||||
Builders<MongoMember>.Filter.Eq(u => u.Id, Id),
|
||||
Builders<MongoMember>.Update.Set(u => u.IsApproved, newStatus));
|
||||
task.Wait();
|
||||
}
|
||||
|
||||
public void ChangeLockStatus(Guid Id, bool newStatus)
|
||||
{
|
||||
var updateDefinition = new List<UpdateDefinition<MongoMember>>();
|
||||
updateDefinition.Add(Builders<MongoMember>.Update.Set(u => u.IsLockedOut, newStatus));
|
||||
|
||||
if (newStatus)
|
||||
{
|
||||
updateDefinition.Add(Builders<MongoMember>.Update.Set(u => u.LastLockoutDate, DateTime.Now));
|
||||
}
|
||||
else
|
||||
{
|
||||
updateDefinition.Add(Builders<MongoMember>.Update.Set(u => u.LockoutWindowAttempts, 0));
|
||||
updateDefinition.Add(Builders<MongoMember>.Update.Set(u => u.LastLockoutDate, DateTime.MinValue));
|
||||
}
|
||||
|
||||
var task = _memberCollection
|
||||
.UpdateOneAsync(
|
||||
Builders<MongoMember>.Filter.Eq(u => u.Id, Id),
|
||||
Builders<MongoMember>.Update.Combine(updateDefinition));
|
||||
task.Wait();
|
||||
}
|
||||
|
||||
public override bool UnlockUser(string userName)
|
||||
{
|
||||
var task = _memberCollection
|
||||
.UpdateOneAsync(
|
||||
Builders<MongoMember>.Filter.Eq(m => m.UserName.ToLower(), userName.ToLower()),
|
||||
Builders<MongoMember>.Update.Set(m => m.IsLockedOut, false));
|
||||
task.Wait();
|
||||
|
||||
return task.Result.IsAcknowledged && task.Result.ModifiedCount == 1;
|
||||
}
|
||||
|
||||
public override void UpdateUser(MembershipUser user)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override bool ValidateUser(string username, string password)
|
||||
{
|
||||
var task = _memberCollection
|
||||
.Find(f => f.UserName.ToLower() == username.ToLower())
|
||||
.FirstOrDefaultAsync();
|
||||
task.Wait();
|
||||
var mm = task.Result;
|
||||
|
||||
if (mm == null || !(mm.IsApproved && !mm.IsLockedOut))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] salt = mm.PassSalt;
|
||||
byte[] hash = calculateHash(password, ref salt);
|
||||
|
||||
bool isFail = false;
|
||||
|
||||
for (int i = 0; i > hash.Length; i++)
|
||||
{
|
||||
isFail |= (hash[i] != mm.PassHash[i]);
|
||||
}
|
||||
|
||||
|
||||
if (isFail)
|
||||
{
|
||||
if (mm.LockoutWindowStart == DateTime.MinValue)
|
||||
{
|
||||
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
|
||||
rng.GetBytes(salt);
|
||||
mm.LockoutWindowStart = DateTime.Now;
|
||||
mm.LockoutWindowAttempts = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mm.LockoutWindowStart.AddMinutes(PasswordAttemptWindow) > DateTime.Now)
|
||||
{
|
||||
// still within window
|
||||
mm.LockoutWindowAttempts++;
|
||||
if (mm.LockoutWindowAttempts >= MaxInvalidPasswordAttempts)
|
||||
{
|
||||
mm.IsLockedOut = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// outside of window, reset
|
||||
mm.LockoutWindowStart = DateTime.Now;
|
||||
mm.LockoutWindowAttempts = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mm.LastLoginDate = DateTime.Now;
|
||||
mm.LockoutWindowStart = DateTime.MinValue;
|
||||
mm.LockoutWindowAttempts = 0;
|
||||
}
|
||||
|
||||
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
|
||||
var updTask = _memberCollection
|
||||
.ReplaceOneAsync(
|
||||
Builders<MongoMember>.Filter.Eq(u => u.Id, mm.Id),
|
||||
mm);
|
||||
updTask.Wait();
|
||||
|
||||
byte[] hashPlaintext = new byte[salt.Length + passwordBytes.Length];
|
||||
return !isFail;
|
||||
}
|
||||
|
||||
passwordBytes.CopyTo(hashPlaintext, 0);
|
||||
salt.CopyTo(hashPlaintext, passwordBytes.Length);
|
||||
private static byte[] calculateHash(string password, ref byte[] salt)
|
||||
{
|
||||
if (!salt.Any(v => v != 0))
|
||||
{
|
||||
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
|
||||
rng.GetBytes(salt);
|
||||
}
|
||||
|
||||
SHA512CryptoServiceProvider sha = new SHA512CryptoServiceProvider();
|
||||
byte[] hash = sha.ComputeHash(hashPlaintext);
|
||||
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
|
||||
|
||||
return hash;
|
||||
}
|
||||
byte[] hashPlaintext = new byte[salt.Length + passwordBytes.Length];
|
||||
|
||||
private static bool tryReadBool(string config, bool defaultValue)
|
||||
{
|
||||
bool temp = false;
|
||||
bool success = bool.TryParse(config, out temp);
|
||||
return success ? temp : defaultValue;
|
||||
}
|
||||
passwordBytes.CopyTo(hashPlaintext, 0);
|
||||
salt.CopyTo(hashPlaintext, passwordBytes.Length);
|
||||
|
||||
private static int tryReadInt(string config, int defaultValue)
|
||||
{
|
||||
int temp = 0;
|
||||
bool success = int.TryParse(config, out temp);
|
||||
return success ? temp : defaultValue;
|
||||
}
|
||||
}
|
||||
SHA512CryptoServiceProvider sha = new SHA512CryptoServiceProvider();
|
||||
byte[] hash = sha.ComputeHash(hashPlaintext);
|
||||
|
||||
public class MongoMember
|
||||
{
|
||||
[BsonId]
|
||||
public Guid Id { get; set; }
|
||||
return hash;
|
||||
}
|
||||
|
||||
public string UserName { get; set; }
|
||||
public byte[] PassHash { get; set; }
|
||||
public byte[] PassSalt { get; set; }
|
||||
public string EmailAddress { get; set; }
|
||||
private static bool tryReadBool(string config, bool defaultValue)
|
||||
{
|
||||
bool temp = false;
|
||||
bool success = bool.TryParse(config, out temp);
|
||||
return success ? temp : defaultValue;
|
||||
}
|
||||
|
||||
public bool IsApproved { get; set; }
|
||||
public bool IsLockedOut { get; set; }
|
||||
private static int tryReadInt(string config, int defaultValue)
|
||||
{
|
||||
int temp = 0;
|
||||
bool success = int.TryParse(config, out temp);
|
||||
return success ? temp : defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime CreationDate { get; set; }
|
||||
public DateTime LastActivityDate { get; set; }
|
||||
public DateTime LastLockoutDate { get; set; }
|
||||
public DateTime LastLoginDate { get; set; }
|
||||
public class MongoMember
|
||||
{
|
||||
[BsonId]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public DateTime LockoutWindowStart { get; set; }
|
||||
public int LockoutWindowAttempts { get; set; }
|
||||
}
|
||||
public string UserName { get; set; }
|
||||
public byte[] PassHash { get; set; }
|
||||
public byte[] PassSalt { get; set; }
|
||||
public string EmailAddress { get; set; }
|
||||
|
||||
public bool IsApproved { get; set; }
|
||||
public bool IsLockedOut { get; set; }
|
||||
|
||||
public DateTime CreationDate { get; set; }
|
||||
public DateTime LastActivityDate { get; set; }
|
||||
public DateTime LastLockoutDate { get; set; }
|
||||
public DateTime LastLoginDate { get; set; }
|
||||
|
||||
public DateTime LockoutWindowStart { get; set; }
|
||||
public int LockoutWindowAttempts { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue