-
Notifications
You must be signed in to change notification settings - Fork 1
JASPER-551: Implement a mapping field in JASPER that can be used to derive the PCSS user details (PART 2) #458
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
4d93971
6532b25
716bf97
ca919cb
6767f4f
abec96a
802386c
946f0d8
47cb242
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,6 +23,7 @@ | |
| using Scv.Api.Helpers; | ||
| using Scv.Api.Helpers.Extensions; | ||
| using Scv.Api.Infrastructure.Encryption; | ||
| using Scv.Api.Models.AccessControlManagement; | ||
| using Scv.Api.Services; | ||
| using Scv.Db.Models; | ||
|
|
||
|
|
@@ -171,34 +172,7 @@ await cookieCtx.HttpContext.SignOutAsync(CookieAuthenticationDefaults | |
| new Claim(CustomClaimTypes.IsSupremeUser, isSupremeUser.ToString()), | ||
| }); | ||
|
|
||
| // Get JudgeId and HomeLocationId from env variable until login process is finalized. | ||
| var judgeId = configuration.GetNonEmptyValue("PCSS:JudgeId"); | ||
| var homeLocationId = configuration.GetNonEmptyValue("PCSS:JudgeHomeLocationId"); | ||
|
|
||
| logger.LogInformation("Acting as Judge Id - {JudgeId}.", judgeId); | ||
|
|
||
| claims.Add(new Claim(CustomClaimTypes.JudgeId, judgeId)); | ||
| claims.Add(new Claim(CustomClaimTypes.JudgeHomeLocationId, homeLocationId)); | ||
|
|
||
| // Remove checking when the "real" mongo db has been configured | ||
| var connectionString = configuration.GetValue<string>("MONGODB_CONNECTION_STRING"); | ||
| if (!string.IsNullOrEmpty(connectionString)) | ||
| { | ||
| // Add user's permissions and roles as claims | ||
| var userService = context.HttpContext.RequestServices.GetRequiredService<IUserService>(); | ||
| var userDto = await userService.GetWithPermissionsAsync(context.Principal.Email()); | ||
| if (userDto != null) | ||
| { | ||
| // UserId's value refers to the id in the User collection from MongoDb. | ||
| claims.Add(new Claim(CustomClaimTypes.UserId, userDto.Id)); | ||
|
|
||
| var permissionsClaims = userDto.Permissions.Select(p => new Claim(CustomClaimTypes.Permission, p)); | ||
| claims.AddRange(permissionsClaims); | ||
|
|
||
| var rolesClaims = userDto.Roles.Select(r => new Claim(CustomClaimTypes.Role, r)); | ||
| claims.AddRange(rolesClaims); | ||
| } | ||
| } | ||
| await OnPostAuthSuccess(configuration, context, logger, claims); | ||
|
|
||
| identity.AddClaims(claims); | ||
| }, | ||
|
|
@@ -271,5 +245,92 @@ await cookieCtx.HttpContext.SignOutAsync(CookieAuthenticationDefaults | |
|
|
||
| return services; | ||
| } | ||
|
|
||
| private static async Task OnPostAuthSuccess( | ||
| IConfiguration configuration, | ||
| Microsoft.AspNetCore.Authentication.OpenIdConnect.TokenValidatedContext context, | ||
| ILogger logger, | ||
| List<Claim> claims) | ||
| { | ||
| var judgeId = configuration.GetNonEmptyValue("PCSS:JudgeId"); | ||
| var homeLocationId = configuration.GetNonEmptyValue("PCSS:JudgeHomeLocationId"); | ||
|
|
||
| // Remove checking when the "real" mongo db has been configured | ||
| var connectionString = configuration.GetValue<string>("MONGODB_CONNECTION_STRING"); | ||
| if (string.IsNullOrWhiteSpace(connectionString)) | ||
| { | ||
| // Defaults the logged in user to the default judge. | ||
| logger.LogInformation("Acting as Judge Id - {JudgeId}.", judgeId); | ||
| claims.Add(new Claim(CustomClaimTypes.JudgeId, judgeId)); | ||
| claims.Add(new Claim(CustomClaimTypes.JudgeHomeLocationId, homeLocationId)); | ||
| return; | ||
| } | ||
|
|
||
| try | ||
| { | ||
| var userService = context.HttpContext.RequestServices.GetRequiredService<IUserService>(); | ||
| var userDto = await userService.GetWithPermissionsAsync(context.Principal.Email()); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ronaldo-macapobre Looking through this again and I'm not sure this is safe, as this will create a new user if the user's email changes. I think in most cases email is stable but may not always be - I know in the case of sso standard they recommend that the preferred_username guid is used instead.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see, thanks for catching that. I only used the email for duplicate checking as a quick safeguard, but we can definitely update it. I’ll switch the code to use |
||
|
|
||
| // Insert new user to the db. In the future, we need to ensure that the currently | ||
| // logged on user has an account in PCSS before it is added to the db. | ||
| if (userDto == null) | ||
| { | ||
| userDto = new UserDto | ||
| { | ||
| FirstName = context.Principal.FindFirstValue(ClaimTypes.GivenName), | ||
| LastName = context.Principal.FindFirstValue(ClaimTypes.Surname), | ||
| Email = context.Principal.Email(), | ||
| UserGuid = context.Principal.UserGuid(), | ||
| IsActive = false | ||
| }; | ||
|
|
||
| var result = await userService.AddAsync(userDto); | ||
| if (!result.Succeeded) | ||
| { | ||
| logger.LogInformation("Unable to add user: {Message}", result.Errors); | ||
| } | ||
|
|
||
| logger.LogInformation("A user has been added to the db."); | ||
|
|
||
| userDto = result.Payload; | ||
| } | ||
|
|
||
| // Attempt to override the default Judge Id and HomeLocationId if the current user has been mapped. | ||
| if (userDto.JudgeId != null) | ||
| { | ||
| var dashboardService = context.HttpContext.RequestServices.GetRequiredService<IDashboardService>(); | ||
|
|
||
| var judges = await dashboardService.GetJudges(); | ||
|
|
||
| var judge = judges.FirstOrDefault(j => j.PersonId == userDto.JudgeId); | ||
| if (judge != null) | ||
| { | ||
| judgeId = judge.PersonId.ToString(); | ||
| homeLocationId = judge.HomeLocationId.ToString(); | ||
| } | ||
| } | ||
|
|
||
| // UserId's value refers to the id in the User collection from MongoDb. | ||
| claims.Add(new Claim(CustomClaimTypes.UserId, userDto.Id)); | ||
|
|
||
| // Add Roles and Permissions as claims if available | ||
| var permissionsClaims = userDto.Permissions.Select(p => new Claim(CustomClaimTypes.Permission, p)); | ||
| claims.AddRange(permissionsClaims); | ||
|
|
||
| var rolesClaims = userDto.Roles.Select(r => new Claim(CustomClaimTypes.Role, r)); | ||
| claims.AddRange(rolesClaims); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| logger.LogError(ex, "Something went wrong during post authentication process: {Exception}", ex); | ||
| } | ||
| finally | ||
| { | ||
| // Add the final value of judgeId and homeLocationId as claims of the current user | ||
| logger.LogInformation("Acting as Judge Id - {JudgeId}.", judgeId); | ||
| claims.Add(new Claim(CustomClaimTypes.JudgeId, judgeId)); | ||
| claims.Add(new Claim(CustomClaimTypes.JudgeHomeLocationId, homeLocationId)); | ||
| } | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some of what's contained in this code is related to what Devin is doing here; #457. So there might be some co-ordination required.
It's fine to populate the user account in JASPER with some basic information regarding a user authenticated by KeyCloak, but we still need to verify and validate (authorize) their access to JASPER before providing any access.
Short term:
Long term:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey Wade, Ronnie and I talked about this, and we think this is logic is consistent with the goals you've listed above. Primarily, this is because Ronnie is only creating a skeleton user during login here - the logic surrounding the judge assignment as well as roles and permissions will not execute unless there is corresponding data in PCSS/Mongo.
However, Ronnie will need to update this to set the skeleton user to disabled.
So when a user logs in for the first time, this logic will run, the skeleton user gets created (which allows an admin to track failed authorization attempts). Next the frontend will redirect the user to the access request page, where they have the option of requesting access. This will allow the user to set a flag indicating their request for admin review.
Of course, the missing part here is the PCSS role synchronization, but that will come in its own PR.