Skip to content
This repository has been archived by the owner on Apr 19, 2023. It is now read-only.

Commit

Permalink
initial version authentication flow endpoints (#126)
Browse files Browse the repository at this point in the history
* initial version authentication flow endpoints

* fix test for keycloak 7
  • Loading branch information
edewit authored Jan 14, 2021
1 parent 0c7e62f commit 6778139
Show file tree
Hide file tree
Showing 8 changed files with 246 additions and 20 deletions.
13 changes: 13 additions & 0 deletions src/defs/authenticationExecutionExportRepresentation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_authenticationexecutionexportrepresentation
*/
export default interface AuthenticationExecutionExportRepresentation {
flowAlias?: string;
userSetupAllowed?: boolean;
authenticatorConfig?: string;
authenticator?: string;
requirement?: string;
priority?: number;
autheticatorFlow?: boolean;
}

18 changes: 18 additions & 0 deletions src/defs/authenticationExecutionInfoRepresentation.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#_authenticationexecutioninforepresentation
*/
export default interface AuthenticationExecutionInfoRepresentation {
id?: string;
requirement?: string;
displayName?: string;
alias?: string;
description?: string;
requirementChoices?: string[];
configurable?: boolean;
authenticationFlow?: boolean;
providerId?: string;
authenticationConfig?: string;
flowId?: string;
level?: number;
index?: number;
}
15 changes: 15 additions & 0 deletions src/defs/authenticationFlowRepresentation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

import AuthenticationExecutionExportRepresentation from './authenticationExecutionExportRepresentation';

/**
* https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_authenticationflowrepresentation
*/
export default interface AuthenticationFlowRepresentation {
id?: string;
alias?: string;
description?: string;
providerId?: string;
topLevel?: boolean;
builtIn?: boolean;
authenticationExecutions?: AuthenticationExecutionExportRepresentation[];
}
2 changes: 1 addition & 1 deletion src/defs/resourceEvaluation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import ResourceRepresentation from "./resourceRepresentation";
import ResourceRepresentation from './resourceRepresentation';

export default interface ResourceEvaluation {
rolesIds?: string[];
Expand Down
74 changes: 74 additions & 0 deletions src/resources/authenticationManagement.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import Resource from './resource';
import RequiredActionProviderRepresentation from '../defs/requiredActionProviderRepresentation';
import {KeycloakAdminClient} from '../client';
import AuthenticationExecutionInfoRepresentation from '../defs/authenticationExecutionInfoRepresentation';
import AuthenticationFlowRepresentation from '../defs/authenticationFlowRepresentation';

export class AuthenticationManagement extends Resource {
/**
Expand Down Expand Up @@ -77,6 +79,78 @@ export class AuthenticationManagement extends Resource {
path: '/unregistered-required-actions',
});

public getFlows = this.makeRequest<void, AuthenticationFlowRepresentation[]>({
method: 'GET',
path: '/flows',
});

public createFlow = this.makeRequest<AuthenticationFlowRepresentation, void>({
method: 'POST',
path: '/flows',
returnResourceIdInLocationHeader: {field: 'id'},
});

public copyFlow = this.makeRequest<{flow: string, newName: string}>({
method: 'POST',
path: '/flows/{flow}/copy',
urlParamKeys: ['flow'],
});

public deleteFlow = this.makeRequest<{flowId: string}>({
method: 'DELETE',
path: '/flows/{flowId}',
urlParamKeys: ['flowId'],
});

public updateFlow = this.makeUpdateRequest<{flowId: string}, AuthenticationFlowRepresentation>({
method: 'PUT',
path: '/flows/{flowId}',
urlParamKeys: ['flowId'],
});

public getExecutions = this.makeRequest<{flow: string}, AuthenticationExecutionInfoRepresentation[]>({
method: 'GET',
path: '/flows/{flow}/executions',
urlParamKeys: ['flow'],
});

public addExecution = this.makeUpdateRequest<{flow: string}, AuthenticationExecutionInfoRepresentation>({
method: 'POST',
path: '/flows/{flow}/executions',
urlParamKeys: ['flow'],
});

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

public updateExecution = this.makeUpdateRequest<{flow: string}, AuthenticationExecutionInfoRepresentation>({
method: 'PUT',
path: '/flows/{flow}/executions',
urlParamKeys: ['flow'],
});

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

public lowerPriorityExecution = this.makeRequest<{id: string}>({
method: 'POST',
path: '/executions/{id}/lower-priority',
urlParamKeys: ['id'],
});

public raisePriorityExecution = this.makeRequest<{id: string}>({
method: 'POST',
path: '/executions/{id}/raise-priority',
urlParamKeys: ['id'],
});

constructor(client: KeycloakAdminClient) {
super(client, {
path: '/admin/realms/{realm}/authentication',
Expand Down
13 changes: 7 additions & 6 deletions src/resources/clients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import ProtocolMapperRepresentation from '../defs/protocolMapperRepresentation';
import RoleRepresentation from '../defs/roleRepresentation';
import UserRepresentation from '../defs/userRepresentation';
import UserSessionRepresentation from '../defs/userSessionRepresentation';
import ResourceEvaluation from '../defs/resourceEvaluation';
import Resource from './resource';
import ResourceEvaluation from "../defs/resourceEvaluation";

export interface ClientQuery {
first?: number;
Expand Down Expand Up @@ -465,7 +465,7 @@ export class Clients extends Resource<{realm?: string}> {
>({
method: 'GET',
path: '{id}/authz/resource-server/resource',
urlParamKeys: ['id']
urlParamKeys: ['id'],
});

public createResource = this.makeUpdateRequest<
Expand All @@ -475,7 +475,7 @@ export class Clients extends Resource<{realm?: string}> {
>({
method: 'POST',
path: '{id}/authz/resource-server/resource',
urlParamKeys: ['id']
urlParamKeys: ['id'],
});

public delResource = this.makeRequest<
Expand All @@ -488,7 +488,7 @@ export class Clients extends Resource<{realm?: string}> {
});

public evaluateResource = this.makeUpdateRequest<
{ id: string },
{id: string},
ResourceEvaluation
>({
method: 'POST',
Expand Down Expand Up @@ -584,8 +584,9 @@ export class Clients extends Resource<{realm?: string}> {
>({
method: 'GET',
path: '/{id}/authz/resource-server/scope',
urlParamKeys: ['id']
})
urlParamKeys: ['id'],
});

public listScopesByResource = this.makeRequest<
{id: string; resourceName: string},
{id: string; name: string}[]
Expand Down
105 changes: 105 additions & 0 deletions test/authenticationManagement.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,109 @@ describe('Authentication management', () => {
kcAdminClient.clients.del({id: createdClient.id});
});
});
describe('Flows', () => {

it('should get authentication flow', async () => {
const flows = await kcAdminClient.authenticationManagement.getFlows();

expect(flows.map(flow => flow.alias)).to.be.deep.eq(['browser', 'direct grant', 'registration', 'reset credentials',
'clients', 'first broker login', 'docker auth', 'http challenge']);
});

it('should create new authentication flow', async () => {
const flowName = 'test';
await kcAdminClient.authenticationManagement.createFlow({alias: flowName, providerId: 'basic-flow', description: '', topLevel: true, builtIn: false});

const flows = await kcAdminClient.authenticationManagement.getFlows();
expect(flows.find(f => f.alias === flowName)).to.be.ok;
});

const flowName = 'copy of browser';
it('should copy existing authentication flow', async () => {
await kcAdminClient.authenticationManagement.copyFlow({flow: 'browser', newName: flowName});

const flows = await kcAdminClient.authenticationManagement.getFlows();
const flow = flows.find(f => f.alias === flowName);
expect(flow).to.be.ok;
});

it('should update authentication flow', async () => {
const flows = await kcAdminClient.authenticationManagement.getFlows();
const flow = flows.find(f => f.alias === flowName);
const description = 'Updated description';
flow.description = description;
const updatedFlow = await kcAdminClient.authenticationManagement.updateFlow({flowId: flow.id}, flow);

expect(updatedFlow.description).to.be.eq(description);
});

it('should delete authentication flow', async () => {
let flows = await kcAdminClient.authenticationManagement.getFlows();
const flow = flows.find(f => f.alias === flowName);
await kcAdminClient.authenticationManagement.deleteFlow({flowId: flow.id});

flows = await kcAdminClient.authenticationManagement.getFlows();
expect(flows.find(f => f.alias === flowName)).to.be.undefined;
});
});
describe('Flow executions', () => {
it('should fetch all executions for a flow', async () => {
const executions = await kcAdminClient.authenticationManagement.getExecutions({flow: 'browser'});
expect(executions.length).to.be.gt(5);
});

const flowName = 'executionTest';
it('should add execution to a flow', async () => {
await kcAdminClient.authenticationManagement.copyFlow({flow: 'browser', newName: flowName});
const execution = await kcAdminClient.authenticationManagement.addExecutionToFlow({flow: flowName, provider: 'auth-otp-form'});

expect(execution.id).to.be.ok;
});

it('should update execution to a flow', async () => {
let executions = await kcAdminClient.authenticationManagement.getExecutions({flow: flowName});
let execution = executions[executions.length - 1];
const choice = execution.requirementChoices[1];
execution.requirement = choice;
await kcAdminClient.authenticationManagement.updateExecution({flow: flowName}, execution);

executions = await kcAdminClient.authenticationManagement.getExecutions({flow: flowName});
execution = executions[executions.length - 1];

expect(execution.requirement).to.be.eq(choice);
});

it('should delete execution', async () => {
let executions = await kcAdminClient.authenticationManagement.getExecutions({flow: flowName});
const id = executions[0].id;
await kcAdminClient.authenticationManagement.delExecution({id});
executions = await kcAdminClient.authenticationManagement.getExecutions({flow: flowName});
expect(executions.find(ex => ex.id === id)).to.be.undefined;
});

it('should raise priority of execution', async () => {
let executions = await kcAdminClient.authenticationManagement.getExecutions({flow: flowName});
let execution = executions[executions.length - 1];
const priority = execution.index;
await kcAdminClient.authenticationManagement.raisePriorityExecution({id: execution.id});

executions = await kcAdminClient.authenticationManagement.getExecutions({flow: flowName});
execution = executions.find(ex => ex.id === execution.id);

expect(execution.index).to.be.eq(priority - 1);
});

it('should lower priority of execution', async () => {
let executions = await kcAdminClient.authenticationManagement.getExecutions({flow: flowName});
let execution = executions[0];
const priority = execution.index;
await kcAdminClient.authenticationManagement.lowerPriorityExecution({id: execution.id});

executions = await kcAdminClient.authenticationManagement.getExecutions({flow: flowName});
execution = executions.find(ex => ex.id === execution.id);

expect(execution.index).to.be.eq(priority + 1);
});

});
}).timeout(10000);
26 changes: 13 additions & 13 deletions test/users.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,10 @@ describe('Users', function () {
const numUsers = await kcAdminClient.users.count({email: '[email protected]'});

if (process.env.KEYCLOAK_VERSION
&& (
process.env.KEYCLOAK_VERSION.startsWith("7.")
|| process.env.KEYCLOAK_VERSION.startsWith("8.")
)) {
&& (
process.env.KEYCLOAK_VERSION.startsWith('7.')
|| process.env.KEYCLOAK_VERSION.startsWith('8.')
)) {
// should be 1, but it seems it doesn't work issue: KEYCLOAK-16081
expect(numUsers).to.equal(2);
} else {
Expand Down Expand Up @@ -140,15 +140,15 @@ describe('Users', function () {
*/

it('should remove totp', async function () {
if (process.env.KEYCLOAK_VERSION && process.env.KEYCLOAK_VERSION.startsWith("7.")) {
// todo: find a way to add totp from api
const userId = currentUser.id;
await kcAdminClient.users.removeTotp({
id: userId,
});
} else {
this.skip();
}
if (process.env.KEYCLOAK_VERSION && process.env.KEYCLOAK_VERSION.startsWith('7.')) {
// todo: find a way to add totp from api
const userId = currentUser.id;
await kcAdminClient.users.removeTotp({
id: userId,
});
} else {
this.skip();
}
});

/**
Expand Down

0 comments on commit 6778139

Please sign in to comment.