mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-09-19 01:57:55 +08:00
Adding an authentication service, and wiring the authenticated user model object into base viewmodel property, and as property of components that implement ICurrentUser. That's done with SecurityFilter and SecurityModule respectively.
--HG-- extra : convert_revision : svn%3A5ff7c347-ad56-4c35-b696-ccb81de16e03/trunk%4039779
This commit is contained in:
@@ -1,13 +1,15 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.Data;
|
||||
using Orchard.Models;
|
||||
using Orchard.Notify;
|
||||
using Orchard.Security;
|
||||
using Orchard.Users.Models;
|
||||
using Orchard.Users.ViewModels;
|
||||
|
||||
namespace Orchard.Users.Controllers {
|
||||
public class AdminController : Controller {
|
||||
public class AdminController : Controller, ICurrentUser {
|
||||
private readonly IModelManager _modelManager;
|
||||
private readonly IRepository<UserRecord> _userRepository;
|
||||
private readonly INotifier _notifier;
|
||||
@@ -62,6 +64,8 @@ namespace Orchard.Users.Controllers {
|
||||
_notifier.Information("User information updated");
|
||||
return RedirectToAction("Edit", new { id });
|
||||
}
|
||||
|
||||
public IUser CurrentUser { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Orchard.Data;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Models;
|
||||
using Orchard.Security;
|
||||
@@ -7,9 +8,11 @@ using Orchard.Users.Models;
|
||||
namespace Orchard.Users.Services {
|
||||
public class MembershipService : IMembershipService {
|
||||
private readonly IModelManager _modelManager;
|
||||
private readonly IRepository<UserRecord> _userRepository;
|
||||
|
||||
public MembershipService(IModelManager modelManager) {
|
||||
public MembershipService(IModelManager modelManager, IRepository<UserRecord> userRepository) {
|
||||
_modelManager = modelManager;
|
||||
_userRepository = userRepository;
|
||||
Logger = NullLogger.Instance;
|
||||
}
|
||||
|
||||
@@ -31,7 +34,15 @@ namespace Orchard.Users.Services {
|
||||
}
|
||||
|
||||
public IUser GetUser(string username) {
|
||||
throw new NotImplementedException();
|
||||
var userRecord = _userRepository.Get(x => x.UserName == username);
|
||||
if (userRecord == null) {
|
||||
return null;
|
||||
}
|
||||
return _modelManager.Get(userRecord.Id).As<IUser>();
|
||||
}
|
||||
|
||||
public IUser Identify(string username, string password) {
|
||||
return GetUser(username);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<AdminViewModel>" %>
|
||||
<%@ Import Namespace="Orchard.Mvc.ViewModels"%>
|
||||
|
||||
<%@ Import Namespace="Orchard.Mvc.ViewModels" %>
|
||||
<div id="doc3" class="yui-t2">
|
||||
<div id="hd" role="banner">
|
||||
<div class="yui-g">
|
||||
@@ -11,14 +11,16 @@
|
||||
<%= Html.ActionLink("Your Site", "Index", new { Area = "", Controller = "Home" })%>
|
||||
</div>
|
||||
<div class="yui-u">
|
||||
<%--<div id="login">
|
||||
User: JoWall | <a href="#">Logout</a></div>--%>
|
||||
<% if (Model.CurrentUser != null) { %>
|
||||
<div id="login">
|
||||
User:
|
||||
<%=Model.CurrentUser.UserName%>
|
||||
| <%=Html.ActionLink("Logout", "LogOff", "Account", new {}, new{area=""})%></div>
|
||||
<%} %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="bd" role="main">
|
||||
<div id="yui-main">
|
||||
<div class="yui-b">
|
||||
|
||||
<% Html.RenderPartial("Messages", Model.Messages); %>
|
||||
|
||||
<% Html.RenderPartial("Messages", Model.Messages); %>
|
||||
|
@@ -4,28 +4,20 @@ using System.Globalization;
|
||||
using System.Security.Principal;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Security;
|
||||
using Orchard.Security;
|
||||
|
||||
namespace Orchard.Controllers {
|
||||
[HandleError]
|
||||
public class AccountController : Controller {
|
||||
// This constructor is used by the MVC framework to instantiate the controller using
|
||||
// the default forms authentication and membership providers.
|
||||
private readonly IAuthenticationService _authenticationService;
|
||||
private readonly IMembershipService _membershipService;
|
||||
|
||||
public AccountController()
|
||||
: this(null, null) {}
|
||||
|
||||
// This constructor is not used by the MVC framework but is instead provided for ease
|
||||
// of unit testing this type. See the comments at the end of this file for more
|
||||
// information.
|
||||
public AccountController(IFormsAuthentication formsAuth, IMembershipServiceShim service) {
|
||||
FormsAuth = formsAuth ?? new FormsAuthenticationService();
|
||||
MembershipService = service ?? new AccountMembershipService();
|
||||
public AccountController(IAuthenticationService authenticationService, IMembershipService membershipService) {
|
||||
_authenticationService = authenticationService;
|
||||
_membershipService = membershipService;
|
||||
}
|
||||
|
||||
public IFormsAuthentication FormsAuth { get; private set; }
|
||||
|
||||
public IMembershipServiceShim MembershipService { get; private set; }
|
||||
|
||||
public ActionResult LogOn() {
|
||||
return View();
|
||||
}
|
||||
@@ -34,11 +26,13 @@ namespace Orchard.Controllers {
|
||||
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
|
||||
Justification = "Needs to take same parameter type as Controller.Redirect()")]
|
||||
public ActionResult LogOn(string userName, string password, bool rememberMe, string returnUrl) {
|
||||
if (!ValidateLogOn(userName, password)) {
|
||||
var user = ValidateLogOn(userName, password);
|
||||
if (!ModelState.IsValid) {
|
||||
return View();
|
||||
}
|
||||
|
||||
FormsAuth.SignIn(userName, rememberMe);
|
||||
_authenticationService.SignIn(user, rememberMe);
|
||||
|
||||
if (!String.IsNullOrEmpty(returnUrl)) {
|
||||
return Redirect(returnUrl);
|
||||
}
|
||||
@@ -48,31 +42,40 @@ namespace Orchard.Controllers {
|
||||
}
|
||||
|
||||
public ActionResult LogOff() {
|
||||
FormsAuth.SignOut();
|
||||
_authenticationService.SignOut();
|
||||
|
||||
return RedirectToAction("Index", "Home");
|
||||
}
|
||||
|
||||
int MinPasswordLength {
|
||||
get {
|
||||
var settings = new MembershipSettings();
|
||||
_membershipService.ReadSettings(settings);
|
||||
return settings.MinRequiredPasswordLength;
|
||||
}
|
||||
}
|
||||
|
||||
public ActionResult Register() {
|
||||
ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
|
||||
ViewData["PasswordLength"] = MinPasswordLength;
|
||||
|
||||
return View();
|
||||
}
|
||||
|
||||
[AcceptVerbs(HttpVerbs.Post)]
|
||||
public ActionResult Register(string userName, string email, string password, string confirmPassword) {
|
||||
ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
|
||||
ViewData["PasswordLength"] = MinPasswordLength;
|
||||
|
||||
if (ValidateRegistration(userName, email, password, confirmPassword)) {
|
||||
// Attempt to register the user
|
||||
var createStatus = MembershipService.CreateUser(userName, password, email);
|
||||
var user = _membershipService.CreateUser(new CreateUserParams(userName, password, email, null, null, true));
|
||||
|
||||
|
||||
if (createStatus == MembershipCreateStatus.Success) {
|
||||
FormsAuth.SignIn(userName, false /* createPersistentCookie */);
|
||||
if (user != null) {
|
||||
_authenticationService.SignIn(user, false /* createPersistentCookie */);
|
||||
return RedirectToAction("Index", "Home");
|
||||
}
|
||||
else {
|
||||
ModelState.AddModelError("_FORM", ErrorCodeToString(createStatus));
|
||||
ModelState.AddModelError("_FORM", ErrorCodeToString(/*createStatus*/MembershipCreateStatus.ProviderError));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +85,7 @@ namespace Orchard.Controllers {
|
||||
|
||||
[Authorize]
|
||||
public ActionResult ChangePassword() {
|
||||
ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
|
||||
ViewData["PasswordLength"] = MinPasswordLength;
|
||||
|
||||
return View();
|
||||
}
|
||||
@@ -92,14 +95,14 @@ namespace Orchard.Controllers {
|
||||
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
|
||||
Justification = "Exceptions result in password not being changed.")]
|
||||
public ActionResult ChangePassword(string currentPassword, string newPassword, string confirmPassword) {
|
||||
ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
|
||||
ViewData["PasswordLength"] = MinPasswordLength;
|
||||
|
||||
if (!ValidateChangePassword(currentPassword, newPassword, confirmPassword)) {
|
||||
return View();
|
||||
}
|
||||
|
||||
try {
|
||||
if (MembershipService.ChangePassword(User.Identity.Name, currentPassword, newPassword)) {
|
||||
if (true/*MembershipService.ChangePassword(User.Identity.Name, currentPassword, newPassword)*/) {
|
||||
return RedirectToAction("ChangePasswordSuccess");
|
||||
}
|
||||
else {
|
||||
@@ -130,11 +133,11 @@ namespace Orchard.Controllers {
|
||||
if (String.IsNullOrEmpty(currentPassword)) {
|
||||
ModelState.AddModelError("currentPassword", "You must specify a current password.");
|
||||
}
|
||||
if (newPassword == null || newPassword.Length < MembershipService.MinPasswordLength) {
|
||||
if (newPassword == null || newPassword.Length < MinPasswordLength) {
|
||||
ModelState.AddModelError("newPassword",
|
||||
String.Format(CultureInfo.CurrentCulture,
|
||||
"You must specify a new password of {0} or more characters.",
|
||||
MembershipService.MinPasswordLength));
|
||||
MinPasswordLength));
|
||||
}
|
||||
|
||||
if (!String.Equals(newPassword, confirmPassword, StringComparison.Ordinal)) {
|
||||
@@ -144,18 +147,19 @@ namespace Orchard.Controllers {
|
||||
return ModelState.IsValid;
|
||||
}
|
||||
|
||||
private bool ValidateLogOn(string userName, string password) {
|
||||
private IUser ValidateLogOn(string userName, string password) {
|
||||
if (String.IsNullOrEmpty(userName)) {
|
||||
ModelState.AddModelError("username", "You must specify a username.");
|
||||
}
|
||||
if (String.IsNullOrEmpty(password)) {
|
||||
ModelState.AddModelError("password", "You must specify a password.");
|
||||
}
|
||||
if (!MembershipService.ValidateUser(userName, password)) {
|
||||
var user = _membershipService.Identify(userName, password);
|
||||
if (user == null) {
|
||||
ModelState.AddModelError("_FORM", "The username or password provided is incorrect.");
|
||||
}
|
||||
|
||||
return ModelState.IsValid;
|
||||
return user;
|
||||
}
|
||||
|
||||
private bool ValidateRegistration(string userName, string email, string password, string confirmPassword) {
|
||||
@@ -165,11 +169,11 @@ namespace Orchard.Controllers {
|
||||
if (String.IsNullOrEmpty(email)) {
|
||||
ModelState.AddModelError("email", "You must specify an email address.");
|
||||
}
|
||||
if (password == null || password.Length < MembershipService.MinPasswordLength) {
|
||||
if (password == null || password.Length < MinPasswordLength) {
|
||||
ModelState.AddModelError("password",
|
||||
String.Format(CultureInfo.CurrentCulture,
|
||||
"You must specify a password of {0} or more characters.",
|
||||
MembershipService.MinPasswordLength));
|
||||
MinPasswordLength));
|
||||
}
|
||||
if (!String.Equals(password, confirmPassword, StringComparison.Ordinal)) {
|
||||
ModelState.AddModelError("_FORM", "The new password and confirmation password do not match.");
|
||||
@@ -219,24 +223,6 @@ namespace Orchard.Controllers {
|
||||
#endregion
|
||||
}
|
||||
|
||||
public interface IFormsAuthentication {
|
||||
void SignIn(string userName, bool createPersistentCookie);
|
||||
void SignOut();
|
||||
}
|
||||
|
||||
public class FormsAuthenticationService : IFormsAuthentication {
|
||||
#region IFormsAuthentication Members
|
||||
|
||||
public void SignIn(string userName, bool createPersistentCookie) {
|
||||
FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
|
||||
}
|
||||
|
||||
public void SignOut() {
|
||||
FormsAuthentication.SignOut();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public interface IMembershipServiceShim {
|
||||
int MinPasswordLength { get; }
|
||||
@@ -250,7 +236,7 @@ namespace Orchard.Controllers {
|
||||
private readonly MembershipProvider _provider;
|
||||
|
||||
public AccountMembershipService()
|
||||
: this(null) {}
|
||||
: this(null) { }
|
||||
|
||||
public AccountMembershipService(MembershipProvider provider) {
|
||||
_provider = provider ?? Membership.Provider;
|
||||
|
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using Autofac.Builder;
|
||||
using Autofac.Integration.Web.Mvc;
|
||||
using Orchard.Environment;
|
||||
@@ -20,10 +21,14 @@ namespace Orchard.Mvc {
|
||||
|
||||
var module = new AutofacControllerModule(assemblies) {
|
||||
ActionInvokerType = typeof(FilterResolvingActionInvoker),
|
||||
IdentificationStrategy = new OrchardControllerIdentificationStrategy()
|
||||
IdentificationStrategy = new OrchardControllerIdentificationStrategy()
|
||||
};
|
||||
|
||||
moduleBuilder.RegisterModule(module);
|
||||
moduleBuilder
|
||||
.Register(ctx => HttpContext.Current ==null ? null : new HttpContextWrapper(HttpContext.Current))
|
||||
.As<HttpContextBase>()
|
||||
.FactoryScoped();
|
||||
}
|
||||
}
|
||||
}
|
@@ -21,7 +21,7 @@ namespace Orchard.Mvc {
|
||||
// Now that the request container is known - try to resolve the controller
|
||||
object controller;
|
||||
if (container != null &&
|
||||
container.TryResolve(serviceName, out controller)) {
|
||||
container.TryResolve(serviceName, out controller, TypedParameter.From(requestContext))) {
|
||||
return (IController) controller;
|
||||
}
|
||||
return base.CreateController(requestContext, controllerName);
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Notify;
|
||||
using Orchard.Security;
|
||||
|
||||
namespace Orchard.Mvc.ViewModels {
|
||||
public class BaseViewModel {
|
||||
@@ -8,5 +9,6 @@ namespace Orchard.Mvc.ViewModels {
|
||||
}
|
||||
|
||||
public IList<NotifyEntry> Messages { get; set; }
|
||||
public IUser CurrentUser { get; set; }
|
||||
}
|
||||
}
|
||||
|
@@ -154,6 +154,11 @@
|
||||
<Compile Include="Mvc\Routes\RouteExtensions.cs" />
|
||||
<Compile Include="Mvc\ViewModels\AdminViewModel.cs" />
|
||||
<Compile Include="Mvc\ViewModels\BaseViewModel.cs" />
|
||||
<Compile Include="Security\IAuthenticationService.cs" />
|
||||
<Compile Include="Security\ICurrentUser.cs" />
|
||||
<Compile Include="Security\Providers\FormsAuthenticationService.cs" />
|
||||
<Compile Include="Security\SecurityFilter.cs" />
|
||||
<Compile Include="Security\SecurityModule.cs" />
|
||||
<Compile Include="UI\Menus\AdminMenuFilter.cs" />
|
||||
<Compile Include="UI\Navigation\INavigationBuilder.cs" />
|
||||
<Compile Include="UI\Navigation\INavigationProvider.cs" />
|
||||
|
9
src/Orchard/Security/IAuthenticationService.cs
Normal file
9
src/Orchard/Security/IAuthenticationService.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System.Web;
|
||||
|
||||
namespace Orchard.Security {
|
||||
public interface IAuthenticationService : IDependency {
|
||||
void SignIn(IUser user, bool createPersistentCookie);
|
||||
void SignOut();
|
||||
IUser Authenticated();
|
||||
}
|
||||
}
|
5
src/Orchard/Security/ICurrentUser.cs
Normal file
5
src/Orchard/Security/ICurrentUser.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
namespace Orchard.Security {
|
||||
public interface ICurrentUser {
|
||||
IUser CurrentUser { get; set; }
|
||||
}
|
||||
}
|
@@ -10,6 +10,8 @@ namespace Orchard.Security {
|
||||
|
||||
IUser CreateUser(CreateUserParams createUserParams);
|
||||
IUser GetUser(string username);
|
||||
|
||||
IUser Identify(string username, string password);
|
||||
}
|
||||
|
||||
public class MembershipSettings {
|
||||
|
75
src/Orchard/Security/Providers/FormsAuthenticationService.cs
Normal file
75
src/Orchard/Security/Providers/FormsAuthenticationService.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using System.Web.Security;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Models;
|
||||
using Orchard.Services;
|
||||
|
||||
namespace Orchard.Security.Providers {
|
||||
public class FormsAuthenticationService : IAuthenticationService {
|
||||
private readonly IClock _clock;
|
||||
private readonly IModelManager _modelManager;
|
||||
private readonly HttpContextBase _httpContext;
|
||||
|
||||
public FormsAuthenticationService(IClock clock, IModelManager modelManager, HttpContextBase httpContext) {
|
||||
_clock = clock;
|
||||
_modelManager = modelManager;
|
||||
_httpContext = httpContext;
|
||||
Logger = NullLogger.Instance;
|
||||
|
||||
// TEMP: who can say...
|
||||
ExpirationTimeSpan = TimeSpan.FromHours(6);
|
||||
}
|
||||
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
public TimeSpan ExpirationTimeSpan { get; set; }
|
||||
|
||||
public void SignIn(IUser user, bool createPersistentCookie) {
|
||||
var now = _clock.UtcNow.ToLocalTime();
|
||||
var userData = Convert.ToString(user.Id);
|
||||
|
||||
var ticket = new FormsAuthenticationTicket(
|
||||
1 /*version*/,
|
||||
user.UserName,
|
||||
now,
|
||||
now.Add(ExpirationTimeSpan),
|
||||
createPersistentCookie,
|
||||
userData,
|
||||
FormsAuthentication.FormsCookiePath);
|
||||
|
||||
var encryptedTicket = FormsAuthentication.Encrypt(ticket);
|
||||
|
||||
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
|
||||
cookie.HttpOnly = true;
|
||||
cookie.Secure = FormsAuthentication.RequireSSL;
|
||||
cookie.Path = FormsAuthentication.FormsCookiePath;
|
||||
if (FormsAuthentication.CookieDomain != null) {
|
||||
cookie.Domain = FormsAuthentication.CookieDomain;
|
||||
}
|
||||
|
||||
_httpContext.Response.Cookies.Add(cookie);
|
||||
}
|
||||
|
||||
public void SignOut() {
|
||||
FormsAuthentication.SignOut();
|
||||
}
|
||||
|
||||
public IUser Authenticated() {
|
||||
if (!_httpContext.Request.IsAuthenticated || !(_httpContext.User.Identity is FormsIdentity)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var formsIdentity = (FormsIdentity)_httpContext.User.Identity;
|
||||
var userData = formsIdentity.Ticket.UserData;
|
||||
int userId;
|
||||
if (!int.TryParse(userData, out userId)) {
|
||||
Logger.Fatal("User id not a parsable integer");
|
||||
}
|
||||
return _modelManager.Get(userId).As<IUser>();
|
||||
}
|
||||
}
|
||||
}
|
34
src/Orchard/Security/SecurityFilter.cs
Normal file
34
src/Orchard/Security/SecurityFilter.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.Mvc.Filters;
|
||||
using Orchard.Mvc.ViewModels;
|
||||
|
||||
namespace Orchard.Security {
|
||||
public class SecurityFilter : FilterProvider, IResultFilter {
|
||||
private readonly IAuthenticationService _authenticationService;
|
||||
|
||||
public SecurityFilter(IAuthenticationService authenticationService) {
|
||||
_authenticationService = authenticationService;
|
||||
}
|
||||
|
||||
public void OnResultExecuting(ResultExecutingContext filterContext) {
|
||||
var viewResult = filterContext.Result as ViewResultBase;
|
||||
if (viewResult == null)
|
||||
return;
|
||||
|
||||
var baseViewModel = viewResult.ViewData.Model as BaseViewModel;
|
||||
if (baseViewModel == null)
|
||||
return;
|
||||
|
||||
if (baseViewModel.CurrentUser == null)
|
||||
baseViewModel.CurrentUser = _authenticationService.Authenticated();
|
||||
}
|
||||
|
||||
public void OnResultExecuted(ResultExecutedContext filterContext) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
23
src/Orchard/Security/SecurityModule.cs
Normal file
23
src/Orchard/Security/SecurityModule.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using Autofac.Builder;
|
||||
|
||||
namespace Orchard.Security {
|
||||
public class SecurityModule : Module {
|
||||
protected override void AttachToComponentRegistration(Autofac.IContainer container, Autofac.IComponentRegistration registration) {
|
||||
if (typeof(ICurrentUser).IsAssignableFrom(registration.Descriptor.BestKnownImplementationType)) {
|
||||
registration.Activated += OnActivated;
|
||||
}
|
||||
}
|
||||
|
||||
static void OnActivated(object sender, Autofac.ActivatedEventArgs e) {
|
||||
var userContainer = (ICurrentUser)e.Instance;
|
||||
var authenticationService = e.Context.Resolve<IAuthenticationService>();
|
||||
userContainer.CurrentUser = authenticationService.Authenticated();
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user