using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Web; using System.Web.Security; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Logging; using Umbraco.Core.Security; using Umbraco.Web.Models; using umbraco; using umbraco.BusinessLogic; using umbraco.DataLayer; using umbraco.businesslogic.Exceptions; using umbraco.cms.businesslogic.member; using GlobalSettings = Umbraco.Core.Configuration.GlobalSettings; using UmbracoSettings = Umbraco.Core.Configuration.UmbracoSettings; namespace Umbraco.Web.Security { /// /// A utility class used for dealing with USER security in Umbraco /// public class WebSecurity { /// /// Returns true or false if the currently logged in member is authorized based on the parameters provided /// /// /// /// /// /// [Obsolete("Use MembershipHelper.IsMemberAuthorized instead")] public bool IsMemberAuthorized( bool allowAll = false, IEnumerable allowTypes = null, IEnumerable allowGroups = null, IEnumerable allowMembers = null) { if (HttpContext.Current == null || ApplicationContext.Current == null) { return false; } var helper = new MembershipHelper(ApplicationContext.Current, new HttpContextWrapper(HttpContext.Current)); return helper.IsMemberAuthorized(allowAll, allowTypes, allowGroups, allowMembers); } /// /// Gets the SQL helper. /// /// The SQL helper. private ISqlHelper SqlHelper { get { return Application.SqlHelper; } } private const long TicksPrMinute = 600000000; private static readonly int UmbracoTimeOutInMinutes = GlobalSettings.TimeOutInMinutes; private User _currentUser; /// /// Gets the current user. /// /// The current user. /// /// This is internal because we don't want to expose the legacy User object on this class, instead we'll wait until IUser /// is public. If people want to reference the current user, they can reference it from the UmbracoContext. /// internal User CurrentUser { get { //only load it once per instance! return _currentUser ?? (_currentUser = User.GetCurrent()); } } /// /// Logs a user in. /// /// The user Id public void PerformLogin(int userId) { var retVal = Guid.NewGuid(); SqlHelper.ExecuteNonQuery( "insert into umbracoUserLogins (contextID, userID, timeout) values (@contextId,'" + userId + "','" + (DateTime.Now.Ticks + (TicksPrMinute * UmbracoTimeOutInMinutes)) + "') ", SqlHelper.CreateParameter("@contextId", retVal)); UmbracoUserContextId = retVal.ToString(); LogHelper.Info("User Id: {0} logged in", () => userId); } /// /// Clears the current login for the currently logged in user /// public void ClearCurrentLogin() { // Added try-catch in case login doesn't exist in the database // Either due to old cookie or running multiple sessions on localhost with different port number try { SqlHelper.ExecuteNonQuery( "DELETE FROM umbracoUserLogins WHERE contextId = @contextId", SqlHelper.CreateParameter("@contextId", UmbracoUserContextId)); } catch (Exception ex) { LogHelper.Error(string.Format("Login with contextId {0} didn't exist in the database", UmbracoUserContextId), ex); } //this clears the cookie UmbracoUserContextId = ""; } public void RenewLoginTimeout() { // only call update if more than 1/10 of the timeout has passed SqlHelper.ExecuteNonQuery( "UPDATE umbracoUserLogins SET timeout = @timeout WHERE contextId = @contextId", SqlHelper.CreateParameter("@timeout", DateTime.Now.Ticks + (TicksPrMinute * UmbracoTimeOutInMinutes)), SqlHelper.CreateParameter("@contextId", UmbracoUserContextId)); } /// /// Validates credentials for a back office user /// /// /// /// internal bool ValidateBackOfficeCredentials(string username, string password) { var membershipProvider = Membership.Providers[UmbracoSettings.DefaultBackofficeProvider]; return membershipProvider != null && membershipProvider.ValidateUser(username, password); } /// /// 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; } return CurrentUser.Applications.Any(uApp => uApp.alias.InvariantEquals(app)); } internal void UpdateLogin(long timeout) { // only call update if more than 1/10 of the timeout has passed if (timeout - (((TicksPrMinute * UmbracoTimeOutInMinutes) * 0.8)) < DateTime.Now.Ticks) SqlHelper.ExecuteNonQuery( "UPDATE umbracoUserLogins SET timeout = @timeout WHERE contextId = @contextId", SqlHelper.CreateParameter("@timeout", DateTime.Now.Ticks + (TicksPrMinute * UmbracoTimeOutInMinutes)), SqlHelper.CreateParameter("@contextId", UmbracoUserContextId)); } internal long GetTimeout(string umbracoUserContextId) { return ApplicationContext.Current.ApplicationCache.GetCacheItem( CacheKeys.UserContextTimeoutCacheKey + umbracoUserContextId, new TimeSpan(0, UmbracoTimeOutInMinutes / 10, 0), () => GetTimeout(true)); } internal long GetTimeout(bool byPassCache) { if (UmbracoSettings.KeepUserLoggedIn) RenewLoginTimeout(); if (byPassCache) { return SqlHelper.ExecuteScalar("select timeout from umbracoUserLogins where contextId=@contextId", SqlHelper.CreateParameter("@contextId", new Guid(UmbracoUserContextId)) ); } return GetTimeout(UmbracoUserContextId); } /// /// Gets the user id. /// /// The umbraco user context ID. /// public int GetUserId(string umbracoUserContextId) { //need to parse to guid Guid guid; if (Guid.TryParse(umbracoUserContextId, out guid) == false) { return -1; } var id = ApplicationContext.Current.ApplicationCache.GetCacheItem( CacheKeys.UserContextCacheKey + umbracoUserContextId, new TimeSpan(0, UmbracoTimeOutInMinutes / 10, 0), () => SqlHelper.ExecuteScalar( "select userID from umbracoUserLogins where contextID = @contextId", SqlHelper.CreateParameter("@contextId", guid))); if (id == null) return -1; return id.Value; } /// /// Validates the user context ID. /// /// The umbraco user context ID. /// public bool ValidateUserContextId(string currentUmbracoUserContextId) { if ((currentUmbracoUserContextId != "")) { int uid = GetUserId(currentUmbracoUserContextId); long timeout = GetTimeout(currentUmbracoUserContextId); if (timeout > DateTime.Now.Ticks) { return true; } var user = User.GetUser(uid); LogHelper.Info(typeof(WebSecurity), "User {0} (Id:{1}) logged out", () => user.Name, () => user.Id); } return false; } /// /// Validates the current user /// /// /// set to true if you want exceptions to be thrown if failed /// internal ValidateRequestAttempt ValidateCurrentUser(HttpContextBase httpContext, bool throwExceptions = false) { if (UmbracoUserContextId != "") { var uid = GetUserId(UmbracoUserContextId); var timeout = GetTimeout(UmbracoUserContextId); if (timeout > DateTime.Now.Ticks) { var user = User.GetUser(uid); // Check for console access if (user.Disabled || (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(timeout); 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(HttpContextBase httpContext, 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(httpContext, throwExceptions); } /// /// Checks if the specified user as access to the app /// /// /// /// 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 uid = User.getUserId(username); if (uid < 0) return false; var usr = User.GetUser(uid); if (usr == null) return false; return UserHasAppAccess(app, usr); } /// /// Gets or sets the umbraco user context ID. /// /// The umbraco user context ID. public string UmbracoUserContextId { get { var authTicket = HttpContext.Current.GetUmbracoAuthTicket(); if (authTicket == null) { return ""; } var identity = authTicket.CreateUmbracoIdentity(); if (identity == null) { HttpContext.Current.UmbracoLogout(); return ""; } return identity.UserContextId; } set { if (value.IsNullOrWhiteSpace()) { HttpContext.Current.UmbracoLogout(); } else { var uid = GetUserId(value); if (uid == -1) { HttpContext.Current.UmbracoLogout(); } else { var user = User.GetUser(uid); HttpContext.Current.CreateUmbracoAuthTicket( new UserData { Id = uid, AllowedApplications = user.Applications.Select(x => x.alias).ToArray(), Culture = ui.Culture(user), RealName = user.Name, Roles = new string[] { user.UserType.Alias }, StartContentNode = user.StartNodeId, StartMediaNode = user.StartMediaId, UserContextId = value, Username = user.LoginName }); } } } } } }