From 287a6dbcd316d5757242cc006a1579951a12ef4d Mon Sep 17 00:00:00 2001 From: Dominik Schwank Date: Thu, 17 Oct 2019 01:32:48 +0200 Subject: [PATCH] Add support for Client Scopes, Scope Mappings and Protocol Mappers (#21) * Features: add support for client scopes and protocol mappers --- .prettierignore | 8 + README.md | 91 +++- src/client.ts | 3 + src/defs/clientScopeRepresentation.ts | 13 + src/resources/clientScopes.ts | 332 +++++++++++++ src/resources/clients.ts | 256 ++++++++++ test/clientScopes.spec.ts | 646 ++++++++++++++++++++++++++ test/clients.spec.ts | 566 +++++++++++++++++++++- test/groupUser.spec.ts | 2 +- test/users.spec.ts | 2 +- 10 files changed, 1913 insertions(+), 6 deletions(-) create mode 100644 .prettierignore create mode 100644 src/defs/clientScopeRepresentation.ts create mode 100644 src/resources/clientScopes.ts create mode 100644 test/clientScopes.spec.ts diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..7dce76fe --- /dev/null +++ b/.prettierignore @@ -0,0 +1,8 @@ +# NODE +node_modules + +# IDE +.vscode + +# GITHUB +.github \ No newline at end of file diff --git a/README.md b/README.md index 27d14463..d67f8082 100644 --- a/README.md +++ b/README.md @@ -221,6 +221,94 @@ Demo code: https://github.com/keycloak/keycloak-nodejs-admin-client/blob/master/ - Delete the identity provider mapper (`DELETE /{realm}/identity-provider/instances/{alias}/mappers/{id}`) - Find the identity provider mapper types (`GET /{realm}/identity-provider/instances/{alias}/mapper-types`) +### [Client Scopes](https://www.keycloak.org/docs-api/6.0/rest-api/index.html#_client_scopes_resource) + +Demo code: https://github.com/keycloak/keycloak-nodejs-admin-client/blob/master/test/clientScopes.spec.ts + +- Create a new client scope (`POST /{realm}/client-scopes`) +- Get client scopes belonging to the realm (`GET /{realm}/client-scopes`) +- Get representation of the client scope (`GET /{realm}/client-scopes/{id}`) +- Update the client scope (`PUT /{realm}/client-scopes/{id}`) +- Delete the client scope (`DELETE /{realm}/client-scopes/{id}`) + +### [Client Scopes for realm](https://www.keycloak.org/docs-api/6.0/rest-api/index.html#_client_scopes_resource) + +Demo code: https://github.com/keycloak/keycloak-nodejs-admin-client/blob/master/test/clientScopes.spec.ts + +- Get realm default client scopes (`GET /{realm}/default-default-client-scopes`) +- Add realm default client scope (`PUT /{realm}/default-default-client-scopes/{id}`) +- Delete realm default client scope (`DELETE /{realm}/default-default-client-scopes/{id}`) +- Get realm optional client scopes (`GET /{realm}/default-optional-client-scopes`) +- Add realm optional client scope (`PUT /{realm}/default-optional-client-scopes/{id}`) +- Delete realm optional client scope (`DELETE /{realm}/default-optional-client-scopes/{id}`) + +### [Client Scopes for client](https://www.keycloak.org/docs-api/6.0/rest-api/index.html#_client_scopes_resource) + +Demo code: https://github.com/keycloak/keycloak-nodejs-admin-client/blob/master/test/clientScopes.spec.ts + +- Get default client scopes (`GET /{realm}/clients/{id}/default-client-scopes`) +- Add default client scope (`PUT /{realm}/clients/{id}/default-client-scopes/{clientScopeId}`) +- Delete default client scope (`DELETE /{realm}/clients/{id}/default-client-scopes/{clientScopeId}`) +- Get optional client scopes (`GET /{realm}/clients/{id}/optional-client-scopes`) +- Add optional client scope (`PUT /{realm}/clients/{id}/optional-client-scopes/{clientScopeId}`) +- Delete optional client scope (`DELETE /{realm}/clients/{id}/optional-client-scopes/{clientScopeId}`) + +### [Scope Mappings for client scopes](https://www.keycloak.org/docs-api/6.0/rest-api/index.html#_scope_mappings_resource) + +Demo code: https://github.com/keycloak/keycloak-nodejs-admin-client/blob/master/test/clientScopes.spec.ts + +- Get all scope mappings for the client (`GET /{realm}/client-scopes/{id}/scope-mappings`) +- Add client-level roles to the client’s scope (`POST /{realm}/client-scopes/{id}/scope-mappings/clients/{client}`) +- Get the roles associated with a client’s scope (`GET /{realm}/client-scopes/{id}/scope-mappings/clients/{client}`) +- The available client-level roles (`GET /{realm}/client-scopes/{id}/scope-mappings/clients/{client}/available`) +- Get effective client roles (`GET /{realm}/client-scopes/{id}/scope-mappings/clients/{client}/composite`) +- Remove client-level roles from the client’s scope. (`DELETE /{realm}/client-scopes/{id}/scope-mappings/clients/{client}`) +- Add a set of realm-level roles to the client’s scope (`POST /{realm}/client-scopes/{id}/scope-mappings/realm`) +- Get realm-level roles associated with the client’s scope (`GET /{realm}/client-scopes/{id}/scope-mappings/realm`) +- Remove a set of realm-level roles from the client’s scope (`DELETE /{realm}/client-scopes/{id}/scope-mappings/realm`) +- Get realm-level roles that are available to attach to this client’s scope (`GET /{realm}/client-scopes/{id}/scope-mappings/realm/available`) +- Get effective realm-level roles associated with the client’s scope (`GET /{realm}/client-scopes/{id}/scope-mappings/realm/composite`) + +### [Scope Mappings for clients](https://www.keycloak.org/docs-api/6.0/rest-api/index.html#_scope_mappings_resource) + +Demo code: https://github.com/keycloak/keycloak-nodejs-admin-client/blob/master/test/clientScopes.spec.ts + +- Get all scope mappings for the client (`GET /{realm}/clients/{id}/scope-mappings`) +- Add client-level roles to the client’s scope (`POST /{realm}/clients/{id}/scope-mappings/clients/{client}`) +- Get the roles associated with a client’s scope (`GET /{realm}/clients/{id}/scope-mappings/clients/{client}`) +- Remove client-level roles from the client’s scope. (`DELETE /{realm}/clients/{id}/scope-mappings/clients/{client}`) +- The available client-level roles (`GET /{realm}/clients/{id}/scope-mappings/clients/{client}/available`) +- Get effective client roles (`GET /{realm}/clients/{id}/scope-mappings/clients/{client}/composite`) +- Add a set of realm-level roles to the client’s scope (`POST /{realm}/clients/{id}/scope-mappings/realm`) +- Get realm-level roles associated with the client’s scope (`GET /{realm}/clients/{id}/scope-mappings/realm`) +- Remove a set of realm-level roles from the client’s scope (`DELETE /{realm}/clients/{id}/scope-mappings/realm`) +- Get realm-level roles that are available to attach to this client’s scope (`GET /{realm}/clients/{id}/scope-mappings/realm/available`) +- Get effective realm-level roles associated with the client’s scope (`GET /{realm}/clients/{id}/scope-mappings/realm/composite`) + +### [Protocol Mappers for client scopes](https://www.keycloak.org/docs-api/6.0/rest-api/index.html#_protocol_mappers_resource) + +Demo code: https://github.com/keycloak/keycloak-nodejs-admin-client/blob/master/test/clientScopes.spec.ts + +- Create multiple mappers (`POST /{realm}/client-scopes/{id}/protocol-mappers/add-models`) +- Create a mapper (`POST /{realm}/client-scopes/{id}/protocol-mappers/models`) +- Get mappers (`GET /{realm}/client-scopes/{id}/protocol-mappers/models`) +- Get mapper by id (`GET /{realm}/client-scopes/{id}/protocol-mappers/models/{mapperId}`) +- Update the mapper (`PUT /{realm}/client-scopes/{id}/protocol-mappers/models/{mapperId}`) +- Delete the mapper (`DELETE /{realm}/client-scopes/{id}/protocol-mappers/models/{mapperId}`) +- Get mappers by name for a specific protocol (`GET /{realm}/client-scopes/{id}/protocol-mappers/protocol/{protocol}`) + +### [Protocol Mappers for clients](https://www.keycloak.org/docs-api/6.0/rest-api/index.html#_protocol_mappers_resource) + +Demo code: https://github.com/keycloak/keycloak-nodejs-admin-client/blob/master/test/clients.spec.ts + +- Create multiple mappers (`POST /{realm}/clients/{id}/protocol-mappers/add-models`) +- Create a mapper (`POST /{realm}/clients/{id}/protocol-mappers/models`) +- Get mappers (`GET /{realm}/clients/{id}/protocol-mappers/models`) +- Get mapper by id (`GET /{realm}/clients/{id}/protocol-mappers/models/{mapperId}`) +- Update the mapper (`PUT /{realm}/clients/{id}/protocol-mappers/models/{mapperId}`) +- Delete the mapper (`DELETE /{realm}/clients/{id}/protocol-mappers/models/{mapperId}`) +- Get mappers by name for a specific protocol (`GET /{realm}/clients/{id}/protocol-mappers/protocol/{protocol}`) + ### [Component]() Supported for [user federation](https://www.keycloak.org/docs/latest/server_admin/index.html#_user-storage-federation). Demo code: https://github.com/keycloak/keycloak-nodejs-admin-client/blob/master/test/components.spec.ts @@ -238,10 +326,7 @@ Supported for [user federation](https://www.keycloak.org/docs/latest/server_admi - [Client Attribute Certificate](https://www.keycloak.org/docs-api/4.1/rest-api/index.html#_client_attribute_certificate_resource) - [Client Initial Access](https://www.keycloak.org/docs-api/4.1/rest-api/index.html#_client_initial_access_resource) - [Client Registration Policy](https://www.keycloak.org/docs-api/4.1/rest-api/index.html#_client_registration_policy_resource) -- [Client Scopes](https://www.keycloak.org/docs-api/4.1/rest-api/index.html#_client_scopes_resource) - [Key](https://www.keycloak.org/docs-api/4.1/rest-api/index.html#_key_resource) -- [Protocol Mappers](https://www.keycloak.org/docs-api/4.1/rest-api/index.html#_protocol_mappers_resource) -- [Scope Mappings](https://www.keycloak.org/docs-api/4.1/rest-api/index.html#_scope_mappings_resource) - [User Storage Provider](https://www.keycloak.org/docs-api/4.1/rest-api/index.html#_user_storage_provider_resource) ## Maintainers diff --git a/src/client.ts b/src/client.ts index 2795bd18..24853c8d 100644 --- a/src/client.ts +++ b/src/client.ts @@ -5,6 +5,7 @@ import {Groups} from './resources/groups'; import {Roles} from './resources/roles'; import {Clients} from './resources/clients'; import {Realms} from './resources/realms'; +import {ClientScopes} from './resources/clientScopes'; import {IdentityProviders} from './resources/identityProviders'; import {Components} from './resources/components'; import {AxiosRequestConfig} from 'axios'; @@ -22,6 +23,7 @@ export class KeycloakAdminClient { public roles: Roles; public clients: Clients; public realms: Realms; + public clientScopes: ClientScopes; public identityProviders: IdentityProviders; public components: Components; @@ -45,6 +47,7 @@ export class KeycloakAdminClient { this.roles = new Roles(this); this.clients = new Clients(this); this.realms = new Realms(this); + this.clientScopes = new ClientScopes(this); this.identityProviders = new IdentityProviders(this); this.components = new Components(this); } diff --git a/src/defs/clientScopeRepresentation.ts b/src/defs/clientScopeRepresentation.ts new file mode 100644 index 00000000..165eb195 --- /dev/null +++ b/src/defs/clientScopeRepresentation.ts @@ -0,0 +1,13 @@ +/** + * https://www.keycloak.org/docs-api/6.0/rest-api/index.html#_clientscoperepresentation + */ +import ProtocolMapperRepresentation from './protocolMapperRepresentation'; + +export default interface ClientScopeRepresentation { + attributes?: Record; + description?: string; + id?: string; + name?: string; + protocol?: string; + protocolMappers?: ProtocolMapperRepresentation[]; +} diff --git a/src/resources/clientScopes.ts b/src/resources/clientScopes.ts new file mode 100644 index 00000000..eee665f0 --- /dev/null +++ b/src/resources/clientScopes.ts @@ -0,0 +1,332 @@ +import ClientScopeRepresentation from '../defs/clientScopeRepresentation'; +import Resource from './resource'; +import {KeycloakAdminClient} from '../client'; +import ProtocolMapperRepresentation from '../defs/protocolMapperRepresentation'; +import MappingsRepresentation from '../defs/mappingsRepresentation'; +import RoleRepresentation from '../defs/roleRepresentation'; + +export class ClientScopes extends Resource<{realm?: string}> { + public find = this.makeRequest<{}, ClientScopeRepresentation[]>({ + method: 'GET', + path: '/client-scopes', + }); + + public create = this.makeRequest({ + method: 'POST', + path: '/client-scopes', + }); + + /** + * Client-Scopes by id + */ + + public findOne = this.makeRequest<{id: string}, ClientScopeRepresentation>({ + method: 'GET', + path: '/client-scopes/{id}', + urlParamKeys: ['id'], + catchNotFound: true, + }); + + public update = this.makeUpdateRequest< + {id: string}, + ClientScopeRepresentation, + void + >({ + method: 'PUT', + path: '/client-scopes/{id}', + urlParamKeys: ['id'], + }); + + public del = this.makeRequest<{id: string}, void>({ + method: 'DELETE', + path: '/client-scopes/{id}', + urlParamKeys: ['id'], + }); + + /** + * Default Client-Scopes + */ + + public listDefaultClientScopes = this.makeRequest< + void, + ClientScopeRepresentation[] + >({ + method: 'GET', + path: '/default-default-client-scopes', + }); + + public addDefaultClientScope = this.makeRequest<{id: string}, void>({ + method: 'PUT', + path: '/default-default-client-scopes/{id}', + urlParamKeys: ['id'], + }); + + public delDefaultClientScope = this.makeRequest<{id: string}, void>({ + method: 'DELETE', + path: '/default-default-client-scopes/{id}', + urlParamKeys: ['id'], + }); + + /** + * Default Optional Client-Scopes + */ + + public listDefaultOptionalClientScopes = this.makeRequest< + void, + ClientScopeRepresentation[] + >({ + method: 'GET', + path: '/default-optional-client-scopes', + }); + + public addDefaultOptionalClientScope = this.makeRequest<{id: string}, void>({ + method: 'PUT', + path: '/default-optional-client-scopes/{id}', + urlParamKeys: ['id'], + }); + + public delDefaultOptionalClientScope = this.makeRequest<{id: string}, void>({ + method: 'DELETE', + path: '/default-optional-client-scopes/{id}', + urlParamKeys: ['id'], + }); + + /** + * Protocol Mappers + */ + + public addMultipleProtocolMappers = this.makeUpdateRequest< + {id: string}, + ProtocolMapperRepresentation[], + void + >({ + method: 'POST', + path: '/client-scopes/{id}/protocol-mappers/add-models', + urlParamKeys: ['id'], + }); + + public addProtocolMapper = this.makeUpdateRequest< + {id: string}, + ProtocolMapperRepresentation, + void + >({ + method: 'POST', + path: '/client-scopes/{id}/protocol-mappers/models', + urlParamKeys: ['id'], + }); + + public listProtocolMappers = this.makeRequest< + {id: string}, + ProtocolMapperRepresentation[] + >({ + method: 'GET', + path: '/client-scopes/{id}/protocol-mappers/models', + urlParamKeys: ['id'], + }); + + public findProtocolMapper = this.makeRequest< + {id: string; mapperId: string}, + ProtocolMapperRepresentation + >({ + method: 'GET', + path: '/client-scopes/{id}/protocol-mappers/models/{mapperId}', + urlParamKeys: ['id', 'mapperId'], + catchNotFound: true, + }); + + public findProtocolMappersByProtocol = this.makeRequest< + {id: string; protocol: string}, + ProtocolMapperRepresentation[] + >({ + method: 'GET', + path: '/client-scopes/{id}/protocol-mappers/protocol/{protocol}', + urlParamKeys: ['id', 'protocol'], + catchNotFound: true, + }); + + public updateProtocolMapper = this.makeUpdateRequest< + {id: string; mapperId: string}, + ProtocolMapperRepresentation, + void + >({ + method: 'PUT', + path: '/client-scopes/{id}/protocol-mappers/models/{mapperId}', + urlParamKeys: ['id', 'mapperId'], + }); + + public delProtocolMapper = this.makeRequest< + {id: string; mapperId: string}, + void + >({ + method: 'DELETE', + path: '/client-scopes/{id}/protocol-mappers/models/{mapperId}', + urlParamKeys: ['id', 'mapperId'], + }); + + /** + * Scope Mappings + */ + public listScopeMappings = this.makeRequest< + {id: string}, + MappingsRepresentation + >({ + method: 'GET', + path: '/client-scopes/{id}/scope-mappings', + urlParamKeys: ['id'], + }); + + public addClientScopeMappings = this.makeUpdateRequest< + {id: string; client: string}, + RoleRepresentation[], + void + >({ + method: 'POST', + path: '/client-scopes/{id}/scope-mappings/clients/{client}', + urlParamKeys: ['id', 'client'], + }); + + public listClientScopeMappings = this.makeRequest< + {id: string; client: string}, + RoleRepresentation[] + >({ + method: 'GET', + path: '/client-scopes/{id}/scope-mappings/clients/{client}', + urlParamKeys: ['id', 'client'], + }); + + public listAvailableClientScopeMappings = this.makeRequest< + {id: string; client: string}, + RoleRepresentation[] + >({ + method: 'GET', + path: '/client-scopes/{id}/scope-mappings/clients/{client}/available', + urlParamKeys: ['id', 'client'], + }); + + public listCompositeClientScopeMappings = this.makeRequest< + {id: string; client: string}, + RoleRepresentation[] + >({ + method: 'GET', + path: '/client-scopes/{id}/scope-mappings/clients/{client}/available', + urlParamKeys: ['id', 'client'], + }); + + public delClientScopeMappings = this.makeUpdateRequest< + {id: string; client: string}, + RoleRepresentation[], + void + >({ + method: 'DELETE', + path: '/client-scopes/{id}/scope-mappings/clients/{client}', + urlParamKeys: ['id', 'client'], + }); + + public addRealmScopeMappings = this.makeUpdateRequest< + {id: string}, + RoleRepresentation[], + void + >({ + method: 'POST', + path: '/client-scopes/{id}/scope-mappings/realm', + urlParamKeys: ['id'], + }); + + public listRealmScopeMappings = this.makeRequest< + {id: string}, + RoleRepresentation[] + >({ + method: 'GET', + path: '/client-scopes/{id}/scope-mappings/realm', + urlParamKeys: ['id'], + }); + + public listAvailableRealmScopeMappings = this.makeRequest< + {id: string}, + RoleRepresentation[] + >({ + method: 'GET', + path: '/client-scopes/{id}/scope-mappings/realm/available', + urlParamKeys: ['id'], + }); + + public listCompositeRealmScopeMappings = this.makeRequest< + {id: string}, + RoleRepresentation[] + >({ + method: 'GET', + path: '/client-scopes/{id}/scope-mappings/realm/available', + urlParamKeys: ['id'], + }); + + public delRealmScopeMappings = this.makeUpdateRequest< + {id: string}, + RoleRepresentation[], + void + >({ + method: 'DELETE', + path: '/client-scopes/{id}/scope-mappings/realm', + urlParamKeys: ['id'], + }); + + constructor(client: KeycloakAdminClient) { + super(client, { + path: '/admin/realms/{realm}', + getUrlParams: () => ({ + realm: client.realmName, + }), + getBaseUrl: () => client.baseUrl, + }); + } + + /** + * Find client scope by name. + */ + public async findOneByName(payload: { + realm?: string; + name: string; + }): Promise { + const allScopes = await this.find({ + ...(payload.realm ? {realm: payload.realm} : {}), + }); + const scope = allScopes.find(item => item.name === payload.name); + return scope ? scope : null; + } + + /** + * Delete client scope by name. + */ + public async delByName(payload: { + realm?: string; + name: string; + }): Promise { + const scope = await this.findOneByName(payload); + + if (!scope) { + throw new Error('Scope not found.'); + } + + await this.del({ + ...(payload.realm ? {realm: payload.realm} : {}), + id: scope.id, + }); + } + + /** + * Find single protocol mapper by name. + */ + public async findProtocolMapperByName(payload: { + realm?: string; + id: string; + name: string; + }): Promise { + const allProtocolMappers = await this.listProtocolMappers({ + id: payload.id, + ...(payload.realm ? {realm: payload.realm} : {}), + }); + const protocolMapper = allProtocolMappers.find( + mapper => mapper.name === payload.name, + ); + return protocolMapper ? protocolMapper : null; + } +} diff --git a/src/resources/clients.ts b/src/resources/clients.ts index 24d568fe..a8fbfb1e 100644 --- a/src/resources/clients.ts +++ b/src/resources/clients.ts @@ -4,6 +4,9 @@ import {KeycloakAdminClient} from '../client'; import RoleRepresentation from '../defs/roleRepresentation'; import UserRepresentation from '../defs/userRepresentation'; import CredentialRepresentation from '../defs/credentialRepresentation'; +import ClientScopeRepresentation from '../defs/clientScopeRepresentation'; +import ProtocolMapperRepresentation from '../defs/protocolMapperRepresentation'; +import MappingsRepresentation from '../defs/mappingsRepresentation'; export interface ClientQuery { clientId?: string; @@ -133,6 +136,241 @@ export class Clients extends Resource<{realm?: string}> { urlParamKeys: ['id'], }); + /** + * Client Scopes + */ + public listDefaultClientScopes = this.makeRequest< + {id: string}, + ClientScopeRepresentation[] + >({ + method: 'GET', + path: '/{id}/default-client-scopes', + urlParamKeys: ['id'], + }); + + public addDefaultClientScope = this.makeRequest< + {id: string; clientScopeId: string}, + void + >({ + method: 'PUT', + path: '/{id}/default-client-scopes/{clientScopeId}', + urlParamKeys: ['id', 'clientScopeId'], + }); + + public delDefaultClientScope = this.makeRequest< + {id: string; clientScopeId: string}, + void + >({ + method: 'DELETE', + path: '/{id}/default-client-scopes/{clientScopeId}', + urlParamKeys: ['id', 'clientScopeId'], + }); + + public listOptionalClientScopes = this.makeRequest< + {id: string}, + ClientScopeRepresentation[] + >({ + method: 'GET', + path: '/{id}/optional-client-scopes', + urlParamKeys: ['id'], + }); + + public addOptionalClientScope = this.makeRequest< + {id: string; clientScopeId: string}, + void + >({ + method: 'PUT', + path: '/{id}/optional-client-scopes/{clientScopeId}', + urlParamKeys: ['id', 'clientScopeId'], + }); + + public delOptionalClientScope = this.makeRequest< + {id: string; clientScopeId: string}, + void + >({ + method: 'DELETE', + path: '/{id}/optional-client-scopes/{clientScopeId}', + urlParamKeys: ['id', 'clientScopeId'], + }); + + /** + * Protocol Mappers + */ + + public addMultipleProtocolMappers = this.makeUpdateRequest< + {id: string}, + ProtocolMapperRepresentation[], + void + >({ + method: 'POST', + path: '/{id}/protocol-mappers/add-models', + urlParamKeys: ['id'], + }); + + public addProtocolMapper = this.makeUpdateRequest< + {id: string}, + ProtocolMapperRepresentation, + void + >({ + method: 'POST', + path: '/{id}/protocol-mappers/models', + urlParamKeys: ['id'], + }); + + public listProtocolMappers = this.makeRequest< + {id: string}, + ProtocolMapperRepresentation[] + >({ + method: 'GET', + path: '/{id}/protocol-mappers/models', + urlParamKeys: ['id'], + }); + + public findProtocolMapperById = this.makeRequest< + {id: string; mapperId: string}, + ProtocolMapperRepresentation + >({ + method: 'GET', + path: '/{id}/protocol-mappers/models/{mapperId}', + urlParamKeys: ['id', 'mapperId'], + catchNotFound: true, + }); + + public findProtocolMappersByProtocol = this.makeRequest< + {id: string; protocol: string}, + ProtocolMapperRepresentation[] + >({ + method: 'GET', + path: '/{id}/protocol-mappers/protocol/{protocol}', + urlParamKeys: ['id', 'protocol'], + catchNotFound: true, + }); + + public updateProtocolMapper = this.makeUpdateRequest< + {id: string; mapperId: string}, + ProtocolMapperRepresentation, + void + >({ + method: 'PUT', + path: '/{id}/protocol-mappers/models/{mapperId}', + urlParamKeys: ['id', 'mapperId'], + }); + + public delProtocolMapper = this.makeRequest< + {id: string; mapperId: string}, + void + >({ + method: 'DELETE', + path: '/{id}/protocol-mappers/models/{mapperId}', + urlParamKeys: ['id', 'mapperId'], + }); + + /** + * Scope Mappings + */ + public listScopeMappings = this.makeRequest< + {id: string}, + MappingsRepresentation + >({ + method: 'GET', + path: '/{id}/scope-mappings', + urlParamKeys: ['id'], + }); + + public addClientScopeMappings = this.makeUpdateRequest< + {id: string; client: string}, + RoleRepresentation[], + void + >({ + method: 'POST', + path: '/{id}/scope-mappings/clients/{client}', + urlParamKeys: ['id', 'client'], + }); + + public listClientScopeMappings = this.makeRequest< + {id: string; client: string}, + RoleRepresentation[] + >({ + method: 'GET', + path: '/{id}/scope-mappings/clients/{client}', + urlParamKeys: ['id', 'client'], + }); + + public listAvailableClientScopeMappings = this.makeRequest< + {id: string; client: string}, + RoleRepresentation[] + >({ + method: 'GET', + path: '/{id}/scope-mappings/clients/{client}/available', + urlParamKeys: ['id', 'client'], + }); + + public listCompositeClientScopeMappings = this.makeRequest< + {id: string; client: string}, + RoleRepresentation[] + >({ + method: 'GET', + path: '/{id}/scope-mappings/clients/{client}/available', + urlParamKeys: ['id', 'client'], + }); + + public delClientScopeMappings = this.makeUpdateRequest< + {id: string; client: string}, + RoleRepresentation[], + void + >({ + method: 'DELETE', + path: '/{id}/scope-mappings/clients/{client}', + urlParamKeys: ['id', 'client'], + }); + + public addRealmScopeMappings = this.makeUpdateRequest< + {id: string}, + RoleRepresentation[], + void + >({ + method: 'POST', + path: '/{id}/scope-mappings/realm', + urlParamKeys: ['id', 'client'], + }); + + public listRealmScopeMappings = this.makeRequest< + {id: string}, + RoleRepresentation[] + >({ + method: 'GET', + path: '/{id}/scope-mappings/realm', + urlParamKeys: ['id'], + }); + + public listAvailableRealmScopeMappings = this.makeRequest< + {id: string}, + RoleRepresentation[] + >({ + method: 'GET', + path: '/{id}/scope-mappings/realm/available', + urlParamKeys: ['id'], + }); + + public listCompositeRealmScopeMappings = this.makeRequest< + {id: string}, + RoleRepresentation[] + >({ + method: 'GET', + path: '/{id}/scope-mappings/realm/available', + urlParamKeys: ['id'], + }); + + public delRealmScopeMappings = this.makeUpdateRequest< + {id: string}, + RoleRepresentation[], + void + >({ + method: 'DELETE', + path: '/{id}/scope-mappings/realm', + urlParamKeys: ['id'], + }); + constructor(client: KeycloakAdminClient) { super(client, { path: '/admin/realms/{realm}/clients', @@ -142,4 +380,22 @@ export class Clients extends Resource<{realm?: string}> { getBaseUrl: () => client.baseUrl, }); } + + /** + * Find single protocol mapper by name. + */ + public async findProtocolMapperByName(payload: { + realm?: string; + id: string; + name: string; + }): Promise { + const allProtocolMappers = await this.listProtocolMappers({ + id: payload.id, + ...(payload.realm ? {realm: payload.realm} : {}), + }); + const protocolMapper = allProtocolMappers.find( + mapper => mapper.name === payload.name, + ); + return protocolMapper ? protocolMapper : null; + } } diff --git a/test/clientScopes.spec.ts b/test/clientScopes.spec.ts new file mode 100644 index 00000000..60d2617d --- /dev/null +++ b/test/clientScopes.spec.ts @@ -0,0 +1,646 @@ +// tslint:disable:no-unused-expression +import * as chai from 'chai'; +import {KeycloakAdminClient} from '../src/client'; +import {credentials} from './constants'; +import ClientScopeRepresentation from '../src/defs/clientScopeRepresentation'; +import ProtocolMapperRepresentation from '../src/defs/protocolMapperRepresentation'; +import ClientRepresentation from '../src/defs/clientRepresentation'; + +const expect = chai.expect; + +declare module 'mocha' { + // tslint:disable-next-line:interface-name + interface ISuiteCallbackContext { + kcAdminClient?: KeycloakAdminClient; + currentClientScope?: ClientScopeRepresentation; + currentClientScopeName?: string; + currentClient?: ClientRepresentation; + } +} + +describe('Client Scopes', () => { + before(async () => { + this.kcAdminClient = new KeycloakAdminClient(); + await this.kcAdminClient.auth(credentials); + }); + + beforeEach(async () => { + this.currentClientScopeName = 'best-of-the-bests-scope'; + await this.kcAdminClient.clientScopes.create({ + name: this.currentClientScopeName, + }); + this.currentClientScope = await this.kcAdminClient.clientScopes.findOneByName( + { + name: this.currentClientScopeName, + }, + ); + }); + + afterEach(async () => { + // cleanup default client scopes + try { + await this.kcAdminClient.clientScopes.delDefaultClientScope({ + id: this.currentClientScope.id, + }); + } catch (e) { + // ignore + } + + // cleanup optional client scopes + try { + await this.kcAdminClient.clientScopes.delDefaultOptionalClientScope({ + id: this.currentClientScope.id, + }); + } catch (e) { + // ignore + } + + // cleanup client scopes + try { + await this.kcAdminClient.clientScopes.delByName({ + name: this.currentClientScopeName, + }); + } catch (e) { + // ignore + } + }); + + it('list client scopes', async () => { + const scopes = await this.kcAdminClient.clientScopes.find(); + expect(scopes).to.be.ok; + }); + + it('create client scope and get by name', async () => { + // ensure that the scope does not exist + try { + await this.kcAdminClient.clientScopes.delByName({ + name: this.currentClientScopeName, + }); + } catch (e) { + // ignore + } + + await this.kcAdminClient.clientScopes.create({ + name: this.currentClientScopeName, + }); + + const scope = await this.kcAdminClient.clientScopes.findOneByName({ + name: this.currentClientScopeName, + }); + expect(scope).to.be.ok; + expect(scope.name).to.equal(this.currentClientScopeName); + }); + + it('find scope by id', async () => { + const scope = await this.kcAdminClient.clientScopes.findOne({ + id: this.currentClientScope.id, + }); + expect(scope).to.be.ok; + expect(scope).to.eql(this.currentClientScope); + }); + + it('find scope by name', async () => { + const scope = await this.kcAdminClient.clientScopes.findOneByName({ + name: this.currentClientScopeName, + }); + expect(scope).to.be.ok; + expect(scope.name).to.eql(this.currentClientScopeName); + }); + + it('return null if scope not found by id', async () => { + const scope = await this.kcAdminClient.clientScopes.findOne({ + id: 'I do not exist', + }); + expect(scope).to.be.null; + }); + + it('return null if scope not found by name', async () => { + const scope = await this.kcAdminClient.clientScopes.findOneByName({ + name: 'I do not exist', + }); + expect(scope).to.be.null; + }); + + it('update client scope', async () => { + const {id, description: oldDescription} = this.currentClientScope; + const description = 'This scope is totally awesome.'; + + await this.kcAdminClient.clientScopes.update({id}, {description}); + const updatedScope = await this.kcAdminClient.clientScopes.findOne({ + id, + }); + expect(updatedScope).to.be.ok; + expect(updatedScope).not.to.eql(this.currentClientScope); + expect(updatedScope.description).to.eq(description); + expect(updatedScope.description).not.to.eq(oldDescription); + }); + + it('delete single client scope by id', async () => { + await this.kcAdminClient.clientScopes.del({ + id: this.currentClientScope.id, + }); + const scope = await this.kcAdminClient.clientScopes.findOne({ + id: this.currentClientScope.id, + }); + expect(scope).not.to.be.ok; + }); + + it('delete single client scope by name', async () => { + await this.kcAdminClient.clientScopes.delByName({ + name: this.currentClientScopeName, + }); + const scope = await this.kcAdminClient.clientScopes.findOneByName({ + name: this.currentClientScopeName, + }); + expect(scope).not.to.be.ok; + }); + + describe('default client scope', () => { + it('list default client scopes', async () => { + const defaultClientScopes = await this.kcAdminClient.clientScopes.listDefaultClientScopes(); + expect(defaultClientScopes).to.be.ok; + }); + + it('add default client scope', async () => { + const {id} = this.currentClientScope; + await this.kcAdminClient.clientScopes.addDefaultClientScope({id}); + + const defaultClientScopeList = await this.kcAdminClient.clientScopes.listDefaultClientScopes(); + const defaultClientScope = defaultClientScopeList.find( + scope => scope.id === id, + ); + + expect(defaultClientScope).to.be.ok; + expect(defaultClientScope.id).to.equal(this.currentClientScope.id); + expect(defaultClientScope.name).to.equal(this.currentClientScope.name); + }); + + it('delete default client scope', async () => { + const {id} = this.currentClientScope; + await this.kcAdminClient.clientScopes.addDefaultClientScope({id}); + + await this.kcAdminClient.clientScopes.delDefaultClientScope({id}); + + const defaultClientScopeList = await this.kcAdminClient.clientScopes.listDefaultClientScopes(); + const defaultClientScope = defaultClientScopeList.find( + scope => scope.id === id, + ); + + expect(defaultClientScope).not.to.be.ok; + }); + }); + + describe('default optional client scopes', () => { + it('list default optional client scopes', async () => { + const defaultOptionalClientScopes = await this.kcAdminClient.clientScopes.listDefaultOptionalClientScopes(); + expect(defaultOptionalClientScopes).to.be.ok; + }); + + it('add default optional client scope', async () => { + const {id} = this.currentClientScope; + await this.kcAdminClient.clientScopes.addDefaultOptionalClientScope({id}); + + const defaultOptionalClientScopeList = await this.kcAdminClient.clientScopes.listDefaultOptionalClientScopes(); + const defaultOptionalClientScope = defaultOptionalClientScopeList.find( + scope => scope.id === id, + ); + + expect(defaultOptionalClientScope).to.be.ok; + expect(defaultOptionalClientScope.id).to.eq(this.currentClientScope.id); + expect(defaultOptionalClientScope.name).to.eq( + this.currentClientScope.name, + ); + }); + + it('delete default optional client scope', async () => { + const {id} = this.currentClientScope; + await this.kcAdminClient.clientScopes.addDefaultOptionalClientScope({id}); + await this.kcAdminClient.clientScopes.delDefaultOptionalClientScope({id}); + + const defaultOptionalClientScopeList = await this.kcAdminClient.clientScopes.listDefaultOptionalClientScopes(); + const defaultOptionalClientScope = defaultOptionalClientScopeList.find( + scope => scope.id === id, + ); + + expect(defaultOptionalClientScope).not.to.be.ok; + }); + }); + + describe('protocol mappers', () => { + let dummyMapper: ProtocolMapperRepresentation; + + beforeEach(() => { + dummyMapper = { + name: 'mapping-maps-mapper', + protocol: 'openid-connect', + protocolMapper: 'oidc-audience-mapper', + }; + }); + + afterEach(async () => { + try { + const {id} = this.currentClientScope; + const { + id: mapperId, + } = await this.kcAdminClient.clientScopes.findProtocolMapperByName({ + id, + name: dummyMapper.name, + }); + await this.kcAdminClient.clientScopes.delProtocolMapper({ + id, + mapperId, + }); + } catch (e) { + // ignore + } + }); + + it('list protocol mappers', async () => { + const {id} = this.currentClientScope; + const mapperList = await this.kcAdminClient.clientScopes.listProtocolMappers( + {id}, + ); + expect(mapperList).to.be.ok; + }); + + it('add multiple protocol mappers', async () => { + const {id} = this.currentClientScope; + await this.kcAdminClient.clientScopes.addMultipleProtocolMappers({id}, [ + dummyMapper, + ]); + + const mapper = await this.kcAdminClient.clientScopes.findProtocolMapperByName( + {id, name: dummyMapper.name}, + ); + expect(mapper).to.be.ok; + expect(mapper.protocol).to.eq(dummyMapper.protocol); + expect(mapper.protocolMapper).to.eq(dummyMapper.protocolMapper); + }); + + it('add single protocol mapper', async () => { + const {id} = this.currentClientScope; + await this.kcAdminClient.clientScopes.addProtocolMapper( + {id}, + dummyMapper, + ); + + const mapper = await this.kcAdminClient.clientScopes.findProtocolMapperByName( + {id, name: dummyMapper.name}, + ); + expect(mapper).to.be.ok; + expect(mapper.protocol).to.eq(dummyMapper.protocol); + expect(mapper.protocolMapper).to.eq(dummyMapper.protocolMapper); + }); + + it('find protocol mapper by id', async () => { + const {id} = this.currentClientScope; + await this.kcAdminClient.clientScopes.addProtocolMapper( + {id}, + dummyMapper, + ); + + const { + id: mapperId, + } = await this.kcAdminClient.clientScopes.findProtocolMapperByName({ + id, + name: dummyMapper.name, + }); + + const mapper = await this.kcAdminClient.clientScopes.findProtocolMapper({ + id, + mapperId, + }); + + expect(mapper).to.be.ok; + expect(mapper.id).to.eql(mapperId); + }); + + it('find protocol mapper by name', async () => { + const {id} = this.currentClientScope; + await this.kcAdminClient.clientScopes.addProtocolMapper( + {id}, + dummyMapper, + ); + + const mapper = await this.kcAdminClient.clientScopes.findProtocolMapperByName( + {id, name: dummyMapper.name}, + ); + + expect(mapper).to.be.ok; + expect(mapper.name).to.eql(dummyMapper.name); + }); + + it('find protocol mappers by protocol', async () => { + const {id} = this.currentClientScope; + await this.kcAdminClient.clientScopes.addProtocolMapper( + {id}, + dummyMapper, + ); + + const mapperList = await this.kcAdminClient.clientScopes.findProtocolMappersByProtocol( + {id, protocol: dummyMapper.protocol}, + ); + + expect(mapperList).to.be.ok; + expect(mapperList.length).to.be.gte(1); + + const mapper = mapperList.find(item => item.name === dummyMapper.name); + expect(mapper).to.be.ok; + }); + + it('update protocol mapper', async () => { + const {id} = this.currentClientScope; + + dummyMapper.config = {'access.token.claim': 'true'}; + await this.kcAdminClient.clientScopes.addProtocolMapper( + {id}, + dummyMapper, + ); + const mapper = await this.kcAdminClient.clientScopes.findProtocolMapperByName( + {id, name: dummyMapper.name}, + ); + + expect(mapper.config['access.token.claim']).to.eq('true'); + + mapper.config = {'access.token.claim': 'false'}; + + await this.kcAdminClient.clientScopes.updateProtocolMapper( + {id, mapperId: mapper.id}, + mapper, + ); + + const updatedMapper = await this.kcAdminClient.clientScopes.findProtocolMapperByName( + {id, name: dummyMapper.name}, + ); + + expect(updatedMapper.config['access.token.claim']).to.eq('false'); + }); + + it('delete protocol mapper', async () => { + const {id} = this.currentClientScope; + await this.kcAdminClient.clientScopes.addProtocolMapper( + {id}, + dummyMapper, + ); + + const { + id: mapperId, + } = await this.kcAdminClient.clientScopes.findProtocolMapperByName({ + id, + name: dummyMapper.name, + }); + + await this.kcAdminClient.clientScopes.delProtocolMapper({id, mapperId}); + + const mapper = await this.kcAdminClient.clientScopes.findProtocolMapperByName( + {id, name: dummyMapper.name}, + ); + + expect(mapper).not.to.be.ok; + }); + }); + + describe('scope mappings', () => { + it('list client and realm scope mappings', async () => { + const {id} = this.currentClientScope; + const scopes = await this.kcAdminClient.clientScopes.listScopeMappings({ + id, + }); + expect(scopes).to.be.ok; + }); + + describe('client', () => { + const dummyClientId = 'scopeMappings-dummy'; + const dummyRoleName = 'scopeMappingsRole-dummy'; + + beforeEach(async () => { + const {id} = await this.kcAdminClient.clients.create({ + clientId: dummyClientId, + }); + this.currentClient = await this.kcAdminClient.clients.findOne({ + id, + }); + + await this.kcAdminClient.clients.createRole({ + id, + name: dummyRoleName, + }); + }); + + afterEach(async () => { + try { + const {id} = this.currentClient; + await this.kcAdminClient.clients.delRole({ + id, + roleName: dummyRoleName, + }); + } catch (e) { + // ignore + } + try { + const {id} = this.currentClient; + await this.kcAdminClient.clients.del({id}); + } catch (e) { + // ignore + console.log(e); + } + }); + + it('add scope mappings', async () => { + const {id} = this.currentClientScope; + const {id: clientUniqueId} = this.currentClient; + + const availableRoles = await this.kcAdminClient.clientScopes.listAvailableClientScopeMappings( + { + id, + client: clientUniqueId, + }, + ); + + const filteredRoles = availableRoles.filter(role => !role.composite); + + await this.kcAdminClient.clientScopes.addClientScopeMappings( + { + id, + client: clientUniqueId, + }, + filteredRoles, + ); + + const roles = await this.kcAdminClient.clientScopes.listClientScopeMappings( + { + id, + client: clientUniqueId, + }, + ); + + expect(roles).to.be.ok; + expect(roles).to.be.eql(filteredRoles); + }); + + it('list scope mappings', async () => { + const {id} = this.currentClientScope; + const {id: clientUniqueId} = this.currentClient; + const roles = await this.kcAdminClient.clientScopes.listClientScopeMappings( + { + id, + client: clientUniqueId, + }, + ); + expect(roles).to.be.ok; + }); + + it('list available scope mappings', async () => { + const {id} = this.currentClientScope; + const {id: clientUniqueId} = this.currentClient; + const roles = await this.kcAdminClient.clientScopes.listAvailableClientScopeMappings( + { + id, + client: clientUniqueId, + }, + ); + expect(roles).to.be.ok; + }); + + it('list composite scope mappings', async () => { + const {id} = this.currentClientScope; + const {id: clientUniqueId} = this.currentClient; + const roles = await this.kcAdminClient.clientScopes.listCompositeClientScopeMappings( + { + id, + client: clientUniqueId, + }, + ); + expect(roles).to.be.ok; + }); + + it('delete scope mappings', async () => { + const {id} = this.currentClientScope; + const {id: clientUniqueId} = this.currentClient; + + const rolesBefore = await this.kcAdminClient.clientScopes.listClientScopeMappings( + { + id, + client: clientUniqueId, + }, + ); + + await this.kcAdminClient.clientScopes.delClientScopeMappings( + { + id, + client: clientUniqueId, + }, + rolesBefore, + ); + + const rolesAfter = await this.kcAdminClient.clientScopes.listClientScopeMappings( + { + id, + client: clientUniqueId, + }, + ); + + expect(rolesAfter).to.be.ok; + expect(rolesAfter).to.eql([]); + }); + }); + + describe('realm', () => { + const dummyRoleName = 'realmScopeMappingsRole-dummy'; + + beforeEach(async () => { + await this.kcAdminClient.roles.create({ + name: dummyRoleName, + }); + }); + + afterEach(async () => { + try { + await this.kcAdminClient.roles.delByName({ + name: dummyRoleName, + }); + } catch (e) { + // ignore + } + }); + + it('add scope mappings', async () => { + const {id} = this.currentClientScope; + + const availableRoles = await this.kcAdminClient.clientScopes.listAvailableRealmScopeMappings( + {id}, + ); + + const filteredRoles = availableRoles.filter(role => !role.composite); + + await this.kcAdminClient.clientScopes.addRealmScopeMappings( + {id}, + filteredRoles, + ); + + const roles = await this.kcAdminClient.clientScopes.listRealmScopeMappings( + {id}, + ); + + expect(roles).to.be.ok; + expect(roles).to.include.deep.members(filteredRoles); + }); + + it('list scope mappings', async () => { + const {id} = this.currentClientScope; + const roles = await this.kcAdminClient.clientScopes.listRealmScopeMappings( + { + id, + }, + ); + expect(roles).to.be.ok; + }); + + it('list available scope mappings', async () => { + const {id} = this.currentClientScope; + const roles = await this.kcAdminClient.clientScopes.listAvailableRealmScopeMappings( + { + id, + }, + ); + expect(roles).to.be.ok; + }); + + it('list composite scope mappings', async () => { + const {id} = this.currentClientScope; + const roles = await this.kcAdminClient.clientScopes.listCompositeRealmScopeMappings( + { + id, + }, + ); + expect(roles).to.be.ok; + }); + + it('delete scope mappings', async () => { + const {id} = this.currentClientScope; + + const rolesBefore = await this.kcAdminClient.clientScopes.listRealmScopeMappings( + { + id, + }, + ); + + await this.kcAdminClient.clientScopes.delRealmScopeMappings( + { + id, + }, + rolesBefore, + ); + + const rolesAfter = await this.kcAdminClient.clientScopes.listRealmScopeMappings( + { + id, + }, + ); + + expect(rolesAfter).to.be.ok; + expect(rolesAfter).to.eql([]); + }); + }); + }); +}); diff --git a/test/clients.spec.ts b/test/clients.spec.ts index 6f2fcc09..0ccb23c8 100644 --- a/test/clients.spec.ts +++ b/test/clients.spec.ts @@ -4,7 +4,8 @@ import {KeycloakAdminClient} from '../src/client'; import {credentials} from './constants'; import faker from 'faker'; import ClientRepresentation from '../src/defs/clientRepresentation'; -import BPromise from 'bluebird'; +import ProtocolMapperRepresentation from '../src/defs/protocolMapperRepresentation'; +import ClientScopeRepresentation from '../src/defs/clientScopeRepresentation'; const expect = chai.expect; declare module 'mocha' { @@ -12,6 +13,7 @@ declare module 'mocha' { interface ISuiteCallbackContext { kcAdminClient?: KeycloakAdminClient; currentClient?: ClientRepresentation; + currentClientScope?: ClientScopeRepresentation; currentRoleName?: string; } } @@ -240,4 +242,566 @@ describe('Clients', function() { expect(serviceAccountUser).to.be.ok; }); }); + + describe('default client scopes', () => { + let dummyClientScope; + + beforeEach(async () => { + dummyClientScope = { + name: 'does-anyone-read-this', + description: 'Oh - seems like you are reading this. Hey there!', + protocol: 'openid-connect', + }; + + // setup dummy client scope + await this.kcAdminClient.clientScopes.create(dummyClientScope); + this.currentClientScope = await this.kcAdminClient.clientScopes.findOneByName( + {name: dummyClientScope.name}, + ); + }); + + afterEach(async () => { + // cleanup default scopes + try { + const {id} = this.currentClient; + const {id: clientScopeId} = this.currentClientScope; + await this.kcAdminClient.clients.delDefaultClientScope({ + clientScopeId, + id, + }); + } catch (e) { + // ignore + } + + // cleanup client scopes + try { + await this.kcAdminClient.clientScopes.delByName({ + name: dummyClientScope.name, + }); + } catch (e) { + // ignore + } + }); + + it('list default client scopes', async () => { + const defaultClientScopes = await this.kcAdminClient.clients.listDefaultClientScopes( + {id: this.currentClient.id}, + ); + + expect(defaultClientScopes).to.be.ok; + }); + + it('add default client scope', async () => { + const {id} = this.currentClient; + const {id: clientScopeId} = this.currentClientScope; + + await this.kcAdminClient.clients.addDefaultClientScope({ + id, + clientScopeId, + }); + + const defaultScopes = await this.kcAdminClient.clients.listDefaultClientScopes( + {id}, + ); + + expect(defaultScopes).to.be.ok; + + const clientScope = defaultScopes.find( + scope => scope.id === clientScopeId, + ); + expect(clientScope).to.be.ok; + }); + + it('delete default client scope', async () => { + const {id} = this.currentClient; + const {id: clientScopeId} = this.currentClientScope; + + await this.kcAdminClient.clients.addDefaultClientScope({ + id, + clientScopeId, + }); + + await this.kcAdminClient.clients.delDefaultClientScope({ + id, + clientScopeId, + }); + const defaultScopes = await this.kcAdminClient.clients.listDefaultClientScopes( + {id}, + ); + + const clientScope = defaultScopes.find( + scope => scope.id === clientScopeId, + ); + expect(clientScope).not.to.be.ok; + }); + }); + + describe('optional client scopes', () => { + let dummyClientScope; + + beforeEach(async () => { + dummyClientScope = { + name: 'i-hope-your-well', + description: 'Everyone has that one friend.', + protocol: 'openid-connect', + }; + + // setup dummy client scope + await this.kcAdminClient.clientScopes.create(dummyClientScope); + this.currentClientScope = await this.kcAdminClient.clientScopes.findOneByName( + {name: dummyClientScope.name}, + ); + }); + + afterEach(async () => { + // cleanup optional scopes + try { + const {id} = this.currentClient; + const {id: clientScopeId} = this.currentClientScope; + await this.kcAdminClient.clients.delOptionalClientScope({ + clientScopeId, + id, + }); + } catch (e) { + // ignore + } + + // cleanup client scopes + try { + await this.kcAdminClient.clientScopes.delByName({ + name: dummyClientScope.name, + }); + } catch (e) { + // ignore + } + }); + + it('list optional client scopes', async () => { + const optionalClientScopes = await this.kcAdminClient.clients.listOptionalClientScopes( + {id: this.currentClient.id}, + ); + + expect(optionalClientScopes).to.be.ok; + }); + + it('add optional client scope', async () => { + const {id} = this.currentClient; + const {id: clientScopeId} = this.currentClientScope; + + await this.kcAdminClient.clients.addOptionalClientScope({ + id, + clientScopeId, + }); + + const optionalScopes = await this.kcAdminClient.clients.listOptionalClientScopes( + {id}, + ); + + expect(optionalScopes).to.be.ok; + + const clientScope = optionalScopes.find( + scope => scope.id === clientScopeId, + ); + expect(clientScope).to.be.ok; + }); + + it('delete optional client scope', async () => { + const {id} = this.currentClient; + const {id: clientScopeId} = this.currentClientScope; + + await this.kcAdminClient.clients.addOptionalClientScope({ + id, + clientScopeId, + }); + + await this.kcAdminClient.clients.delOptionalClientScope({ + id, + clientScopeId, + }); + const optionalScopes = await this.kcAdminClient.clients.listOptionalClientScopes( + {id}, + ); + + const clientScope = optionalScopes.find( + scope => scope.id === clientScopeId, + ); + expect(clientScope).not.to.be.ok; + }); + }); + + describe('protocol mappers', () => { + let dummyMapper: ProtocolMapperRepresentation; + + beforeEach(() => { + dummyMapper = { + name: 'become-a-farmer', + protocol: 'openid-connect', + protocolMapper: 'oidc-role-name-mapper', + config: { + role: 'admin', + 'new.role.name': 'farmer', + }, + }; + }); + + afterEach(async () => { + try { + const {id: clientUniqueId} = this.currentClient; + const { + id: mapperId, + } = await this.kcAdminClient.clients.findProtocolMapperByName({ + id: clientUniqueId, + name: dummyMapper.name, + }); + await this.kcAdminClient.clients.delProtocolMapper({ + id: clientUniqueId, + mapperId, + }); + } catch (e) { + // ignore + } + }); + + it('list protocol mappers', async () => { + const {id} = this.currentClient; + const mapperList = await this.kcAdminClient.clients.listProtocolMappers({ + id, + }); + expect(mapperList).to.be.ok; + }); + + it('add multiple protocol mappers', async () => { + const {id} = this.currentClient; + await this.kcAdminClient.clients.addMultipleProtocolMappers({id}, [ + dummyMapper, + ]); + + const mapper = await this.kcAdminClient.clients.findProtocolMapperByName({ + id, + name: dummyMapper.name, + }); + expect(mapper).to.be.ok; + expect(mapper.protocol).to.eq(dummyMapper.protocol); + expect(mapper.protocolMapper).to.eq(dummyMapper.protocolMapper); + }); + + it('add single protocol mapper', async () => { + const {id} = this.currentClient; + await this.kcAdminClient.clients.addProtocolMapper({id}, dummyMapper); + + const mapper = await this.kcAdminClient.clients.findProtocolMapperByName({ + id, + name: dummyMapper.name, + }); + expect(mapper).to.be.ok; + expect(mapper.protocol).to.eq(dummyMapper.protocol); + expect(mapper.protocolMapper).to.eq(dummyMapper.protocolMapper); + }); + + it('find protocol mapper by id', async () => { + const {id} = this.currentClient; + await this.kcAdminClient.clients.addProtocolMapper({id}, dummyMapper); + + const { + id: mapperId, + } = await this.kcAdminClient.clients.findProtocolMapperByName({ + id, + name: dummyMapper.name, + }); + + const mapper = await this.kcAdminClient.clients.findProtocolMapperById({ + mapperId, + id, + }); + + expect(mapper).to.be.ok; + expect(mapper.id).to.eql(mapperId); + }); + + it('find protocol mapper by name', async () => { + const {id} = this.currentClient; + await this.kcAdminClient.clients.addProtocolMapper({id}, dummyMapper); + + const mapper = await this.kcAdminClient.clients.findProtocolMapperByName({ + id, + name: dummyMapper.name, + }); + + expect(mapper).to.be.ok; + expect(mapper.name).to.eql(dummyMapper.name); + }); + + it('find protocol mappers by protocol', async () => { + const {id} = this.currentClient; + await this.kcAdminClient.clients.addProtocolMapper({id}, dummyMapper); + + const mapperList = await this.kcAdminClient.clients.findProtocolMappersByProtocol( + { + id, + protocol: dummyMapper.protocol, + }, + ); + + expect(mapperList).to.be.ok; + expect(mapperList.length).to.be.gte(1); + + const mapper = mapperList.find(item => item.name === dummyMapper.name); + expect(mapper).to.be.ok; + }); + + it('update protocol mapper', async () => { + const {id} = this.currentClient; + + dummyMapper.config = {'access.token.claim': 'true'}; + await this.kcAdminClient.clients.addProtocolMapper({id}, dummyMapper); + const mapper = await this.kcAdminClient.clients.findProtocolMapperByName({ + id, + name: dummyMapper.name, + }); + + expect(mapper.config['access.token.claim']).to.eq('true'); + + mapper.config = {'access.token.claim': 'false'}; + + await this.kcAdminClient.clients.updateProtocolMapper( + {id, mapperId: mapper.id}, + mapper, + ); + + const updatedMapper = await this.kcAdminClient.clients.findProtocolMapperByName( + { + id, + name: dummyMapper.name, + }, + ); + + expect(updatedMapper.config['access.token.claim']).to.eq('false'); + }); + + it('delete protocol mapper', async () => { + const {id} = this.currentClient; + await this.kcAdminClient.clients.addProtocolMapper({id}, dummyMapper); + + const { + id: mapperId, + } = await this.kcAdminClient.clients.findProtocolMapperByName({ + id, + name: dummyMapper.name, + }); + + await this.kcAdminClient.clients.delProtocolMapper({id, mapperId}); + + const mapper = await this.kcAdminClient.clients.findProtocolMapperByName({ + id, + name: dummyMapper.name, + }); + + expect(mapper).not.to.be.ok; + }); + }); + + describe('scope mappings', () => { + it('list client and realm scope mappings', async () => { + const {id} = this.currentClient; + const scopes = await this.kcAdminClient.clients.listScopeMappings({ + id, + }); + expect(scopes).to.be.ok; + }); + + describe('client', () => { + const dummyRoleName = 'clientScopeMappingsRole-dummy'; + + beforeEach(async () => { + const {id} = this.currentClient; + await this.kcAdminClient.clients.createRole({ + id, + name: dummyRoleName, + }); + }); + + afterEach(async () => { + try { + const {id} = this.currentClient; + await this.kcAdminClient.clients.delRole({ + id, + roleName: dummyRoleName, + }); + } catch (e) { + // ignore + } + }); + + it('add scope mappings', async () => { + const {id: clientUniqueId} = this.currentClient; + + const availableRoles = await this.kcAdminClient.clients.listAvailableClientScopeMappings( + { + id: clientUniqueId, + client: clientUniqueId, + }, + ); + + await this.kcAdminClient.clients.addClientScopeMappings( + { + id: clientUniqueId, + client: clientUniqueId, + }, + availableRoles, + ); + + const roles = await this.kcAdminClient.clients.listClientScopeMappings({ + id: clientUniqueId, + client: clientUniqueId, + }); + + expect(roles).to.be.ok; + expect(roles).to.be.eql(availableRoles); + }); + + it('list scope mappings', async () => { + const {id: clientUniqueId} = this.currentClient; + const roles = await this.kcAdminClient.clients.listClientScopeMappings({ + id: clientUniqueId, + client: clientUniqueId, + }); + expect(roles).to.be.ok; + }); + + it('list available scope mappings', async () => { + const {id: clientUniqueId} = this.currentClient; + const roles = await this.kcAdminClient.clients.listAvailableClientScopeMappings( + { + id: clientUniqueId, + client: clientUniqueId, + }, + ); + expect(roles).to.be.ok; + }); + + it('list composite scope mappings', async () => { + const {id: clientUniqueId} = this.currentClient; + const roles = await this.kcAdminClient.clients.listCompositeClientScopeMappings( + { + id: clientUniqueId, + client: clientUniqueId, + }, + ); + expect(roles).to.be.ok; + }); + + it('delete scope mappings', async () => { + const {id: clientUniqueId} = this.currentClient; + + const rolesBefore = await this.kcAdminClient.clients.listClientScopeMappings( + { + id: clientUniqueId, + client: clientUniqueId, + }, + ); + + await this.kcAdminClient.clients.delClientScopeMappings( + { + id: clientUniqueId, + client: clientUniqueId, + }, + rolesBefore, + ); + + const rolesAfter = await this.kcAdminClient.clients.listClientScopeMappings( + { + id: clientUniqueId, + client: clientUniqueId, + }, + ); + + expect(rolesAfter).to.be.ok; + expect(rolesAfter).to.eql([]); + }); + }); + + describe('realm', () => { + const dummyRoleName = 'realmScopeMappingsRole-dummy'; + + beforeEach(async () => { + await this.kcAdminClient.roles.create({ + name: dummyRoleName, + }); + }); + + afterEach(async () => { + try { + await this.kcAdminClient.roles.delByName({ + name: dummyRoleName, + }); + } catch (e) { + // ignore + } + }); + + it('add scope mappings', async () => { + const {id} = this.currentClient; + + const availableRoles = await this.kcAdminClient.clients.listAvailableRealmScopeMappings( + {id}, + ); + + await this.kcAdminClient.clients.addRealmScopeMappings( + {id}, + availableRoles, + ); + + const roles = await this.kcAdminClient.clients.listRealmScopeMappings({ + id, + }); + + expect(roles).to.be.ok; + expect(roles).to.deep.members(availableRoles); + }); + + it('list scope mappings', async () => { + const {id} = this.currentClient; + const roles = await this.kcAdminClient.clients.listRealmScopeMappings({ + id, + }); + expect(roles).to.be.ok; + }); + + it('list available scope mappings', async () => { + const {id} = this.currentClient; + const roles = await this.kcAdminClient.clients.listAvailableRealmScopeMappings( + {id}, + ); + expect(roles).to.be.ok; + }); + + it('list composite scope mappings', async () => { + const {id} = this.currentClient; + const roles = await this.kcAdminClient.clients.listCompositeRealmScopeMappings( + {id}, + ); + expect(roles).to.be.ok; + }); + + it('delete scope mappings', async () => { + const {id} = this.currentClient; + + const rolesBefore = await this.kcAdminClient.clients.listRealmScopeMappings( + {id}, + ); + + await this.kcAdminClient.clients.delRealmScopeMappings( + {id}, + rolesBefore, + ); + + const rolesAfter = await this.kcAdminClient.clients.listRealmScopeMappings( + {id}, + ); + + expect(rolesAfter).to.be.ok; + expect(rolesAfter).to.eql([]); + }); + }); + }); }); diff --git a/test/groupUser.spec.ts b/test/groupUser.spec.ts index 698b8f89..18fe1ece 100644 --- a/test/groupUser.spec.ts +++ b/test/groupUser.spec.ts @@ -48,7 +48,7 @@ describe('Group user integration', function() { }); }); - it('should list user\'s group and expect empty', async () => { + it("should list user's group and expect empty", async () => { const groups = await this.kcAdminClient.users.listGroups({ id: this.currentUser.id, }); diff --git a/test/users.spec.ts b/test/users.spec.ts index 0a64414f..a3a26007 100644 --- a/test/users.spec.ts +++ b/test/users.spec.ts @@ -472,7 +472,7 @@ describe('Users', function () { }); }); - it('should list user\'s federated identities and expect empty', async () => { + it("should list user's federated identities and expect empty", async () => { const federatedIdentities = await this.kcAdminClient.users.listFederatedIdentities( { id: this.currentUser.id,