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