testcontainers-keycloak
Version:
A Testcontainers implementation for Keycloak
183 lines (182 loc) • 7.93 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.Keycloak24Container = void 0;
const testcontainers_1 = require("testcontainers");
const axios_1 = __importDefault(require("axios"));
const qs_1 = __importDefault(require("qs"));
class Keycloak24Container extends testcontainers_1.GenericContainer {
waitingLog = /Listening on:/;
startCmd = ['start-dev'];
adminUsername = 'admin';
adminPassword = 'admin';
constructor(image = 'quay.io/keycloak/keycloak:24.0.1') {
super(image);
}
withWaitingLog(message) {
this.waitingLog = message;
return this;
}
withStartCommand(command) {
this.startCmd = command;
return this;
}
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_ADMIN: this.adminUsername })
.withEnvironment({ KEYCLOAK_ADMIN_PASSWORD: this.adminPassword })
.withStartupTimeout(120000)
.withCommand(this.startCmd);
return new StartedKeycloakContainer(await super.start(), this.adminUsername, this.adminPassword);
}
}
exports.Keycloak24Container = Keycloak24Container;
class StartedKeycloakContainer extends testcontainers_1.AbstractStartedContainer {
adminUsername;
adminPassword;
KCADM = `/opt/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} --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, email, enabled = true) {
return await this.runCmd(`${this.KCADM} create users -r ${realmName} -s username=${username} -s firstName=${firstName} -s lastName=${lastName} -s email=${email} -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)}/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) {
console.log(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)}/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;