using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Security; using Newtonsoft.Json.Linq; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Models.Membership; using Umbraco.Core.Security; using umbraco; using umbraco.DataLayer; using umbraco.businesslogic.Exceptions; using GlobalSettings = Umbraco.Core.Configuration.GlobalSettings; using Member = umbraco.cms.businesslogic.member.Member; using User = umbraco.BusinessLogic.User; namespace Umbraco.Web.Security { /// /// A utility class used for dealing with security in Umbraco /// public class WebSecurity : DisposableObject { private HttpContextBase _httpContext; private ApplicationContext _applicationContext; public WebSecurity(HttpContextBase httpContext, ApplicationContext applicationContext) { _httpContext = httpContext; _applicationContext = applicationContext; //This ensures the dispose method is called when the request terminates, though // we also ensure this happens in the Umbraco module because the UmbracoContext is added to the // http context items. _httpContext.DisposeOnPipelineCompleted(this); } /// /// Returns true or false if the currently logged in member is authorized based on the parameters provided /// /// /// /// /// /// public bool IsMemberAuthorized( bool allowAll = false, IEnumerable allowTypes = null, IEnumerable allowGroups = null, IEnumerable allowMembers = null) { if (allowAll) return true; if (allowTypes == null) allowTypes = Enumerable.Empty(); if (allowGroups == null) allowGroups = Enumerable.Empty(); if (allowMembers == null) allowMembers = Enumerable.Empty(); // Allow by default var allowAction = true; // Get member details var member = Member.GetCurrentMember(); if (member == null) { // If not logged on, not allowed allowAction = false; } else { // If types defined, check member is of one of those types var allowTypesList = allowTypes as IList ?? allowTypes.ToList(); if (allowTypesList.Any(allowType => allowType != string.Empty)) { // Allow only if member's type is in list allowAction = allowTypesList.Select(x => x.ToLowerInvariant()).Contains(member.ContentType.Alias.ToLowerInvariant()); } // If groups defined, check member is of one of those groups var allowGroupsList = allowGroups as IList ?? allowGroups.ToList(); if (allowAction && allowGroupsList.Any(allowGroup => allowGroup != string.Empty)) { // Allow only if member is assigned to a group in the list var groups = Roles.GetRolesForUser(member.LoginName); allowAction = allowGroupsList.Select(s => s.ToLowerInvariant()).Intersect(groups.Select(myGroup => myGroup.ToLowerInvariant())).Any(); } // If specific members defined, check member is of one of those if (allowAction && allowMembers.Any()) { // Allow only if member's Id is in the list allowAction = allowMembers.Contains(member.Id); } } return allowAction; } private static readonly int UmbracoTimeOutInMinutes = GlobalSettings.TimeOutInMinutes; private IUser _currentUser; /// /// Gets the current user. /// /// The current user. internal IUser CurrentUser { get { //only load it once per instance! if (_currentUser == null) { var id = GetUserId(); if (id == -1) { return null; } _currentUser = _applicationContext.Services.UserService.GetUserById(id); } return _currentUser; } } /// /// Logs a user in. /// /// The user Id public void PerformLogin(int userId) { var user = _applicationContext.Services.UserService.GetUserById(userId); PerformLogin(user); } internal void PerformLogin(IUser user) { _httpContext.CreateUmbracoAuthTicket(new UserData { Id = user.Id, AllowedApplications = user.AllowedSections.ToArray(), RealName = user.Name, //currently we only have one user type! Roles = new[] { user.UserType.Alias }, StartContentNode = user.StartContentId, StartMediaNode = user.StartMediaId, Username = user.Username, Culture = ui.Culture(user.Language) }); LogHelper.Info("User Id: {0} logged in", () => user.Id); } /// /// Clears the current login for the currently logged in user /// public void ClearCurrentLogin() { _httpContext.UmbracoLogout(); } public void RenewLoginTimeout() { _httpContext.RenewUmbracoAuthTicket(UmbracoTimeOutInMinutes); } /// /// Validates credentials for a back office user /// /// /// /// internal bool ValidateBackOfficeCredentials(string username, string password) { var membershipProvider = Membership.Providers[UmbracoConfiguration.Current.UmbracoSettings.Providers.DefaultBackOfficeUserProvider]; return membershipProvider != null && membershipProvider.ValidateUser(username, password); } /// /// Changes password for a back office user /// /// /// /// internal bool ChangePassword(string oldpassword, string newpassword) { var membershipProvider = Membership.Providers[LegacyUmbracoSettings.DefaultBackofficeProvider]; return membershipProvider.GetUser(CurrentUser.Username, true).ChangePassword(oldpassword, newpassword); } /// /// Validates the user node tree permissions. /// /// /// The path. /// The action. /// internal bool ValidateUserNodeTreePermissions(User umbracoUser, string path, string action) { var permissions = umbracoUser.GetPermissions(path); if (permissions.IndexOf(action, StringComparison.Ordinal) > -1 && (path.Contains("-20") || ("," + path + ",").Contains("," + umbracoUser.StartNodeId + ","))) return true; var user = umbracoUser; LogHelper.Info("User {0} has insufficient permissions in UmbracoEnsuredPage: '{1}', '{2}', '{3}'", () => user.Name, () => path, () => permissions, () => action); return false; } /// /// Validates the current user to see if they have access to the specified app /// /// /// internal bool ValidateUserApp(string app) { //if it is empty, don't validate if (app.IsNullOrWhiteSpace()) { return true; } var userApps = _applicationContext.Services.UserService.GetUserSections(CurrentUser); return userApps.Any(uApp => uApp.InvariantEquals(app)); } internal void UpdateLogin() { _httpContext.RenewUmbracoAuthTicket(UmbracoTimeOutInMinutes); } internal long GetTimeout() { var ticket = _httpContext.GetUmbracoAuthTicket(); var ticks = ticket.Expiration.Ticks - DateTime.Now.Ticks; return ticks; } /// /// Gets the user id. /// /// This is not used /// [Obsolete("This method is no longer used, use the GetUserId() method without parameters instead")] public int GetUserId(string umbracoUserContextId) { return GetUserId(); } /// /// Gets the currnet user's id. /// /// public int GetUserId() { var identity = _httpContext.GetCurrentIdentity(); if (identity == null) return -1; return Convert.ToInt32(identity.Id); } /// /// Validates the user context ID. /// /// This doesn't do anything /// [Obsolete("This method is no longer used, use the ValidateCurrentUser() method instead")] public bool ValidateUserContextId(string currentUmbracoUserContextId) { return ValidateCurrentUser(); } /// /// Validates the currently logged in user and ensures they are not timed out /// /// public bool ValidateCurrentUser() { var ticket = _httpContext.GetUmbracoAuthTicket(); if (ticket != null) { if (ticket.Expired == false) { return true; } } return false; } /// /// Validates the current user /// /// set to true if you want exceptions to be thrown if failed /// internal ValidateRequestAttempt ValidateCurrentUser(bool throwExceptions) { var ticket = _httpContext.GetUmbracoAuthTicket(); if (ticket != null) { if (ticket.Expired == false) { var user = CurrentUser; // Check for console access if (user.IsLockedOut || (user.NoConsole && GlobalSettings.RequestIsInUmbracoApplication(_httpContext) && GlobalSettings.RequestIsLiveEditRedirector(_httpContext) == false)) { if (throwExceptions) throw new ArgumentException("You have no priviledges to the umbraco console. Please contact your administrator"); return ValidateRequestAttempt.FailedNoPrivileges; } UpdateLogin(); return ValidateRequestAttempt.Success; } if (throwExceptions) throw new ArgumentException("User has timed out!!"); return ValidateRequestAttempt.FailedTimedOut; } if (throwExceptions) throw new InvalidOperationException("The user has no umbraco contextid - try logging in"); return ValidateRequestAttempt.FailedNoContextId; } /// /// Authorizes the full request, checks for SSL and validates the current user /// /// set to true if you want exceptions to be thrown if failed /// internal ValidateRequestAttempt AuthorizeRequest(bool throwExceptions = false) { // check for secure connection if (GlobalSettings.UseSSL && _httpContext.Request.IsSecureConnection == false) { if (throwExceptions) throw new UserAuthorizationException("This installation requires a secure connection (via SSL). Please update the URL to include https://"); return ValidateRequestAttempt.FailedNoSsl; } return ValidateCurrentUser(throwExceptions); } /// /// Checks if the specified user as access to the app /// /// /// /// internal bool UserHasAppAccess(string app, IUser user) { var apps = _applicationContext.Services.UserService.GetUserSections(user); return apps.Any(uApp => uApp.InvariantEquals(app)); } [Obsolete("Do not use this method if you don't have to, use the overload with IUser instead")] internal bool UserHasAppAccess(string app, User user) { return user.Applications.Any(uApp => uApp.alias == app); } /// /// Checks if the specified user by username as access to the app /// /// /// /// internal bool UserHasAppAccess(string app, string username) { var user = _applicationContext.Services.UserService.GetUserByUserName(username); if (user == null) { return false; } return UserHasAppAccess(app, user); } [Obsolete("This is no longer used at all, it will always return a new GUID though if a user is logged in")] public string UmbracoUserContextId { get { return _httpContext.GetUmbracoAuthTicket() == null ? "" : Guid.NewGuid().ToString(); } set { } } protected override void DisposeResources() { _httpContext = null; _applicationContext = null; } } }