/// <summary> /// Create a new <see cref="SetUser"/>. /// </summary> /// <param name="userAccount"> /// The <see cref="UserAccount"/> to switch to. /// </param> /// <param name="secondaryAccount"> /// The secondary account. /// </param> /// <exception cref="ArgumentNullException"> /// <paramref name="userAccount"/> cannot be null. /// </exception> /// <exception cref="InvalidOperationException"> /// <see cref="RequestContext"/> must be set. /// </exception> public SetUser(UserAccount userAccount, UserAccount secondaryAccount = null) { if (userAccount == null) { throw new ArgumentNullException("userAccount"); } if (!RequestContext.IsSet) { throw new InvalidOperationException("RequestContext is not set."); } UserAccount = userAccount; var identity = new IdentityInfo(userAccount.Id, userAccount.Name); var secondaryIdentity = secondaryAccount != null ? new IdentityInfo(secondaryAccount.Id, secondaryAccount.Name) : null; OldContext = RequestContext.GetContext(); RequestContext.SetContext( identity, OldContext.Tenant, OldContext.Culture, OldContext.TimeZone, secondaryIdentity ); }
/// <summary> /// Checks the credentials for the specified user account. /// </summary> /// <param name="username">A string containing the username associated with the user account. This cannot be null or empty.</param> /// <param name="password">A string containing the password associated with the user account. This cannot be null.</param> /// <param name="tenantName">Name of the tenant. This cannot be null or empty.</param> /// <param name="updateAccount">A Boolean value that controls whether or not user account information associated with the request is updated.</param> /// <param name="skipPasswordExpiryCheck">A Boolean that controls whether to perform the password expiry check.</param> /// <returns>An object representing the identity of the user account</returns> /// <exception cref="ArgumentException"> /// The given account details are incorrect. /// </exception> /// <exception cref="TenantDisabledException"> /// The tenant is disabled, meaning no user in that tenant can authenticate. /// </exception> private static RequestContextData ValidateAccount(string username, string password, string tenantName, bool updateAccount, bool skipPasswordExpiryCheck = false) { if (String.IsNullOrEmpty(username)) { throw new ArgumentException("The specified username parameter is invalid."); } if (password == null) { throw new ArgumentNullException("password"); } if (String.IsNullOrEmpty(tenantName)) { throw new ArgumentException("The specified tenant parameter is invalid."); } RequestContextData contextData; // Cache the original request context RequestContext originalContextData = RequestContext.GetContext( ); try { TenantInfo tenantInfo; UserAccount userAccount; PasswordPolicy passwordPolicy; // Set the system administrators context RequestContext.SetSystemAdministratorContext( ); try { if (password.Length > MaxPasswordLength) { throw new ArgumentException( string.Format("Password cannot be longer than {0} characters", MaxPasswordLength), "password"); } if (tenantName == SpecialStrings.GlobalTenant) { // Create a Dummy Tenant Info // No need to set the context as we are already the global admin tenantInfo = new TenantInfo(0) { Name = SpecialStrings.GlobalTenant }; } else { // Get the tenant with the specified name Tenant tenant = TenantHelper.Find(tenantName); if (tenant == null) { throw new ArgumentException(string.Format("Unknown tenant '{0}'", tenantName), "tenantName"); } if (tenant.IsTenantDisabled ?? false) { throw new TenantDisabledException(tenantName); } // Set the tenant administrators context RequestContext.SetTenantAdministratorContext(tenant.Id); tenantInfo = new TenantInfo(tenant.Id) { Name = tenantName.ToUpperInvariant() }; } // Get the user account with the specified name userAccount = Entity.GetByField <UserAccount>(username, true, new EntityRef("core", "name")).FirstOrDefault( ); if (userAccount == null) { throw new ArgumentException(string.Format("Could not find user '{0}' in tenant '{1}'", username, tenantName)); } // Get the password policy passwordPolicy = Entity.Get <PasswordPolicy>(new EntityRef("core:passwordPolicyInstance")); if (passwordPolicy == null) { throw new ArgumentException(string.Format("Could not find password policy for tenant '{0}'", tenantName)); } } catch (Exception ex) { EventLog.Application.WriteWarning("Login failed: " + ex.Message); // Validate a password here to mitigate timing attacks. An attacker could use this // to guess which passwords are valid by timing the login. Without this, logins that // have a invalid user name and tenant will be quicker than those with a valid user name. CryptoHelper.CreateEncodedSaltedHash("test password"); throw; } ValidateAccount(userAccount, passwordPolicy, password, updateAccount, skipPasswordExpiryCheck); // Set the context data var identityInfo = new IdentityInfo(userAccount.Id, userAccount.Name); contextData = new RequestContextData(identityInfo, tenantInfo, CultureHelper.GetUiThreadCulture(CultureType.Specific)); } finally { // Restore the original request context if ((originalContextData != null) && (originalContextData.IsValid)) { RequestContext.SetContext(originalContextData); } } return(contextData); }
/// <summary> /// Gets the request context data factory implementation. /// </summary> /// <param name="key">The key.</param> /// <returns>RequestContextData.</returns> private IdentityProviderContextCacheValue GetRequestContextDataFactoryImpl(IdentityProviderContextCacheKey key) { IdentityProviderContextCacheValue cacheValue; var isSystem = key.IdentityProviderId == WellKnownAliases.CurrentTenant.ReadiNowIdentityProviderInstance; QueryBuild queryResult = GetSql(isSystem); using (var dbContext = DatabaseContext.GetContext()) { using (var command = dbContext.CreateCommand(queryResult.Sql)) { command.AddParameterWithValue("@identityProviderUser", key.IdentityProviderUserName, 500); if (!isSystem) { command.AddParameterWithValue("@identityProviderId", key.IdentityProviderId); } dbContext.AddParameter(command, "@tenant", DbType.Int64, key.TenantId); if (queryResult.SharedParameters != null) { foreach (var parameter in queryResult.SharedParameters) { dbContext.AddParameter(command, parameter.Value, parameter.Key.Type, parameter.Key.Value); } } using (var reader = command.ExecuteReader()) { if (!reader.Read()) { return(null); } var userAccountId = reader.GetInt64(0); var userAccountName = reader.GetString(1); var identityProviderId = isSystem ? key.IdentityProviderId : reader.GetInt64(2); var identityProviderUserId = isSystem ? userAccountId : reader.GetInt64(3); var identityProviderTypeAlias = isSystem ? "core:readiNowIdentityProvider" : "core:" + reader.GetString(4); int accountStatusColumnIndex = isSystem ? 2 : 5; long accountStatusId = -1; if (!reader.IsDBNull(accountStatusColumnIndex)) { accountStatusId = reader.GetInt64(accountStatusColumnIndex); } var identityInfo = new IdentityInfo(userAccountId, userAccountName) { IdentityProviderId = identityProviderId, IdentityProviderUserId = identityProviderUserId, IdentityProviderTypeAlias = identityProviderTypeAlias }; var tenantInfo = new TenantInfo(key.TenantId); var contextData = new RequestContextData(identityInfo, tenantInfo, CultureHelper.GetUiThreadCulture(CultureType.Specific)); cacheValue = new IdentityProviderContextCacheValue(contextData, accountStatusId); if (CacheContext.IsSet()) { using (CacheContext cacheContext = CacheContext.GetContext()) { cacheContext.Entities.Add(userAccountId); cacheContext.Entities.Add(identityProviderId); if (identityProviderUserId != userAccountId) { cacheContext.Entities.Add(identityProviderUserId); } } } } } } return(cacheValue); }
/// <summary> /// Obtain a request context for the specified user belonging to the indicated tenant /// if available. If the specified tenant or user does not exist, or the given password /// is incorrect, null is returned. /// </summary> /// <param name="username">Username for which the request context is to be retrieved.</param> /// <param name="password">Users password to validate that the user is known.</param> /// <param name="tenantName">Name of the tenant.</param> /// <returns> /// A new <see cref="RequestContextData" /> representing the specified user and tenant. /// </returns> /// <remarks> /// The returned RequestContextData object is not reused between requests so modifications made to /// its parameters dictionary etc are specific to this instance. The internal IdentityInfo and TenantInfo /// are references to the cached instances so modifications should not be made to them. /// </remarks> public static RequestContextData GetRequestContext(string username, string password, string tenantName) { TenantUserAccountCacheEntry tenantEntry; TenantUserAccountCacheEntry.UserAccountCacheEntry accountEntry = null; bool foundValue; lock ( LockObject ) { foundValue = Cache.TryGetValue(tenantName, out tenantEntry); } if (foundValue) { ///// // Tenant has been previously cached. ///// if (!tenantEntry.Valid) { return(null); } } else { TenantInfo tenantInfo; if (tenantName == SpecialStrings.GlobalTenant) { tenantInfo = new TenantInfo(0) { Name = SpecialStrings.GlobalTenant }; } else { Tenant tenant; using (new AdministratorContext()) { tenant = Entity.GetByName <Tenant>(tenantName).FirstOrDefault(); } if (tenant == null) { lock (LockObject) { ///// // Store an invalid entry to ensure multiple requests to this tenant don't // continually hit the database. ///// Cache[tenantName] = new TenantUserAccountCacheEntry(); } return(null); } using (new AdministratorContext()) { tenantInfo = new TenantInfo(tenant.Id) { Name = tenantName.ToUpperInvariant() }; } } ///// // Construct the new cache entry and cache it. ///// using (new AdministratorContext( )) { tenantEntry = new TenantUserAccountCacheEntry(tenantInfo); } lock ( LockObject ) { Cache[tenantName] = tenantEntry; } } ///// // Get the account information from the cache if possible. ///// if (tenantEntry != null) { accountEntry = tenantEntry.Get(username, password); } if (accountEntry == null) { return(null); } ///// // Construct a new RequestContextData instance and return it. ///// var identityInfo = new IdentityInfo(accountEntry.Id, username); return(new RequestContextData(identityInfo, tenantEntry.Tenant, CultureHelper.GetUiThreadCulture(CultureType.Neutral))); }