testcontainers-keycloak
Version:
A Testcontainers implementation for Keycloak
174 lines (173 loc) • 7.68 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.StartedKeycloakContainer = exports.KeycloakContainer = void 0;
const testcontainers_1 = require("testcontainers");
const axios_1 = __importDefault(require("axios"));
const qs_1 = __importDefault(require("qs"));
class KeycloakContainer extends testcontainers_1.GenericContainer {
waitingLog = 'Admin console listening on http://127.0.0.1:9990';
adminUsername = 'admin';
adminPassword = 'admin';
constructor(image = 'quay.io/keycloak/keycloak:16.1.1') {
super(image);
}
withWaitingLog(log) {
this.waitingLog = log;
}
withAdminUsername(username) {
this.adminUsername = username;
return this;
}
withAdminPassword(password) {
this.adminPassword = password;
return this;
}
async start() {
this.withWaitStrategy(testcontainers_1.Wait.forLogMessage(this.waitingLog))
.withEnvironment({ KEYCLOAK_USER: this.adminUsername })
.withEnvironment({ KEYCLOAK_PASSWORD: this.adminPassword });
return new StartedKeycloakContainer(await super.start(), this.adminUsername, this.adminPassword);
}
}
exports.KeycloakContainer = KeycloakContainer;
class StartedKeycloakContainer extends testcontainers_1.AbstractStartedContainer {
adminUsername;
adminPassword;
KCADM = `/opt/jboss/keycloak/bin/kcadm.sh`;
SERVER = 'http://localhost:8080';
constructor(startedTestContainer, adminUsername, adminPassword) {
super(startedTestContainer);
this.adminUsername = adminUsername;
this.adminPassword = adminPassword;
}
getAdminUsername() {
return this.adminUsername;
}
getAdminPassword() {
return this.adminPassword;
}
async runCmd(command) {
const commandArray = command.split(' ');
const execResult = await this.exec(commandArray);
if (execResult.exitCode === 0) {
return Promise.resolve(execResult.output.trim());
}
else {
return Promise.reject(execResult.output.trim());
}
}
/**
* Start an authenticated session on this keycloak server
* @params realmName th3 realm name you want to config
* @params user the user who starting this session, usually the username of admin
* @params user password, usually is the password of admin
*/
async configCredentials(realmName, user, password) {
return await this.runCmd(`${this.KCADM} config credentials --server ${this.SERVER}/auth --realm ${realmName} --user ${user} --password ${password}`);
}
async createRealm(realmName, enabled = true) {
return await this.runCmd(`${this.KCADM} create realms -s realm=${realmName} -s enabled=${enabled}`);
}
async getRealm(realmName) {
const realmResult = await this.runCmd(`${this.KCADM} get realms/${realmName}`);
const realm = JSON.parse(realmResult);
return realm;
}
async createUser(realmName, username, firstName, lastName, enabled = true) {
return await this.runCmd(`${this.KCADM} create users -r ${realmName} -s username=${username} -s firstName=${firstName} -s lastName=${lastName} -s enabled=${enabled}`);
}
async getUserById(realmName, userId) {
const userResult = await this.runCmd(`${this.KCADM} get users/${userId} -r ${realmName}`);
const user = JSON.parse(userResult);
return user;
}
async getUserIdByUsername(realmName, username) {
const usersResult = await this.runCmd(`${this.KCADM} get users -r ${realmName} -q username=${username}`);
const userArray = JSON.parse(usersResult);
if (userArray.length === 1) {
return Promise.resolve(userArray[0].id);
}
else {
return Promise.reject(`Cannot find username '${username}' in realm '${realmName}'`);
}
}
async setUserPassword(realmName, username, password) {
return await this.runCmd(`${this.KCADM} set-password -r ${realmName} --username ${username} --new-password ${password}`);
}
async createClient(realmName, clientId, clientSecret, redirectUris = [], webOrigins = [], directAccessGrantsEnabled = true, enabled = true) {
const redirectUrisString = redirectUris.map((uri) => `"${uri}"`).join(',');
const webOriginsString = webOrigins.map((uri) => `"${uri}"`).join(',');
return await this.runCmd(`${this.KCADM} create clients -r ${realmName} -s clientId=${clientId} -s secret=${clientSecret} -s enabled=${enabled} -s redirectUris=[${redirectUrisString}] -s webOrigins=[${webOriginsString}] -s directAccessGrantsEnabled=${directAccessGrantsEnabled}`);
}
async getCidByClientId(realmName, clientId) {
const clientsResult = await this.runCmd(`${this.KCADM} get clients -r ${realmName} --fields id -q clientId=${clientId}`);
const clients = JSON.parse(clientsResult);
if (clients.length === 1) {
return Promise.resolve(clients[0]['id']);
}
else {
return Promise.reject(`Can't find client '${clientId}' in realm '${realmName}'`);
}
}
async getClientByCid(realmName, cid) {
const clientResult = await this.runCmd(`${this.KCADM} get clients/${cid} -r ${realmName}`);
const client = JSON.parse(clientResult);
return client;
}
async getClientSecretByCid(realmName, cid) {
const clientSecretResult = await this.runCmd(`${this.KCADM} get clients/${cid}/client-secret -r ${realmName}`);
const secret = JSON.parse(clientSecretResult);
return secret;
}
async getAccessToken(realmName, username, password, clientId, clientSecret) {
const tokenEndpoint = `http://${this.getHost()}:${this.getMappedPort(8080)}/auth/realms/${realmName}/protocol/openid-connect/token`;
const payload = qs_1.default.stringify({
username,
password,
client_id: clientId,
client_secret: clientSecret,
grant_type: 'password'
});
try {
const response = await axios_1.default.post(tokenEndpoint, payload);
const accessToken = response.data['access_token'];
if (accessToken) {
return accessToken;
}
else {
throw new Error(`Failed to get access_token: access_token undefined`);
}
}
catch (error) {
throw new Error(`Failed to get access_token: ${error}`);
}
}
async getIdToken(realmName, username, password, clientId, clientSecret) {
const tokenEndpoint = `http://${this.getHost()}:${this.getMappedPort(8080)}/auth/realms/${realmName}/protocol/openid-connect/token`;
const payload = qs_1.default.stringify({
username,
password,
client_id: clientId,
client_secret: clientSecret,
grant_type: 'password',
scope: 'openid'
});
try {
const response = await axios_1.default.post(tokenEndpoint, payload);
const idToken = response.data['id_token'];
if (idToken) {
return idToken;
}
else {
throw new Error(`Failed to get id_token: id_token undefined`);
}
}
catch (error) {
throw new Error(`Failed to get id_token: ${error}`);
}
}
}
exports.StartedKeycloakContainer = StartedKeycloakContainer;