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))); }
#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))); }
#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)); } }
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))); }