Example #1
0
        /// <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
                );
        }
Example #2
0
        /// <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)));
        }