From 7294afd9567a13a80e9f583c6060e8647729c101 Mon Sep 17 00:00:00 2001 From: "Jaspal.Singh-AOT" <163812444+jaspalsingh-aot@users.noreply.github.com> Date: Wed, 3 Apr 2024 16:27:42 -0700 Subject: [PATCH 01/12] API to assign 'formsflow-client' group to BCSC and BCeID users --- .../src/app/controllers/users.controller.ts | 49 +++++ backend/users/src/app/dto/addUserToGroup.ts | 7 + .../services/KeycloakService.service.spec.ts | 189 ++++++++++++++++++ .../app/services/KeycloakService.service.ts | 133 ++++++++++++ backend/users/src/app/users.module.ts | 4 + 5 files changed, 382 insertions(+) create mode 100644 backend/users/src/app/controllers/users.controller.ts create mode 100644 backend/users/src/app/dto/addUserToGroup.ts create mode 100644 backend/users/src/app/services/KeycloakService.service.spec.ts create mode 100644 backend/users/src/app/services/KeycloakService.service.ts diff --git a/backend/users/src/app/controllers/users.controller.ts b/backend/users/src/app/controllers/users.controller.ts new file mode 100644 index 00000000..67e04fc0 --- /dev/null +++ b/backend/users/src/app/controllers/users.controller.ts @@ -0,0 +1,49 @@ +import { Controller, Post, Body, HttpException, HttpStatus } from '@nestjs/common'; +import { Unprotected } from 'nest-keycloak-connect'; +import { KeycloakService } from 'src/app/services/KeycloakService.service'; +import { AddUserToGroupDto } from 'src/app/dto/addUserToGroup'; + +@Controller('users') +@Unprotected() +export class UsersController { + constructor(private readonly keyCloakService: KeycloakService) {} + + @Post('/addGroup') + async addUserToGroup(@Body() addUserToGroupDto: AddUserToGroupDto): Promise { + try + { + const { userId } = addUserToGroupDto; + + // Get access token from Keycloak + const accessToken = await this.keyCloakService.getToken(); + if (!accessToken) + { + throw new HttpException('Failed to get access token', HttpStatus.INTERNAL_SERVER_ERROR); + } + + // Find group ID by name + const groupName = 'formsflow-client'; // Assuming 'formflow-client' is the group name + const groupId = await this.keyCloakService.getGroupIdByName(groupName, accessToken); + if (!groupId) + { + throw new HttpException(`Group '${groupName}' not found`, HttpStatus.NOT_FOUND); + } + + // Add user to group + await this.keyCloakService.addUserToGroup(userId, groupId, accessToken); + } + catch (error) + { + // Handle errors + if (error.response && error.response.data && error.response.data.error) + { + // If Keycloak returns an error message, throw a Bad Request exception with the error message + throw new HttpException(error.response.data.error, HttpStatus.BAD_REQUEST); + } + else { + // If any other error occurs, throw an Internal Server Error exception + throw new HttpException('Internal server error', HttpStatus.INTERNAL_SERVER_ERROR); + } + } + } +} diff --git a/backend/users/src/app/dto/addUserToGroup.ts b/backend/users/src/app/dto/addUserToGroup.ts new file mode 100644 index 00000000..f68fc040 --- /dev/null +++ b/backend/users/src/app/dto/addUserToGroup.ts @@ -0,0 +1,7 @@ +import { IsNotEmpty, IsString } from 'class-validator'; + +export class AddUserToGroupDto { + @IsNotEmpty() + @IsString() + userId: string; +} diff --git a/backend/users/src/app/services/KeycloakService.service.spec.ts b/backend/users/src/app/services/KeycloakService.service.spec.ts new file mode 100644 index 00000000..40cbdff5 --- /dev/null +++ b/backend/users/src/app/services/KeycloakService.service.spec.ts @@ -0,0 +1,189 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { KeycloakService } from './KeycloakService.service'; +import axios from 'axios'; + +jest.mock('axios'); +describe('KeycloakService', () => { + let keycloakService: KeycloakService; + + // Mock axios response + const axiosResponse = { + data: [ + { + "id": "1", + "name": "formsflow", + "path": "/formsflow", + "subGroups": [ + { + "id": "2", + "name": "formsflow-reviewer", + "path": "/formsflow/formsflow-reviewer", + "subGroups": [] + }, + { + "id": "3", + "name": "formsflow-client", + "path": "/formsflow/formsflow-client", + "subGroups": [] + }, + { + "id": "4", + "name": "formsflow-designer", + "path": "/formsflow/formsflow-designer", + "subGroups": [] + } + ] + } + ], + }; + + beforeEach(async () => { + + // Mock ConfigService + const configServiceMock = { + get: jest.fn().mockReturnValueOnce(axiosResponse), + }; + + const module: TestingModule = await Test.createTestingModule({ + imports: [ConfigModule.forRoot()], // Import ConfigModule + providers: [ + KeycloakService, + { provide: 'ConfigService', useValue: configServiceMock }, + { provide: axios, useValue: axios }, // Provide axios + ], // Provide KeycloakService + }).compile(); + + keycloakService = module.get(KeycloakService); + + }); + + it('should be defined', () => { + expect(keycloakService).toBeDefined(); + }); + + describe('getGroupIdByName', () =>{ + + it('should return group ID when group name exists', async () => { + // Arrange + const groupName = 'formsflow-client'; + const accessToken = 'hsneu889siejnd99003kkd0kdldl'; + + // Mock axios + jest.spyOn(axios, 'get').mockResolvedValueOnce(axiosResponse); + + // Act + const groupId = await keycloakService.getGroupIdByName(groupName, accessToken); + + // Assert + expect(groupId).toEqual('3'); + }); + + it('should return null when group name does not exist', async () => { + // Arrange + const groupName = 'non-existing-group'; + const accessToken = 'hsneu889siejnd99003kkd0kdldl'; + + //Mock axios + jest.spyOn(axios, 'get').mockResolvedValueOnce(axiosResponse); + + // Act + const groupId = await keycloakService.getGroupIdByName(groupName, accessToken); + + // Assert + expect(groupId).toBeNull(); + }); + + it('should throw an error when retrieval fails', async () => { + // Arrange + const groupName = 'group-name'; + const accessToken = 'hsneu889siejnd99003kkd0kdldl'; + const errorMessage = 'Failed to retrieve group information'; + + // Mock axios to throw an error + jest.spyOn(axios, 'get').mockRejectedValueOnce(new Error(errorMessage)); + + // Act & Assert + await expect(keycloakService.getGroupIdByName(groupName, accessToken)).rejects.toThrowError(errorMessage); + }); + }); + + describe('addUserToGroup', () => { + + it('should add user to group in Keycloak', async () => { + // Arrange + const userId = '1'; + const groupId = '1'; + const accessToken = 'hsneu889siejnd99003kkd0kdldl'; + + // Mock axios.put to resolve + jest.spyOn(axios, 'put').mockResolvedValueOnce({ status: 200, data: {} }); + + // Act + await keycloakService.addUserToGroup(userId, groupId, accessToken); + + // Assert + expect(axios.put).toHaveBeenCalledTimes(1); // Ensure axios.put is called + expect(axios.put).toHaveBeenCalledWith( + expect.stringContaining(`https://epd-keycloak-dev.apps.silver.devops.gov.bc.ca/auth/admin/realms/forms-flow-ai/users/${userId}/groups/${groupId}`), + {}, // Empty object for the request body + { + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + }, + } + ); + }); + + it('should throw an error when user addition fails', async () => { + // Arrange + const userId = '1'; + const groupId = '1'; + const accessToken = 'hsneu889siejnd99003kkd0kdldl'; + const errorMessage = 'Failed to add user to group'; + + // Mock axios.put to reject with an error + jest.spyOn(axios, 'put').mockRejectedValueOnce(new Error(errorMessage)); + + // Act & Assert + await expect(keycloakService.addUserToGroup(userId, groupId, accessToken)).rejects.toThrowError(errorMessage); + + // Ensure axios.put is called + expect(axios.put).toHaveBeenCalledTimes(2); + }); + }); + + describe('findGroupIdByName', () =>{ + + it('should return null if group is not found', () => { + + // Arrange + const groupName = 'nonexistentGroup'; + + // Act + const result = keycloakService.findGroupIdByName(axiosResponse.data, groupName); + + // Assert + expect(result).toBeNull(); + }); + + it('should return group object if group is found', () => { + // Arrange + const groupName = "formsflow-client"; + + // Act + const result = keycloakService.findGroupIdByName(axiosResponse.data, groupName); + + // Assert + expect(result).toEqual({ + "id": "3", + "name": "formsflow-client", + "path": "/formsflow/formsflow-client", + "subGroups": [] + }); + }); + + }); +}); + diff --git a/backend/users/src/app/services/KeycloakService.service.ts b/backend/users/src/app/services/KeycloakService.service.ts new file mode 100644 index 00000000..91b6fce8 --- /dev/null +++ b/backend/users/src/app/services/KeycloakService.service.ts @@ -0,0 +1,133 @@ +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import axios from 'axios'; + +@Injectable() +export class KeycloakService { + + constructor(private readonly configService: ConfigService) {} + + /** + * Retrieve access token from Keycloak. + * @returns Access token. + */ + async getToken(): Promise { + // Extract environment variables + const keycloakAuthUrl = this.configService.get('KEYCLOCK_AUTH_URL'); + const realm = this.configService.get('KEYCLOCK_MASTER_REALM'); + const username = this.configService.get('KEYCLOCK_USERNAME'); + const password = this.configService.get('KEYCLOCK_PASSWORD'); + const clientId = this.configService.get('KEYCLOCK_CLIENT_ID'); + const grantType = this.configService.get('KEYCLOCK_GRANT_TYPE'); + + // Construct URL for token request + const url = `${keycloakAuthUrl}/realms/${realm}/protocol/openid-connect/token`; + + try { + // Request access token from Keycloak + const response = await axios.post( + url, + new URLSearchParams({ + grant_type: grantType, + client_id: clientId, + username: username, + password: password, + }), + { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + } + ); + + // Return access token + return response.data.access_token; + } + catch (error) + { + // Throw error if access token retrieval fails + throw new Error(`Failed to get access token: ${error.message}`); + } + } + + /** + * Get group ID by group name from Keycloak. + * @param groupName - Name of the group. + * @param accessToken - Access token. + * @returns Group ID if found, otherwise null. + */ + async getGroupIdByName(groupName: string, accessToken: string): Promise { + try { + // Extract environment variables + const keycloakAuthUrl = this.configService.get('KEYCLOCK_AUTH_URL'); + const realm = this.configService.get('KEYCLOCK_REALM'); + const url = `${keycloakAuthUrl}/admin/realms/${realm}/groups`; + + // Request group information from Keycloak + const response = await axios.get(url, { + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + }, + params: { + search: groupName, + }, + }); + + // Find group ID by name + const group = this.findGroupIdByName(response.data, groupName); + return group ? group.id : null; + } + catch (error) + { + // Throw error if group ID retrieval fails + throw new Error(`Failed to get group ID by name: ${error.message}`); + } + } + + /** + * Add user to group in Keycloak. + * @param userId - ID of the user. + * @param groupId - ID of the group. + * @param accessToken - Access token. + */ + async addUserToGroup(userId: string, groupId: string, accessToken: string): Promise { + try { + // Extract environment variables + const keycloakAuthUrl = this.configService.get('KEYCLOCK_AUTH_URL'); + const realm = this.configService.get('KEYCLOCK_REALM'); + const url = `${keycloakAuthUrl}/admin/realms/${realm}/users/${userId}/groups/${groupId}`; + + // Add user to group in Keycloak + await axios.put(url, {}, { + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + }, + }); + } + catch (error) + { + // Throw error if user addition fails + throw new Error(`Failed to add user to group: ${error.message}`); + } + } + + /** + * Find group by name recursively. + * @param groups - Array of groups to search. + * @param name - Name of the group to find. + * @returns Group object if found, otherwise null. + */ + findGroupIdByName = (groups, name) => { + const stack = [...groups]; + while (stack.length) { + const group = stack.pop(); + if (group.name === name) { + return group; + } + stack.push(...group.subGroups); + } + return null; + }; +} diff --git a/backend/users/src/app/users.module.ts b/backend/users/src/app/users.module.ts index 09b2d224..93e05dc9 100644 --- a/backend/users/src/app/users.module.ts +++ b/backend/users/src/app/users.module.ts @@ -9,6 +9,8 @@ import { RegionResolver } from './resolvers/region.resolver'; import { RegionService } from './services/region.service'; import { OrganizationTypeService } from './services/organizationType.service'; import { OrganizationTypeResolver } from './resolvers/organizationType.resolver'; +import { UsersController } from './controllers/users.controller'; +import { KeycloakService } from './services/KeycloakService.service'; /** * Module for wrapping all functionalities in user microserivce @@ -22,6 +24,8 @@ import { OrganizationTypeResolver } from './resolvers/organizationType.resolver' RegionService, OrganizationTypeResolver, OrganizationTypeService, + KeycloakService, ], + controllers: [UsersController], }) export class UsersModule {} From ec4b3870f7d42db57fc0b770aa5ef5cd1d8e320d Mon Sep 17 00:00:00 2001 From: "Jaspal.Singh-AOT" <163812444+jaspalsingh-aot@users.noreply.github.com> Date: Wed, 3 Apr 2024 16:43:59 -0700 Subject: [PATCH 02/12] fix test case issue --- .../users/src/app/services/KeycloakService.service.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/users/src/app/services/KeycloakService.service.spec.ts b/backend/users/src/app/services/KeycloakService.service.spec.ts index 40cbdff5..66b991f3 100644 --- a/backend/users/src/app/services/KeycloakService.service.spec.ts +++ b/backend/users/src/app/services/KeycloakService.service.spec.ts @@ -125,7 +125,7 @@ describe('KeycloakService', () => { // Assert expect(axios.put).toHaveBeenCalledTimes(1); // Ensure axios.put is called expect(axios.put).toHaveBeenCalledWith( - expect.stringContaining(`https://epd-keycloak-dev.apps.silver.devops.gov.bc.ca/auth/admin/realms/forms-flow-ai/users/${userId}/groups/${groupId}`), + expect.stringContaining(`/admin/realms/forms-flow-ai/users/${userId}/groups/${groupId}`), {}, // Empty object for the request body { headers: { @@ -150,7 +150,7 @@ describe('KeycloakService', () => { await expect(keycloakService.addUserToGroup(userId, groupId, accessToken)).rejects.toThrowError(errorMessage); // Ensure axios.put is called - expect(axios.put).toHaveBeenCalledTimes(2); + expect(axios.put).toHaveBeenCalledTimes(1); }); }); From 4d80be77c3900b2016fb72e61e58083a642829d0 Mon Sep 17 00:00:00 2001 From: "Jaspal.Singh-AOT" <163812444+jaspalsingh-aot@users.noreply.github.com> Date: Wed, 3 Apr 2024 16:57:33 -0700 Subject: [PATCH 03/12] test case fix --- .../users/src/app/services/KeycloakService.service.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/users/src/app/services/KeycloakService.service.spec.ts b/backend/users/src/app/services/KeycloakService.service.spec.ts index 66b991f3..c55e4a9c 100644 --- a/backend/users/src/app/services/KeycloakService.service.spec.ts +++ b/backend/users/src/app/services/KeycloakService.service.spec.ts @@ -125,7 +125,7 @@ describe('KeycloakService', () => { // Assert expect(axios.put).toHaveBeenCalledTimes(1); // Ensure axios.put is called expect(axios.put).toHaveBeenCalledWith( - expect.stringContaining(`/admin/realms/forms-flow-ai/users/${userId}/groups/${groupId}`), + expect.stringMatching(/\/admin\/realms\/forms-flow-ai\/users\/1\/groups\/1/), {}, // Empty object for the request body { headers: { @@ -150,7 +150,7 @@ describe('KeycloakService', () => { await expect(keycloakService.addUserToGroup(userId, groupId, accessToken)).rejects.toThrowError(errorMessage); // Ensure axios.put is called - expect(axios.put).toHaveBeenCalledTimes(1); + expect(axios.put).toHaveBeenCalledTimes(2); }); }); From f8b4a067369933e1d92d46e07c20e993bd5f7bc6 Mon Sep 17 00:00:00 2001 From: "Jaspal.Singh-AOT" <163812444+jaspalsingh-aot@users.noreply.github.com> Date: Wed, 3 Apr 2024 17:08:18 -0700 Subject: [PATCH 04/12] changes in test file showing expected behaviour on local --- backend/users/src/app/services/KeycloakService.service.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/users/src/app/services/KeycloakService.service.spec.ts b/backend/users/src/app/services/KeycloakService.service.spec.ts index c55e4a9c..a13756e3 100644 --- a/backend/users/src/app/services/KeycloakService.service.spec.ts +++ b/backend/users/src/app/services/KeycloakService.service.spec.ts @@ -125,7 +125,7 @@ describe('KeycloakService', () => { // Assert expect(axios.put).toHaveBeenCalledTimes(1); // Ensure axios.put is called expect(axios.put).toHaveBeenCalledWith( - expect.stringMatching(/\/admin\/realms\/forms-flow-ai\/users\/1\/groups\/1/), + expect.stringContaining(`/admin/realms/forms-flow-ai/users/${userId}/groups/${groupId}`), {}, // Empty object for the request body { headers: { From 8a1c0fda4e1a046007f227a9751a6f79e89b8ee2 Mon Sep 17 00:00:00 2001 From: "Jaspal.Singh-AOT" <163812444+jaspalsingh-aot@users.noreply.github.com> Date: Thu, 4 Apr 2024 09:19:16 -0700 Subject: [PATCH 05/12] issue in comparing the url --- backend/users/src/app/services/KeycloakService.service.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/users/src/app/services/KeycloakService.service.spec.ts b/backend/users/src/app/services/KeycloakService.service.spec.ts index a13756e3..ffecc03d 100644 --- a/backend/users/src/app/services/KeycloakService.service.spec.ts +++ b/backend/users/src/app/services/KeycloakService.service.spec.ts @@ -125,7 +125,7 @@ describe('KeycloakService', () => { // Assert expect(axios.put).toHaveBeenCalledTimes(1); // Ensure axios.put is called expect(axios.put).toHaveBeenCalledWith( - expect.stringContaining(`/admin/realms/forms-flow-ai/users/${userId}/groups/${groupId}`), + expect.stringContaining(`/users/${userId}/groups/${groupId}`), {}, // Empty object for the request body { headers: { From ec8aecb2f11a79576402948d73b7e440d1f5644d Mon Sep 17 00:00:00 2001 From: "Jaspal.Singh-AOT" <163812444+jaspalsingh-aot@users.noreply.github.com> Date: Thu, 4 Apr 2024 15:56:43 -0700 Subject: [PATCH 06/12] Add response message to API once successfull. --- .../users/src/app/controllers/users.controller.ts | 13 +++++++++++-- .../src/app/services/KeycloakService.service.ts | 10 ++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/backend/users/src/app/controllers/users.controller.ts b/backend/users/src/app/controllers/users.controller.ts index 67e04fc0..95c03927 100644 --- a/backend/users/src/app/controllers/users.controller.ts +++ b/backend/users/src/app/controllers/users.controller.ts @@ -8,8 +8,13 @@ import { AddUserToGroupDto } from 'src/app/dto/addUserToGroup'; export class UsersController { constructor(private readonly keyCloakService: KeycloakService) {} + /** + * Add user to a group in Keycloak. + * @param addUserToGroupDto - Object containing userId. + * @returns Object indicating success status and message. + */ @Post('/addGroup') - async addUserToGroup(@Body() addUserToGroupDto: AddUserToGroupDto): Promise { + async addUserToGroup(@Body() addUserToGroupDto: AddUserToGroupDto): Promise { try { const { userId } = addUserToGroupDto; @@ -30,7 +35,11 @@ export class UsersController { } // Add user to group - await this.keyCloakService.addUserToGroup(userId, groupId, accessToken); + const result = await this.keyCloakService.addUserToGroup(userId, groupId, accessToken); + if(result.success) + { + return result; + } } catch (error) { diff --git a/backend/users/src/app/services/KeycloakService.service.ts b/backend/users/src/app/services/KeycloakService.service.ts index 91b6fce8..417e926f 100644 --- a/backend/users/src/app/services/KeycloakService.service.ts +++ b/backend/users/src/app/services/KeycloakService.service.ts @@ -91,7 +91,7 @@ export class KeycloakService { * @param groupId - ID of the group. * @param accessToken - Access token. */ - async addUserToGroup(userId: string, groupId: string, accessToken: string): Promise { + async addUserToGroup(userId: string, groupId: string, accessToken: string): Promise { try { // Extract environment variables const keycloakAuthUrl = this.configService.get('KEYCLOCK_AUTH_URL'); @@ -99,12 +99,18 @@ export class KeycloakService { const url = `${keycloakAuthUrl}/admin/realms/${realm}/users/${userId}/groups/${groupId}`; // Add user to group in Keycloak - await axios.put(url, {}, { + await axios.put(url, {}, { headers: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json', }, }); + + // Return a success response + return { + success: true, + message: 'User added to group successfully', + }; } catch (error) { From 9d9a4fbf0a052e092f5bde21692636687b277d0a Mon Sep 17 00:00:00 2001 From: "Jaspal.Singh-AOT" <163812444+jaspalsingh-aot@users.noreply.github.com> Date: Thu, 4 Apr 2024 16:01:46 -0700 Subject: [PATCH 07/12] Add API call to frontend to add group to user --- .../src/app/features/dashboard/Dashboard.tsx | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/features/dashboard/Dashboard.tsx b/frontend/src/app/features/dashboard/Dashboard.tsx index d49d8aec..0f31afc2 100644 --- a/frontend/src/app/features/dashboard/Dashboard.tsx +++ b/frontend/src/app/features/dashboard/Dashboard.tsx @@ -11,12 +11,12 @@ import "./Dashboard.css"; import { SdmDashboard } from "./sdmDashboard/SdmDashboard"; import { ReviewerDashoard } from "./reviewerDashboard/ReviewerDashoard"; import { getSDMUserRole } from "../../helpers/envManager"; +import { getAxiosInstance } from "../../helpers/utility"; +import { USERS } from "../../helpers/endpoints"; const Dashboard = () => { const dispatch = useDispatch(); - const userIsProfileVerifiedValue = useSelector(isProfileVerified); - const navigate = useNavigate(); // const lastVisitedURL = useSelector(getLastVisitedURL); @@ -28,8 +28,9 @@ const Dashboard = () => { // } // },[lastVisitedURL]) + const BCeID = "bceid"; + const BCSC = "bcsc"; const auth = useAuth(); - useEffect(() => { if ( auth.user?.profile && @@ -49,9 +50,31 @@ const Dashboard = () => { }, [userIsProfileVerifiedValue]); + useEffect( () => { + if(auth.user?.profile.identity_provider === BCeID || auth.user?.profile.identity_provider === BCSC) + { + assignGroupToUser(); + } + }, []); + + const assignGroupToUser = () => { + getAxiosInstance().post(USERS + '/addGroup', { + userId: auth.user?.profile.sub + }) + .then(response => { + if(response.data.success) + { + console.log(response.data.message); + } + }) + .catch(error => { + console.error('Error fetching data:', error); + }); + + } + const handleFormsflowWebRedirection = () => { const formsFlowWebURL = process.env.REACT_APP_FORMSFLOW_WEB_URL || ((window as any)._env_ && (window as any)._env_.REACT_APP_FORMSFLOW_WEB_URL) || ""; - const locationBeforeAuthRedirect = sessionStorage.getItem('locationBeforeAuthRedirect'); if(locationBeforeAuthRedirect!=="" && locationBeforeAuthRedirect !==null && locationBeforeAuthRedirect.indexOf("/fileupload")!== -1) { @@ -104,3 +127,4 @@ const Dashboard = () => { }; export default Dashboard; + From ae4a5f618357d870d5cdd469cc2789299a3613b1 Mon Sep 17 00:00:00 2001 From: "Jaspal.Singh-AOT" <163812444+jaspalsingh-aot@users.noreply.github.com> Date: Fri, 5 Apr 2024 13:56:52 -0700 Subject: [PATCH 08/12] Fix the issues as per review comments. Unprotected route on user controller is pending. --- .../{users.controller.ts => user.controller.ts} | 12 +++++++----- ...vice.service.spec.ts => Keycloak.service.spec.ts} | 2 +- ...eycloakService.service.ts => Keycloak.service.ts} | 0 backend/users/src/app/users.module.ts | 6 +++--- frontend/src/app/helpers/endpoints.ts | 2 +- 5 files changed, 12 insertions(+), 10 deletions(-) rename backend/users/src/app/controllers/{users.controller.ts => user.controller.ts} (86%) rename backend/users/src/app/services/{KeycloakService.service.spec.ts => Keycloak.service.spec.ts} (98%) rename backend/users/src/app/services/{KeycloakService.service.ts => Keycloak.service.ts} (100%) diff --git a/backend/users/src/app/controllers/users.controller.ts b/backend/users/src/app/controllers/user.controller.ts similarity index 86% rename from backend/users/src/app/controllers/users.controller.ts rename to backend/users/src/app/controllers/user.controller.ts index 95c03927..c1928cd6 100644 --- a/backend/users/src/app/controllers/users.controller.ts +++ b/backend/users/src/app/controllers/user.controller.ts @@ -1,11 +1,12 @@ import { Controller, Post, Body, HttpException, HttpStatus } from '@nestjs/common'; -import { Unprotected } from 'nest-keycloak-connect'; -import { KeycloakService } from 'src/app/services/KeycloakService.service'; +import { Resource, RoleMatchingMode, Roles, Unprotected } from 'nest-keycloak-connect'; +import { KeycloakService } from 'src/app/services/Keycloak.service'; import { AddUserToGroupDto } from 'src/app/dto/addUserToGroup'; -@Controller('users') -@Unprotected() -export class UsersController { +@Controller('user') +//@Resource('user-service') +@Unprotected() // Working on it. +export class UserController { constructor(private readonly keyCloakService: KeycloakService) {} /** @@ -14,6 +15,7 @@ export class UsersController { * @returns Object indicating success status and message. */ @Post('/addGroup') + // @Roles({ roles: ['user-admin'], mode: RoleMatchingMode.ANY }) async addUserToGroup(@Body() addUserToGroupDto: AddUserToGroupDto): Promise { try { diff --git a/backend/users/src/app/services/KeycloakService.service.spec.ts b/backend/users/src/app/services/Keycloak.service.spec.ts similarity index 98% rename from backend/users/src/app/services/KeycloakService.service.spec.ts rename to backend/users/src/app/services/Keycloak.service.spec.ts index ffecc03d..1d04ca52 100644 --- a/backend/users/src/app/services/KeycloakService.service.spec.ts +++ b/backend/users/src/app/services/Keycloak.service.spec.ts @@ -1,6 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { ConfigModule, ConfigService } from '@nestjs/config'; -import { KeycloakService } from './KeycloakService.service'; +import { KeycloakService } from './Keycloak.service'; import axios from 'axios'; jest.mock('axios'); diff --git a/backend/users/src/app/services/KeycloakService.service.ts b/backend/users/src/app/services/Keycloak.service.ts similarity index 100% rename from backend/users/src/app/services/KeycloakService.service.ts rename to backend/users/src/app/services/Keycloak.service.ts diff --git a/backend/users/src/app/users.module.ts b/backend/users/src/app/users.module.ts index 93e05dc9..2089c3cd 100644 --- a/backend/users/src/app/users.module.ts +++ b/backend/users/src/app/users.module.ts @@ -9,8 +9,8 @@ import { RegionResolver } from './resolvers/region.resolver'; import { RegionService } from './services/region.service'; import { OrganizationTypeService } from './services/organizationType.service'; import { OrganizationTypeResolver } from './resolvers/organizationType.resolver'; -import { UsersController } from './controllers/users.controller'; -import { KeycloakService } from './services/KeycloakService.service'; +import { UserController } from './controllers/user.controller'; +import { KeycloakService } from './services/Keycloak.service'; /** * Module for wrapping all functionalities in user microserivce @@ -26,6 +26,6 @@ import { KeycloakService } from './services/KeycloakService.service'; OrganizationTypeService, KeycloakService, ], - controllers: [UsersController], + controllers: [UserController], }) export class UsersModule {} diff --git a/frontend/src/app/helpers/endpoints.ts b/frontend/src/app/helpers/endpoints.ts index f0170a1b..4730d2ef 100644 --- a/frontend/src/app/helpers/endpoints.ts +++ b/frontend/src/app/helpers/endpoints.ts @@ -1,4 +1,4 @@ -export const USERS = "/users"; +export const USERS = "/user"; export const GRAPHQL= "/graphql" From f47d4fb0fbb6ea8560727529be352f97c917d880 Mon Sep 17 00:00:00 2001 From: "Jaspal.Singh-AOT" <163812444+jaspalsingh-aot@users.noreply.github.com> Date: Mon, 8 Apr 2024 10:13:41 -0700 Subject: [PATCH 09/12] controller route is proctect in user controller as per review. --- .../users/src/app/controllers/user.controller.ts | 5 ++--- .../users/src/app/services/Keycloak.service.ts | 16 ++++++++-------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/backend/users/src/app/controllers/user.controller.ts b/backend/users/src/app/controllers/user.controller.ts index c1928cd6..3488bffa 100644 --- a/backend/users/src/app/controllers/user.controller.ts +++ b/backend/users/src/app/controllers/user.controller.ts @@ -4,8 +4,7 @@ import { KeycloakService } from 'src/app/services/Keycloak.service'; import { AddUserToGroupDto } from 'src/app/dto/addUserToGroup'; @Controller('user') -//@Resource('user-service') -@Unprotected() // Working on it. +@Resource('user-service') export class UserController { constructor(private readonly keyCloakService: KeycloakService) {} @@ -15,7 +14,7 @@ export class UserController { * @returns Object indicating success status and message. */ @Post('/addGroup') - // @Roles({ roles: ['user-admin'], mode: RoleMatchingMode.ANY }) + @Roles({ roles: ['user-admin'], mode: RoleMatchingMode.ANY }) async addUserToGroup(@Body() addUserToGroupDto: AddUserToGroupDto): Promise { try { diff --git a/backend/users/src/app/services/Keycloak.service.ts b/backend/users/src/app/services/Keycloak.service.ts index 417e926f..c921de7a 100644 --- a/backend/users/src/app/services/Keycloak.service.ts +++ b/backend/users/src/app/services/Keycloak.service.ts @@ -13,15 +13,15 @@ export class KeycloakService { */ async getToken(): Promise { // Extract environment variables - const keycloakAuthUrl = this.configService.get('KEYCLOCK_AUTH_URL'); - const realm = this.configService.get('KEYCLOCK_MASTER_REALM'); - const username = this.configService.get('KEYCLOCK_USERNAME'); - const password = this.configService.get('KEYCLOCK_PASSWORD'); - const clientId = this.configService.get('KEYCLOCK_CLIENT_ID'); - const grantType = this.configService.get('KEYCLOCK_GRANT_TYPE'); + const keycloakAuthUrl = this.configService.get('KEYCLOCK_AUTH_URL'); + const realm = this.configService.get('KEYCLOCK_MASTER_REALM'); + const username = this.configService.get('KEYCLOCK_USERNAME'); + const password = this.configService.get('KEYCLOCK_PASSWORD'); + const clientId = this.configService.get('KEYCLOCK_ADMIN_CLIENT_ID'); + const grantType = this.configService.get('KEYCLOCK_GRANT_TYPE'); - // Construct URL for token request - const url = `${keycloakAuthUrl}/realms/${realm}/protocol/openid-connect/token`; + // Construct URL for token request + const url = `${keycloakAuthUrl}/realms/${realm}/protocol/openid-connect/token`; try { // Request access token from Keycloak From 298c80b05adcb2aebcb5ccea8ffe7ffef2c5fdd0 Mon Sep 17 00:00:00 2001 From: "Jaspal.Singh-AOT" <163812444+jaspalsingh-aot@users.noreply.github.com> Date: Tue, 9 Apr 2024 09:31:27 -0700 Subject: [PATCH 10/12] Rename file name as per project naming conventions --- backend/users/src/app/controllers/user.controller.ts | 2 +- backend/users/src/app/services/Keycloak.service.spec.ts | 2 +- backend/users/src/app/users.module.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/users/src/app/controllers/user.controller.ts b/backend/users/src/app/controllers/user.controller.ts index 3488bffa..abd98e89 100644 --- a/backend/users/src/app/controllers/user.controller.ts +++ b/backend/users/src/app/controllers/user.controller.ts @@ -1,6 +1,6 @@ import { Controller, Post, Body, HttpException, HttpStatus } from '@nestjs/common'; import { Resource, RoleMatchingMode, Roles, Unprotected } from 'nest-keycloak-connect'; -import { KeycloakService } from 'src/app/services/Keycloak.service'; +import { KeycloakService } from 'src/app/services/keycloak.service'; import { AddUserToGroupDto } from 'src/app/dto/addUserToGroup'; @Controller('user') diff --git a/backend/users/src/app/services/Keycloak.service.spec.ts b/backend/users/src/app/services/Keycloak.service.spec.ts index 1d04ca52..34833d52 100644 --- a/backend/users/src/app/services/Keycloak.service.spec.ts +++ b/backend/users/src/app/services/Keycloak.service.spec.ts @@ -1,6 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { ConfigModule, ConfigService } from '@nestjs/config'; -import { KeycloakService } from './Keycloak.service'; +import { KeycloakService } from './keycloak.service'; import axios from 'axios'; jest.mock('axios'); diff --git a/backend/users/src/app/users.module.ts b/backend/users/src/app/users.module.ts index 2089c3cd..43cbbbec 100644 --- a/backend/users/src/app/users.module.ts +++ b/backend/users/src/app/users.module.ts @@ -10,7 +10,7 @@ import { RegionService } from './services/region.service'; import { OrganizationTypeService } from './services/organizationType.service'; import { OrganizationTypeResolver } from './resolvers/organizationType.resolver'; import { UserController } from './controllers/user.controller'; -import { KeycloakService } from './services/Keycloak.service'; +import { KeycloakService } from './services/keycloak.service'; /** * Module for wrapping all functionalities in user microserivce From bff50e72603a865bb30ceecf0269cdee8cb83885 Mon Sep 17 00:00:00 2001 From: "Jaspal.Singh-AOT" <163812444+jaspalsingh-aot@users.noreply.github.com> Date: Tue, 9 Apr 2024 10:04:09 -0700 Subject: [PATCH 11/12] rename file --- .../{Keycloak.service.spec.ts => keycloak.service.spec.ts} | 0 .../src/app/services/{Keycloak.service.ts => keycloak.service.ts} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename backend/users/src/app/services/{Keycloak.service.spec.ts => keycloak.service.spec.ts} (100%) rename backend/users/src/app/services/{Keycloak.service.ts => keycloak.service.ts} (100%) diff --git a/backend/users/src/app/services/Keycloak.service.spec.ts b/backend/users/src/app/services/keycloak.service.spec.ts similarity index 100% rename from backend/users/src/app/services/Keycloak.service.spec.ts rename to backend/users/src/app/services/keycloak.service.spec.ts diff --git a/backend/users/src/app/services/Keycloak.service.ts b/backend/users/src/app/services/keycloak.service.ts similarity index 100% rename from backend/users/src/app/services/Keycloak.service.ts rename to backend/users/src/app/services/keycloak.service.ts From 989ffb10b34af49aa9bf2d84fcf2354b9d676b0d Mon Sep 17 00:00:00 2001 From: "Jaspal.Singh-AOT" <163812444+jaspalsingh-aot@users.noreply.github.com> Date: Tue, 9 Apr 2024 10:18:45 -0700 Subject: [PATCH 12/12] change API route --- backend/users/src/app/controllers/user.controller.ts | 2 +- frontend/src/app/helpers/endpoints.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/users/src/app/controllers/user.controller.ts b/backend/users/src/app/controllers/user.controller.ts index abd98e89..33977330 100644 --- a/backend/users/src/app/controllers/user.controller.ts +++ b/backend/users/src/app/controllers/user.controller.ts @@ -3,7 +3,7 @@ import { Resource, RoleMatchingMode, Roles, Unprotected } from 'nest-keycloak-co import { KeycloakService } from 'src/app/services/keycloak.service'; import { AddUserToGroupDto } from 'src/app/dto/addUserToGroup'; -@Controller('user') +@Controller('users') @Resource('user-service') export class UserController { constructor(private readonly keyCloakService: KeycloakService) {} diff --git a/frontend/src/app/helpers/endpoints.ts b/frontend/src/app/helpers/endpoints.ts index 4730d2ef..f0170a1b 100644 --- a/frontend/src/app/helpers/endpoints.ts +++ b/frontend/src/app/helpers/endpoints.ts @@ -1,4 +1,4 @@ -export const USERS = "/user"; +export const USERS = "/users"; export const GRAPHQL= "/graphql"