Skip to content
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

feat: #1145 use cached access for applications list #1209

Merged
merged 24 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
753984b
Adjust AuthService: add new refreshCachedUserAccess.
ianliuwk1019 Feb 16, 2024
613be05
Refactor to new FamLoginUserState and some refactoring for AuthService.
ianliuwk1019 Feb 17, 2024
c13b827
Add function for loginUser accesses: getApplicationsAdministeredByAdm…
ianliuwk1019 Feb 20, 2024
dc0750b
Remove unused code.
ianliuwk1019 Feb 20, 2024
cd9f473
Add getApplicationsUserAdministers and temporary adjust current logic
ianliuwk1019 Feb 20, 2024
844ce57
Add sorting for getApplicationsUserAdministers function.
ianliuwk1019 Feb 20, 2024
f6ac76d
Make getUserAdminRoleGroups available.
ianliuwk1019 Feb 21, 2024
a9a3124
Add adminRoles to profile
ianliuwk1019 Feb 21, 2024
82a9b7e
Adjust adminRoles
ianliuwk1019 Feb 21, 2024
d6cea54
Rename variable
ianliuwk1019 Feb 21, 2024
26d1e40
Adjust some comment.
ianliuwk1019 Feb 21, 2024
bd78e11
Use getApplicationsAdministeredByAdminRole for applications selection…
ianliuwk1019 Feb 21, 2024
76b2a8b
Remove unused code to resolve sonar issue.
ianliuwk1019 Feb 21, 2024
d582e7f
Fix build error.
ianliuwk1019 Feb 21, 2024
5005adb
Add applicationsUserAdministers computed property and removed unneces…
ianliuwk1019 Feb 22, 2024
e6042ac
Add refreshCachedUserAccess on token refreshed.
ianliuwk1019 Feb 22, 2024
5126ac4
Carry over accesses when token is being refreshed.
ianliuwk1019 Feb 22, 2024
ab2ed3d
Only do refreshCachedUserAccess when login.
ianliuwk1019 Feb 22, 2024
04a5bfe
Minor name refactoring.
ianliuwk1019 Feb 22, 2024
3b1ed0f
Update comment.
ianliuwk1019 Feb 22, 2024
895a7be
Minor adjustment on name.
ianliuwk1019 Feb 22, 2024
3973e92
Fix bug for using SET.
ianliuwk1019 Feb 22, 2024
2708f4a
Rename to cacheUserAccess and adjust getApplicationsUserAdministers l…
ianliuwk1019 Feb 23, 2024
6fdcc98
Remove unused import.
ianliuwk1019 Feb 23, 2024
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
2 changes: 1 addition & 1 deletion frontend/src/components/AuthCallbackHandler.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import authService from '@/services/AuthService';
* This component is to deal with Auth callback.
*/

authService.methods.handlePostLogin().then((data) => {
authService.handlePostLogin().then((data) => {
router.push('/dashboard');
});
</script>
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/components/Landing.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import Button from '@/components/common/Button.vue';
import { IconSize } from '@/enum/IconEnum';
import authService from '@/services/AuthService';
import AuthService from '@/services/AuthService';
import logo from '@/assets/images/bc-gov-logo.png';
import TreeLogs from '@/assets/images/tree-logs.jpg';
</script>
Expand All @@ -23,7 +23,7 @@ import TreeLogs from '@/assets/images/tree-logs.jpg';
class="landing-button"
label="Login with IDIR"
id="login-idir-button"
@click="authService.methods.login()"
@click="AuthService.login()"
>
<Icon icon="login" :size="IconSize.medium" />
</Button>
Expand All @@ -33,7 +33,7 @@ import TreeLogs from '@/assets/images/tree-logs.jpg';
label="Login with BCeID"
id="login-bceid-button"
disabled
@click="authService.methods.login()"
@click="AuthService.login()"
>
<Icon icon="login" :size="IconSize.medium" />
</Button>
Expand Down
31 changes: 22 additions & 9 deletions frontend/src/components/common/ProfileSidebar.vue
Original file line number Diff line number Diff line change
@@ -1,28 +1,38 @@
<script setup lang="ts">
import { computed, ref } from 'vue';
import Avatar from 'primevue/avatar';
import Button from '@/components/common/Button.vue';
import { IconSize } from '@/enum/IconEnum';
import authService from '@/services/AuthService';
import LoginUserState from '@/store/FamLoginUserState';
import { profileSidebarState } from '@/store/ProfileSidebarState';
import Avatar from 'primevue/avatar';
import { computed, ref } from 'vue';

const userName = authService.state.value.famLoginUser!.username;
const initals = userName ? userName.slice(0, 2) : '';
const displayName = authService.state.value.famLoginUser!.displayName;
const email = authService.state.value.famLoginUser!.email;
const userName = LoginUserState.state.value.famLoginUser!.username;
const initials = userName ? userName.slice(0, 2) : '';
const displayName = LoginUserState.state.value.famLoginUser!.displayName;
const email = LoginUserState.state.value.famLoginUser!.email;

// use local loading state, can't use LoadingState instance
// due to logout() is handled by library.
const loading = ref(false);

const logout = () => {
authService.methods.logout();
authService.logout();
loading.value = true;
};

const buttonLabel = computed(() => {
return loading.value ? 'Signing out...' : 'Sign out';
});

const adminRoles = computed(() => {
const userAdminRoles = LoginUserState.getUserAdminRoleGroups()
if (userAdminRoles) {
return userAdminRoles.map((adminRole) => {
return adminRole.replace("_", " ")
}).join(", ")
}
})
</script>

<template>
Expand All @@ -44,7 +54,7 @@ const buttonLabel = computed(() => {
</div>
<div class="sidebar-body">
<Avatar
:label="initals"
:label="initials"
class="mr-2 profile-avatar"
size="xlarge"
shape="circle"
Expand All @@ -53,6 +63,7 @@ const buttonLabel = computed(() => {
<p class="profile-name">{{ displayName }}</p>
<p class="profile-idir">IDIR: {{ userName }}</p>
<p class="profile-email">{{ email }}</p>
<p class="profile-admin-level">Granted: <strong>{{ adminRoles }}</strong></p>
</div>
</div>
<hr class="profile-divider" />
Expand Down Expand Up @@ -132,7 +143,8 @@ const buttonLabel = computed(() => {
}

.profile-name,
.profile-idir {
.profile-idir,
.profile-email {
margin-bottom: 0.375rem;
}
}
Expand Down Expand Up @@ -167,6 +179,7 @@ const buttonLabel = computed(() => {

.profile-idir,
.profile-email,
.profile-admin-level,
.options {
font-size: 0.75rem;
font-weight: 400;
Expand Down
28 changes: 11 additions & 17 deletions frontend/src/components/grantaccess/GrantApplicationAdmin.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { ErrorMessage, Field, Form as VeeForm } from 'vee-validate';
import { object, string } from 'yup';
import router from '@/router';
import Dropdown from 'primevue/dropdown';
import { AdminMgmtApiService } from '@/services/ApiServiceFactory';
import { UserType, type FamApplication } from 'fam-app-acsctl-api';
import type { FamAppAdminCreateRequest } from 'fam-admin-mgmt-api/model/fam-app-admin-create-request';
import type { FamApplicationGetResponse } from 'fam-admin-mgmt-api/model/fam-application-get-response';
import Button from '@/components/common/Button.vue';
import { IconSize } from '@/enum/IconEnum';
import { Severity, ErrorDescription } from '@/enum/SeverityEnum';

import { isLoading } from '@/store/LoadingState';
import { setNotificationMsg } from '@/store/NotificationState';
import LoginUserState from '@/store/FamLoginUserState';
import { computed, ref } from 'vue';
import { UserType } from 'fam-app-acsctl-api/model';

const defaultFormData = {
userId: '',
Expand All @@ -28,13 +27,8 @@ const formValidationSchema = object({
application: object().required('Application is required'),
});

const applications = ref<FamApplicationGetResponse[]>();

// This is going to be changed when the new backend API is ready
onMounted(async () => {
applications.value = (
await AdminMgmtApiService.applicationsApi.getApplications()
).data;
const applicationOptions = computed(() => {
return LoginUserState.getAppsForFamAdminRole();
});

/* ------------------ User information method ------------------------- */
Expand All @@ -55,8 +49,8 @@ const cancelForm = () => {
const toRequestPayload = (formData: any) => {
const request = {
user_name: formData.userId,
application_id: formData.application.application_id,
user_type_code: UserType.I,
application_id: formData.application.id,
user_type_code: UserType.I
} as FamAppAdminCreateRequest;
return request;
};
Expand All @@ -69,7 +63,7 @@ const handleSubmit = async () => {
setNotificationMsg(
Severity.Success,
`Admin privilege has been added to ${formData.value.userId.toUpperCase()} for application ${
formData.value.application.application_name
formData.value.application.name
}`
);
})
Expand All @@ -78,7 +72,7 @@ const handleSubmit = async () => {
setNotificationMsg(
Severity.Error,
`User ${formData.value.userId.toUpperCase()} is already a ${
formData.value.application.application_name
formData.value.application.name
} admin`
);
} else if (
Expand Down Expand Up @@ -136,8 +130,8 @@ const handleSubmit = async () => {
<label>Select application</label>
<Dropdown
v-model="formData.application"
:options="(applications as FamApplication[])"
optionLabel="application_description"
:options="applicationOptions"
optionLabel="description"
placeholder="Choose an application"
/>
</div>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/header/Header.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const props = defineProps({

<a
title="Profile"
v-if="authService.getters.isLoggedIn()"
v-if="authService.isLoggedIn()"
@click="profileSidebarState.toggleVisible()"
>
<Icon icon="user--avatar" :size="IconSize.medium" />
Expand Down
26 changes: 8 additions & 18 deletions frontend/src/components/managePermissions/ManagePermissions.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
<script setup lang="ts">
import { onUnmounted, shallowRef, type PropType } from 'vue';
import { onUnmounted, shallowRef, type PropType, computed } from 'vue';
import Dropdown, { type DropdownChangeEvent } from 'primevue/dropdown';
import TabView from 'primevue/tabview';
import TabPanel from 'primevue/tabpanel';
import ManagePermissionsTitle from '@/components/managePermissions/ManagePermissionsTitle.vue';
import UserDataTable from '@/components/managePermissions/table/UserDataTable.vue';
import ApplicationAdminTable from '@/components/managePermissions/table/ApplicationAdminTable.vue';

import LoginUserState from '@/store/FamLoginUserState';
import {
applicationsUserAdministers,
isApplicationSelected,
selectedApplication,
setSelectedApplication,
Expand Down Expand Up @@ -50,13 +49,17 @@ const applicationAdmins = shallowRef<FamAppAdminGetResponse[]>(
props.applicationAdmins
);

const applicationsUserAdministers = computed(() => {
return LoginUserState.getApplicationsUserAdministers();
});

onUnmounted(() => {
resetNotification();
});

const onApplicationSelected = async (e: DropdownChangeEvent) => {
setSelectedApplication(e.value ? JSON.stringify(e.value) : null);
if (e.value.application_id === FAM_APPLICATION_ID) {
if (e.value.id === FAM_APPLICATION_ID) {
applicationAdmins.value = await fetchApplicationAdmins();
} else {
userRoleAssignments.value = await fetchUserRoleAssignments(
Expand Down Expand Up @@ -115,7 +118,7 @@ const deleteAppAdmin = async (admin: FamAppAdminGetResponse) => {
v-model="selectedApplication"
@change="onApplicationSelected"
:options="applicationsUserAdministers"
optionLabel="application_description"
optionLabel="description"
placeholder="Choose an application to manage permissions"
class="application-dropdown"
/>
Expand Down Expand Up @@ -159,19 +162,6 @@ const deleteAppAdmin = async (admin: FamAppAdminGetResponse) => {
@deleteUserRoleAssignment="deleteUserRoleAssignment"
/>
</TabPanel>

<!-- waiting for the Delegated admins table
<TabPanel
header="Delegated admins"
:disabled="false"
>
<template #header>
<Icon
icon="enterprise"
:size="IconSize.small"
/>
</template>
</TabPanel> -->
</TabView>
</div>
</div>
Expand Down
22 changes: 9 additions & 13 deletions frontend/src/router/routeHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,21 @@ import { FamRouteError, RouteErrorName } from '@/errors/FamCustomError';
import { routeItems } from '@/router/routeItem';
import AuthService from '@/services/AuthService';
import {
fetchApplicationAdmins,
fetchApplicationRoles,
fetchApplications,
fetchUserRoleAssignments,
fetchApplicationAdmins
fetchUserRoleAssignments
} from '@/services/fetchData';
import { asyncWrap } from '@/services/utils';
import {
isApplicationSelected,
selectedApplication,
selectedApplicationId
selectedApplicationId,
} from '@/store/ApplicationState';
import { populateBreadcrumb } from '@/store/BreadcrumbState';
import { FAM_APPLICATION_ID } from '@/store/Constants';
import LoginUserState from '@/store/FamLoginUserState';
import { setRouteToastError as emitRouteToastError } from '@/store/ToastState';
import type { RouteLocationNormalized } from 'vue-router';

/**
* This file should contain only the Vue router handler and necessary
* helpers for router's life-cycle methods (beforeEach, beforeEnter etc...)
Expand All @@ -39,8 +38,6 @@ const ACCESS_RESTRICTED_ERROR = new FamRouteError(
const beforeEnterDashboardRoute = async (to: RouteLocationNormalized) => {
let applicationAdmins;
let userRolesFetchResult;
// Requires fetching applications the user administers.
await asyncWrap(fetchApplications());
if (selectedApplicationId.value === FAM_APPLICATION_ID) {
applicationAdmins = await asyncWrap(fetchApplicationAdmins())
} else {
Expand All @@ -66,7 +63,7 @@ const beforeEnterGrantUserPermissionRoute = async (
}

const appRolesFetchResult = await asyncWrap(
fetchApplicationRoles(selectedApplication.value!.application_id)
fetchApplicationRoles(selectedApplication.value!.id)
);
if (appRolesFetchResult.error) {
emitRouteToastError(appRolesFetchResult.error);
Expand Down Expand Up @@ -108,7 +105,7 @@ export const beforeEachRouteHandler = async (
from: RouteLocationNormalized
) => {
// Authentication guard. Always check first.
if (to.meta.requiresAuth && !AuthService.getters.isLoggedIn()) {
if (to.meta.requiresAuth && !AuthService.isLoggedIn()) {
// Only to compose this custom error, but not to throw.
// Due to throwing error from router cannot be caught by Primevue toast.
// The RouteError will be emitted to a state.
Expand Down Expand Up @@ -137,17 +134,16 @@ export const beforeEachRouteHandler = async (
// Access privilege guard. This logic might need to be adjusted soon.
if (to.meta.requiredPrivileges) {
for (let role of (to.meta.requiredPrivileges as Array<string>)) {
if (!AuthService.methods.hasAccessRole(role)) {
if (!LoginUserState.hasAccessRole(role)) {
emitRouteToastError(ACCESS_RESTRICTED_ERROR);
return { path: routeItems.dashboard.path };
}
}
}

// Refresh token before navigation.
if (AuthService.state.value.famLoginUser) {
if (LoginUserState.state.value.famLoginUser) {
// condition needed to prevent infinite redirect
await AuthService.methods.refreshToken();
await AuthService.methods.getUserAccess();
await AuthService.refreshToken();
}
};
Loading
Loading