Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
vinh-thieu committed Apr 7, 2021
2 parents a8c5c7e + 73df58f commit 4b3d51f
Show file tree
Hide file tree
Showing 23 changed files with 444 additions and 107 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,15 @@ setInterval(async () => {
}, 58 * 1000); // 58 seconds
```

In cases where you don't have a refresh token, eg. in a client credentials flow, you can simply call `kcAdminClient.auth` to get a new access token, like this:

```js
const credentials = { grantType: 'client_credentials', clientId: 'clientId', clientSecret: 'some-client-secret-uuid' };
await kcAdminClient.auth(credentials);

setInterval(() => kcAdminClient.auth(credentials), 58 * 1000); // 58 seconds
```

## Supported APIs

### [Realm admin](https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_realms_admin_resource)
Expand All @@ -102,6 +111,7 @@ Demo code: https://github.com/keycloak/keycloak-nodejs-admin-client/blob/master/
- Get the top-level representation of the realm (`GET /{realm}`)
- Update the top-level information of the realm (`PUT /{realm}`)
- Delete the realm (`DELETE /{realm}`)
- Partial export of existing realm into a JSON file (`POST /{realm}/partial-export`)
- Get users management permissions (`GET /{realm}/users-management-permissions`)
- Enable users management permissions (`PUT /{realm}/users-management-permissions`)
- Get events (`GET /{realm}/events`)
Expand Down Expand Up @@ -150,7 +160,9 @@ Demo code: https://github.com/keycloak/keycloak-nodejs-admin-client/blob/master/
- Send an email-verification email to the user An email contains a link the user can click to verify their email address. (`PUT /{realm}/users/{id}/send-verify-email`)

### User group-mapping

Demo code: https://github.com/keycloak/keycloak-nodejs-admin-client/blob/master/test/users.spec.ts#L178

- Add user to group (`PUT /{id}/groups/{groupId}`)
- List all user groups (`GET /{id}/groups`)
- Count user groups (`GET /{id}/groups/count`)
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@keycloak/keycloak-admin-client",
"version": "1.14.5",
"version": "1.14.9",
"description": "keycloak admin client",
"main": "lib/index.js",
"files": [
Expand Down
26 changes: 15 additions & 11 deletions src/client.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {getToken, Credentials} from './utils/auth';
import {defaultBaseUrl, defaultRealm} from './utils/constants';
import {Cache} from './resources/cache';
import {Users} from './resources/users';
import {Groups} from './resources/groups';
import {Roles} from './resources/roles';
Expand All @@ -13,13 +14,9 @@ import {ServerInfo} from './resources/serverInfo';
import {WhoAmI} from './resources/whoAmI';
import {AttackDetection} from './resources/attackDetection';
import {AxiosRequestConfig} from 'axios';
import './utils/window-polyfill';
import Keycloak, {
KeycloakConfig,
KeycloakInitOptions,
KeycloakInstance,
} from 'keycloak-js';

import {Sessions} from './resources/sessions';
import {UserStorageProvider} from './resources/userStorageProvider';

export interface ConnectionConfig {
baseUrl?: string;
Expand All @@ -30,6 +27,7 @@ export interface ConnectionConfig {
export class KeycloakAdminClient {
// Resources
public users: Users;
public userStorageProvider: UserStorageProvider;
public groups: Groups;
public roles: Roles;
public clients: Clients;
Expand All @@ -42,13 +40,14 @@ export class KeycloakAdminClient {
public attackDetection: AttackDetection;
public sessions: Sessions;
public authenticationManagement: AuthenticationManagement;
public cache: Cache;

// Members
public baseUrl: string;
public realmName: string;
public accessToken: string;
public refreshToken: string;
public keycloak: KeycloakInstance;
public keycloak: any;

private requestConfig?: AxiosRequestConfig;

Expand All @@ -61,6 +60,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);
Expand All @@ -73,6 +73,7 @@ export class KeycloakAdminClient {
this.whoAmI = new WhoAmI(this);
this.sessions = new Sessions(this);
this.attackDetection = new AttackDetection(this);
this.cache = new Cache(this);
}

public async auth(credentials: Credentials) {
Expand All @@ -86,10 +87,13 @@ export class KeycloakAdminClient {
this.refreshToken = refreshToken;
}

public async init(init?: KeycloakInitOptions, config?: KeycloakConfig) {
this.keycloak = Keycloak(config);
await this.keycloak.init(init);
this.baseUrl = this.keycloak.authServerUrl;
public async init(init?, config?) {
if (window) {
const Keycloak = (await import('keycloak-js')).default;
this.keycloak = Keycloak(config);
await this.keycloak.init(init);
this.baseUrl = this.keycloak.authServerUrl;
}
}

public setAccessToken(token: string) {
Expand Down
11 changes: 11 additions & 0 deletions src/defs/clientInitialAccessPresentation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_clientinitialaccesspresentation
*/
export default interface ClientInitialAccessPresentation {
id?: string;
token?: string;
timestamp?: number;
expiration?: number;
count?: number;
remainingCount?: number;
}
2 changes: 1 addition & 1 deletion src/defs/componentRepresentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ export default interface ComponentRepresentation {
providerType?: string;
parentId?: string;
subType?: string;
config?: {[index: string]: string};
config?: {[index: string]: string | string[]};
}
7 changes: 7 additions & 0 deletions src/defs/globalRequestResult.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_globalrequestresult
*/
export default interface GlobalRequestResult {
successRequests?: string[];
failedRequests?: string[];
}
18 changes: 18 additions & 0 deletions src/defs/keyMetadataRepresentation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_keysmetadatarepresentation-keymetadatarepresentation
*/
export default interface KeysMetadataRepresentation {
active?: {[index: string]: string};
keys?: KeyMetadataRepresentation[];
}

export interface KeyMetadataRepresentation {
providerId?: string;
providerPriority?: number;
kid?: string;
status?: string;
type?: string;
algorithm?: string;
publicKey?: string;
certificate?: string;
}
12 changes: 12 additions & 0 deletions src/defs/synchronizationResultRepresentation.ts
Original file line number Diff line number Diff line change
@@ -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;
}
11 changes: 9 additions & 2 deletions src/resources/authenticationManagement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,19 @@ export class AuthenticationManagement extends Resource {
urlParamKeys: ['flow'],
});

public addExecutionToFlow = this.makeRequest<{flow: string, provider: string}>({
public addExecutionToFlow = this.makeRequest<{flow: string, provider: string}, AuthenticationExecutionInfoRepresentation>({
method: 'POST',
path: '/flows/{flow}/executions/execution',
urlParamKeys: ['flow'],
returnResourceIdInLocationHeader: {field: 'id'},
})
});

public addFlowToFlow = this.makeRequest<{flow: string, alias: string, type: string, provider: string, description: string}, AuthenticationFlowRepresentation>({
method: 'POST',
path: '/flows/{flow}/executions/flow',
urlParamKeys: ['flow'],
returnResourceIdInLocationHeader: {field: 'id'},
});

public updateExecution = this.makeUpdateRequest<{flow: string}, AuthenticationExecutionInfoRepresentation>({
method: 'PUT',
Expand Down
20 changes: 20 additions & 0 deletions src/resources/cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Resource from './resource';
import {KeycloakAdminClient} from '../client';

export class Cache extends Resource<{realm?: string}> {

public clearUserCache = this.makeRequest<{}, void>({
method: 'POST',
path: '/clear-user-cache',
});

constructor(client: KeycloakAdminClient) {
super(client, {
path: '/admin/realms/{realm}',
getUrlParams: () => ({
realm: client.realmName,
}),
getBaseUrl: () => client.baseUrl,
});
}
}
44 changes: 39 additions & 5 deletions src/resources/clients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import RoleRepresentation from '../defs/roleRepresentation';
import UserRepresentation from '../defs/userRepresentation';
import UserSessionRepresentation from '../defs/userSessionRepresentation';
import ResourceEvaluation from '../defs/resourceEvaluation';
import GlobalRequestResult from '../defs/globalRequestResult';
import Resource from './resource';

export interface ClientQuery {
Expand All @@ -20,7 +21,16 @@ export interface ClientQuery {
}

export interface PolicyQuery {
name: string;
id?: string;
name?: string;
type?: string;
resource?: string;
scope?: string;
permission?: string;
owner?: string;
fields?: string;
first?: number;
max?: number;
}

export class Clients extends Resource<{realm?: string}> {
Expand Down Expand Up @@ -500,15 +510,15 @@ export class Clients extends Resource<{realm?: string}> {
* Policy
*/
public listPolicies = this.makeRequest<
{id: string, name: string},
PolicyQuery,
PolicyRepresentation[]
>({
method: 'GET',
path: '{id}/authz/resource-server/policy',
urlParamKeys: ['id'],
});

public findByName = this.makeRequest<
public findPolicyByName = this.makeRequest<
{id: string; name: string},
PolicyRepresentation
>({
Expand Down Expand Up @@ -558,7 +568,7 @@ export class Clients extends Resource<{realm?: string}> {
policyName: string;
policy: PolicyRepresentation;
}): Promise<PolicyRepresentation> {
const policyFound = await this.findByName({
const policyFound = await this.findPolicyByName({
id: payload.id,
name: payload.policyName,
});
Expand All @@ -567,7 +577,7 @@ export class Clients extends Resource<{realm?: string}> {
{id: payload.id, policyId: policyFound.id, type: payload.policy.type},
payload.policy,
);
return this.findByName({id: payload.id, name: payload.policyName});
return this.findPolicyByName({id: payload.id, name: payload.policyName});
} else {
return this.createPolicy(
{id: payload.id, type: payload.policy.type},
Expand Down Expand Up @@ -673,6 +683,30 @@ export class Clients extends Resource<{realm?: string}> {
urlParamKeys: ['id', 'providerId'],
});

public pushRevocation = this.makeRequest<{id: string}, void>({
method: 'POST',
path: '/{id}/push-revocation',
urlParamKeys: ['id'],
});

public addClusterNode = this.makeRequest<{id: string, node: string}, void>({
method: 'POST',
path: '/{id}/nodes',
urlParamKeys: ['id'],
});

public deleteClusterNode = this.makeRequest<{id: string, node: string}, void>({
method: 'DELETE',
path: '/{id}/nodes/{node}',
urlParamKeys: ['id', 'node'],
});

public testNodesAvailable = this.makeRequest<{id: string}, GlobalRequestResult>({
method: 'GET',
path: '/{id}/test-nodes-available',
urlParamKeys: ['id'],
});

constructor(client: KeycloakAdminClient) {
super(client, {
path: '/admin/realms/{realm}/clients',
Expand Down
Loading

0 comments on commit 4b3d51f

Please sign in to comment.