Merge branch temp8 into temp8-di

This commit is contained in:
Stephan
2018-07-06 17:36:33 +02:00
1777 changed files with 275222 additions and 275689 deletions

View File

@@ -300,7 +300,7 @@ namespace Umbraco.Web.Security
if (member == null)
{
//this should not happen
Current.Logger.Warn<MembershipHelper>("The member validated but then no member was returned with the username " + username);
Current.Logger.Warn<MembershipHelper>(() => $"The member validated but then no member was returned with the username {username}");
return false;
}
//Log them in

View File

@@ -1,115 +1,115 @@
using System.Collections.Specialized;
using System.Configuration.Provider;
using System.Web.Security;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
using Umbraco.Core.Models.Membership;
using Umbraco.Web.Composing;
namespace Umbraco.Web.Security.Providers
{
/// <summary>
/// Custom Membership Provider for Umbraco Members (User authentication for Frontend applications NOT umbraco CMS)
/// </summary>
public class MembersMembershipProvider : UmbracoMembershipProvider<IMembershipMemberService, IMember>, IUmbracoMemberTypeMembershipProvider
{
public MembersMembershipProvider()
: this(Current.Services.MemberService, Current.Services.MemberTypeService)
{ }
public MembersMembershipProvider(IMembershipMemberService<IMember> memberService, IMemberTypeService memberTypeService)
: base(memberService)
{
LockPropertyTypeAlias = Constants.Conventions.Member.IsLockedOut;
LastLockedOutPropertyTypeAlias = Constants.Conventions.Member.LastLockoutDate;
FailedPasswordAttemptsPropertyTypeAlias = Constants.Conventions.Member.FailedPasswordAttempts;
ApprovedPropertyTypeAlias = Constants.Conventions.Member.IsApproved;
CommentPropertyTypeAlias = Constants.Conventions.Member.Comments;
LastLoginPropertyTypeAlias = Constants.Conventions.Member.LastLoginDate;
LastPasswordChangedPropertyTypeAlias = Constants.Conventions.Member.LastPasswordChangeDate;
PasswordRetrievalQuestionPropertyTypeAlias = Constants.Conventions.Member.PasswordQuestion;
PasswordRetrievalAnswerPropertyTypeAlias = Constants.Conventions.Member.PasswordAnswer;
_memberTypeService = memberTypeService;
}
private readonly IMemberTypeService _memberTypeService;
private string _defaultMemberTypeAlias = "Member";
private volatile bool _hasDefaultMember;
private static readonly object Locker = new object();
private bool _providerKeyAsGuid;
public override string ProviderName => "MembersMembershipProvider";
protected override MembershipUser ConvertToMembershipUser(IMember entity)
{
return entity.AsConcreteMembershipUser(Name, _providerKeyAsGuid);
}
public string LockPropertyTypeAlias { get; }
public string LastLockedOutPropertyTypeAlias { get; }
public string FailedPasswordAttemptsPropertyTypeAlias { get; }
public string ApprovedPropertyTypeAlias { get; }
public string CommentPropertyTypeAlias { get; }
public string LastLoginPropertyTypeAlias { get; }
public string LastPasswordChangedPropertyTypeAlias { get; }
public string PasswordRetrievalQuestionPropertyTypeAlias { get; }
public string PasswordRetrievalAnswerPropertyTypeAlias { get; }
public override void Initialize(string name, NameValueCollection config)
{
base.Initialize(name, config);
// test for membertype (if not specified, choose the first member type available)
if (config["defaultMemberTypeAlias"] != null)
{
_defaultMemberTypeAlias = config["defaultMemberTypeAlias"];
if (_defaultMemberTypeAlias.IsNullOrWhiteSpace())
{
throw new ProviderException("No default member type alias is specified in the web.config string. Please add a 'defaultUserTypeAlias' to the add element in the provider declaration in web.config");
}
_hasDefaultMember = true;
}
//devs can configure the provider user key to be a guid if they want, by default it is int
if (config["providerKeyType"] != null)
{
if (config["providerKeyType"] == "guid")
{
_providerKeyAsGuid = true;
}
}
}
protected override Attempt<string> GetRawPassword(string username)
{
var found = MemberService.GetByUsername(username);
if (found == null) return Attempt<string>.Fail();
return Attempt.Succeed(found.RawPasswordValue);
}
public override string DefaultMemberTypeAlias
{
get
{
if (_hasDefaultMember == false)
{
lock (Locker)
{
if (_hasDefaultMember == false)
{
_defaultMemberTypeAlias = _memberTypeService.GetDefault();
if (_defaultMemberTypeAlias.IsNullOrWhiteSpace())
{
throw new ProviderException("No default member type alias is specified in the web.config string. Please add a 'defaultUserTypeAlias' to the add element in the provider declaration in web.config");
}
_hasDefaultMember = true;
}
}
}
return _defaultMemberTypeAlias;
}
}
}
}
using System.Collections.Specialized;
using System.Configuration.Provider;
using System.Web.Security;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
using Umbraco.Core.Models.Membership;
using Umbraco.Web.Composing;
namespace Umbraco.Web.Security.Providers
{
/// <summary>
/// Custom Membership Provider for Umbraco Members (User authentication for Frontend applications NOT umbraco CMS)
/// </summary>
public class MembersMembershipProvider : UmbracoMembershipProvider<IMembershipMemberService, IMember>, IUmbracoMemberTypeMembershipProvider
{
public MembersMembershipProvider()
: this(Current.Services.MemberService, Current.Services.MemberTypeService)
{ }
public MembersMembershipProvider(IMembershipMemberService<IMember> memberService, IMemberTypeService memberTypeService)
: base(memberService)
{
LockPropertyTypeAlias = Constants.Conventions.Member.IsLockedOut;
LastLockedOutPropertyTypeAlias = Constants.Conventions.Member.LastLockoutDate;
FailedPasswordAttemptsPropertyTypeAlias = Constants.Conventions.Member.FailedPasswordAttempts;
ApprovedPropertyTypeAlias = Constants.Conventions.Member.IsApproved;
CommentPropertyTypeAlias = Constants.Conventions.Member.Comments;
LastLoginPropertyTypeAlias = Constants.Conventions.Member.LastLoginDate;
LastPasswordChangedPropertyTypeAlias = Constants.Conventions.Member.LastPasswordChangeDate;
PasswordRetrievalQuestionPropertyTypeAlias = Constants.Conventions.Member.PasswordQuestion;
PasswordRetrievalAnswerPropertyTypeAlias = Constants.Conventions.Member.PasswordAnswer;
_memberTypeService = memberTypeService;
}
private readonly IMemberTypeService _memberTypeService;
private string _defaultMemberTypeAlias = "Member";
private volatile bool _hasDefaultMember;
private static readonly object Locker = new object();
private bool _providerKeyAsGuid;
public override string ProviderName => "MembersMembershipProvider";
protected override MembershipUser ConvertToMembershipUser(IMember entity)
{
return entity.AsConcreteMembershipUser(Name, _providerKeyAsGuid);
}
public string LockPropertyTypeAlias { get; }
public string LastLockedOutPropertyTypeAlias { get; }
public string FailedPasswordAttemptsPropertyTypeAlias { get; }
public string ApprovedPropertyTypeAlias { get; }
public string CommentPropertyTypeAlias { get; }
public string LastLoginPropertyTypeAlias { get; }
public string LastPasswordChangedPropertyTypeAlias { get; }
public string PasswordRetrievalQuestionPropertyTypeAlias { get; }
public string PasswordRetrievalAnswerPropertyTypeAlias { get; }
public override void Initialize(string name, NameValueCollection config)
{
base.Initialize(name, config);
// test for membertype (if not specified, choose the first member type available)
if (config["defaultMemberTypeAlias"] != null)
{
_defaultMemberTypeAlias = config["defaultMemberTypeAlias"];
if (_defaultMemberTypeAlias.IsNullOrWhiteSpace())
{
throw new ProviderException("No default member type alias is specified in the web.config string. Please add a 'defaultUserTypeAlias' to the add element in the provider declaration in web.config");
}
_hasDefaultMember = true;
}
//devs can configure the provider user key to be a guid if they want, by default it is int
if (config["providerKeyType"] != null)
{
if (config["providerKeyType"] == "guid")
{
_providerKeyAsGuid = true;
}
}
}
protected override Attempt<string> GetRawPassword(string username)
{
var found = MemberService.GetByUsername(username);
if (found == null) return Attempt<string>.Fail();
return Attempt.Succeed(found.RawPasswordValue);
}
public override string DefaultMemberTypeAlias
{
get
{
if (_hasDefaultMember == false)
{
lock (Locker)
{
if (_hasDefaultMember == false)
{
_defaultMemberTypeAlias = _memberTypeService.GetDefault();
if (_defaultMemberTypeAlias.IsNullOrWhiteSpace())
{
throw new ProviderException("No default member type alias is specified in the web.config string. Please add a 'defaultUserTypeAlias' to the add element in the provider declaration in web.config");
}
_hasDefaultMember = true;
}
}
}
return _defaultMemberTypeAlias;
}
}
}
}

View File

@@ -149,7 +149,7 @@ namespace Umbraco.Web.Security.Providers
if (MemberService.Exists(username))
{
status = MembershipCreateStatus.DuplicateUserName;
Current.Logger.Warn<UmbracoMembershipProvider<T, TEntity>>("Cannot create member as username already exists: " + username);
Current.Logger.Warn<UmbracoMembershipProvider<T, TEntity>>(() => $"Cannot create member as username already exists: {username}");
return null;
}
@@ -157,8 +157,7 @@ namespace Umbraco.Web.Security.Providers
if (MemberService.GetByEmail(email) != null && RequiresUniqueEmail)
{
status = MembershipCreateStatus.DuplicateEmail;
Current.Logger.Warn<UmbracoMembershipProvider<T, TEntity>>(
"Cannot create member as a member with the same email address exists: " + email);
Current.Logger.Warn<UmbracoMembershipProvider<T, TEntity>>(() => $"Cannot create member as a member with the same email address exists: {email}");
return null;
}
@@ -525,11 +524,8 @@ namespace Umbraco.Web.Security.Providers
if (member == null)
{
Current.Logger.Info<UmbracoMembershipProviderBase>(
string.Format(
"Login attempt failed for username {0} from IP address {1}, the user does not exist",
username,
GetCurrentRequestIpAddress()));
Current.Logger.Info<UmbracoMembershipProviderBase>(() =>
$"Login attempt failed for username {username} from IP address {GetCurrentRequestIpAddress()}, the user does not exist" );
return new ValidateUserResult
{
@@ -539,11 +535,8 @@ namespace Umbraco.Web.Security.Providers
if (member.IsApproved == false)
{
Current.Logger.Info<UmbracoMembershipProviderBase>(
string.Format(
"Login attempt failed for username {0} from IP address {1}, the user is not approved",
username,
GetCurrentRequestIpAddress()));
Current.Logger.Info<UmbracoMembershipProviderBase>(() =>
$"Login attempt failed for username {username} from IP address {GetCurrentRequestIpAddress()}, the user is not approved");
return new ValidateUserResult
{
@@ -553,11 +546,8 @@ namespace Umbraco.Web.Security.Providers
}
if (member.IsLockedOut)
{
Current.Logger.Info<UmbracoMembershipProviderBase>(
string.Format(
"Login attempt failed for username {0} from IP address {1}, the user is locked",
username,
GetCurrentRequestIpAddress()));
Current.Logger.Info<UmbracoMembershipProviderBase>(() =>
$"Login attempt failed for username {username} from IP address {GetCurrentRequestIpAddress()}, the user is locked");
return new ValidateUserResult
{
@@ -581,19 +571,13 @@ namespace Umbraco.Web.Security.Providers
member.IsLockedOut = true;
member.LastLockoutDate = DateTime.Now;
Current.Logger.Info<UmbracoMembershipProviderBase>(
string.Format(
"Login attempt failed for username {0} from IP address {1}, the user is now locked out, max invalid password attempts exceeded",
username,
GetCurrentRequestIpAddress()));
Current.Logger.Info<UmbracoMembershipProviderBase>(() =>
$"Login attempt failed for username {username} from IP address {GetCurrentRequestIpAddress()}, the user is now locked out, max invalid password attempts exceeded");
}
else
{
Current.Logger.Info<UmbracoMembershipProviderBase>(
string.Format(
"Login attempt failed for username {0} from IP address {1}",
username,
GetCurrentRequestIpAddress()));
Current.Logger.Info<UmbracoMembershipProviderBase>(() =>
$"Login attempt failed for username {username} from IP address {GetCurrentRequestIpAddress()}");
}
}
else
@@ -606,11 +590,8 @@ namespace Umbraco.Web.Security.Providers
member.LastLoginDate = DateTime.Now;
Current.Logger.Info<UmbracoMembershipProviderBase>(
string.Format(
"Login attempt succeeded for username {0} from IP address {1}",
username,
GetCurrentRequestIpAddress()));
Current.Logger.Info<UmbracoMembershipProviderBase>(() =>
$"Login attempt succeeded for username {username} from IP address {GetCurrentRequestIpAddress()}");
}
//don't raise events for this! It just sets the member dates, if we do raise events this will

View File

@@ -1,188 +1,188 @@
using System;
using System.Configuration.Provider;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using System.Web.Security;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Models.Identity;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
using Umbraco.Web.Composing;
namespace Umbraco.Web.Security.Providers
{
/// <summary>
/// Custom Membership Provider for Umbraco Users (User authentication for Umbraco Backend CMS)
/// </summary>
public class UsersMembershipProvider : UmbracoMembershipProvider<IMembershipUserService, IUser>, IUsersMembershipProvider
{
public UsersMembershipProvider()
: this(Current.Services.UserService, Current.Services.MemberTypeService)
{
}
public UsersMembershipProvider(IMembershipMemberService<IUser> memberService, IMemberTypeService memberTypeService)
: base(memberService)
{
_memberTypeService = memberTypeService;
}
private readonly IMemberTypeService _memberTypeService;
private string _defaultMemberTypeAlias = "writer";
private volatile bool _hasDefaultMember = false;
private static readonly object Locker = new object();
public override string ProviderName
{
get { return UmbracoConfig.For.UmbracoSettings().Providers.DefaultBackOfficeUserProvider; }
}
protected override MembershipUser ConvertToMembershipUser(IUser entity)
{
//the provider user key is always the int id
return entity.AsConcreteMembershipUser(Name, true);
}
private bool _allowManuallyChangingPassword = false;
private bool _enablePasswordReset = false;
/// <summary>
/// Indicates whether the membership provider is configured to allow users to reset their passwords.
/// </summary>
/// <value></value>
/// <returns>true if the membership provider supports password reset; otherwise, false. The default is FALSE for users.</returns>
public override bool EnablePasswordReset
{
get { return _enablePasswordReset; }
}
/// <summary>
/// For backwards compatibility, this provider supports this option by default it is FALSE for users
/// </summary>
public override bool AllowManuallyChangingPassword
{
get { return _allowManuallyChangingPassword; }
}
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
base.Initialize(name, config);
if (config == null) { throw new ArgumentNullException("config"); }
_allowManuallyChangingPassword = config.GetValue("allowManuallyChangingPassword", false);
_enablePasswordReset = config.GetValue("enablePasswordReset", false);
// test for membertype (if not specified, choose the first member type available)
// We'll support both names for legacy reasons: defaultUserTypeAlias & defaultUserGroupAlias
if (config["defaultUserTypeAlias"] != null)
{
if (config["defaultUserTypeAlias"].IsNullOrWhiteSpace() == false)
{
_defaultMemberTypeAlias = config["defaultUserTypeAlias"];
_hasDefaultMember = true;
}
}
if (_hasDefaultMember == false && config["defaultUserGroupAlias"] != null)
{
if (config["defaultUserGroupAlias"].IsNullOrWhiteSpace() == false)
{
_defaultMemberTypeAlias = config["defaultUserGroupAlias"];
_hasDefaultMember = true;
}
}
}
public override string DefaultMemberTypeAlias
{
get
{
if (_hasDefaultMember == false)
{
lock (Locker)
{
if (_hasDefaultMember == false)
{
_defaultMemberTypeAlias = _memberTypeService.GetDefault();
if (_defaultMemberTypeAlias.IsNullOrWhiteSpace())
{
throw new ProviderException("No default user group alias is specified in the web.config string. Please add a 'defaultUserTypeAlias' to the add element in the provider declaration in web.config");
}
_hasDefaultMember = true;
}
}
}
return _defaultMemberTypeAlias;
}
}
/// <summary>
/// Overridden in order to call the BackOfficeUserManager.UnlockUser method in order to raise the user audit events
/// </summary>
/// <param name="username"></param>
/// <param name="member"></param>
/// <returns></returns>
internal override bool PerformUnlockUser(string username, out IUser member)
{
var result = base.PerformUnlockUser(username, out member);
if (result)
{
var userManager = GetBackofficeUserManager();
if (userManager != null)
{
userManager.RaiseAccountUnlockedEvent(member.Id);
}
}
return result;
}
/// <summary>
/// Override in order to raise appropriate events via the <see cref="BackOfficeUserManager"/>
/// </summary>
/// <param name="username"></param>
/// <param name="password"></param>
/// <returns></returns>
internal override ValidateUserResult PerformValidateUser(string username, string password)
{
var result = base.PerformValidateUser(username, password);
var userManager = GetBackofficeUserManager();
if (userManager == null) return result;
if (result.Authenticated == false)
{
var count = result.Member.FailedPasswordAttempts;
if (count >= MaxInvalidPasswordAttempts)
{
userManager.RaiseAccountLockedEvent(result.Member.Id);
}
}
else
{
if (result.Member.FailedPasswordAttempts > 0)
{
//we have successfully logged in, if the failed password attempts was modified it means it was reset
if (result.Member.WasPropertyDirty("FailedPasswordAttempts"))
{
userManager.RaiseResetAccessFailedCountEvent(result.Member.Id);
}
}
}
return result;
}
internal BackOfficeUserManager<BackOfficeIdentityUser> GetBackofficeUserManager()
{
return HttpContext.Current == null
? null
: HttpContext.Current.GetOwinContext().GetBackOfficeUserManager();
}
}
}
using System;
using System.Configuration.Provider;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using System.Web.Security;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Models.Identity;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
using Umbraco.Web.Composing;
namespace Umbraco.Web.Security.Providers
{
/// <summary>
/// Custom Membership Provider for Umbraco Users (User authentication for Umbraco Backend CMS)
/// </summary>
public class UsersMembershipProvider : UmbracoMembershipProvider<IMembershipUserService, IUser>, IUsersMembershipProvider
{
public UsersMembershipProvider()
: this(Current.Services.UserService, Current.Services.MemberTypeService)
{
}
public UsersMembershipProvider(IMembershipMemberService<IUser> memberService, IMemberTypeService memberTypeService)
: base(memberService)
{
_memberTypeService = memberTypeService;
}
private readonly IMemberTypeService _memberTypeService;
private string _defaultMemberTypeAlias = "writer";
private volatile bool _hasDefaultMember = false;
private static readonly object Locker = new object();
public override string ProviderName
{
get { return UmbracoConfig.For.UmbracoSettings().Providers.DefaultBackOfficeUserProvider; }
}
protected override MembershipUser ConvertToMembershipUser(IUser entity)
{
//the provider user key is always the int id
return entity.AsConcreteMembershipUser(Name, true);
}
private bool _allowManuallyChangingPassword = false;
private bool _enablePasswordReset = false;
/// <summary>
/// Indicates whether the membership provider is configured to allow users to reset their passwords.
/// </summary>
/// <value></value>
/// <returns>true if the membership provider supports password reset; otherwise, false. The default is FALSE for users.</returns>
public override bool EnablePasswordReset
{
get { return _enablePasswordReset; }
}
/// <summary>
/// For backwards compatibility, this provider supports this option by default it is FALSE for users
/// </summary>
public override bool AllowManuallyChangingPassword
{
get { return _allowManuallyChangingPassword; }
}
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
base.Initialize(name, config);
if (config == null) { throw new ArgumentNullException("config"); }
_allowManuallyChangingPassword = config.GetValue("allowManuallyChangingPassword", false);
_enablePasswordReset = config.GetValue("enablePasswordReset", false);
// test for membertype (if not specified, choose the first member type available)
// We'll support both names for legacy reasons: defaultUserTypeAlias & defaultUserGroupAlias
if (config["defaultUserTypeAlias"] != null)
{
if (config["defaultUserTypeAlias"].IsNullOrWhiteSpace() == false)
{
_defaultMemberTypeAlias = config["defaultUserTypeAlias"];
_hasDefaultMember = true;
}
}
if (_hasDefaultMember == false && config["defaultUserGroupAlias"] != null)
{
if (config["defaultUserGroupAlias"].IsNullOrWhiteSpace() == false)
{
_defaultMemberTypeAlias = config["defaultUserGroupAlias"];
_hasDefaultMember = true;
}
}
}
public override string DefaultMemberTypeAlias
{
get
{
if (_hasDefaultMember == false)
{
lock (Locker)
{
if (_hasDefaultMember == false)
{
_defaultMemberTypeAlias = _memberTypeService.GetDefault();
if (_defaultMemberTypeAlias.IsNullOrWhiteSpace())
{
throw new ProviderException("No default user group alias is specified in the web.config string. Please add a 'defaultUserTypeAlias' to the add element in the provider declaration in web.config");
}
_hasDefaultMember = true;
}
}
}
return _defaultMemberTypeAlias;
}
}
/// <summary>
/// Overridden in order to call the BackOfficeUserManager.UnlockUser method in order to raise the user audit events
/// </summary>
/// <param name="username"></param>
/// <param name="member"></param>
/// <returns></returns>
internal override bool PerformUnlockUser(string username, out IUser member)
{
var result = base.PerformUnlockUser(username, out member);
if (result)
{
var userManager = GetBackofficeUserManager();
if (userManager != null)
{
userManager.RaiseAccountUnlockedEvent(member.Id);
}
}
return result;
}
/// <summary>
/// Override in order to raise appropriate events via the <see cref="BackOfficeUserManager"/>
/// </summary>
/// <param name="username"></param>
/// <param name="password"></param>
/// <returns></returns>
internal override ValidateUserResult PerformValidateUser(string username, string password)
{
var result = base.PerformValidateUser(username, password);
var userManager = GetBackofficeUserManager();
if (userManager == null) return result;
if (result.Authenticated == false)
{
var count = result.Member.FailedPasswordAttempts;
if (count >= MaxInvalidPasswordAttempts)
{
userManager.RaiseAccountLockedEvent(result.Member.Id);
}
}
else
{
if (result.Member.FailedPasswordAttempts > 0)
{
//we have successfully logged in, if the failed password attempts was modified it means it was reset
if (result.Member.WasPropertyDirty("FailedPasswordAttempts"))
{
userManager.RaiseResetAccessFailedCountEvent(result.Member.Id);
}
}
}
return result;
}
internal BackOfficeUserManager<BackOfficeIdentityUser> GetBackofficeUserManager()
{
return HttpContext.Current == null
? null
: HttpContext.Current.GetOwinContext().GetBackOfficeUserManager();
}
}
}

View File

@@ -1,14 +1,14 @@
namespace Umbraco.Web.Security
{
public enum ValidateRequestAttempt
{
Success = 0,
FailedNoPrivileges = 100,
//FailedTimedOut,
FailedNoContextId = 101,
FailedNoSsl = 102
}
}
namespace Umbraco.Web.Security
{
public enum ValidateRequestAttempt
{
Success = 0,
FailedNoPrivileges = 100,
//FailedTimedOut,
FailedNoContextId = 101,
FailedNoSsl = 102
}
}

View File

@@ -1,299 +1,299 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Security;
using System.Web;
using System.Web.Security;
using AutoMapper;
using Umbraco.Core;
using Umbraco.Core.Services;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Security;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Identity;
using Umbraco.Web.Composing;
using GlobalSettings = Umbraco.Core.Configuration.GlobalSettings;
namespace Umbraco.Web.Security
{
/// <summary>
/// A utility class used for dealing with USER security in Umbraco
/// </summary>
public class WebSecurity : DisposableObjectSlim
{
private HttpContextBase _httpContext;
private readonly IUserService _userService;
private readonly IGlobalSettings _globalSettings;
public WebSecurity(HttpContextBase httpContext, IUserService userService, IGlobalSettings globalSettings)
{
_httpContext = httpContext;
_userService = userService;
_globalSettings = globalSettings;
}
/// <summary>
/// Returns true or false if the currently logged in member is authorized based on the parameters provided
/// </summary>
/// <param name="allowAll"></param>
/// <param name="allowTypes"></param>
/// <param name="allowGroups"></param>
/// <param name="allowMembers"></param>
/// <returns></returns>
[Obsolete("Use MembershipHelper.IsMemberAuthorized instead")]
public bool IsMemberAuthorized(
bool allowAll = false,
IEnumerable<string> allowTypes = null,
IEnumerable<string> allowGroups = null,
IEnumerable<int> allowMembers = null)
{
if (Current.UmbracoContext == null)
{
return false;
}
var helper = Current.Container.GetInstance<MembershipHelper>();
return helper.IsMemberAuthorized(allowAll, allowTypes, allowGroups, allowMembers);
}
private IUser _currentUser;
/// <summary>
/// Gets the current user.
/// </summary>
/// <value>The current user.</value>
public virtual IUser CurrentUser
{
get
{
//only load it once per instance! (but make sure groups are loaded)
if (_currentUser == null)
{
var id = GetUserId();
_currentUser = id ? _userService.GetUserById(id.Result) : null;
}
return _currentUser;
}
}
private BackOfficeSignInManager _signInManager;
private BackOfficeSignInManager SignInManager
{
get
{
if (_signInManager == null)
{
var mgr = _httpContext.GetOwinContext().Get<BackOfficeSignInManager>();
if (mgr == null)
{
throw new NullReferenceException("Could not resolve an instance of " + typeof(BackOfficeSignInManager) + " from the " + typeof(IOwinContext));
}
_signInManager = mgr;
}
return _signInManager;
}
}
private BackOfficeUserManager<BackOfficeIdentityUser> _userManager;
protected BackOfficeUserManager<BackOfficeIdentityUser> UserManager
=> _userManager ?? (_userManager = _httpContext.GetOwinContext().GetBackOfficeUserManager());
/// <summary>
/// Logs a user in.
/// </summary>
/// <param name="userId">The user Id</param>
/// <returns>returns the number of seconds until their session times out</returns>
public virtual double PerformLogin(int userId)
{
var owinCtx = _httpContext.GetOwinContext();
//ensure it's done for owin too
owinCtx.Authentication.SignOut(Constants.Security.BackOfficeExternalAuthenticationType);
var user = UserManager.FindByIdAsync(userId).Result;
SignInManager.SignInAsync(user, isPersistent: true, rememberBrowser: false).Wait();
_httpContext.SetPrincipalForRequest(owinCtx.Request.User);
return TimeSpan.FromMinutes(_globalSettings.TimeOutInMinutes).TotalSeconds;
}
/// <summary>
/// Clears the current login for the currently logged in user
/// </summary>
public virtual void ClearCurrentLogin()
{
_httpContext.UmbracoLogout();
_httpContext.GetOwinContext().Authentication.SignOut(
Core.Constants.Security.BackOfficeAuthenticationType,
Core.Constants.Security.BackOfficeExternalAuthenticationType);
}
/// <summary>
/// Renews the user's login ticket
/// </summary>
public virtual void RenewLoginTimeout()
{
_httpContext.RenewUmbracoAuthTicket();
}
/// <summary>
/// Validates credentials for a back office user
/// </summary>
/// <param name="username"></param>
/// <param name="password"></param>
/// <returns></returns>
/// <remarks>
/// This uses ASP.NET Identity to perform the validation
/// </remarks>
public virtual bool ValidateBackOfficeCredentials(string username, string password)
{
//find the user by username
var user = UserManager.FindByNameAsync(username).Result;
return user != null && UserManager.CheckPasswordAsync(user, password).Result;
}
/// <summary>
/// Validates the current user to see if they have access to the specified app
/// </summary>
/// <param name="app"></param>
/// <returns></returns>
internal bool ValidateUserApp(string app)
{
//if it is empty, don't validate
if (app.IsNullOrWhiteSpace())
{
return true;
}
return CurrentUser.AllowedSections.Any(uApp => uApp.InvariantEquals(app));
}
/// <summary>
/// Gets the current user's id.
/// </summary>
/// <returns></returns>
public virtual Attempt<int> GetUserId()
{
var identity = _httpContext.GetCurrentIdentity(false);
return identity == null ? Attempt.Fail<int>() : Attempt.Succeed(Convert.ToInt32(identity.Id));
}
/// <summary>
/// Returns the current user's unique session id - used to mitigate csrf attacks or any other reason to validate a request
/// </summary>
/// <returns></returns>
public virtual string GetSessionId()
{
var identity = _httpContext.GetCurrentIdentity(false);
return identity?.SessionId;
}
/// <summary>
/// Validates the currently logged in user and ensures they are not timed out
/// </summary>
/// <returns></returns>
public virtual bool ValidateCurrentUser()
{
return ValidateCurrentUser(false, true) == ValidateRequestAttempt.Success;
}
/// <summary>
/// Validates the current user assigned to the request and ensures the stored user data is valid
/// </summary>
/// <param name="throwExceptions">set to true if you want exceptions to be thrown if failed</param>
/// <param name="requiresApproval">If true requires that the user is approved to be validated</param>
/// <returns></returns>
public virtual ValidateRequestAttempt ValidateCurrentUser(bool throwExceptions, bool requiresApproval = true)
{
//This will first check if the current user is already authenticated - which should be the case in nearly all circumstances
// since the authentication happens in the Module, that authentication also checks the ticket expiry. We don't
// need to check it a second time because that requires another decryption phase and nothing can tamper with it during the request.
if (IsAuthenticated() == false)
{
//There is no user
if (throwExceptions) throw new InvalidOperationException("The user has no umbraco contextid - try logging in");
return ValidateRequestAttempt.FailedNoContextId;
}
var user = CurrentUser;
// Check for console access
if (user == null || (requiresApproval && user.IsApproved == false) || (user.IsLockedOut && RequestIsInUmbracoApplication(_httpContext)))
{
if (throwExceptions) throw new ArgumentException("You have no priviledges to the umbraco console. Please contact your administrator");
return ValidateRequestAttempt.FailedNoPrivileges;
}
return ValidateRequestAttempt.Success;
}
private static bool RequestIsInUmbracoApplication(HttpContextBase context)
{
return context.Request.Path.ToLower().IndexOf(IOHelper.ResolveUrl(SystemDirectories.Umbraco).ToLower(), StringComparison.Ordinal) > -1;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Security;
using System.Web;
using System.Web.Security;
using AutoMapper;
using Umbraco.Core;
using Umbraco.Core.Services;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Security;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Identity;
using Umbraco.Web.Composing;
using GlobalSettings = Umbraco.Core.Configuration.GlobalSettings;
namespace Umbraco.Web.Security
{
/// <summary>
/// A utility class used for dealing with USER security in Umbraco
/// </summary>
public class WebSecurity : DisposableObjectSlim
{
private HttpContextBase _httpContext;
private readonly IUserService _userService;
private readonly IGlobalSettings _globalSettings;
public WebSecurity(HttpContextBase httpContext, IUserService userService, IGlobalSettings globalSettings)
{
_httpContext = httpContext;
_userService = userService;
_globalSettings = globalSettings;
}
/// <summary>
/// Authorizes the full request, checks for SSL and validates the current user
/// </summary>
/// <param name="throwExceptions">set to true if you want exceptions to be thrown if failed</param>
/// <returns></returns>
internal ValidateRequestAttempt AuthorizeRequest(bool throwExceptions = false)
{
// check for secure connection
if (_globalSettings.UseHttps && _httpContext.Request.IsSecureConnection == false)
{
if (throwExceptions) throw new SecurityException("This installation requires a secure connection (via SSL). Please update the URL to include https://");
return ValidateRequestAttempt.FailedNoSsl;
}
return ValidateCurrentUser(throwExceptions);
}
/// <summary>
/// Checks if the specified user as access to the app
/// </summary>
/// <param name="section"></param>
/// <param name="user"></param>
/// <returns></returns>
internal virtual bool UserHasSectionAccess(string section, IUser user)
{
return user.HasSectionAccess(section);
}
/// <summary>
/// Checks if the specified user by username as access to the app
/// </summary>
/// <param name="section"></param>
/// <param name="username"></param>
/// <returns></returns>
internal bool UserHasSectionAccess(string section, string username)
{
var user = _userService.GetByUsername(username);
if (user == null)
{
return false;
}
return user.HasSectionAccess(section);
}
/// <summary>
/// Ensures that a back office user is logged in
/// </summary>
/// <returns></returns>
public bool IsAuthenticated()
{
return _httpContext.User.Identity.IsAuthenticated && _httpContext.GetCurrentIdentity(false) != null;
}
protected override void DisposeResources()
{
_httpContext = null;
}
}
}
/// <summary>
/// Returns true or false if the currently logged in member is authorized based on the parameters provided
/// </summary>
/// <param name="allowAll"></param>
/// <param name="allowTypes"></param>
/// <param name="allowGroups"></param>
/// <param name="allowMembers"></param>
/// <returns></returns>
[Obsolete("Use MembershipHelper.IsMemberAuthorized instead")]
public bool IsMemberAuthorized(
bool allowAll = false,
IEnumerable<string> allowTypes = null,
IEnumerable<string> allowGroups = null,
IEnumerable<int> allowMembers = null)
{
if (Current.UmbracoContext == null)
{
return false;
}
var helper = Current.Container.GetInstance<MembershipHelper>();
return helper.IsMemberAuthorized(allowAll, allowTypes, allowGroups, allowMembers);
}
private IUser _currentUser;
/// <summary>
/// Gets the current user.
/// </summary>
/// <value>The current user.</value>
public virtual IUser CurrentUser
{
get
{
//only load it once per instance! (but make sure groups are loaded)
if (_currentUser == null)
{
var id = GetUserId();
_currentUser = id ? _userService.GetUserById(id.Result) : null;
}
return _currentUser;
}
}
private BackOfficeSignInManager _signInManager;
private BackOfficeSignInManager SignInManager
{
get
{
if (_signInManager == null)
{
var mgr = _httpContext.GetOwinContext().Get<BackOfficeSignInManager>();
if (mgr == null)
{
throw new NullReferenceException("Could not resolve an instance of " + typeof(BackOfficeSignInManager) + " from the " + typeof(IOwinContext));
}
_signInManager = mgr;
}
return _signInManager;
}
}
private BackOfficeUserManager<BackOfficeIdentityUser> _userManager;
protected BackOfficeUserManager<BackOfficeIdentityUser> UserManager
=> _userManager ?? (_userManager = _httpContext.GetOwinContext().GetBackOfficeUserManager());
/// <summary>
/// Logs a user in.
/// </summary>
/// <param name="userId">The user Id</param>
/// <returns>returns the number of seconds until their session times out</returns>
public virtual double PerformLogin(int userId)
{
var owinCtx = _httpContext.GetOwinContext();
//ensure it's done for owin too
owinCtx.Authentication.SignOut(Constants.Security.BackOfficeExternalAuthenticationType);
var user = UserManager.FindByIdAsync(userId).Result;
SignInManager.SignInAsync(user, isPersistent: true, rememberBrowser: false).Wait();
_httpContext.SetPrincipalForRequest(owinCtx.Request.User);
return TimeSpan.FromMinutes(_globalSettings.TimeOutInMinutes).TotalSeconds;
}
/// <summary>
/// Clears the current login for the currently logged in user
/// </summary>
public virtual void ClearCurrentLogin()
{
_httpContext.UmbracoLogout();
_httpContext.GetOwinContext().Authentication.SignOut(
Core.Constants.Security.BackOfficeAuthenticationType,
Core.Constants.Security.BackOfficeExternalAuthenticationType);
}
/// <summary>
/// Renews the user's login ticket
/// </summary>
public virtual void RenewLoginTimeout()
{
_httpContext.RenewUmbracoAuthTicket();
}
/// <summary>
/// Validates credentials for a back office user
/// </summary>
/// <param name="username"></param>
/// <param name="password"></param>
/// <returns></returns>
/// <remarks>
/// This uses ASP.NET Identity to perform the validation
/// </remarks>
public virtual bool ValidateBackOfficeCredentials(string username, string password)
{
//find the user by username
var user = UserManager.FindByNameAsync(username).Result;
return user != null && UserManager.CheckPasswordAsync(user, password).Result;
}
/// <summary>
/// Validates the current user to see if they have access to the specified app
/// </summary>
/// <param name="app"></param>
/// <returns></returns>
internal bool ValidateUserApp(string app)
{
//if it is empty, don't validate
if (app.IsNullOrWhiteSpace())
{
return true;
}
return CurrentUser.AllowedSections.Any(uApp => uApp.InvariantEquals(app));
}
/// <summary>
/// Gets the current user's id.
/// </summary>
/// <returns></returns>
public virtual Attempt<int> GetUserId()
{
var identity = _httpContext.GetCurrentIdentity(false);
return identity == null ? Attempt.Fail<int>() : Attempt.Succeed(Convert.ToInt32(identity.Id));
}
/// <summary>
/// Returns the current user's unique session id - used to mitigate csrf attacks or any other reason to validate a request
/// </summary>
/// <returns></returns>
public virtual string GetSessionId()
{
var identity = _httpContext.GetCurrentIdentity(false);
return identity?.SessionId;
}
/// <summary>
/// Validates the currently logged in user and ensures they are not timed out
/// </summary>
/// <returns></returns>
public virtual bool ValidateCurrentUser()
{
return ValidateCurrentUser(false, true) == ValidateRequestAttempt.Success;
}
/// <summary>
/// Validates the current user assigned to the request and ensures the stored user data is valid
/// </summary>
/// <param name="throwExceptions">set to true if you want exceptions to be thrown if failed</param>
/// <param name="requiresApproval">If true requires that the user is approved to be validated</param>
/// <returns></returns>
public virtual ValidateRequestAttempt ValidateCurrentUser(bool throwExceptions, bool requiresApproval = true)
{
//This will first check if the current user is already authenticated - which should be the case in nearly all circumstances
// since the authentication happens in the Module, that authentication also checks the ticket expiry. We don't
// need to check it a second time because that requires another decryption phase and nothing can tamper with it during the request.
if (IsAuthenticated() == false)
{
//There is no user
if (throwExceptions) throw new InvalidOperationException("The user has no umbraco contextid - try logging in");
return ValidateRequestAttempt.FailedNoContextId;
}
var user = CurrentUser;
// Check for console access
if (user == null || (requiresApproval && user.IsApproved == false) || (user.IsLockedOut && RequestIsInUmbracoApplication(_httpContext)))
{
if (throwExceptions) throw new ArgumentException("You have no priviledges to the umbraco console. Please contact your administrator");
return ValidateRequestAttempt.FailedNoPrivileges;
}
return ValidateRequestAttempt.Success;
}
private static bool RequestIsInUmbracoApplication(HttpContextBase context)
{
return context.Request.Path.ToLower().IndexOf(IOHelper.ResolveUrl(SystemDirectories.Umbraco).ToLower(), StringComparison.Ordinal) > -1;
}
/// <summary>
/// Authorizes the full request, checks for SSL and validates the current user
/// </summary>
/// <param name="throwExceptions">set to true if you want exceptions to be thrown if failed</param>
/// <returns></returns>
internal ValidateRequestAttempt AuthorizeRequest(bool throwExceptions = false)
{
// check for secure connection
if (_globalSettings.UseHttps && _httpContext.Request.IsSecureConnection == false)
{
if (throwExceptions) throw new SecurityException("This installation requires a secure connection (via SSL). Please update the URL to include https://");
return ValidateRequestAttempt.FailedNoSsl;
}
return ValidateCurrentUser(throwExceptions);
}
/// <summary>
/// Checks if the specified user as access to the app
/// </summary>
/// <param name="section"></param>
/// <param name="user"></param>
/// <returns></returns>
internal virtual bool UserHasSectionAccess(string section, IUser user)
{
return user.HasSectionAccess(section);
}
/// <summary>
/// Checks if the specified user by username as access to the app
/// </summary>
/// <param name="section"></param>
/// <param name="username"></param>
/// <returns></returns>
internal bool UserHasSectionAccess(string section, string username)
{
var user = _userService.GetByUsername(username);
if (user == null)
{
return false;
}
return user.HasSectionAccess(section);
}
/// <summary>
/// Ensures that a back office user is logged in
/// </summary>
/// <returns></returns>
public bool IsAuthenticated()
{
return _httpContext.User.Identity.IsAuthenticated && _httpContext.GetCurrentIdentity(false) != null;
}
protected override void DisposeResources()
{
_httpContext = null;
}
}
}