/// <inheritdoc /> public ulong GetRight(RightsType rightsType) { var isInstance = RightsHelper.IsInstanceRight(rightsType); //forces the null user check var pullThis = User; if (isInstance && InstanceUser == null) { return(0); } var rightsEnum = RightsHelper.RightToType(rightsType); // use the api versions because they're the ones that contain the actual properties var typeToCheck = isInstance ? typeof(InstanceUser) : typeof(User); var nullableType = typeof(Nullable <>); var nullableRightsType = nullableType.MakeGenericType(rightsEnum); var prop = typeToCheck.GetProperties().Where(x => x.PropertyType == nullableRightsType).First(); var right = prop.GetMethod.Invoke(isInstance ? (object)InstanceUser : User, Array.Empty <object>()); if (right == null) { throw new InvalidOperationException("A user right was null!"); } return((ulong)right); }
/// <inheritdoc /> public void Apply(OpenApiOperation operation, OperationFilterContext context) { if (operation == null) { throw new ArgumentNullException(nameof(operation)); } if (context == null) { throw new ArgumentNullException(nameof(context)); } operation.OperationId = $"{context.MethodInfo.DeclaringType.Name}.{context.MethodInfo.Name}"; var authAttributes = context .MethodInfo .DeclaringType .GetCustomAttributes(true) .Union( context .MethodInfo .GetCustomAttributes(true)) .OfType <TgsAuthorizeAttribute>(); if (authAttributes.Any()) { var tokenScheme = new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = TokenSecuritySchemeId } }; operation.Security = new List <OpenApiSecurityRequirement> { new OpenApiSecurityRequirement { { tokenScheme, new List <string>() } } }; if (authAttributes.Any(attr => attr.RightsType.HasValue && RightsHelper.IsInstanceRight(attr.RightsType.Value))) { operation.Parameters.Add(new OpenApiParameter { Reference = new OpenApiReference { Type = ReferenceType.Parameter, Id = ApiHeaders.InstanceIdHeader } }); } } else { // HomeController.CreateToken var passwordScheme = new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = PasswordSecuritySchemeId } }; operation.Security = new List <OpenApiSecurityRequirement> { new OpenApiSecurityRequirement { { passwordScheme, new List <string>() } } }; } }
/// <inheritdoc /> public async Task InjectClaimsIntoContext(TokenValidatedContext tokenValidatedContext, CancellationToken cancellationToken) { if (tokenValidatedContext == null) { throw new ArgumentNullException(nameof(tokenValidatedContext)); } // Find the user id in the token var userIdClaim = tokenValidatedContext.Principal.FindFirst(JwtRegisteredClaimNames.Sub); if (userIdClaim == default) { throw new InvalidOperationException("Missing required claim!"); } long userId; try { userId = Int64.Parse(userIdClaim.Value, CultureInfo.InvariantCulture); } catch (Exception e) { throw new InvalidOperationException("Failed to parse user ID!", e); } ApiHeaders apiHeaders; try { apiHeaders = new ApiHeaders(tokenValidatedContext.HttpContext.Request.GetTypedHeaders()); } catch (InvalidOperationException) { // we are not responsible for handling header validation issues return; } // This populates the CurrentAuthenticationContext field for use by us and subsequent controllers await authenticationContextFactory.CreateAuthenticationContext(userId, apiHeaders.InstanceId, tokenValidatedContext.SecurityToken.ValidFrom, cancellationToken).ConfigureAwait(false); var authenticationContext = authenticationContextFactory.CurrentAuthenticationContext; var enumerator = Enum.GetValues(typeof(RightsType)); var claims = new List <Claim>(); foreach (RightsType I in enumerator) { // if there's no instance user, do a weird thing and add all the instance roles // we need it so we can get to OnActionExecutionAsync where we can properly decide between BadRequest and Forbid // if user is null that means they got the token with an expired password var rightInt = authenticationContext.User == null || (RightsHelper.IsInstanceRight(I) && authenticationContext.InstanceUser == null) ? ~0U : authenticationContext.GetRight(I); var rightEnum = RightsHelper.RightToType(I); var right = (Enum)Enum.ToObject(rightEnum, rightInt); foreach (Enum J in Enum.GetValues(rightEnum)) { if (right.HasFlag(J)) { claims.Add(new Claim(ClaimTypes.Role, RightsHelper.RoleName(I, J))); } } } tokenValidatedContext.Principal.AddIdentity(new ClaimsIdentity(claims)); }
/// <summary> /// Runs after a <see cref="Token"/> has been validated. Creates the <see cref="IAuthenticationContext"/> for the <see cref="ControllerBase.Request"/> /// </summary> /// <param name="context">The <see cref="TokenValidatedContext"/> for the operation</param> /// <returns>A <see cref="Task"/> representing the running operation</returns> public static async Task OnTokenValidated(TokenValidatedContext context) { var databaseContext = context.HttpContext.RequestServices.GetRequiredService <IDatabaseContext>(); var authenticationContextFactory = context.HttpContext.RequestServices.GetRequiredService <IAuthenticationContextFactory>(); var userIdClaim = context.Principal.FindFirst(JwtRegisteredClaimNames.Sub); if (userIdClaim == default(Claim)) { throw new InvalidOperationException("Missing required claim!"); } long userId; try { userId = Int64.Parse(userIdClaim.Value, CultureInfo.InvariantCulture); } catch (Exception e) { throw new InvalidOperationException("Failed to parse user ID!", e); } ApiHeaders apiHeaders; try { apiHeaders = new ApiHeaders(context.HttpContext.Request.GetTypedHeaders()); } catch { //let OnActionExecutionAsync handle the reponse return; } await authenticationContextFactory.CreateAuthenticationContext(userId, apiHeaders.InstanceId, context.SecurityToken.ValidFrom, context.HttpContext.RequestAborted).ConfigureAwait(false); var authenticationContext = authenticationContextFactory.CurrentAuthenticationContext; var enumerator = Enum.GetValues(typeof(RightsType)); var claims = new List <Claim>(); foreach (RightsType I in enumerator) { //if there's no instance user, do a weird thing and add all the instance roles //we need it so we can get to OnActionExecutionAsync where we can properly decide between BadRequest and Forbid //if user is null that means they got the token with an expired password var rightInt = authenticationContext.User == null || (RightsHelper.IsInstanceRight(I) && authenticationContext.InstanceUser == null) ? ~0U : authenticationContext.GetRight(I); var rightEnum = RightsHelper.RightToType(I); var right = (Enum)Enum.ToObject(rightEnum, rightInt); foreach (Enum J in Enum.GetValues(rightEnum)) { if (right.HasFlag(J)) { claims.Add(new Claim(ClaimTypes.Role, RightsHelper.RoleName(I, J))); } } } context.Principal.AddIdentity(new ClaimsIdentity(claims)); }