Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
79b2554
feat: nutrient updates
JTraill Sep 3, 2025
bb68c27
Use JASPER's favicon (#480)
ronaldo-macapobre Sep 3, 2025
f16b700
refactor: make the groupings more generic
JTraill Sep 4, 2025
53e093a
tests: fix nutrient tests
JTraill Sep 4, 2025
421af97
JASPER-548: Case Details: Civil: Document Filtering by Category (#482)
ronaldo-macapobre Sep 5, 2025
5656bf8
refactor: remove commented code
JTraill Sep 5, 2025
1f450b3
refactor: var to const
JTraill Sep 5, 2025
4f5cdda
fix: wrong doc reference
JTraill Sep 5, 2025
0e7c3ba
Merge pull request #483 from bcgov/nutrient-updates
JTraill Sep 5, 2025
ab18022
JASPER-581: Configure JASPER to use AWS MongoDb (#484)
ronaldo-macapobre Sep 5, 2025
c8468f2
Merge pull request #457 from bcgov/jasper-121
devinleighsmith Sep 5, 2025
0ba89a8
correct request access screen's logo display.
devinleighsmith Sep 5, 2025
ed8db17
Bump GdPicture.API from 14.3.13 to 14.3.14
dependabot[bot] Sep 8, 2025
1d2375b
build(deps): bump uuid from 11.1.0 to 12.0.0 in /web
dependabot[bot] Sep 8, 2025
9ff3a65
JASPER-548: Additional updates to Document Categories (#492)
ronaldo-macapobre Sep 8, 2025
d31026a
Merge pull request #486 from bcgov/dependabot/nuget/api/GdPicture.API…
JTraill Sep 8, 2025
5aa72b6
build(deps): bump aws-actions/configure-aws-credentials (#485)
dependabot[bot] Sep 8, 2025
e3ee9b3
Merge pull request #489 from bcgov/dependabot/npm_and_yarn/web/uuid-1…
JTraill Sep 9, 2025
062ef40
build(deps-dev): bump vite from 7.0.4 to 7.1.5 in /web
dependabot[bot] Sep 9, 2025
88d0750
feat: add viewability to court-list documents
JTraill Sep 9, 2025
362ceca
Merge pull request #493 from bcgov/viewable-coourt-list-docs
JTraill Sep 9, 2025
6129199
Merge pull request #487 from bcgov/dependabot/npm_and_yarn/web/vite-7…
JTraill Sep 10, 2025
21276ff
feat: rename document tab title on load
JTraill Sep 11, 2025
0662912
refactor: function naming
JTraill Sep 11, 2025
4391004
Merge pull request #496 from bcgov/rename-file-tab
JTraill Sep 11, 2025
e9573cf
build(deps): bump vite from 6.3.4 to 6.3.6 in /aws (#494)
dependabot[bot] Sep 15, 2025
8a28a27
feat: port over bulde viewing changes
JTraill Sep 15, 2025
e9ab344
fix: wrongly mapped field
JTraill Sep 15, 2025
fb80d70
refactor: remove no longer used file
JTraill Sep 15, 2025
c4d0f5c
fix: rename fileid=>physicalfileid
JTraill Sep 15, 2025
06a77eb
test: rename fileid
JTraill Sep 15, 2025
a719d38
test: rename fileid to physicalfileid
JTraill Sep 15, 2025
5ce1977
fix: sonar issues
JTraill Sep 16, 2025
1723cfc
JASPER-583/578: Court List Document Bundle (#495)
ronaldo-macapobre Sep 16, 2025
c250063
Merge branch 'master' of https://github.com/bcgov/jasper into fe-bund…
JTraill Sep 16, 2025
135c780
refactor: types into own files and rename them
JTraill Sep 16, 2025
a2c393f
refactor: remove courtlist service from table
JTraill Sep 16, 2025
3a37ef5
refactor: remove inject from courtlisttable
JTraill Sep 16, 2025
b218fe1
Merge pull request #509 from bcgov/fe-bundle-view
JTraill Sep 16, 2025
3d89fb0
build(deps): bump axios from 1.8.2 to 1.12.0 in /aws (#497)
dependabot[bot] Sep 17, 2025
aad19b2
build(deps): bump axios from 1.11.0 to 1.12.0 in /web (#498)
dependabot[bot] Sep 17, 2025
0ca3b95
Bump Microsoft.AspNetCore.Authentication.OpenIdConnect from 9.0.8 to …
dependabot[bot] Sep 17, 2025
6bec8b2
update working directory for api gha (#511)
ronaldo-macapobre Sep 17, 2025
4d5eddd
- Default All Documents dropdown to Scheduled if available. Otherwise…
ronaldo-macapobre Sep 17, 2025
dcb1e7b
refactor: force new tab each time file is viewed
JTraill Sep 17, 2025
b19f628
- Add setting to delete pods after 3 days (#512)
ronaldo-macapobre Sep 17, 2025
eec6979
Bump Microsoft.AspNetCore.Authentication.JwtBearer from 9.0.8 to 9.0.…
dependabot[bot] Sep 17, 2025
049928c
Bump Microsoft.AspNetCore.Mvc.NewtonsoftJson from 9.0.8 to 9.0.9 (#505)
dependabot[bot] Sep 17, 2025
897d58f
Merge branch 'master' of https://github.com/bcgov/jasper into file-vi…
JTraill Sep 17, 2025
7e53906
Merge pull request #513 from bcgov/file-view-new-tab
JTraill Sep 17, 2025
12c68b2
Update working directory to use . (#514)
ronaldo-macapobre Sep 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/actions/build-api/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ runs:

- run: dotnet format --verify-no-changes --severity info
shell: bash
working-directory: ${{ inputs.working_directory }}
working-directory: ${{ inputs.working_directory }}/api
continue-on-error: true

- run: dotnet restore
Expand All @@ -29,8 +29,8 @@ runs:

- run: dotnet build --configuration Release --no-restore
shell: bash
working-directory: ${{ inputs.working_directory }}
working-directory: ${{ inputs.working_directory }}/api

- run: dotnet test --no-restore --verbosity normal
shell: bash
working-directory: ${{ inputs.working_directory }}
working-directory: ${{ inputs.working_directory }}/tests
6 changes: 3 additions & 3 deletions .github/workflows/build-and-test-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ on:
branches:
- master
paths:
- "api/**"
- "db/**"
- 'api/**'
- 'db/**'

workflow_dispatch:

env:
WORKING_DIRECTORY: ./api
WORKING_DIRECTORY: .

jobs:
build-and-test:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build-infra.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ jobs:
sarif_file: tfsec.sarif

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@v5
with:
role-skip-session-tagging: true
aws-region: ${{ vars.AWS_REGION }}
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/publish-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ on:
branches:
- master
paths:
- "api/**"
- "db/**"
- 'api/**'
- 'db/**'

workflow_dispatch:

env:
WORKING_DIRECTORY: ./api
WORKING_DIRECTORY: .
IMAGE_NAME: api
GITHUB_IMAGE_REPO: ghcr.io/bcgov/jasper

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-infra.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ jobs:
sarif_file: tfsec.sarif

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@v5
with:
role-skip-session-tagging: true
aws-region: ${{ vars.AWS_REGION }}
Expand Down
6 changes: 4 additions & 2 deletions api/Controllers/AuthController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@
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";
var baseUri = HttpContext.Request.Headers.ContainsKey("X-Forwarded-Host") ? $"{HttpContext.Request.Headers["X-Base-Href"]}" : "/api";

Check warning

Code scanning / SonarCloud

Use model binding instead of reading raw request data

<!--SONAR_ISSUE_KEY:AZGPidDNWuRymkuDhHtd-->Use model binding instead of accessing the raw request data <p>See more on <a href="https://sonarcloud.io/project/issues?id=bcgov_jasper&issues=AZGPidDNWuRymkuDhHtd&open=AZGPidDNWuRymkuDhHtd">SonarQube Cloud</a></p>

Check warning

Code scanning / SonarCloud

Use model binding instead of reading raw request data

<!--SONAR_ISSUE_KEY:AZGPidDNWuRymkuDhHte-->Use model binding instead of accessing the raw request data <p>See more on <a href="https://sonarcloud.io/project/issues?id=bcgov_jasper&issues=AZGPidDNWuRymkuDhHte&open=AZGPidDNWuRymkuDhHte">SonarQube Cloud</a></p>

var applicationUrl = $"{XForwardedForHelper.BuildUrlString(forwardedHost, forwardedPort, baseUri)}";
var keycloakLogoutUrl = $"{logoutUrl}?post_logout_redirect_uri={applicationUrl}";
var keycloakLogoutUrl = $"{logoutUrl}?post_logout_redirect_uri={applicationUrl}"; //TODO: add id_token_hint using id_token, currently removed in AuthenticationServiceCollectionExtension
var siteMinderLogoutUrl = $"{Configuration.GetNonEmptyValue("SiteMinderLogoutUrl")}?returl={keycloakLogoutUrl}&retnow=1";
return Redirect(siteMinderLogoutUrl);

Check warning

Code scanning / CodeQL

URL redirection from remote source Medium

Untrusted URL redirection due to
user-provided value
.
Untrusted URL redirection due to
user-provided value
.
}

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
Expand Down Expand Up @@ -131,6 +131,8 @@
AgencyCode = HttpContext.User.AgencyCode(),
UserId = HttpContext.User.UserId(),
JudgeId = HttpContext.User.JudgeId(),
Email = HttpContext.User.Email(),
IsActive = HttpContext.User.IsActive(),
DateTime.UtcNow
}));
}
Expand Down
20 changes: 19 additions & 1 deletion api/Controllers/BindersController.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Linq;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
Expand Down Expand Up @@ -62,4 +63,21 @@ public virtual async Task<IActionResult> DeleteBinder(string id)

return NoContent();
}

[HttpPost]
[Route("bundle")]
public async Task<IActionResult> CreateDocumentBundle([FromBody] List<Dictionary<string, string>> request)
{
if (request == null || request.Count == 0)
{
return BadRequest("Invalid request.");
}

var result = await _binderService.CreateDocumentBundle(request);
if (!result.Succeeded)
{
return BadRequest(new { error = result.Errors });
}
return Ok(result);
}
}
80 changes: 77 additions & 3 deletions api/Controllers/UsersController.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
using System.Threading.Tasks;
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using FluentValidation;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Scv.Api.Helpers.Extensions;
using Scv.Api.Infrastructure.Authorization;
using Scv.Api.Models.AccessControlManagement;
using Scv.Api.Services;
Expand All @@ -14,17 +20,85 @@ namespace Scv.Api.Controllers;
[ApiController]
public class UsersController(
IUserService userService,
IValidator<UserDto> validator
IValidator<UserDto> validator,
ILogger<UsersController> logger
) : AccessControlManagementControllerBase<IUserService, UserDto>(userService, validator)
{

/// <summary>
/// Get all active users
/// </summary>
/// <returns>Active users</returns>
[HttpGet]
[RequiresPermission(permissions: [Permission.LOCK_UNLOCK_USERS])]
[RequiresPermission(permissions: Permission.LOCK_UNLOCK_USERS)]
public override Task<IActionResult> GetAll()
{
return base.GetAll();
}

/// <summary>
/// Get the user information for the currently logged-in user.
/// </summary>
/// <returns>Active users</returns>
[HttpGet]
[Route("me")]
public async Task<IActionResult> GetMyUser()
{
var userId = User.UserId();
logger.LogInformation("User Id {UserId}, returning their own user information", userId);
if (string.IsNullOrWhiteSpace(userId))
{
return BadRequest("Invalid user. Please contact the JASPER admin.");
}
var user = await base.Service.GetByIdWithPermissionsAsync(User.UserId());

if (user != null)
{
return Ok(user);
}
return NotFound("Unable to locate JASPER user. Please contact the JASPER admin.");
}

/// <summary>
/// Allows a new user without authorization to JASPER to request access to the application.
/// </summary>
/// <returns>The user resulting from the access request.</returns>
[HttpPut]
[Route("request-access")]
public async Task<IActionResult> RequestAccess()
{
var userId = User.UserId();
logger.LogInformation("User Id {UserId}, requested access", userId);
if (string.IsNullOrWhiteSpace(userId))
{
return BadRequest("Invalid user. Please contact the JASPER admin.");
}

var existingUserResponse = await base.GetById(User.UserId());

var email = User.Email();
if (existingUserResponse is OkObjectResult okResult)
{
var existingUser = (UserDto)okResult.Value;
if (existingUser != null)
{
if (email != existingUser.Email)
{
existingUser.Email = email;
}
existingUser.IsPendingRegistration = true;
var result = await base.Update(User.UserId(), existingUser);

return result;
}
else
{
return NotFound("Unable to locate JASPER user. Please contact the JASPER admin.");
}
}
else
{
return existingUserResponse;
}
}
}
51 changes: 51 additions & 0 deletions api/Documents/DocumentConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using JCCommon.Clients.FileServices;
using MapsterMapper;
using Scv.Api.Models.Criminal.Detail;
using Scv.Api.Services;

namespace Scv.Api.Documents;

public class DocumentConverter(IMapper mapper, LookupService lookupService) : IDocumentConverter
{
private readonly IMapper _mapper = mapper;
private readonly LookupService _lookupService = lookupService;

public async Task<ICollection<CriminalDocument>> GetCriminalDocuments(CfcAccusedFile ac)
{
var criminalDocuments = _mapper.Map<List<CriminalDocument>>(ac.Document);

//Create ROPs.
if (ac.Appearance != null && ac.Appearance.Count != 0)
{
criminalDocuments.Insert(0, new CriminalDocument
{
DocumentTypeDescription = "Record of Proceedings",
DocmFormDsc = "Record of Proceedings",
ImageId = ac.PartId,
Category = "rop",
PartId = ac.PartId,
HasFutureAppearance = ac.Appearance?.Any(a =>
a?.AppearanceDate != null && DateTime.Parse(a.AppearanceDate) >= DateTime.Today)
});
}

//Populate extra fields.
foreach (var document in criminalDocuments)
{
document.Category = string.IsNullOrEmpty(document.Category)
? await _lookupService.GetDocumentCategory(document.DocmFormId, document.DocmClassification)
: document.Category;
document.DocumentTypeDescription = document.DocmFormDsc;
document.PartId = string.IsNullOrEmpty(ac.PartId) ? null : ac.PartId;
document.DocmId = string.IsNullOrEmpty(document.DocmId) ? null : document.DocmId;
document.ImageId = string.IsNullOrEmpty(document.ImageId) ? null : document.ImageId;
document.HasFutureAppearance = ac.Appearance?.Any(a =>
a?.AppearanceDate != null && DateTime.Parse(a.AppearanceDate) >= DateTime.Today);
}
return criminalDocuments;
}
}
11 changes: 11 additions & 0 deletions api/Documents/IDocumentConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using JCCommon.Clients.FileServices;
using Scv.Api.Models.Criminal.Detail;

namespace Scv.Api.Documents;

public interface IDocumentConverter
{
Task<ICollection<CriminalDocument>> GetCriminalDocuments(CfcAccusedFile ac);
}
28 changes: 12 additions & 16 deletions api/Documents/Strategies/ReportStrategy.cs
Original file line number Diff line number Diff line change
@@ -1,30 +1,26 @@
using System.IO;
using System.Threading.Tasks;
using Scv.Api.Models.CourtList;
using PCSSCommon.Clients.ReportServices;
using Scv.Api.Models.Document;
using Scv.Api.Services;

namespace Scv.Api.Documents.Strategies;

public class ReportStrategy(CourtListService courtListService) : IDocumentStrategy
public class ReportStrategy(ReportServicesClient reportServiceClient) : IDocumentStrategy
{
private readonly CourtListService _courtListService = courtListService;
private readonly ReportServicesClient _reportServiceClient = reportServiceClient;

public DocumentType Type => DocumentType.Report;

public async Task<MemoryStream> Invoke(PdfDocumentRequestDetails documentRequest)
{
var courtListReportRequest = new CourtListReportRequest
{
CourtDivision = documentRequest.CourtDivisionCd,
Date = documentRequest.Date,
LocationId = documentRequest.LocationId,
CourtClass = documentRequest.CourtClassCd,
RoomCode = documentRequest.RoomCode,
AdditionsList = documentRequest.AdditionsList,
ReportType = documentRequest.ReportType
};
(Stream pdfStream, _) = await _courtListService.GenerateReportAsync(courtListReportRequest);
(Stream pdfStream, _) = await _reportServiceClient.GetCourtListReportAsync(
documentRequest.CourtDivisionCd,
documentRequest.Date.GetValueOrDefault(),
documentRequest.LocationId.GetValueOrDefault(),
documentRequest.CourtClassCd,
documentRequest.RoomCode,
documentRequest.AdditionsList,
documentRequest.ReportType);

return (MemoryStream)pdfStream;
}
Expand Down
1 change: 1 addition & 0 deletions api/Helpers/ClaimsTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public class CustomClaimTypes
public const string JcParticipantId = nameof(CustomClaimTypes) + nameof(JcParticipantId);
public const string JcAgencyCode = nameof(CustomClaimTypes) + nameof(JcAgencyCode);
public const string IsSupremeUser = nameof(CustomClaimTypes) + nameof(IsSupremeUser);
public const string IsActive = nameof(CustomClaimTypes) + nameof(IsActive);
public const string CivilFileAccess = nameof(CustomClaimTypes) + nameof(CivilFileAccess);
public const string ExternalRole = nameof(CustomClaimTypes) + nameof(ExternalRole);
public const string SubRole = nameof(CustomClaimTypes) + nameof(SubRole);
Expand Down
13 changes: 12 additions & 1 deletion api/Helpers/Extensions/ClaimsPrincipalExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using Scv.Api.Models.AccessControlManagement;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
Expand Down Expand Up @@ -53,6 +54,10 @@ public static bool IsSupremeUser(this ClaimsPrincipal claimsPrincipal)
=> claimsPrincipal.HasClaim(c => c.Type == CustomClaimTypes.IsSupremeUser) &&
claimsPrincipal.FindFirstValue(CustomClaimTypes.IsSupremeUser).Equals("true", StringComparison.OrdinalIgnoreCase);

public static bool IsActive(this ClaimsPrincipal claimsPrincipal)
=> claimsPrincipal.HasClaim(c => c.Type == CustomClaimTypes.IsActive) &&
claimsPrincipal.FindFirstValue(CustomClaimTypes.IsActive).Equals("true", StringComparison.OrdinalIgnoreCase);

public static string ExternalRole(this ClaimsPrincipal claimsPrincipal) =>
claimsPrincipal.FindFirstValue(CustomClaimTypes.ExternalRole);

Expand Down Expand Up @@ -129,5 +134,11 @@ public static string UserGuid(this ClaimsPrincipal claimsPrincipal)

public static string ExternalJudgeId(this ClaimsPrincipal claimsPrincipal)
=> claimsPrincipal.FindFirstValue(CustomClaimTypes.ExternalJudgeId);

// Check if any of the user's claims have meaningfully changed compared to the current user data
public static bool HasChanged(this ClaimsPrincipal claimsPrincipal, UserDto currentUser)
=> claimsPrincipal.IsActive() != currentUser.IsActive ||
!claimsPrincipal.Roles().Order().SequenceEqual(currentUser.Roles.Order()) ||
!claimsPrincipal.Permissions().Order().SequenceEqual(currentUser.Permissions.Order());
}
}
Loading
Loading