Skip to content

Commit

Permalink
feat(FSADT1-1261): adding rbac to submission list/review (#889)
Browse files Browse the repository at this point in the history
Co-authored-by: Maria Martinez <[email protected]>
  • Loading branch information
paulushcgcj and mamartinezmejia authored Mar 20, 2024
1 parent 458fbea commit 2ab8d1a
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 18 deletions.
2 changes: 2 additions & 0 deletions backend/src/main/java/ca/bc/gov/app/ApplicationConstant.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,9 @@ left join nrfc.province_code pc on (pc.province_code = sl.province_code and pc.c
public static final String ROLE_IDIR_USER = "IDIR_USER";
public static final String ROLE_SERVICE_USER = "S2S_USER";

public static final String ROLE_VIEWER = "CLIENT_VIEWER";
public static final String ROLE_EDITOR = "CLIENT_EDITOR";
public static final String ROLE_ADMIN = "CLIENT_ADMIN";

}

Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,13 @@ public void customize(AuthorizeExchangeSpec authorize) {
ApplicationConstant.ROLE_BCSC_USER,
ApplicationConstant.ROLE_SERVICE_USER
)
// Only Editors users can get details and approve/reject submissions
// Only Editors and Admin can approve/reject submissions
.pathMatchers(HttpMethod.POST,"/api/clients/submissions/{id:[0-9]+}")
.hasAnyRole(
ApplicationConstant.ROLE_EDITOR,
ApplicationConstant.ROLE_ADMIN
)
// Only Editors, Viewers and Admin users can get details
.pathMatchers("/api/clients/submissions/{id:[0-9]+}")
.hasAnyRole(
ApplicationConstant.ROLE_EDITOR
Expand All @@ -78,7 +84,9 @@ public void customize(AuthorizeExchangeSpec authorize) {
)
.pathMatchers(HttpMethod.GET, "/api/clients/submissions/**")
.hasAnyRole(
ApplicationConstant.ROLE_EDITOR
ApplicationConstant.ROLE_VIEWER,
ApplicationConstant.ROLE_EDITOR,
ApplicationConstant.ROLE_ADMIN
)
// All BCSC, BCEID, and IDIR users can access the client APIs
.pathMatchers("/api/clients/**")
Expand Down
3 changes: 3 additions & 0 deletions frontend/cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,16 @@ Cypress.Commands.add(

const userId = generateRandomHex(32);

const roles = provider === "idir" ? ["CLIENT_VIEWER", "CLIENT_EDITOR", "CLIENT_ADMIN"] : ["USER"];

const jwtBody = {
"custom:idp_display_name": name,
"custom:idp_name": provider,
"custom:idp_user_id": userId,
email,
firstName: "UAT",
lastName: "Test",
"cognito:groups": roles,
...extra,
};

Expand Down
1 change: 1 addition & 0 deletions frontend/src/dto/CommonTypesDto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export interface ModalNotification {
export interface SessionProperties {
user: Submitter | undefined;
token: string | undefined;
authorities: string[];
logIn: (provider: string) => void;
logOut: () => void;
isLoggedIn: () => boolean;
Expand Down
24 changes: 21 additions & 3 deletions frontend/src/helpers/ForestClientUserSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { cognitoEnvironment, nodeEnv, cognitoClientId } from "@/CoreConstants";
class ForestClientUserSession implements SessionProperties {
public user: Submitter | undefined;
public token: string | undefined;
public authorities: string[] = [];

logIn = (provider: string): void => {
signInWithRedirect({
Expand All @@ -19,6 +20,8 @@ class ForestClientUserSession implements SessionProperties {

logOut = (): void => {
this.user = undefined;
this.token = undefined;
this.authorities = [];
signOut();

if (nodeEnv === "test") {
Expand All @@ -39,16 +42,21 @@ class ForestClientUserSession implements SessionProperties {
if (idToken) {
const parsedUser: any = idToken.payload;
const address = parsedUser["address"];
const streetAddress = address !== undefined ? JSON.parse(address.formatted) : {};
const streetAddress =
address !== undefined ? JSON.parse(address.formatted) : {};

const provider =parsedUser["custom:idp_name"].startsWith("ca.bc.gov.flnr.fam.")
const provider = parsedUser["custom:idp_name"].startsWith(
"ca.bc.gov.flnr.fam."
)
? "bcsc"
: parsedUser["custom:idp_name"];

this.user = {
name: toTitleCase(parsedUser["custom:idp_display_name"]),
provider: provider,
userId: `${provider}\\${parsedUser["custom:idp_username"] ?? parsedUser["custom:idp_user_id"]}`,
userId: `${provider}\\${
parsedUser["custom:idp_username"] ?? parsedUser["custom:idp_user_id"]
}`,
businessId: parsedUser["custom:idp_business_id"] ?? "",
birthdate: parsedUser["birthdate"],
address: {
Expand All @@ -68,6 +76,16 @@ class ForestClientUserSession implements SessionProperties {
email: parsedUser.email,
...this.processName(parsedUser, parsedUser["custom:idp_name"]),
};
this.authorities.push(`${provider}_USER`.toUpperCase());

if (parsedUser["cognito:groups"]) {
const groups: string[] | undefined = parsedUser["cognito:groups"];
console.log(groups, typeof groups);
if (parsedUser["cognito:groups"]) {
const groups: string[] | undefined = parsedUser["cognito:groups"];
groups?.forEach((group) => this.authorities.push(group));
}
}
} else {
this.user = undefined;
}
Expand Down
29 changes: 25 additions & 4 deletions frontend/src/pages/SubmissionListPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,18 @@ import "@carbon/web-components/es/components/tooltip/index";
// Composables
import { useFetchTo } from "@/composables/useFetch";
import { useRouter } from "vue-router";
import { useEventBus } from "@vueuse/core";
// Types
import type { SubmissionList } from "@/dto/CommonTypesDto";
import { formatDistanceToNow, format } from "date-fns";
import { toTitleCase } from "@/services/ForestClientService";
// Session
import ForestClientUserSession from "@/helpers/ForestClientUserSession";
// @ts-ignore
import Approved16 from "@carbon/icons-vue/es/task--complete/16";
// @ts-ignore
import Review16 from "@carbon/icons-vue/es/data--view--alt/16";
import { toTitleCase } from "@/services/ForestClientService";
const router = useRouter();
Expand Down Expand Up @@ -108,11 +112,13 @@ onMounted(() => {
disableSkelleton();
watch(skeletonReference, disableSkelleton);
});
const userhasAuthority = ["CLIENT_VIEWER", "CLIENT_EDITOR", "CLIENT_ADMIN"].some(authority => ForestClientUserSession.authorities.includes(authority));
</script>

<template>
<div id="screen" class="submission-list">
<div id="title">
<div id="title" v-if="userhasAuthority">
<div>
<div class="form-header-title mg-sd-25">
<h1>Submissions</h1>
Expand All @@ -121,7 +127,7 @@ onMounted(() => {
</div>
</div>

<div id="datatable">
<div id="datatable" v-if="userhasAuthority">
<cds-table use-zebra-styles v-if="!loading">
<cds-table-head>
<cds-table-header-row>
Expand Down Expand Up @@ -159,7 +165,8 @@ onMounted(() => {
:headers="['Client name', 'Client type', 'District', 'Submitted on', 'Submission status']"
/>
</div>
<div class="paginator" v-if="totalItems">

<div class="paginator" v-if="totalItems && userhasAuthority">
<cds-pagination
items-per-page-text="Submissions per page"
:page-size="pageSize"
Expand All @@ -173,6 +180,20 @@ onMounted(() => {
<cds-select-item :value="50">50</cds-select-item>
</cds-pagination>
</div>

<cds-actionable-notification
v-if="!userhasAuthority"
v-shadow="true"
low-contrast="true"
hide-close-button="true"
open="true"
kind="error"
title="You are not authorized to access this page"
>
<div>
Please email [email protected] for help
</div>
</cds-actionable-notification>
</div>

</template>
Expand Down
60 changes: 52 additions & 8 deletions frontend/src/pages/SubmissionReviewPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,13 @@ const renderListItem = (label, clientNumbers) => {
finalLabel
);
};
const userhasAuthority = ["CLIENT_VIEWER", "CLIENT_EDITOR", "CLIENT_ADMIN"].some(authority => ForestClientUserSession.authorities.includes(authority));
const isNotEditor = !ForestClientUserSession.authorities.includes('CLIENT_EDITOR') && !ForestClientUserSession.authorities.includes('CLIENT_ADMIN');
if(isNotEditor){
submitDisabled.value = true;
}
</script>

<template>
Expand All @@ -303,21 +310,23 @@ const renderListItem = (label, clientNumbers) => {

</cds-breadcrumb>

<h1 class="submission-details--title">
<h1 class="submission-details--title" v-if="userhasAuthority">
<span>
{{ toTitleCase(data.business.organizationName) }}
</span>
</h1>
<div v-if="userhasAuthority">
<p class="body-02 light-theme-text-text-secondary" data-testid="subtitle" v-if="data.submissionType === 'Auto approved client'">Check this new client data</p>
<p class="body-02 light-theme-text-text-secondary" data-testid="subtitle" v-else>Check and manage this submission for a new client number</p>
</div>
</div>

<div class="hide-when-less-than-two-children"><!--
This div is necessary to avoid the div.header-offset below from interfering in the flex flow.
-->
<div data-scroll="top-notification" class="header-offset"></div>
<cds-actionable-notification
v-if="networkErrorMsg !== ''"
v-if="networkErrorMsg !== '' && userhasAuthority"
v-shadow="true"
low-contrast="true"
hide-close-button="true"
Expand All @@ -331,7 +340,7 @@ const renderListItem = (label, clientNumbers) => {
</cds-actionable-notification>

<cds-actionable-notification
v-if="reviewConflictError"
v-if="reviewConflictError && userhasAuthority"
v-shadow="true"
low-contrast="true"
hide-close-button="true"
Expand All @@ -346,7 +355,7 @@ const renderListItem = (label, clientNumbers) => {
</div>

<cds-actionable-notification
v-if="data.submissionType === 'Auto approved client'"
v-if="data.submissionType === 'Auto approved client' && userhasAuthority"
v-shadow="true"
low-contrast="true"
hide-close-button="true"
Expand All @@ -363,7 +372,7 @@ const renderListItem = (label, clientNumbers) => {
</cds-actionable-notification>

<cds-inline-notification
v-if="data.submissionType === 'Review new client' && data.submissionStatus === 'Approved'"
v-if="data.submissionType === 'Review new client' && data.submissionStatus === 'Approved' && userhasAuthority"
v-shadow="true"
low-contrast="true"
hide-close-button="true"
Expand All @@ -378,7 +387,8 @@ const renderListItem = (label, clientNumbers) => {
v-if="
data.submissionType === 'Review new client' &&
data.matchers.goodStanding === 'Value not found' &&
data.submissionStatus !== 'Approved'
data.submissionStatus !== 'Approved' &&
userhasAuthority
"
v-shadow="true"
low-contrast="true"
Expand All @@ -398,7 +408,12 @@ const renderListItem = (label, clientNumbers) => {
</cds-actionable-notification>

<cds-actionable-notification
v-if="data.submissionType === 'Review new client' && matchingData.length > 0 && data.submissionStatus !== 'Approved'"
v-if="
data.submissionType === 'Review new client' &&
matchingData.length > 0 &&
data.submissionStatus !== 'Approved' &&
userhasAuthority
"
v-shadow="true"
low-contrast="true"
hide-close-button="true"
Expand All @@ -424,7 +439,36 @@ const renderListItem = (label, clientNumbers) => {
</div>
</cds-actionable-notification>

<div class="grouping-14">
<cds-actionable-notification
v-if="!userhasAuthority"
v-shadow="true"
low-contrast="true"
hide-close-button="true"
open="true"
kind="error"
title="You are not authorized to access this page"
>
<div>
Please email [email protected] for help
</div>
</cds-actionable-notification>

<cds-actionable-notification
v-if="isNotEditor"
v-shadow="true"
low-contrast="true"
hide-close-button="true"
open="true"
kind="warning"
title="You are not authorized to modify client information"
>
<div>
<p>To change your role please contact Client Admin through email [email protected] for help
</p>
</div>
</cds-actionable-notification>

<div class="grouping-14" v-if="userhasAuthority">
<div class="grouping-05-short">
<div>
<h2 class="mg-tl-2 heading-06">Client summary</h2>
Expand Down
2 changes: 1 addition & 1 deletion frontend/stub/mappings/fam.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"Location": "http://{{request.host}}:3000/dashboard",
"Set-Cookie": [
"CognitoIdentityServiceProvider.69u4hdmcoiuuhpmi59qmndhikk.LastAuthUser=abc123; sameSite=Lax; path=/; Expires={{now offset='10 years' format='EEE, d MMM yyyy HH:mm:ss'}} GMT; domain={{request.host}}",
"CognitoIdentityServiceProvider.69u4hdmcoiuuhpmi59qmndhikk.abc123.idToken=eyJhbGciOiJIUzI1NiJ9.eyJwcmVmZXJyZWRfdXNlcm5hbWUiOiJiNWVjZGIwOTRkZmI0MTQ5YTZhODQ0NWEwMWE5NmJmMEBpZGlyIiwiY3VzdG9tOmlkcF91c2VyX2lkIjoiQjVFQ0RCMDk0REZCNDE0OUE2QTg0NDVBMDFBOTZCRjAiLCJjdXN0b206aWRwX3VzZXJuYW1lIjoiSlJZQU4iLCJjdXN0b206aWRwX2Rpc3BsYXlfbmFtZSI6IlJ5YW4sIEphY2sgQ0lBOklOIiwiZW1haWwiOiJqYWNrLnJ5YW5AZ292LmJjLmNhIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJjdXN0b206aWRwX25hbWUiOiJpZGlyIiwiZ2l2ZW5fbmFtZSI6IkphY2siLCJuYW1lIjoiSmFjayBSeWFuIiwiZmFtaWx5X25hbWUiOiJSeWFuIn0.J7UgluYBloUkw29_ubfRFSNYLW4rw0SdMjFI5uunoUE; sameSite=Lax; path=/; Expires={{now offset='10 years' format='EEE, d MMM yyyy HH:mm:ss'}} GMT; domain={{request.host}}"
"CognitoIdentityServiceProvider.69u4hdmcoiuuhpmi59qmndhikk.abc123.idToken=eyJhbGciOiJIUzI1NiJ9.eyJjb2duaXRvOmdyb3VwcyI6WyJDTElFTlRfVklFV0VSIiwiQ0xJRU5UX0VESVRPUiIsIkNMSUVOVF9BRE1JTiJdLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJiNWVjZGIwOTRkZmI0MTQ5YTZhODQ0NWEwMWE5NmJmMEBpZGlyIiwiY3VzdG9tOmlkcF91c2VyX2lkIjoiQjVFQ0RCMDk0REZCNDE0OUE2QTg0NDVBMDFBOTZCRjAiLCJjdXN0b206aWRwX3VzZXJuYW1lIjoiSlJZQU4iLCJjdXN0b206aWRwX2Rpc3BsYXlfbmFtZSI6IlJ5YW4sIEphY2sgQ0lBOklOIiwiZW1haWwiOiJqYWNrLnJ5YW5AZ292LmJjLmNhIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJjdXN0b206aWRwX25hbWUiOiJpZGlyIiwiZ2l2ZW5fbmFtZSI6IkphY2siLCJuYW1lIjoiSmFjayBSeWFuIiwiZmFtaWx5X25hbWUiOiJSeWFuIn0.b9EAJNDPl9ycrMjCE_g34F-tjvsLpdm4Ui_4Jr1TPao; sameSite=Lax; path=/; Expires={{now offset='10 years' format='EEE, d MMM yyyy HH:mm:ss'}} GMT; domain={{request.host}}"
]
}
}
Expand Down

0 comments on commit 2ab8d1a

Please sign in to comment.