mirror of
https://gitlab.com/buildfeed/BuildFeed.git
synced 2024-03-22 21:10:34 +08:00
Membership Provider improvements, tweaks for me
This commit is contained in:
parent
b35447fdf7
commit
18740b6336
|
@ -30,5 +30,19 @@ public ActionResult unapprove(Guid id)
|
|||
provider.ChangeApproval(id, false);
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
public ActionResult @lock(Guid id)
|
||||
{
|
||||
var provider = (Membership.Provider as RedisMembershipProvider);
|
||||
provider.ChangeLockStatus(id, true);
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
public ActionResult unlock(Guid id)
|
||||
{
|
||||
var provider = (Membership.Provider as RedisMembershipProvider);
|
||||
provider.ChangeLockStatus(id, false);
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,6 +17,10 @@
|
|||
<td>
|
||||
Last Login Time
|
||||
</td>
|
||||
<td>
|
||||
Last Lockout Time
|
||||
</td>
|
||||
<th style="width:108px;"></th>
|
||||
<th style="width:108px;"></th>
|
||||
</tr>
|
||||
|
||||
|
@ -32,16 +36,29 @@
|
|||
<td>
|
||||
@Html.DisplayFor(modelItem => mu.LastLoginDate)
|
||||
</td>
|
||||
<td>
|
||||
@Html.DisplayFor(modelItem => mu.LastLockoutDate)
|
||||
</td>
|
||||
<td class="text-right">
|
||||
@if (mu.IsApproved)
|
||||
{
|
||||
@Html.ActionLink("Unapprove", "unapprove", new { id = mu.ProviderUserKey }, new { @class = "btn btn-danger", style = "width:90px;" })
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@Html.ActionLink("Approve", "approve", new { id = mu.ProviderUserKey }, new { @class = "btn btn-success", style = "width:90px;" })
|
||||
}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
@if (!mu.IsLockedOut)
|
||||
{
|
||||
@Html.ActionLink("Lock", "lock", new { id = mu.ProviderUserKey }, new { @class = "btn btn-danger", style = "width:90px;" })
|
||||
}
|
||||
else
|
||||
{
|
||||
@Html.ActionLink("Unlock", "unlock", new { id = mu.ProviderUserKey }, new { @class = "btn btn-success", style = "width:90px;" })
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using System.Web.Security;
|
||||
using NServiceKit.DataAnnotations;
|
||||
using NServiceKit.DesignPatterns.Model;
|
||||
|
@ -16,6 +15,13 @@ namespace BuildFeed.Auth
|
|||
{
|
||||
public class RedisMembershipProvider : MembershipProvider
|
||||
{
|
||||
private bool _enablePasswordReset = true;
|
||||
private int _maxInvalidPasswordAttempts = 5;
|
||||
private int _minRequiredNonAlphanumericCharacters = 1;
|
||||
private int _minRequriedPasswordLength = 12;
|
||||
private int _passwordAttemptWindow = 60;
|
||||
private bool _requiresUniqueEmail = true;
|
||||
|
||||
public override string ApplicationName
|
||||
{
|
||||
get { return ""; }
|
||||
|
@ -24,7 +30,7 @@ public override string ApplicationName
|
|||
|
||||
public override bool EnablePasswordReset
|
||||
{
|
||||
get { return true; }
|
||||
get { return _enablePasswordReset; }
|
||||
}
|
||||
|
||||
public override bool EnablePasswordRetrieval
|
||||
|
@ -34,22 +40,22 @@ public override bool EnablePasswordRetrieval
|
|||
|
||||
public override int MaxInvalidPasswordAttempts
|
||||
{
|
||||
get { return 5; }
|
||||
get { return _maxInvalidPasswordAttempts; }
|
||||
}
|
||||
|
||||
public override int MinRequiredNonAlphanumericCharacters
|
||||
{
|
||||
get { return 1; }
|
||||
get { return _minRequiredNonAlphanumericCharacters; }
|
||||
}
|
||||
|
||||
public override int MinRequiredPasswordLength
|
||||
{
|
||||
get { return 12; }
|
||||
get { return _minRequriedPasswordLength; }
|
||||
}
|
||||
|
||||
public override int PasswordAttemptWindow
|
||||
{
|
||||
get { return 60; }
|
||||
get { return _passwordAttemptWindow; }
|
||||
}
|
||||
|
||||
public override MembershipPasswordFormat PasswordFormat
|
||||
|
@ -69,7 +75,24 @@ public override bool RequiresQuestionAndAnswer
|
|||
|
||||
public override bool RequiresUniqueEmail
|
||||
{
|
||||
get { return true; }
|
||||
get { return _requiresUniqueEmail; }
|
||||
}
|
||||
|
||||
public override void Initialize(string name, NameValueCollection config)
|
||||
{
|
||||
if (config == null)
|
||||
{
|
||||
throw new ArgumentNullException("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);
|
||||
}
|
||||
|
||||
public override bool ChangePassword(string username, string oldPassword, string newPassword)
|
||||
|
@ -313,6 +336,32 @@ public void ChangeApproval(Guid Id, bool newStatus)
|
|||
}
|
||||
}
|
||||
|
||||
public void ChangeLockStatus(Guid Id, bool newStatus)
|
||||
{
|
||||
using (RedisClient rClient = new RedisClient(DatabaseConfig.Host, DatabaseConfig.Port, db: DatabaseConfig.Database))
|
||||
{
|
||||
var client = rClient.As<RedisMember>();
|
||||
var rm = client.GetById(Id);
|
||||
|
||||
if (rm != null)
|
||||
{
|
||||
rm.IsLockedOut = newStatus;
|
||||
|
||||
if (newStatus)
|
||||
{
|
||||
rm.LastLockoutDate = DateTime.Now;
|
||||
}
|
||||
else
|
||||
{
|
||||
rm.LockoutWindowAttempts = 0;
|
||||
rm.LockoutWindowStart = DateTime.MinValue;
|
||||
}
|
||||
|
||||
client.Store(rm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool UnlockUser(string userName)
|
||||
{
|
||||
using (RedisClient rClient = new RedisClient(DatabaseConfig.Host, DatabaseConfig.Port, db: DatabaseConfig.Database))
|
||||
|
@ -340,7 +389,7 @@ public override bool ValidateUser(string username, string password)
|
|||
var client = rClient.As<RedisMember>();
|
||||
var rm = client.GetAll().SingleOrDefault(m => m.UserName.ToLower() == username.ToLower());
|
||||
|
||||
if (rm == null || !rm.IsApproved)
|
||||
if (rm == null || !(rm.IsApproved && !rm.IsLockedOut))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -355,15 +404,57 @@ public override bool ValidateUser(string username, string password)
|
|||
isFail |= (hash[i] != rm.PassHash[i]);
|
||||
}
|
||||
|
||||
if(!isFail)
|
||||
if (isFail)
|
||||
{
|
||||
if (rm.LockoutWindowStart == DateTime.MinValue)
|
||||
{
|
||||
rm.LockoutWindowStart = DateTime.Now;
|
||||
rm.LockoutWindowAttempts = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rm.LockoutWindowStart.AddMinutes(PasswordAttemptWindow) > DateTime.Now)
|
||||
{
|
||||
// still within window
|
||||
rm.LockoutWindowAttempts++;
|
||||
if (rm.LockoutWindowAttempts >= MaxInvalidPasswordAttempts)
|
||||
{
|
||||
rm.IsLockedOut = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// outside of window, reset
|
||||
rm.LockoutWindowStart = DateTime.Now;
|
||||
rm.LockoutWindowAttempts = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rm.LastLoginDate = DateTime.Now;
|
||||
client.Store(rm);
|
||||
rm.LockoutWindowStart = DateTime.MinValue;
|
||||
rm.LockoutWindowAttempts = 0;
|
||||
}
|
||||
client.Store(rm);
|
||||
|
||||
return !isFail;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool tryReadBool(string config, bool defaultValue)
|
||||
{
|
||||
bool temp = false;
|
||||
bool success = bool.TryParse(config, out temp);
|
||||
return success ? temp : defaultValue;
|
||||
}
|
||||
|
||||
private static int tryReadInt(string config, int defaultValue)
|
||||
{
|
||||
int temp = 0;
|
||||
bool success = int.TryParse(config, out temp);
|
||||
return success ? temp : defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
[DataObject]
|
||||
|
@ -398,5 +489,8 @@ public class RedisMember : IHasId<Guid>
|
|||
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; }
|
||||
}
|
||||
}
|
|
@ -33,7 +33,13 @@ public ActionResult login(LoginUser ru)
|
|||
|
||||
if (isAuthenticated)
|
||||
{
|
||||
FormsAuthentication.SetAuthCookie(ru.UserName, ru.RememberMe);
|
||||
int expiryLength = ru.RememberMe ? 129600 : 60;
|
||||
var ticket = new FormsAuthenticationTicket(ru.UserName, true, expiryLength);
|
||||
var encryptedTicket = FormsAuthentication.Encrypt(ticket);
|
||||
var cookieTicket = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
|
||||
cookieTicket.Expires = DateTime.Now.AddMinutes(expiryLength);
|
||||
cookieTicket.Path = FormsAuthentication.FormsCookiePath;
|
||||
Response.Cookies.Add(cookieTicket);
|
||||
|
||||
string returnUrl = string.IsNullOrEmpty(Request.QueryString["ReturnUrl"]) ? "/" : Request.QueryString["ReturnUrl"];
|
||||
|
||||
|
|
|
@ -18,6 +18,22 @@
|
|||
<h2>@Model.FullBuildString</h2>
|
||||
|
||||
<div class="form-horizontal form-details">
|
||||
@if (User.Identity.IsAuthenticated)
|
||||
{
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">Editor Actions</label>
|
||||
<div class="col-sm-10">
|
||||
<p class="form-control-static">
|
||||
@Html.ActionLink("Edit", "edit", new { id = Model.Id }, new { @class = "btn btn-default btn-xs" })
|
||||
|
||||
@if (User.Identity.Name == "hounsell")
|
||||
{
|
||||
@Html.ActionLink("Delete", "delete", new { id = Model.Id }, new { @class = "btn btn-danger btn-xs" })
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="form-group">
|
||||
@Html.LabelFor(model => model.MajorVersion, new { @class = "control-label col-sm-2" })
|
||||
<div class="col-sm-10">
|
||||
|
@ -154,14 +170,6 @@
|
|||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<a href="/" class="btn btn-info">Return to Listing</a>
|
||||
@if (User.Identity.IsAuthenticated)
|
||||
{
|
||||
@Html.ActionLink("Edit", "edit", new { id = Model.Id }, new { @class = "btn btn-default" })
|
||||
}
|
||||
@if (User.Identity.Name == "hounsell")
|
||||
{
|
||||
@Html.ActionLink("Delete", "delete", new { id = Model.Id }, new { @class = "btn btn-danger" })
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -29,6 +29,10 @@
|
|||
-->
|
||||
</system.web>
|
||||
<system.webServer>
|
||||
<modules>
|
||||
<remove name="ApplicationInsightsWebTracking" xdt:Transform="Insert" />
|
||||
<add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Extensibility.Web.RequestTracking.WebRequestTrackingModule, Microsoft.ApplicationInsights.Extensibility.Web" preCondition="managedHandler" xdt:Transform="Insert" />
|
||||
</modules>
|
||||
<rewrite xdt:Transform="Insert">
|
||||
<rules>
|
||||
<rule name="CanonicalHost" stopProcessing="true">
|
||||
|
|
|
@ -71,8 +71,6 @@
|
|||
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
|
||||
</handlers>
|
||||
<modules>
|
||||
<remove name="ApplicationInsightsWebTracking" />
|
||||
<add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Extensibility.Web.RequestTracking.WebRequestTrackingModule, Microsoft.ApplicationInsights.Extensibility.Web" preCondition="managedHandler" />
|
||||
</modules>
|
||||
<urlCompression doDynamicCompression="true" />
|
||||
</system.webServer>
|
||||
|
|
Loading…
Reference in New Issue
Block a user