From cccb2faa553a4fdff29018aff3749d3e866d3561 Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Mon, 15 Feb 2021 09:37:11 +0100 Subject: [PATCH] create endpoint for user storage provider (#138) see: https://www.keycloak.org/docs-api/11.0/rest-api/#_user_storage_provider_resource --- src/client.ts | 3 + .../synchronizationResultRepresentation.ts | 12 ++++ src/resources/userStorageProvider.ts | 60 +++++++++++++++++++ test/userStorageProvider.spec.ts | 52 ++++++++++++++++ 4 files changed, 127 insertions(+) create mode 100644 src/defs/synchronizationResultRepresentation.ts create mode 100644 src/resources/userStorageProvider.ts create mode 100644 test/userStorageProvider.spec.ts diff --git a/src/client.ts b/src/client.ts index 5e4ee8df..aa425b47 100644 --- a/src/client.ts +++ b/src/client.ts @@ -20,6 +20,7 @@ import Keycloak, { KeycloakInstance, } from 'keycloak-js'; import {Sessions} from './resources/sessions'; +import {UserStorageProvider} from './resources/userStorageProvider'; export interface ConnectionConfig { baseUrl?: string; @@ -30,6 +31,7 @@ export interface ConnectionConfig { export class KeycloakAdminClient { // Resources public users: Users; + public userStorageProvider: UserStorageProvider; public groups: Groups; public roles: Roles; public clients: Clients; @@ -61,6 +63,7 @@ export class KeycloakAdminClient { // Initialize resources this.users = new Users(this); + this.userStorageProvider = new UserStorageProvider(this); this.groups = new Groups(this); this.roles = new Roles(this); this.clients = new Clients(this); diff --git a/src/defs/synchronizationResultRepresentation.ts b/src/defs/synchronizationResultRepresentation.ts new file mode 100644 index 00000000..6efb54fa --- /dev/null +++ b/src/defs/synchronizationResultRepresentation.ts @@ -0,0 +1,12 @@ +/** + * https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_synchronizationresult + */ + +export default interface SynchronizationResultRepresentation { + ignored?: boolean; + added?: number; + updated?: number; + removed?: number; + failed?: number; + status?: string; +} diff --git a/src/resources/userStorageProvider.ts b/src/resources/userStorageProvider.ts new file mode 100644 index 00000000..ad443332 --- /dev/null +++ b/src/resources/userStorageProvider.ts @@ -0,0 +1,60 @@ +import {KeycloakAdminClient} from '../client'; +import SynchronizationResultRepresentation from '../defs/synchronizationResultRepresentation'; +import Resource from './resource'; + +type ActionType = 'triggerFullSync' | 'triggerChangedUsersSync'; +type DirectionType = 'fedToKeycloak' | 'keycloakToFed'; +type NameResponse = { + id: string; + name: string; +}; + +export class UserStorageProvider extends Resource<{realm?: string}> { + public name = this.makeRequest<{id: string}, NameResponse>({ + method: 'GET', + path: '/{id}/name', + urlParamKeys: ['id'], + }); + + public removeImportedUsers = this.makeRequest<{id: string}, void>({ + method: 'POST', + path: '/{id}/remove-imported-users', + urlParamKeys: ['id'], + }); + + public sync = this.makeRequest< + {id: string; action?: ActionType}, + SynchronizationResultRepresentation + >({ + method: 'POST', + path: '/{id}/sync', + urlParamKeys: ['id'], + queryParamKeys: ['action'], + }); + + public unlinkUsers = this.makeRequest<{id: string}, void>({ + method: 'POST', + path: '/{id}/unlink-users', + urlParamKeys: ['id'], + }); + + public mappersSync = this.makeRequest< + {id: string; parentId: string; direction?: DirectionType}, + SynchronizationResultRepresentation + >({ + method: 'POST', + path: '/{parentId}/mappers/{id}/sync', + urlParamKeys: ['id', 'parentId'], + queryParamKeys: ['direction'], + }); + + constructor(client: KeycloakAdminClient) { + super(client, { + path: '/admin/realms/{realm}/user-storage', + getUrlParams: () => ({ + realm: client.realmName, + }), + getBaseUrl: () => client.baseUrl, + }); + } +} diff --git a/test/userStorageProvider.spec.ts b/test/userStorageProvider.spec.ts new file mode 100644 index 00000000..f39989ad --- /dev/null +++ b/test/userStorageProvider.spec.ts @@ -0,0 +1,52 @@ +// tslint:disable:no-unused-expression +import * as chai from 'chai'; +import {KeycloakAdminClient} from '../src/client'; +import {credentials} from './constants'; +import faker from 'faker'; + +import ComponentRepresentation from '../src/defs/componentRepresentation'; + +const expect = chai.expect; + +describe('Users federation provider', () => { + let kcAdminClient: KeycloakAdminClient; + let currentUserFed: ComponentRepresentation; + + before(async () => { + kcAdminClient = new KeycloakAdminClient(); + await kcAdminClient.auth(credentials); + + const name = faker.internet.userName(); + currentUserFed = await kcAdminClient.components.create({ + name, + parentId: 'master', + providerId: 'ldap', + providerType: 'org.keycloak.storage.UserStorageProvider', + }); + }); + + after(async () => { + await kcAdminClient.components.del({ + id: currentUserFed.id, + }); + }); + + it('list storage provider', async () => { + const name = await kcAdminClient.userStorageProvider.name({ + id: currentUserFed.id, + }); + expect(name).to.be.ok; + }); + + it('remove imported users', async () => { + await kcAdminClient.userStorageProvider.removeImportedUsers({ + id: currentUserFed.id, + }); + }); + + it('unlink users', async () => { + await kcAdminClient.userStorageProvider.unlinkUsers({ + id: currentUserFed.id, + }); + }); +});