public async Task <IActionResult> Create([FromBody] UserUpdate model, CancellationToken cancellationToken)
        {
            if (model == null)
            {
                throw new ArgumentNullException(nameof(model));
            }

            if (!(model.Password == null ^ model.SystemIdentifier == null))
            {
                return(BadRequest(new ErrorMessage {
                    Message = "User must have exactly one of either a password or system identifier!"
                }));
            }

            model.Name = model.Name?.Trim();
            if (model.Name?.Length == 0)
            {
                model.Name = null;
            }

            if (!(model.Name == null ^ model.SystemIdentifier == null))
            {
                return(BadRequest(new ErrorMessage {
                    Message = "User must have a name if and only if user has no system identifier!"
                }));
            }

            var fail = CheckValidName(model);

            if (fail != null)
            {
                return(fail);
            }

            var dbUser = new Models.User
            {
                AdministrationRights = model.AdministrationRights ?? AdministrationRights.None,
                CreatedAt            = DateTimeOffset.Now,
                CreatedBy            = AuthenticationContext.User,
                Enabled = model.Enabled ?? false,
                InstanceManagerRights = model.InstanceManagerRights ?? InstanceManagerRights.None,
                Name             = model.Name,
                SystemIdentifier = model.SystemIdentifier,
                InstanceUsers    = new List <Models.InstanceUser>()
            };

            if (model.SystemIdentifier != null)
            {
                try
                {
                    using (var sysIdentity = await systemIdentityFactory.CreateSystemIdentity(dbUser, cancellationToken).ConfigureAwait(false))
                    {
                        if (sysIdentity == null)
                        {
                            return(StatusCode((int)HttpStatusCode.Gone));
                        }
                        dbUser.Name             = sysIdentity.Username;
                        dbUser.SystemIdentifier = sysIdentity.Uid;
                    }
                }
                catch (NotImplementedException)
                {
                    return(StatusCode((int)HttpStatusCode.NotImplemented));
                }
            }
            else
            {
                var result = TrySetPassword(dbUser, model.Password);
                if (result != null)
                {
                    return(result);
                }
            }

            dbUser.CanonicalName = Models.User.CanonicalizeName(dbUser.Name);

            DatabaseContext.Users.Add(dbUser);

            await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);

            return(StatusCode((int)HttpStatusCode.Created, dbUser.ToApi(true)));
        }
Пример #2
0
#pragma warning disable CA1506 // TODO: Decomplexify
        public async Task <IActionResult> CreateToken(CancellationToken cancellationToken)
        {
            if (ApiHeaders == null)
            {
                Response.Headers.Add(HeaderNames.WWWAuthenticate, new StringValues("basic realm=\"Create TGS4 bearer token\""));
                return(HeadersIssue(false));
            }

            if (ApiHeaders.IsTokenAuthentication)
            {
                return(BadRequest(new ErrorMessage(ErrorCode.TokenWithToken)));
            }

            var oAuthLogin = ApiHeaders.OAuthProvider.HasValue;

            ISystemIdentity systemIdentity = null;

            if (!oAuthLogin)
            {
                try
                {
                    // trust the system over the database because a user's name can change while still having the same SID
                    systemIdentity = await systemIdentityFactory.CreateSystemIdentity(ApiHeaders.Username, ApiHeaders.Password, cancellationToken).ConfigureAwait(false);
                }
                catch (NotImplementedException ex)
                {
                    Logger.LogTrace(ex, "System identities not implemented!");
                }
            }

            using (systemIdentity)
            {
                // Get the user from the database
                IQueryable <Models.User> query = DatabaseContext.Users.AsQueryable();
                if (oAuthLogin)
                {
                    string externalUserId;
                    try
                    {
                        var validator = oAuthProviders
                                        .GetValidator(ApiHeaders.OAuthProvider.Value);

                        if (validator == null)
                        {
                            return(BadRequest(new ErrorMessage(ErrorCode.OAuthProviderDisabled)));
                        }

                        externalUserId = await validator
                                         .ValidateResponseCode(ApiHeaders.Token, cancellationToken)
                                         .ConfigureAwait(false);
                    }
                    catch (RateLimitExceededException ex)
                    {
                        return(RateLimit(ex));
                    }

                    if (externalUserId == null)
                    {
                        return(Unauthorized());
                    }

                    query = query.Where(
                        x => x.OAuthConnections.Any(
                            y => y.Provider == ApiHeaders.OAuthProvider.Value &&
                            y.ExternalUserId == externalUserId));
                }
                else
                {
                    string canonicalName = Models.User.CanonicalizeName(ApiHeaders.Username);
                    if (systemIdentity == null)
                    {
                        query = query.Where(x => x.CanonicalName == canonicalName);
                    }
                    else
                    {
                        query = query.Where(x => x.CanonicalName == canonicalName || x.SystemIdentifier == systemIdentity.Uid);
                    }
                }

                var users = await query.Select(x => new Models.User
                {
                    Id           = x.Id,
                    PasswordHash = x.PasswordHash,
                    Enabled      = x.Enabled,
                    Name         = x.Name
                }).ToListAsync(cancellationToken).ConfigureAwait(false);

                // Pick the DB user first
                var user = users
                           .OrderByDescending(dbUser => dbUser.PasswordHash != null)
                           .FirstOrDefault();

                // No user? You're not allowed
                if (user == null)
                {
                    return(Unauthorized());
                }

                // A system user may have had their name AND password changed to one in our DB...
                // Or a DB user was created that had the same user/pass as a system user
                // Dumb admins...
                // FALLBACK TO THE DB USER HERE, DO NOT REVEAL A SYSTEM LOGIN!!!
                // This of course, allows system users to discover TGS users in this (HIGHLY IMPROBABLE) case but that is not our fault
                var  originalHash        = user.PasswordHash;
                var  isDbUser            = originalHash != null;
                bool usingSystemIdentity = systemIdentity != null && !isDbUser;
                if (!oAuthLogin)
                {
                    if (!usingSystemIdentity)
                    {
                        // DB User password check and update
                        if (!cryptographySuite.CheckUserPassword(user, ApiHeaders.Password))
                        {
                            return(Unauthorized());
                        }
                        if (user.PasswordHash != originalHash)
                        {
                            Logger.LogDebug("User ID {0}'s password hash needs a refresh, updating database.", user.Id);
                            var updatedUser = new Models.User
                            {
                                Id = user.Id
                            };
                            DatabaseContext.Users.Attach(updatedUser);
                            updatedUser.PasswordHash = user.PasswordHash;
                            await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
                        }
                    }
                    else if (systemIdentity.Username != user.Name)
                    {
                        // System identity username change update
                        Logger.LogDebug("User ID {0}'s system identity needs a refresh, updating database.", user.Id);
                        DatabaseContext.Users.Attach(user);
                        user.Name          = systemIdentity.Username;
                        user.CanonicalName = Models.User.CanonicalizeName(user.Name);
                        await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
                    }
                }

                // Now that the bookeeping is done, tell them to f**k off if necessary
                if (!user.Enabled.Value)
                {
                    Logger.LogTrace("Not logging in disabled user {0}.", user.Id);
                    return(Forbid());
                }

                var token = await tokenFactory.CreateToken(user, oAuthLogin, cancellationToken).ConfigureAwait(false);

                if (usingSystemIdentity)
                {
                    // expire the identity slightly after the auth token in case of lag
                    var identExpiry = token.ExpiresAt;
                    identExpiry += tokenFactory.ValidationParameters.ClockSkew;
                    identExpiry += TimeSpan.FromSeconds(15);
                    identityCache.CacheSystemIdentity(user, systemIdentity, identExpiry);
                }

                Logger.LogDebug("Successfully logged in user {0}!", user.Id);

                return(Json(token));
            }
        }
        public override async Task <IActionResult> Create([FromBody] UserUpdate model, CancellationToken cancellationToken)
        {
            if (model == null)
            {
                throw new ArgumentNullException(nameof(model));
            }

            if (!(model.Password == null ^ model.SystemIdentifier == null))
            {
                return(BadRequest(new ErrorMessage {
                    Message = "User must have exactly one of either a password or system identifier!"
                }));
            }

            model.Name = model.Name?.Trim();
            if (model.Name?.Length == 0)
            {
                model.Name = null;
            }

            if (!(model.Name == null ^ model.SystemIdentifier == null))
            {
                return(BadRequest(new ErrorMessage {
                    Message = "User must have a name if and only if user has no system identifier!"
                }));
            }

            var dbUser = new Models.User
            {
                AdministrationRights = model.AdministrationRights ?? AdministrationRights.None,
                CreatedAt            = DateTimeOffset.Now,
                CreatedBy            = AuthenticationContext.User,
                Enabled = model.Enabled ?? false,
                InstanceManagerRights = model.InstanceManagerRights ?? InstanceManagerRights.None,
                Name             = model.Name,
                SystemIdentifier = model.SystemIdentifier,
                InstanceUsers    = new List <Models.InstanceUser>()
            };

            if (model.SystemIdentifier != null)
            {
                try
                {
                    using (var sysIdentity = await systemIdentityFactory.CreateSystemIdentity(dbUser, cancellationToken).ConfigureAwait(false))
                    {
                        if (sysIdentity == null)
                        {
                            return(StatusCode((int)HttpStatusCode.Gone));
                        }
                        dbUser.Name             = sysIdentity.Username;
                        dbUser.SystemIdentifier = sysIdentity.Uid;
                    }
                }
                catch (NotImplementedException)
                {
                    return(StatusCode((int)HttpStatusCode.NotImplemented));
                }
            }
            else
            {
                if (model.Password.Length < generalConfiguration.MinimumPasswordLength)
                {
                    return(BadRequest(new ErrorMessage {
                        Message = String.Format(CultureInfo.InvariantCulture, "Password must be at least {0} characters long!", generalConfiguration.MinimumPasswordLength)
                    }));
                }
                cryptographySuite.SetUserPassword(dbUser, model.Password, true);
            }

            dbUser.CanonicalName = dbUser.Name.ToUpperInvariant();

            DatabaseContext.Users.Add(dbUser);

            await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);

            return(StatusCode((int)HttpStatusCode.Created, dbUser.ToApi(true)));
        }
Пример #4
0
#pragma warning disable CA1502, CA1506
        public async Task <IActionResult> Create([FromBody] UserCreateRequest model, CancellationToken cancellationToken)
        {
            if (model == null)
            {
                throw new ArgumentNullException(nameof(model));
            }

            if (model.OAuthConnections?.Any(x => x == null) == true)
            {
                return(BadRequest(new ErrorMessageResponse(ErrorCode.ModelValidationFailure)));
            }

            if ((model.Password != null && model.SystemIdentifier != null) ||
                (model.Password == null && model.SystemIdentifier == null && model.OAuthConnections?.Any() != true))
            {
                return(BadRequest(new ErrorMessageResponse(ErrorCode.UserMismatchPasswordSid)));
            }

            if (model.Group != null && model.PermissionSet != null)
            {
                return(BadRequest(new ErrorMessageResponse(ErrorCode.UserGroupAndPermissionSet)));
            }

            model.Name = model.Name?.Trim();
            if (model.Name?.Length == 0)
            {
                model.Name = null;
            }

            if (!(model.Name == null ^ model.SystemIdentifier == null))
            {
                return(BadRequest(new ErrorMessageResponse(ErrorCode.UserMismatchNameSid)));
            }

            var fail = CheckValidName(model, true);

            if (fail != null)
            {
                return(fail);
            }

            var totalUsers = await DatabaseContext
                             .Users
                             .AsQueryable()
                             .CountAsync(cancellationToken)
                             .ConfigureAwait(false);

            if (totalUsers >= generalConfiguration.UserLimit)
            {
                return(Conflict(new ErrorMessageResponse(ErrorCode.UserLimitReached)));
            }

            var dbUser = await CreateNewUserFromModel(model, cancellationToken).ConfigureAwait(false);

            if (dbUser == null)
            {
                return(Gone());
            }

            if (model.SystemIdentifier != null)
            {
                try
                {
                    using var sysIdentity = await systemIdentityFactory.CreateSystemIdentity(dbUser, cancellationToken).ConfigureAwait(false);

                    if (sysIdentity == null)
                    {
                        return(Gone());
                    }
                    dbUser.Name             = sysIdentity.Username;
                    dbUser.SystemIdentifier = sysIdentity.Uid;
                }
                catch (NotImplementedException)
                {
                    return(RequiresPosixSystemIdentity());
                }
            }
            else if (!(model.Password?.Length == 0 && model.OAuthConnections?.Any() == true))
            {
                var result = TrySetPassword(dbUser, model.Password, true);
                if (result != null)
                {
                    return(result);
                }
            }

            dbUser.CanonicalName = Models.User.CanonicalizeName(dbUser.Name);

            DatabaseContext.Users.Add(dbUser);

            await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);

            Logger.LogInformation("Created new user {0} ({1})", dbUser.Name, dbUser.Id);

            return(Created(dbUser.ToApi()));
        }
        public async Task <IActionResult> CreateToken(CancellationToken cancellationToken)
        {
            if (ApiHeaders.IsTokenAuthentication)
            {
                return(BadRequest(new Api.Models.ErrorMessage {
                    Message = "Cannot create a token using another token!"
                }));
            }

            ISystemIdentity identity;

            try
            {
                //trust the system over the database because a user's name can change while still having the same SID
                identity = await systemIdentityFactory.CreateSystemIdentity(ApiHeaders.Username, ApiHeaders.Password, cancellationToken).ConfigureAwait(false);
            }
            catch (NotImplementedException)
            {
                identity = null;
            }
            using (identity)
            {
                IQueryable <User> query;
                if (identity == null)
                {
                    query = DatabaseContext.Users.Where(x => x.CanonicalName == ApiHeaders.Username.ToUpperInvariant());
                }
                else
                {
                    query = DatabaseContext.Users.Where(x => x.SystemIdentifier == identity.Uid);
                }
                var user = await query.Select(x => new User
                {
                    Id           = x.Id,
                    PasswordHash = x.PasswordHash,
                    Enabled      = x.Enabled,
                    Name         = x.Name
                }).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);

                if (user == null)
                {
                    return(Unauthorized());
                }

                if (identity == null)
                {
                    var originalHash = user.PasswordHash;
                    if (!cryptographySuite.CheckUserPassword(user, ApiHeaders.Password))
                    {
                        return(Unauthorized());
                    }
                    if (user.PasswordHash != originalHash)
                    {
                        var updatedUser = new User
                        {
                            Id = user.Id
                        };
                        DatabaseContext.Users.Attach(updatedUser);
                        updatedUser.PasswordHash = user.PasswordHash;
                        await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
                    }
                }
                //check if the name changed and updoot accordingly
                else if (identity.Username != user.Name)
                {
                    DatabaseContext.Users.Attach(user);
                    user.Name          = identity.Username;
                    user.CanonicalName = user.Name.ToUpperInvariant();
                    await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
                }

                if (!user.Enabled.Value)
                {
                    return(Forbid());
                }

                var token = tokenFactory.CreateToken(user);
                if (identity != null)
                {
                    identityCache.CacheSystemIdentity(user, identity, token.ExpiresAt.Value.AddMinutes(1));                       //expire the identity slightly after the auth token in case of lag
                }
                Logger.LogDebug("Successfully logged in user {0}!", user.Id);

                return(Json(token));
            }
        }
Пример #6
0
        public async Task <IActionResult> Create([FromBody] UserUpdate model, CancellationToken cancellationToken)
        {
            if (model == null)
            {
                throw new ArgumentNullException(nameof(model));
            }

            if (!(model.Password == null ^ model.SystemIdentifier == null))
            {
                return(BadRequest(new ErrorMessage(ErrorCode.UserMismatchPasswordSid)));
            }

            model.Name = model.Name?.Trim();
            if (model.Name?.Length == 0)
            {
                model.Name = null;
            }

            if (!(model.Name == null ^ model.SystemIdentifier == null))
            {
                return(BadRequest(new ErrorMessage(ErrorCode.UserMismatchNameSid)));
            }

            var fail = CheckValidName(model, true);

            if (fail != null)
            {
                return(fail);
            }

            var dbUser = new Models.User
            {
                AdministrationRights = RightsHelper.Clamp(model.AdministrationRights ?? AdministrationRights.None),
                CreatedAt            = DateTimeOffset.Now,
                CreatedBy            = AuthenticationContext.User,
                Enabled = model.Enabled ?? false,
                InstanceManagerRights = RightsHelper.Clamp(model.InstanceManagerRights ?? InstanceManagerRights.None),
                Name             = model.Name,
                SystemIdentifier = model.SystemIdentifier,
                InstanceUsers    = new List <Models.InstanceUser>()
            };

            if (model.SystemIdentifier != null)
            {
                try
                {
                    using var sysIdentity = await systemIdentityFactory.CreateSystemIdentity(dbUser, cancellationToken).ConfigureAwait(false);

                    if (sysIdentity == null)
                    {
                        return(Gone());
                    }
                    dbUser.Name             = sysIdentity.Username;
                    dbUser.SystemIdentifier = sysIdentity.Uid;
                }
                catch (NotImplementedException)
                {
                    return(RequiresPosixSystemIdentity());
                }
            }
            else
            {
                var result = TrySetPassword(dbUser, model.Password, true);
                if (result != null)
                {
                    return(result);
                }
            }

            dbUser.CanonicalName = Models.User.CanonicalizeName(dbUser.Name);

            DatabaseContext.Users.Add(dbUser);

            await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);

            return(Created(dbUser.ToApi(true)));
        }