From 486a002023c34a326702e0a0b438a3e801c58f91 Mon Sep 17 00:00:00 2001 From: Denys Vuika Date: Thu, 6 Aug 2020 11:01:03 +0100 Subject: [PATCH] [AAE-2718] List available roles for a group (#5954) * oath2 api service, cleanup identity users service * cleanup groups service * get awailable role mappings * cleanup tests and mocks * fix lint --- lib/core/mock/identity-group.service.mock.ts | 84 +---- .../services/identity-group.service.spec.ts | 40 +- lib/core/services/identity-group.service.ts | 220 +++-------- lib/core/services/identity-user.service.ts | 350 ++++-------------- lib/core/services/oauth2.service.ts | 84 +++++ 5 files changed, 229 insertions(+), 549 deletions(-) create mode 100644 lib/core/services/oauth2.service.ts diff --git a/lib/core/mock/identity-group.service.mock.ts b/lib/core/mock/identity-group.service.mock.ts index ce3327d4e1..20ffb684b2 100644 --- a/lib/core/mock/identity-group.service.mock.ts +++ b/lib/core/mock/identity-group.service.mock.ts @@ -18,55 +18,39 @@ import { IdentityGroupModel, IdentityGroupCountModel } from '../models/identity-group.model'; import { IdentityRoleModel } from '../models/identity-role.model'; -export let mockIdentityGroup1 = { +export const mockIdentityGroup1 = { id: 'mock-group-id-1', name: 'Mock Group 1', path: '/mock', subGroups: [] }; -export let mockIdentityGroup2 = { +export const mockIdentityGroup2 = { id: 'mock-group-id-2', name: 'Mock Group 2', path: '', subGroups: [] }; -export let mockIdentityGroup3 = { +export const mockIdentityGroup3 = { id: 'mock-group-id-3', name: 'Mock Group 3', path: '', subGroups: [] }; -export let mockIdentityGroup4 = { +export const mockIdentityGroup4 = { id: 'mock-group-id-4', name: 'Mock Group 4', path: '', subGroups: [] }; -export let mockIdentityGroup5 = { +export const mockIdentityGroup5 = { id: 'mock-group-id-5', name: 'Mock Group 5', path: '', subGroups: [] }; -export let mockIdentityGroupsCount = { count: 10 }; +export const mockIdentityGroupsCount = { count: 10 }; -export let mockIdentityGroups = [ +export const mockIdentityGroups = [ mockIdentityGroup1, mockIdentityGroup2, mockIdentityGroup3, mockIdentityGroup4, mockIdentityGroup5 ]; -export let mockApplicationDetails = {id: 'mock-app-id', name: 'mock-app-name'}; +export const mockApplicationDetails = {id: 'mock-app-id', name: 'mock-app-name'}; -export let groupAPIMockError = { - error: { - errorKey: 'failed', - statusCode: 400, - stackTrace: 'For security reasons the stack trace is no longer displayed, but the property is kept for previous versions.' - } -}; - -export let mockApiError = { - oauth2Auth: { - callCustomApi: () => { - return Promise.reject(groupAPIMockError); - } - } -}; - -export let roleMappingMock = [ +export const roleMappingMock = [ { id: 'role-id-1', name: 'role-name-1' }, { id: 'role-id-2', name: 'role-name-2' } ]; -export let roleMappingApi = { +export const roleMappingApi = { oauth2Auth: { callCustomApi: () => { return Promise.resolve(roleMappingMock); @@ -74,7 +58,7 @@ export let roleMappingApi = { } }; -export let noRoleMappingApi = { +export const noRoleMappingApi = { oauth2Auth: { callCustomApi: () => { return Promise.resolve([]); @@ -82,7 +66,7 @@ export let noRoleMappingApi = { } }; -export let groupsMockApi = { +export const groupsMockApi = { oauth2Auth: { callCustomApi: () => { return Promise.resolve(mockIdentityGroups); @@ -90,23 +74,7 @@ export let groupsMockApi = { } }; -export let getGroupsCountMockApi = { - oauth2Auth: { - callCustomApi: () => { - return Promise.resolve(10); - } - } -}; - -export let queryGroupsMockApi = { - oauth2Auth: { - callCustomApi: () => { - return Promise.resolve(mockIdentityGroups); - } - } -}; - -export let createGroupMappingApi = { +export const createGroupMappingApi = { oauth2Auth: { callCustomApi: () => { return Promise.resolve(); @@ -114,7 +82,7 @@ export let createGroupMappingApi = { } }; -export let updateGroupMappingApi = { +export const updateGroupMappingApi = { oauth2Auth: { callCustomApi: () => { return Promise.resolve(); @@ -122,7 +90,7 @@ export let updateGroupMappingApi = { } }; -export let deleteGroupMappingApi = { +export const deleteGroupMappingApi = { oauth2Auth: { callCustomApi: () => { return Promise.resolve(); @@ -130,23 +98,7 @@ export let deleteGroupMappingApi = { } }; -export let returnCallQueryParameters = { - oauth2Auth: { - callCustomApi: (_queryUrl, _operation, _context, queryParams) => { - return Promise.resolve(queryParams); - } - } -}; - -export let returnCallUrl = { - oauth2Auth: { - callCustomApi: (queryUrl) => { - return Promise.resolve(queryUrl); - } - } -}; - -export let applicationDetailsMockApi = { +export const applicationDetailsMockApi = { oauth2Auth: { callCustomApi: () => { return Promise.resolve([mockApplicationDetails]); @@ -154,10 +106,10 @@ export let applicationDetailsMockApi = { } }; -export let mockIdentityRoles = [ +export const mockIdentityRoles = [ new IdentityRoleModel({id: 'mock-role-id', name: 'MOCK-ADMIN-ROLE'}), new IdentityRoleModel({id: 'mock-role-id', name: 'MOCK-USER-ROLE'}), new IdentityRoleModel({id: 'mock-role-id', name: 'MOCK-ROLE-1'}) ]; -export let clientRoles = [ 'MOCK-ADMIN-ROLE', 'MOCK-USER-ROLE']; +export const clientRoles = [ 'MOCK-ADMIN-ROLE', 'MOCK-USER-ROLE']; diff --git a/lib/core/services/identity-group.service.spec.ts b/lib/core/services/identity-group.service.spec.ts index 92b42586bf..33bc019e44 100644 --- a/lib/core/services/identity-group.service.spec.ts +++ b/lib/core/services/identity-group.service.spec.ts @@ -16,14 +16,7 @@ */ import { async, TestBed } from '@angular/core/testing'; -import { - setupTestBed, - AlfrescoApiService, - LogService, - IdentityGroupService, - IdentityGroupSearchParam, - groupAPIMockError -} from '@alfresco/adf-core'; +import { setupTestBed, AlfrescoApiService, IdentityGroupService, IdentityGroupSearchParam } from '@alfresco/adf-core'; import { HttpErrorResponse } from '@angular/common/http'; import { throwError, of } from 'rxjs'; import { @@ -33,7 +26,6 @@ import { roleMappingApi, clientRoles, applicationDetailsMockApi, - mockApiError, mockIdentityGroup1, createGroupMappingApi, updateGroupMappingApi, @@ -46,7 +38,6 @@ import { TranslateModule } from '@ngx-translate/core'; describe('IdentityGroupService', () => { let service: IdentityGroupService; let apiService: AlfrescoApiService; - let logService: LogService; setupTestBed({ imports: [ @@ -58,7 +49,6 @@ describe('IdentityGroupService', () => { beforeEach(async(() => { service = TestBed.inject(IdentityGroupService); apiService = TestBed.inject(AlfrescoApiService); - logService = TestBed.inject(LogService); })); it('should be able to fetch groups based on group name', (done) => { @@ -231,18 +221,6 @@ describe('IdentityGroupService', () => { ); }); - it('should return only the properties of IdentityGroupSearchParam', (done) => { - spyOn(apiService, 'getInstance').and.returnValue(groupsMockApi); - service.findGroupsByName( {name: 'mock'}).subscribe((groups) => { - expect(groups).toBeDefined(); - expect(groups).toBeDefined(); - expect(groups[0].id).toEqual('mock-group-id-1'); - expect(groups[0].name).toEqual('Mock Group 1'); - expect(groups[0]['subGroups']).not.toBeDefined(); - done(); - }); - }); - it('should be able to fetch the client id', (done) => { spyOn(apiService, 'getInstance').and.returnValue(applicationDetailsMockApi); service.getClientIdByApplicationName('mock-app-name').subscribe((clientId) => { @@ -253,25 +231,9 @@ describe('IdentityGroupService', () => { }); }); - it('should notify errors returned from the API', (done) => { - const logServiceSpy = spyOn(logService, 'error').and.callThrough(); - spyOn(apiService, 'getInstance').and.returnValue(mockApiError); - service.findGroupsByName( {name: 'mock'}).subscribe( - () => {}, - (res: any) => { - expect(res).toBeDefined(); - expect(res).toEqual(groupAPIMockError); - expect(logServiceSpy).toHaveBeenCalled(); - done(); - } - ); - }); - it('should be able to all fetch groups', (done) => { spyOn(apiService, 'getInstance').and.returnValue(groupsMockApi); service.getGroups().subscribe((res) => { - expect(res).toBeDefined(); - expect(res).not.toBeNull(); expect(res.length).toBe(5); expect(res[0].id).toBe('mock-group-id-1'); expect(res[0].name).toBe('Mock Group 1'); diff --git a/lib/core/services/identity-group.service.ts b/lib/core/services/identity-group.service.ts index 2ed631e7dd..ba3702bb49 100644 --- a/lib/core/services/identity-group.service.ts +++ b/lib/core/services/identity-group.service.ts @@ -16,11 +16,9 @@ */ import { Injectable } from '@angular/core'; -import { Observable, of, from, throwError } from 'rxjs'; -import { catchError, map, switchMap } from 'rxjs/operators'; +import { Observable, of } from 'rxjs'; +import { map, switchMap } from 'rxjs/operators'; import { AppConfigService } from '../app-config/app-config.service'; -import { AlfrescoApiService } from './alfresco-api.service'; -import { LogService } from './log.service'; import { IdentityGroupSearchParam, IdentityGroupQueryCloudRequestModel, @@ -29,35 +27,37 @@ import { IdentityGroupCountModel } from '../models/identity-group.model'; import { IdentityRoleModel } from '../models/identity-role.model'; +import { OAuth2Service } from './oauth2.service'; -@Injectable({ - providedIn: 'root' -}) +@Injectable({ providedIn: 'root' }) export class IdentityGroupService { constructor( - private alfrescoApiService: AlfrescoApiService, - private appConfigService: AppConfigService, - private logService: LogService + private oAuth2Service: OAuth2Service, + private appConfigService: AppConfigService ) {} + private get identityHost(): string { + return `${this.appConfigService.get('identityHost')}`; + } + /** * Gets all groups. * @returns Array of group information objects */ getGroups(): Observable { - const url = this.getGroupsApi(); - const httpMethod = 'GET', pathParams = {}, - queryParams = {}, bodyParam = {}, headerParams = {}, - formParams = {}, authNames = [], contentTypes = ['application/json']; + const url = `${this.identityHost}/groups`; + return this.oAuth2Service.get({ url }); + } - return from(this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, authNames, - contentTypes, null, null, null - )).pipe( - catchError((error) => this.handleError(error)) - ); + /** + * Gets available roles + * @param groupId Id of the group. + * @returns Array of available roles information objects + */ + getAvailableRoles(groupId: string): Observable { + const url = `${this.identityHost}/groups/${groupId}/role-mappings/realm/available`; + return this.oAuth2Service.get({ url }); } /** @@ -65,18 +65,13 @@ export class IdentityGroupService { * @returns Array of user information objects */ queryGroups(requestQuery: IdentityGroupQueryCloudRequestModel): Observable { - const url = this.getGroupsApi(); - const httpMethod = 'GET', pathParams = {}, - queryParams = { first: requestQuery.first || 0, max: requestQuery.max || 5 }, bodyParam = {}, headerParams = {}, - formParams = {}, authNames = [], contentTypes = ['application/json']; + const url = `${this.identityHost}/groups`; + const queryParams = { first: requestQuery.first || 0, max: requestQuery.max || 5 }; + return this.getTotalGroupsCount().pipe( switchMap((totalCount: IdentityGroupCountModel) => - from(this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, authNames, - contentTypes, null, null, null) - ).pipe( - map((response: any[]) => { + this.oAuth2Service.get({ url, queryParams }).pipe( + map((response) => { return { entries: response, pagination: { @@ -86,11 +81,10 @@ export class IdentityGroupService { hasMoreItems: false, totalItems: totalCount.count } - }; - }), - catchError((error) => this.handleError(error)) - )) - ); + }; + }) + )) + ); } /** @@ -98,15 +92,8 @@ export class IdentityGroupService { * @returns Number of groups count. */ getTotalGroupsCount(): Observable { - const url = this.getGroupsApi() + `/count`; - const contentTypes = ['application/json'], accepts = ['application/json']; - return from(this.alfrescoApiService.getInstance() - .oauth2Auth.callCustomApi(url, 'GET', - null, null, null, - null, null, contentTypes, - accepts, null, null, null)).pipe( - catchError((error) => this.handleError(error)) - ); + const url = `${this.identityHost}/groups/count`; + return this.oAuth2Service.get({ url }); } /** @@ -115,17 +102,10 @@ export class IdentityGroupService { * @returns Empty response when the group created. */ createGroup(newGroup: IdentityGroupModel): Observable { - const url = this.getGroupsApi(); - const httpMethod = 'POST', pathParams = {}, queryParams = {}, bodyParam = newGroup, headerParams = {}, - formParams = {}, contentTypes = ['application/json'], accepts = ['application/json']; + const url = `${this.identityHost}/groups`; + const bodyParam = newGroup; - return from(this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, - contentTypes, accepts, null, null, null - )).pipe( - catchError((error) => this.handleError(error)) - ); + return this.oAuth2Service.post({ url, bodyParam }); } /** @@ -135,18 +115,10 @@ export class IdentityGroupService { * @returns Empty response when the group updated. */ updateGroup(groupId: string, updatedGroup: IdentityGroupModel): Observable { - const url = this.getGroupsApi() + `/${groupId}`; - const request = JSON.stringify(updatedGroup); - const httpMethod = 'PUT', pathParams = {} , queryParams = {}, bodyParam = request, headerParams = {}, - formParams = {}, contentTypes = ['application/json'], accepts = ['application/json']; + const url = `${this.identityHost}/groups/${groupId}`; + const bodyParam = JSON.stringify(updatedGroup); - return from(this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, - contentTypes, accepts, null, null, null - )).pipe( - catchError((error) => this.handleError(error)) - ); + return this.oAuth2Service.put({ url, bodyParam }); } /** @@ -155,17 +127,8 @@ export class IdentityGroupService { * @returns Empty response when the group deleted. */ deleteGroup(groupId: string): Observable { - const url = this.getGroupsApi() + `/${groupId}`; - const httpMethod = 'DELETE', pathParams = {} , queryParams = {}, bodyParam = {}, headerParams = {}, - formParams = {}, contentTypes = ['application/json'], accepts = ['application/json']; - - return from(this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, - contentTypes, accepts, null, null, null - )).pipe( - catchError((error) => this.handleError(error)) - ); + const url = `${this.identityHost}/groups/${groupId}`; + return this.oAuth2Service.delete({ url }); } /** @@ -177,22 +140,10 @@ export class IdentityGroupService { if (searchParams.name === '') { return of([]); } - const url = this.getGroupsApi(); - const httpMethod = 'GET', pathParams = {}, queryParams = {search: searchParams.name}, bodyParam = {}, headerParams = {}, - formParams = {}, contentTypes = ['application/json'], accepts = ['application/json']; + const url = `${this.identityHost}/groups`; + const queryParams = { search: searchParams.name }; - return from(this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, - contentTypes, accepts, Object, null, null) - ).pipe( - map((response: []) => { - return response.map( (group: IdentityGroupModel) => { - return {id: group.id, name: group.name}; - }); - }), - catchError((err) => this.handleError(err)) - ); + return this.oAuth2Service.get({ url, queryParams }); } /** @@ -202,16 +153,7 @@ export class IdentityGroupService { */ getGroupRoles(groupId: string): Observable { const url = this.buildRolesUrl(groupId); - const httpMethod = 'GET', pathParams = {}, queryParams = {}, bodyParam = {}, headerParams = {}, - formParams = {}, contentTypes = ['application/json'], accepts = ['application/json']; - - return (from(this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, - contentTypes, accepts, Object, null, null) - )).pipe( - catchError((error) => this.handleError(error)) - ); + return this.oAuth2Service.get({ url }); } /** @@ -221,13 +163,11 @@ export class IdentityGroupService { * @returns True if the group has one or more of the roles, false otherwise */ checkGroupHasRole(groupId: string, roleNames: string[]): Observable { - return this.getGroupRoles(groupId).pipe(map((groupRoles: IdentityRoleModel[]) => { + return this.getGroupRoles(groupId).pipe(map((groupRoles) => { let hasRole = false; if (groupRoles && groupRoles.length > 0) { roleNames.forEach((roleName: string) => { - const role = groupRoles.find((groupRole) => { - return roleName === groupRole.name; - }); + const role = groupRoles.find(({ name }) => roleName === name); if (role) { hasRole = true; return; @@ -244,20 +184,12 @@ export class IdentityGroupService { * @returns client Id string */ getClientIdByApplicationName(applicationName: string): Observable { - const url = this.getApplicationIdApi(); - const httpMethod = 'GET', pathParams = {}, queryParams = {clientId: applicationName}, bodyParam = {}, headerParams = {}, formParams = {}, - contentTypes = ['application/json'], accepts = ['application/json']; - return from(this.alfrescoApiService.getInstance() - .oauth2Auth.callCustomApi(url, httpMethod, pathParams, queryParams, headerParams, - formParams, bodyParam, contentTypes, - accepts, Object, null, null) - ).pipe( - map((response: any[]) => { - const clientId = response && response.length > 0 ? response[0].id : ''; - return clientId; - }), - catchError((error) => this.handleError(error)) - ); + const url = `${this.identityHost}/clients`; + const queryParams = {clientId: applicationName}; + + return this.oAuth2Service.get({ url, queryParams }).pipe( + map((response) => response && response.length > 0 ? response[0].id : '') + ); } /** @@ -267,15 +199,8 @@ export class IdentityGroupService { * @returns List of roles */ getClientRoles(groupId: string, clientId: string): Observable { - const url = this.groupClientRoleMappingApi(groupId, clientId); - const httpMethod = 'GET', pathParams = {}, queryParams = {}, bodyParam = {}, headerParams = {}, - formParams = {}, contentTypes = ['application/json'], accepts = ['application/json']; - - return from(this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, - contentTypes, accepts, Object, null, null) - ); + const url = `${this.identityHost}/groups/${groupId}/role-mappings/clients/${clientId}`; + return this.oAuth2Service.get({ url }); } /** @@ -286,11 +211,8 @@ export class IdentityGroupService { */ checkGroupHasClientApp(groupId: string, clientId: string): Observable { return this.getClientRoles(groupId, clientId).pipe( - map((response: any[]) => { - return response && response.length > 0; - }), - catchError((error) => this.handleError(error)) - ); + map((response) => response && response.length > 0) + ); } /** @@ -306,9 +228,7 @@ export class IdentityGroupService { let hasRole = false; if (clientRoles.length > 0) { roleNames.forEach((roleName) => { - const role = clientRoles.find((availableRole) => { - return availableRole.name === roleName; - }); + const role = clientRoles.find(({ name }) => name === roleName); if (role) { hasRole = true; @@ -317,33 +237,11 @@ export class IdentityGroupService { }); } return hasRole; - }), - catchError((error) => this.handleError(error)) + }) ); } - private groupClientRoleMappingApi(groupId: string, clientId: string): string { - return `${this.appConfigService.get('identityHost')}/groups/${groupId}/role-mappings/clients/${clientId}`; - } - - private getApplicationIdApi(): string { - return `${this.appConfigService.get('identityHost')}/clients`; - } - - private getGroupsApi(): string { - return `${this.appConfigService.get('identityHost')}/groups`; - } - private buildRolesUrl(groupId: string): string { - return `${this.appConfigService.get('identityHost')}/groups/${groupId}/role-mappings/realm/composite`; - } - - /** - * Throw the error - * @param error - */ - private handleError(error: Response) { - this.logService.error(error); - return throwError(error || 'Server error'); + return `${this.identityHost}/groups/${groupId}/role-mappings/realm/composite`; } } diff --git a/lib/core/services/identity-user.service.ts b/lib/core/services/identity-user.service.ts index a171ee7b20..381a9a6d69 100644 --- a/lib/core/services/identity-user.service.ts +++ b/lib/core/services/identity-user.service.ts @@ -17,15 +17,14 @@ import { Pagination } from '@alfresco/js-api'; import { Injectable } from '@angular/core'; -import { from, Observable, of, throwError } from 'rxjs'; -import { catchError, map, switchMap } from 'rxjs/operators'; +import { Observable, of } from 'rxjs'; +import { map, switchMap } from 'rxjs/operators'; import { AppConfigService } from '../app-config/app-config.service'; import { IdentityGroupModel } from '../models/identity-group.model'; import { IdentityRoleModel } from '../models/identity-role.model'; import { IdentityUserModel } from '../models/identity-user.model'; -import { AlfrescoApiService } from './alfresco-api.service'; import { JwtHelperService } from './jwt-helper.service'; -import { LogService } from './log.service'; +import { OAuth2Service } from './oauth2.service'; export interface IdentityUserQueryResponse { @@ -57,9 +56,16 @@ export class IdentityUserService { constructor( private jwtHelperService: JwtHelperService, - private alfrescoApiService: AlfrescoApiService, - private appConfigService: AppConfigService, - private logService: LogService) { } + private oAuth2Service: OAuth2Service, + private appConfigService: AppConfigService) { } + + private get identityHost(): string { + return `${this.appConfigService.get('identityHost')}`; + } + + private buildUserUrl(): string { + return `${this.identityHost}/users`; + } /** * Gets the name and other basic details of the current user. @@ -83,21 +89,9 @@ export class IdentityUserService { return of([]); } const url = this.buildUserUrl(); - const httpMethod = 'GET', pathParams = {}, queryParams = { search: search }, bodyParam = {}, headerParams = {}, - formParams = {}, contentTypes = ['application/json'], accepts = ['application/json']; + const queryParams = { search: search }; - return from(this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, - contentTypes, accepts, Object, null, null) - ).pipe( - map((response: []) => { - return response.map( (user: IdentityUserModel) => { - return {id: user.id, firstName: user.firstName, lastName: user.lastName, email: user.email, username: user.username}; - }); - }), - catchError((err) => this.handleError(err)) - ); + return this.oAuth2Service.get({ url, queryParams }); } /** @@ -110,21 +104,9 @@ export class IdentityUserService { return of([]); } const url = this.buildUserUrl(); - const httpMethod = 'GET', pathParams = {}, queryParams = { username: username }, bodyParam = {}, headerParams = {}, - formParams = {}, contentTypes = ['application/json'], accepts = ['application/json']; + const queryParams = { username: username }; - return from(this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, - contentTypes, accepts, Object, null, null) - ).pipe( - map((response: []) => { - return response.map( (user: IdentityUserModel) => { - return {id: user.id, firstName: user.firstName, lastName: user.lastName, email: user.email, username: user.username}; - }); - }), - catchError((err) => this.handleError(err)) - ); + return this.oAuth2Service.get({url, queryParams }); } /** @@ -137,21 +119,9 @@ export class IdentityUserService { return of([]); } const url = this.buildUserUrl(); - const httpMethod = 'GET', pathParams = {}, queryParams = { email: email }, bodyParam = {}, headerParams = {}, - formParams = {}, contentTypes = ['application/json'], accepts = ['application/json']; + const queryParams = { email: email }; - return from(this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, - contentTypes, accepts, Object, null, null) - ).pipe( - map((response: []) => { - return response.map( (user: IdentityUserModel) => { - return {id: user.id, firstName: user.firstName, lastName: user.lastName, email: user.email, username: user.username}; - }); - }), - catchError((err) => this.handleError(err)) - ); + return this.oAuth2Service.get({ url, queryParams }); } /** @@ -164,14 +134,7 @@ export class IdentityUserService { return of([]); } const url = this.buildUserUrl() + '/' + id; - const httpMethod = 'GET', pathParams = {}, queryParams = {}, bodyParam = {}, headerParams = {}, - formParams = {}, contentTypes = ['application/json'], accepts = ['application/json']; - - return (from(this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, - contentTypes, accepts, Object, null, null) - )); + return this.oAuth2Service.get({ url }); } /** @@ -181,15 +144,8 @@ export class IdentityUserService { * @returns List of client roles */ getClientRoles(userId: string, clientId: string): Observable { - const url = this.buildUserClientRoleMapping(userId, clientId); - const httpMethod = 'GET', pathParams = {}, queryParams = {}, bodyParam = {}, headerParams = {}, - formParams = {}, contentTypes = ['application/json'], accepts = ['application/json']; - - return from(this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, - contentTypes, accepts, Object, null, null) - ); + const url = `${this.identityHost}/users/${userId}/role-mappings/clients/${clientId}/composite`; + return this.oAuth2Service.get({ url }); } /** @@ -200,9 +156,7 @@ export class IdentityUserService { */ checkUserHasClientApp(userId: string, clientId: string): Observable { return this.getClientRoles(userId, clientId).pipe( - map((clientRoles: any[]) => { - return clientRoles.length > 0; - }) + map((clientRoles) => clientRoles.length > 0) ); } @@ -219,9 +173,7 @@ export class IdentityUserService { let hasRole = false; if (clientRoles.length > 0) { roleNames.forEach((roleName) => { - const role = clientRoles.find((availableRole) => { - return availableRole.name === roleName; - }); + const role = clientRoles.find(({ name }) => name === roleName); if (role) { hasRole = true; @@ -240,19 +192,14 @@ export class IdentityUserService { * @returns Client ID string */ getClientIdByApplicationName(applicationName: string): Observable { - const url = this.buildGetClientsUrl(); - const httpMethod = 'GET', pathParams = {}, queryParams = { clientId: applicationName }, bodyParam = {}, headerParams = {}, formParams = {}, - contentTypes = ['application/json'], accepts = ['application/json']; - return from(this.alfrescoApiService.getInstance() - .oauth2Auth.callCustomApi(url, httpMethod, pathParams, queryParams, headerParams, - formParams, bodyParam, contentTypes, - accepts, Object, null, null) - ).pipe( - map((response: any[]) => { - const clientId = response && response.length > 0 ? response[0].id : ''; - return clientId; - }) - ); + const url = `${this.identityHost}/clients`; + const queryParams = { clientId: applicationName }; + + return this.oAuth2Service + .get({url, queryParams }) + .pipe( + map((response) => response && response.length > 0 ? response[0].id : '') + ); } /** @@ -290,18 +237,7 @@ export class IdentityUserService { */ getUsers(): Observable { const url = this.buildUserUrl(); - const httpMethod = 'GET', pathParams = {}, queryParams = {}, bodyParam = {}, headerParams = {}, - formParams = {}, authNames = [], contentTypes = ['application/json'], accepts = ['application/json']; - - return from(this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, authNames, - contentTypes, accepts, null, null) - ).pipe( - map((response: IdentityUserModel[]) => { - return response; - }) - ); + return this.oAuth2Service.get({ url }); } /** @@ -310,19 +246,8 @@ export class IdentityUserService { * @returns Array of role info objects */ getUserRoles(userId: string): Observable { - const url = this.buildRolesUrl(userId); - const httpMethod = 'GET', pathParams = {}, queryParams = {}, bodyParam = {}, headerParams = {}, - formParams = {}, contentTypes = ['application/json'], accepts = ['application/json']; - - return from(this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, - contentTypes, accepts, Object, null, null) - ).pipe( - map((response: IdentityRoleModel[]) => { - return response; - }) - ); + const url = `${this.identityHost}/users/${userId}/role-mappings/realm/composite`; + return this.oAuth2Service.get({ url }); } /** @@ -357,7 +282,7 @@ export class IdentityUserService { const currentUser = this.getCurrentUserInfo(); let users = await this.getUsers().toPromise(); - users = users.filter((user) => { return user.username !== currentUser.username; }); + users = users.filter(({ username }) => username !== currentUser.username); for (let i = 0; i < users.length; i++) { const hasAnyRole = await this.userHasAnyRole(users[i].id, roleNames); @@ -394,9 +319,7 @@ export class IdentityUserService { let hasRole = false; if (userRoles && userRoles.length > 0) { roleNames.forEach((roleName: string) => { - const role = userRoles.find((userRole) => { - return roleName === userRole.name; - }); + const role = userRoles.find(({ name }) => roleName === name); if (role) { hasRole = true; return; @@ -413,18 +336,12 @@ export class IdentityUserService { */ queryUsers(requestQuery: IdentityUserQueryCloudRequestModel): Observable { const url = this.buildUserUrl(); - const httpMethod = 'GET', pathParams = {}, - queryParams = { first: requestQuery.first, max: requestQuery.max }, bodyParam = {}, headerParams = {}, - formParams = {}, authNames = [], contentTypes = ['application/json']; + const queryParams = { first: requestQuery.first, max: requestQuery.max }; return this.getTotalUsersCount().pipe( - switchMap((totalCount: any) => - from(this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, authNames, - contentTypes, null, null, null) - ).pipe( - map((response: IdentityUserModel[]) => { + switchMap((totalCount) => + this.oAuth2Service.get({ url, queryParams }).pipe( + map((response) => { return { entries: response, pagination: { @@ -434,10 +351,10 @@ export class IdentityUserService { hasMoreItems: false, totalItems: totalCount } - }; - }), - catchError((error) => this.handleError(error)) - )) + }; + }) + ) + ) ); } @@ -447,15 +364,7 @@ export class IdentityUserService { */ getTotalUsersCount(): Observable { const url = this.buildUserUrl() + `/count`; - const contentTypes = ['application/json'], accepts = ['application/json']; - return from(this.alfrescoApiService.getInstance() - .oauth2Auth.callCustomApi(url, 'GET', - null, null, null, - null, null, contentTypes, - accepts, null, null, null - )).pipe( - catchError((error) => this.handleError(error)) - ); + return this.oAuth2Service.get({ url }); } /** @@ -465,17 +374,9 @@ export class IdentityUserService { */ createUser(newUser: IdentityUserModel): Observable { const url = this.buildUserUrl(); - const request = JSON.stringify(newUser); - const httpMethod = 'POST', pathParams = {}, queryParams = {}, bodyParam = request, headerParams = {}, - formParams = {}, contentTypes = ['application/json'], accepts = ['application/json']; + const bodyParam = JSON.stringify(newUser); - return from( - this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, - contentTypes, accepts, null, null, null - ) - ).pipe(catchError(error => this.handleError(error))); + return this.oAuth2Service.post({ url, bodyParam }); } /** @@ -486,17 +387,9 @@ export class IdentityUserService { */ updateUser(userId: string, updatedUser: IdentityUserModel): Observable { const url = this.buildUserUrl() + '/' + userId; - const request = JSON.stringify(updatedUser); - const httpMethod = 'PUT', pathParams = {} , queryParams = {}, bodyParam = request, headerParams = {}, - formParams = {}, contentTypes = ['application/json'], accepts = ['application/json']; + const bodyParam = JSON.stringify(updatedUser); - return from(this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, - contentTypes, accepts, null, null, null - )).pipe( - catchError((error) => this.handleError(error)) - ); + return this.oAuth2Service.put({ url, bodyParam }); } /** @@ -506,16 +399,7 @@ export class IdentityUserService { */ deleteUser(userId: string): Observable { const url = this.buildUserUrl() + '/' + userId; - const httpMethod = 'DELETE', pathParams = {} , queryParams = {}, bodyParam = {}, headerParams = {}, - formParams = {}, contentTypes = ['application/json'], accepts = ['application/json']; - - return from(this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, - contentTypes, accepts, null, null, null - )).pipe( - catchError((error) => this.handleError(error)) - ); + return this.oAuth2Service.delete({ url }); } /** @@ -526,17 +410,9 @@ export class IdentityUserService { */ changePassword(userId: string, newPassword: IdentityUserPasswordModel): Observable { const url = this.buildUserUrl() + '/' + userId + '/reset-password'; - const request = JSON.stringify(newPassword); - const httpMethod = 'PUT', pathParams = {} , queryParams = {}, bodyParam = request, headerParams = {}, - formParams = {}, contentTypes = ['application/json'], accepts = ['application/json']; + const bodyParam = JSON.stringify(newPassword); - return from(this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, - contentTypes, accepts, null, null, null - )).pipe( - catchError((error) => this.handleError(error)) - ); + return this.oAuth2Service.put({ url, bodyParam }); } /** @@ -546,17 +422,9 @@ export class IdentityUserService { */ getInvolvedGroups(userId: string): Observable { const url = this.buildUserUrl() + '/' + userId + '/groups/'; - const httpMethod = 'GET', pathParams = { id: userId}, - queryParams = {}, bodyParam = {}, headerParams = {}, - formParams = {}, authNames = [], contentTypes = ['application/json']; + const pathParams = { id: userId }; - return from(this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, authNames, - contentTypes, null, null, null - )).pipe( - catchError((error) => this.handleError(error)) - ); + return this.oAuth2Service.get({ url, pathParams }); } /** @@ -566,17 +434,9 @@ export class IdentityUserService { */ joinGroup(joinGroupRequest: IdentityJoinGroupRequestModel): Observable { const url = this.buildUserUrl() + '/' + joinGroupRequest.userId + '/groups/' + joinGroupRequest.groupId; - const request = JSON.stringify(joinGroupRequest); - const httpMethod = 'PUT', pathParams = {} , queryParams = {}, bodyParam = request, headerParams = {}, - formParams = {}, contentTypes = ['application/json'], accepts = ['application/json']; + const bodyParam = JSON.stringify(joinGroupRequest); - return from(this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, - contentTypes, accepts, null, null, null - )).pipe( - catchError((error) => this.handleError(error)) - ); + return this.oAuth2Service.put({ url, bodyParam }); } /** @@ -587,16 +447,7 @@ export class IdentityUserService { */ leaveGroup(userId: any, groupId: string): Observable { const url = this.buildUserUrl() + '/' + userId + '/groups/' + groupId; - const httpMethod = 'DELETE', pathParams = {} , queryParams = {}, bodyParam = {}, headerParams = {}, - formParams = {}, contentTypes = ['application/json'], accepts = ['application/json']; - - return from(this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, - contentTypes, accepts, null, null, null - )).pipe( - catchError((error) => this.handleError(error)) - ); + return this.oAuth2Service.delete({ url }); } /** @@ -606,17 +457,7 @@ export class IdentityUserService { */ getAvailableRoles(userId: string): Observable { const url = this.buildUserUrl() + '/' + userId + '/role-mappings/realm/available'; - const httpMethod = 'GET', pathParams = {}, - queryParams = {}, bodyParam = {}, headerParams = {}, - formParams = {}, authNames = [], contentTypes = ['application/json']; - - return from(this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, authNames, - contentTypes, null, null, null - )).pipe( - catchError((error) => this.handleError(error)) - ); + return this.oAuth2Service.get({ url }); } /** @@ -626,17 +467,9 @@ export class IdentityUserService { */ getAssignedRoles(userId: string): Observable { const url = this.buildUserUrl() + '/' + userId + '/role-mappings/realm'; - const httpMethod = 'GET', pathParams = { id: userId}, - queryParams = {}, bodyParam = {}, headerParams = {}, - formParams = {}, authNames = [], contentTypes = ['application/json']; + const pathParams = { id: userId }; - return from(this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, authNames, - contentTypes, null, null, null - )).pipe( - catchError((error) => this.handleError(error)) - ); + return this.oAuth2Service.get({ url, pathParams }); } /** @@ -646,17 +479,9 @@ export class IdentityUserService { */ getEffectiveRoles(userId: string): Observable { const url = this.buildUserUrl() + '/' + userId + '/role-mappings/realm/composite'; - const httpMethod = 'GET', pathParams = { id: userId}, - queryParams = {}, bodyParam = {}, headerParams = {}, - formParams = {}, authNames = [], contentTypes = ['application/json']; + const pathParams = { id: userId }; - return from(this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, authNames, - contentTypes, null, null, null - )).pipe( - catchError((error) => this.handleError(error)) - ); + return this.oAuth2Service.get({ url, pathParams }); } /** @@ -667,17 +492,9 @@ export class IdentityUserService { */ assignRoles(userId: string, roles: IdentityRoleModel[]): Observable { const url = this.buildUserUrl() + '/' + userId + '/role-mappings/realm'; - const request = JSON.stringify(roles); - const httpMethod = 'POST', pathParams = {} , queryParams = {}, bodyParam = request, headerParams = {}, - formParams = {}, contentTypes = ['application/json'], accepts = ['application/json']; + const bodyParam = JSON.stringify(roles); - return from(this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, - contentTypes, accepts, null, null, null - )).pipe( - catchError((error) => this.handleError(error)) - ); + return this.oAuth2Service.post({ url, bodyParam }); } /** @@ -688,41 +505,8 @@ export class IdentityUserService { */ removeRoles(userId: string, removedRoles: IdentityRoleModel[]): Observable { const url = this.buildUserUrl() + '/' + userId + '/role-mappings/realm'; - const request = JSON.stringify(removedRoles); - const httpMethod = 'DELETE', pathParams = {} , queryParams = {}, bodyParam = request, headerParams = {}, - formParams = {}, contentTypes = ['application/json'], accepts = ['application/json']; + const bodyParam = JSON.stringify(removedRoles); - return from(this.alfrescoApiService.getInstance().oauth2Auth.callCustomApi( - url, httpMethod, pathParams, queryParams, - headerParams, formParams, bodyParam, - contentTypes, accepts, null, null, null - )).pipe( - catchError((error) => this.handleError(error)) - ); - } - - private buildUserUrl(): string { - return `${this.appConfigService.get('identityHost')}/users`; - } - - private buildUserClientRoleMapping(userId: string, clientId: string): string { - return `${this.appConfigService.get('identityHost')}/users/${userId}/role-mappings/clients/${clientId}/composite`; - } - - private buildRolesUrl(userId: string): string { - return `${this.appConfigService.get('identityHost')}/users/${userId}/role-mappings/realm/composite`; - } - - private buildGetClientsUrl(): string { - return `${this.appConfigService.get('identityHost')}/clients`; - } - - /** - * Throw the error - * @param error - */ - private handleError(error: Response) { - this.logService.error(error); - return throwError(error || 'Server error'); + return this.oAuth2Service.delete({ url, bodyParam }); } } diff --git a/lib/core/services/oauth2.service.ts b/lib/core/services/oauth2.service.ts new file mode 100644 index 0000000000..814f68bd1a --- /dev/null +++ b/lib/core/services/oauth2.service.ts @@ -0,0 +1,84 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Injectable } from '@angular/core'; +import { AlfrescoApiService } from './alfresco-api.service'; +import { Observable, from } from 'rxjs'; + +export const JSON_TYPE = ['application/json']; + +export interface OAuth2RequestParams { + url: string; + httpMethod?: string; + pathParams?: any; + queryParams?: any; + bodyParam?: any; +} + +@Injectable({ providedIn: 'root' }) +export class OAuth2Service { + constructor(private alfrescoApiService: AlfrescoApiService) {} + + get apiClient() { + return this.alfrescoApiService.getInstance().oauth2Auth; + } + + request(opts: OAuth2RequestParams): Observable { + return from( + this.apiClient.callCustomApi( + opts.url, + opts.httpMethod, + opts.pathParams, + opts.queryParams, + {}, + {}, + opts.bodyParam, + JSON_TYPE, + JSON_TYPE, + Object + ) + ); + } + + get(opts: OAuth2RequestParams): Observable { + return this.request({ + ...opts, + httpMethod: 'GET' + }); + } + + put(opts: OAuth2RequestParams): Observable { + return this.request({ + ...opts, + httpMethod: 'PUT' + }); + } + + post(opts: OAuth2RequestParams): Observable { + return this.request({ + ...opts, + httpMethod: 'PUT' + }); + } + + delete(opts: OAuth2RequestParams): Observable { + return this.request({ + ...opts, + httpMethod: 'DELETE' + }); + } +}