Skip to content

jasper-121 add access request page with basic api to allow saving new…#457

Merged
devinleighsmith merged 10 commits intomasterfrom
jasper-121
Sep 5, 2025
Merged

jasper-121 add access request page with basic api to allow saving new…#457
devinleighsmith merged 10 commits intomasterfrom
jasper-121

Conversation

@devinleighsmith
Copy link
Contributor

@devinleighsmith devinleighsmith commented Aug 27, 2025

… user access request.

Pull Request for JIRA Ticket: ----121----

Issue ticket number and link

Include the JIRA ticket # and link here
https://jira.justice.gov.bc.ca/browse/JASPER-121

Description

Partial implementation of 121, handles case where user is new, has no roles, and does not exist in PCSS.

Fixes # (issue)

Type of change

Please delete options that are not relevant.

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration

Test using idir login with no user in mongoDB. Validate that the user is redirected to access request screen and that user is able to submit request.

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules

Documentation References

Put any doc references here

@devinleighsmith devinleighsmith self-assigned this Aug 27, 2025
@devinleighsmith devinleighsmith added the enhancement New feature or request label Aug 27, 2025
@devinleighsmith
Copy link
Contributor Author

Let me know if there is anything I missed - first pull request, tried to follow the checklist and what I've seen from PRs I've reviewed.

@devinleighsmith
Copy link
Contributor Author

will deal with conflict.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're in the process of deprecating vue-bootstrap-next in favor of Vuetify. All new FE components should use Vuetify

</b-card>
</template>

<script lang="ts">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recommend converting this to a script setup

@devinleighsmith devinleighsmith marked this pull request as draft August 28, 2025 00:37
@devinleighsmith
Copy link
Contributor Author

I'm putting this in draft as I'm no longer seeing claims for first name, last name, or a guid coming back from keycloak.

[Route("request-access")]
public async Task<ActionResult> RequestAccess(string email)
{
var userIdentifier = User.Claims.FirstOrDefault(c => c.Type == "preferred_username")?.Value;
Copy link
Contributor

@ronaldo-macapobre ronaldo-macapobre Aug 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You will be able to utilize the extension methods in ClaimsPrincipalExtensions.cs. e.g. User.PreferredUsername()

Guid.TryParse(userIdentifier?.Split("@").FirstOrDefault(), out userGuid);
var newUser = new UserDto()
{
FirstName = User.Claims.FirstOrDefault(c => c.Type == "given_name")?.Value,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might better to use the code below to avoid hard coded strings.

Suggested change
FirstName = User.Claims.FirstOrDefault(c => c.Type == "given_name")?.Value,
FirstName = User.FindFirstValue(ClaimTypes.GivenName),
LastName = User.FindFirstValue(ClaimTypes.Surname),

ADId = userGuid,
ADUsername = userIdentifier,
};
var result = await base.Create(newUser); // Note: this handles validation and duplicate prevention.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be better to change the route to HttpPost because it creates a user?

UserId = HttpContext.User.UserId(),
JudgeId = HttpContext.User.JudgeId(),
Email = HttpContext.User.Email(),
IsActive = HttpContext.User.IsActive(),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ronaldo-macapobre I added the active flag here for now, but the issue I was having in testing with the current implementation is that changes to a user in the database are not reflected in these UserContext properties without a user being logged out or starting a new incognito session - we may want to rethink this flow if we want to, for example, disable a user and have their access revoked immediately.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@devinleighsmith I see, we can force the user to logout or invalidate his session upon updating the access. That way the user is forced to relogin.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ronaldo-macapobre perhaps a check in the permissionhandler, if the active flag or the groupIds have changed? That logic needs to be in one of the handlers in my OP to catch when a change has been made in the db that isn't reflected in the current user's cached claims.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moving PR to draft to add this.

return Task.CompletedTask;
}

if (!user.IsActive())
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ronaldo-macapobre I added this here to correspond with my changes - however, in current state the session has to expire for updates to this property to change here - we may want to get the user from the db here instead.

@devinleighsmith devinleighsmith marked this pull request as ready for review September 2, 2025 22:57
… user access request.

updates from SONAR recommendations.

codeql, sonarqube updates. Includes Josh's requested changes.

snapshot updates.

remove debugging console logs.

sanitize logged user email (codeQL).

add snapshot serializer to avoid data-v component unique ids failing snapshot tests.

correct pascal case warning.

force snapshot render to wait for component to be finished rendering.

wrap snapshot in wait in case difference caused by async logic.

correct snapshot by running all tests not using test runner.

updates based on Ronnie's changes.
perform an update on an existing user, as we can assume the userId will already be in place.
check for user based on userid instead of email.

updates based on ronaldo's changes, updates existing user.

snapshot correction.

Correct potential null-pointer if user not found.

jasper-121 final updates based on testing - ensure that active/inactive users are not redirected appropriately.
…udicial styling.

correct build issue.

add jasper png

public bool IsActive { get; set; }

public bool? IsPendingRegistration { get; set; } = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need this to be nullable because the same field in UserDto is not nullable.

roles: string[];
subRole: string;
isSupremeUser: string;
isPendingRegistration?: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kindly update if this is no longer nullable.

return _.isNumber(value) && value > 0;
};

export class CustomAPIError<T> extends Error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this should be placed in the ApiResponse.ts instead?

@devinleighsmith devinleighsmith marked this pull request as draft September 3, 2025 16:44
… on. Check user for changes during authorization handler.
@devinleighsmith devinleighsmith marked this pull request as ready for review September 4, 2025 16:32
var forwardedPort = HttpContext.Request.Headers["X-Forwarded-Port"];

//We are always sending X-Forwarded-Port, only time we aren't is when we are hitting the API directly.
var baseUri = HttpContext.Request.Headers.ContainsKey("X-Forwarded-Host") ? $"{HttpContext.Request.Headers["X-Base-Href"]}logout" : "/api";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears to have been copy/pasted from sheriff-scheduling, but doesn't work for us at the moment since we don't have a /logout page on the frontend.

Copy link
Contributor

@ronaldo-macapobre ronaldo-macapobre Sep 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup. This came along when we forked the Supreme Court Viewer repo.

/// Allows a new user without authorization to JASPER to request access to the application.
/// </summary>
/// <returns>The user resulting from the access request.</returns>
[Authorize(AuthenticationSchemes = "SiteMinder, OpenIdConnect", Policy = nameof(ProviderAuthorizationHandler))]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is already present at the controller level.

{
public static IServiceCollection AddScvAuthorization(this IServiceCollection services)
{
services.AddScoped<IUserService, UserService>();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these need to be scoped, or race conditions may cause multiple, simultaneous requests to the user repository methods.

var databaseUser = await _userService.GetByIdWithPermissionsAsync(userId);
if (databaseUser == null || context.User.HasChanged(databaseUser))
{
context.Fail(new AuthorizationFailureReason(this, "User claims changed since last authentication."));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AuthorizationHandlers are not supposed to handle redirects directly - so flag the authorizationRedirectMiddleware with a unique failure reason.

if (result == null || !result.Any())
{
this.Logger.LogInformation("User with email: {email} is not found", email);
this.Logger.LogInformation("User with email: {Email} is not found", email.Replace(Environment.NewLine, ""));

Check warning

Code scanning / CodeQL

Exposure of private information Medium

Private data returned by
call to method Email
is written to an external location.
Private data returned by
access to local variable email
is written to an external location.
Private data returned by
access to local variable email
is written to an external location.
Private data returned by
access to parameter email
is written to an external location.
Private data returned by
call to method Email
is written to an external location.
Private data returned by
call to method Email
is written to an external location.
Private data returned by
call to method Email
is written to an external location.
Private data returned by
call to method Email
is written to an external location.
Private data returned by call to method Email is written to an external location.
Private data returned by call to method Email is written to an external location.
@@ -1,4 +1,6 @@
using System;
using DocumentFormat.OpenXml.InkML;
Copy link
Contributor

@ronaldo-macapobre ronaldo-macapobre Sep 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we really need this namespace?

@sonarqubecloud
Copy link

sonarqubecloud bot commented Sep 4, 2025

@devinleighsmith devinleighsmith merged commit c8468f2 into master Sep 5, 2025
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants