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>())
69
71 ?? throw new InvalidOperationException("Failed to locate rights handler function!");
73 {
74 var genericMethod = method.MakeGenericMethod(rightType);
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 .AsQueryable()
110 .Where(user => user.Id == userId)
111 .Select(user => new
112 {
113 Enabled = user.Enabled!.Value,
114 user.LastPasswordUpdate,
115 })
116 .TagWith("user_session_validation")
117 .FirstOrDefaultAsync(cancellationToken);
118
119 lock (context)
120 {
121 if (sessionData == null)
122 context.Fail(
123 new AuthorizationFailureReason(this, $"Unable to retrieve user {userId}!"));
124 else if (!sessionData.Enabled)
125 context.Fail(
126 new AuthorizationFailureReason(this, "User is disabled!"));
127 else if (sessionData.LastPasswordUpdate >= nbf)
128 context.Fail(
129 new AuthorizationFailureReason(this, "User has been modified since logging in!"));
130 else
131 context.Succeed(requirement);
132 }
133 });
134 }
135
146 {
147 var rightsType = RightsHelper.TypeToRight<TRights>();
149 var userId = context.User.GetTgsUserId();
150
151 return databaseContextFactory.UseContext(async databaseContext =>
152 {
153 var queryableUsers = databaseContext
154 .Users
155 .AsQueryable();
156
158 .Where(user => user.Id == userId && user.PermissionSet != null)
159 .Select(user => user.PermissionSet!.Id);
160
162 .Where(user => user.Id == userId && user.Group != null)
163 .Select(user => user.Group!.PermissionSet!.Id);
164
165 object? permissionSet;
166 if (isInstance)
167 {
168 if (context.Resource is not Instance instance)
169 throw new InvalidOperationException("Instance should have been passed in as authorization resource!");
170
171 var instanceId = instance.Require(i => i.Id);
172
173 permissionSet = await databaseContext
174 .InstancePermissionSets
175 .AsQueryable()
176 .Where(ips => ips.InstanceId == instanceId
177 && (matchingUniquePermissionSetIds.Contains(ips.PermissionSetId) || matchingGroupPermissionSetIds.Contains(ips.PermissionSetId)))
178 .TagWith("rights_authorization_handler_instance_permission_set")
179 .FirstOrDefaultAsync(cancellationToken);
180 }
181 else
182 permissionSet = await databaseContext
183 .PermissionSets
184 .AsQueryable()
185 .Where(permissionSet => matchingUniquePermissionSetIds.Contains(permissionSet.Id) || matchingGroupPermissionSetIds.Contains(permissionSet.Id))
186 .TagWith("rights_authorization_handler_permission_set")
187 .FirstOrDefaultAsync(cancellationToken);
188
189 if (permissionSet == null)
190 {
191 context.Fail(
192 new AuthorizationFailureReason(this, $"Unable to find {(isInstance ? "instance " : String.Empty)}permission set for user."));
193 return;
194 }
195
196 // use the api versions because they're the ones that contain the actual properties
198
201
203 .GetProperties()
204 .Where(propertyInfo => propertyInfo.PropertyType == nullableRightsType && propertyInfo.CanRead)
205 .Single();
206
208 if (rightPropertyGetMethod == null)
209 throw new InvalidOperationException($"Rights property {rightPropertyInfo.Name} on {rightsClrType.FullName} has no getter!");
210
212 permissionSet,
213 Array.Empty<object>())
214 ?? throw new InvalidOperationException("A user right was null!");
215
216 var result = requirement.Evaluate((TRights)right);
217
218 lock (context)
219 {
220 if (result)
221 context.Succeed(requirement);
222 else
223 context.Fail(
224 new AuthorizationFailureReason(this, $"Failed to successfully evaluate rights requirement: {requirement}"));
225 }
226 });
227 }
228 }
229}
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 .
Represents an Api.Models.Instance in the database.
Definition Instance.cs:11
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.