tgstation-server 6.19.0
The /tg/station 13 server suite
Loading...
Searching...
No Matches
AuthorizationHandler.cs
Go to the documentation of this file.
1using System;
4using System.Linq;
8
11
19
21{
26 {
31
36
47
50 {
51 // https://github.com/dotnet/aspnetcore/issues/56272
52 CancellationToken cancellationToken = CancellationToken.None;
53
54 ArgumentNullException.ThrowIfNull(context);
55
56 // all the requirements we process require authentication
57 if (context.User.Identity?.IsAuthenticated != true)
58 {
59 context.Fail(
60 new AuthorizationFailureReason(this, "User is not authenticated!"));
61 return Task.CompletedTask;
62 }
63
65
66 foreach (var req in context.Requirements.OfType<UserSessionValidRequirement>())
68 HandleSessionValidRequirement(context, req, cancellationToken));
69
71 ?? throw new InvalidOperationException("Failed to locate rights handler function!");
73 {
74 var genericMethod = method.MakeGenericMethod(rightType);
75 processingRequirements.AddRange((IEnumerable<ValueTask>)genericMethod.Invoke(this, [context, cancellationToken])!);
76 }
77
79 }
80
91
100 {
101 var userId = context.User.GetTgsUserId();
102
103 var nbf = context.User.ParseTime(JwtRegisteredClaimNames.Nbf);
104
105 return databaseContextFactory.UseContext(async databaseContext =>
106 {
107 var sessionData = await databaseContext
108 .Users
109 .Where(user => user.Id == userId)
110 .Select(user => new
111 {
112 Enabled = user.Enabled!.Value,
113 user.LastPasswordUpdate,
114 })
115 .TagWith("user_session_validation")
116 .FirstOrDefaultAsync(cancellationToken);
117
118 lock (context)
119 {
120 if (sessionData == null)
121 context.Fail(
122 new AuthorizationFailureReason(this, $"Unable to retrieve user {userId}!"));
123 else if (!sessionData.Enabled)
124 context.Fail(
125 new AuthorizationFailureReason(this, "User is disabled!"));
126 else if (sessionData.LastPasswordUpdate >= nbf)
127 context.Fail(
128 new AuthorizationFailureReason(this, "User has been modified since logging in!"));
129 else
130 context.Succeed(requirement);
131 }
132 });
133 }
134
145 {
146 var rightsType = RightsHelper.TypeToRight<TRights>();
148 var userId = context.User.GetTgsUserId();
149
150 return databaseContextFactory.UseContext(async databaseContext =>
151 {
152 var queryableUsers = databaseContext
153 .Users;
154
156 .Where(user => user.Id == userId && user.PermissionSet != null)
157 .Select(user => user.PermissionSet!.Id);
158
160 .Where(user => user.Id == userId && user.Group != null)
161 .Select(user => user.Group!.PermissionSet!.Id);
162
163 object? permissionSet;
164 if (isInstance)
165 {
166 if (context.Resource is not long instanceId)
167 throw new InvalidOperationException("Instance ID should have been passed in as authorization resource!");
168
169 permissionSet = await databaseContext
170 .InstancePermissionSets
171 .Where(ips => ips.InstanceId == instanceId
172 && (matchingUniquePermissionSetIds.Contains(ips.PermissionSetId) || matchingGroupPermissionSetIds.Contains(ips.PermissionSetId)))
173 .TagWith("rights_authorization_handler_instance_permission_set")
174 .FirstOrDefaultAsync(cancellationToken);
175 }
176 else
177 permissionSet = await databaseContext
178 .PermissionSets
179 .Where(permissionSet => matchingUniquePermissionSetIds.Contains(permissionSet.Id) || matchingGroupPermissionSetIds.Contains(permissionSet.Id))
180 .TagWith("rights_authorization_handler_permission_set")
181 .FirstOrDefaultAsync(cancellationToken);
182
183 if (permissionSet == null)
184 {
185 context.Fail(
186 new AuthorizationFailureReason(this, $"Unable to find {(isInstance ? "instance " : String.Empty)}permission set for user."));
187 return;
188 }
189
190 // use the api versions because they're the ones that contain the actual properties
192
195
197 .GetProperties()
198 .Where(propertyInfo => propertyInfo.PropertyType == nullableRightsType && propertyInfo.CanRead)
199 .Single();
200
202 if (rightPropertyGetMethod == null)
203 throw new InvalidOperationException($"Rights property {rightPropertyInfo.Name} on {rightsClrType.FullName} has no getter!");
204
206 permissionSet,
207 Array.Empty<object>())
208 ?? throw new InvalidOperationException("A user right was null!");
209
210 var result = requirement.Evaluate((TRights)right);
211
212 lock (context)
213 {
214 if (result)
215 context.Succeed(requirement);
216 else
217 context.Fail(
218 new AuthorizationFailureReason(this, $"Failed to successfully evaluate rights requirement: {requirement}"));
219 }
220 });
221 }
222 }
223}
static bool IsInstanceRight(RightsType rightsType)
Check if a given rightsType is meant for an Models.Instance.
static IEnumerable< Type > AllRightTypes()
Iterate the Type of each right.
Extension methods for the ValueTask and ValueTask<TResult> classes.
static async ValueTask WhenAll(IEnumerable< ValueTask > tasks)
Fully await a given list of tasks .
IAuthorizationHandler for RightsConditional<TRights>s and UserSessionValidRequirements.
AuthorizationHandler(IDatabaseContextFactory databaseContextFactory, IApiHeadersProvider apiHeadersProvider)
Initializes a new instance of the AuthorizationHandler class.
readonly IDatabaseContextFactory databaseContextFactory
The IDatabaseContextFactory for the AuthorizationHandler.
IEnumerable< ValueTask > InvokeHandleRightsConditionalRequirement< TRights >(AuthorizationHandlerContext context, CancellationToken cancellationToken)
Handle invoking HandleRightsConditionalRequirement<TRights>(AuthorizationHandlerContext,...
ValueTask HandleRightsConditionalRequirement< TRights >(AuthorizationHandlerContext context, RightsConditional< TRights > requirement, CancellationToken cancellationToken)
Handle RightsConditional<TRights> authorization requirements.
ValueTask HandleSessionValidRequirement(AuthorizationHandlerContext context, UserSessionValidRequirement requirement, CancellationToken cancellationToken)
Handle UserSessionValidRequirement authorization requirements.
readonly IApiHeadersProvider apiHeadersProvider
The IApiHeadersProvider for the AuthorizationHandler.
Task HandleAsync(AuthorizationHandlerContext context)
IAuthorizationRequirement for testing if a user is enabled and their session is valid.
Factory for scoping usage of IDatabaseContexts. Meant for use by Components.
ValueTask UseContext(Func< IDatabaseContext, ValueTask > operation)
Run an operation in the scope of an IDatabaseContext.
@ List
User may list files if the Models.Instance allows it.