Created
March 20, 2024 21:24
-
-
Save slutske22/4bcd9920ccfa4c4fef60cb22d5bfb745 to your computer and use it in GitHub Desktop.
Setup script using the keycloak admin REST api
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* eslint no-console: 0 */ | |
const axios = require("axios"); | |
const chalk = require("chalk"); | |
const KEYCLOAK_BASE_URL = process.env.DEMO | |
? "http://keycloak_iam:9090" | |
: "http://localhost:9090"; | |
const CLIENT_NAME = "sva"; | |
const CLIENT_ROLES = ["ROLE_DEP_ADMIN", "ROLE_DEP_GENERAL"]; | |
const MAPPERS = [ | |
{ | |
attribute: "firstName", | |
jsonLabel: "String", | |
multivalued: false, | |
}, | |
{ | |
attribute: "lastName", | |
jsonLabel: "String", | |
multivalued: false, | |
}, | |
{ | |
attribute: "email", | |
jsonLabel: "String", | |
multivalued: false, | |
}, | |
{ | |
attribute: "externalUser", | |
jsonLabel: "boolean", | |
multivalued: false, | |
}, | |
{ | |
attribute: "securityContext.application", | |
jsonLabel: "String", | |
multivalued: false, | |
}, | |
{ | |
attribute: "securityContext.securityRoles", | |
jsonLabel: "JSON", | |
multivalued: true, | |
}, | |
]; | |
const USERS = [ | |
{ | |
username: "dep_admin", | |
firstName: "Deppy", | |
lastName: "Admin", | |
email: "dep_admin@sva-fake.iam", | |
// Other custom attributes | |
externalUser: false, | |
securityRoles: [ | |
{ | |
name: "Employee", | |
securityLevel: -1, | |
}, | |
{ | |
name: "FDEP-Admin", | |
securityLevel: 150, | |
}, | |
], | |
}, | |
{ | |
username: "dep_org_admin", | |
firstName: "Dorgy", | |
lastName: "Admin", | |
email: "dep_org_admin@sva-fake.iam", | |
externalUser: false, | |
securityRoles: [ | |
{ | |
name: "FDEP-Org-Admin", | |
securityLevel: 100, | |
}, | |
], | |
}, | |
{ | |
username: "general", | |
firstName: "Jen", | |
lastName: "Eral", | |
email: "general@sva-fake.iam", | |
externalUser: true, | |
securityRoles: [], | |
}, | |
{ | |
username: "eligible_entity", | |
firstName: "Elle", | |
lastName: "Enty", | |
email: "eligible_entity@sva-fake.iam", | |
externalUser: true, | |
securityRoles: [], | |
}, | |
{ | |
username: "eligible_entity_admin", | |
firstName: "Elle", | |
lastName: "Bossy", | |
email: "eligible_entity_admin@sva-fake.iam", | |
externalUser: true, | |
securityRoles: [], | |
}, | |
{ | |
username: "contractor", | |
firstName: "Con", | |
lastName: "Tractor", | |
email: "contractor@sva-fake.iam", | |
externalUser: true, | |
securityRoles: [], | |
}, | |
{ | |
username: "other_state_staff", | |
firstName: "Otter", | |
lastName: "Statestf", | |
email: "other_state_staff@sva-fake.iam", | |
externalUser: true, | |
securityRoles: [], | |
}, | |
{ | |
username: "other_state_staff_admin", | |
firstName: "Otter", | |
lastName: "Stateboss", | |
email: "other_state_staff_admin@sva-fake.iam", | |
externalUser: true, | |
securityRoles: [], | |
}, | |
{ | |
username: "sysadmin", | |
firstName: "God", | |
lastName: "Mode", | |
email: "sysadmin@sva-fake.iam", | |
externalUser: true, | |
securityRoles: [], | |
}, | |
]; | |
async function ping() { | |
return new Promise((res) => | |
setTimeout(async () => { | |
console.log(chalk.blue(`Checking if keycloak is available ...`)); | |
try { | |
await axios.get(`${KEYCLOAK_BASE_URL}/health/live`); | |
res("ok"); | |
} catch (e) { | |
res(undefined); | |
} | |
}, 3000), | |
); | |
} | |
async function main() { | |
let access_token = ""; | |
let keycloakOk = false; | |
console.log(`Using keycloak base URL: ${KEYCLOAK_BASE_URL}`); | |
/** | |
* ---------------------------------------------- Health check keycloak online | |
*/ | |
for (let i = 0; i < 120; i++) { | |
keycloakOk = await ping(); | |
if (keycloakOk) break; | |
} | |
if (!keycloakOk) { | |
console.log(chalk.red("Could not find keycloak, aborting")); | |
return; | |
} | |
await new Promise((res) => { | |
setTimeout(() => { | |
res(); | |
}, 3000); | |
}); | |
/** | |
* ---------------------------------------------- Log in to REST API | |
*/ | |
console.log(chalk.blue("Logging in . . . \n")); | |
try { | |
const token = await axios.post( | |
`${KEYCLOAK_BASE_URL}/realms/master/protocol/openid-connect/token`, | |
{ | |
username: "admin", | |
password: "admin", | |
grant_type: "password", | |
client_id: "admin-cli", | |
}, | |
{ | |
headers: { | |
"Content-Type": "application/x-www-form-urlencoded", | |
}, | |
}, | |
); | |
access_token = token.data.access_token; | |
axios.defaults.headers.common["Authorization"] = `Bearer ${access_token}`; | |
console.log(chalk.green("Logged in!\n")); | |
} catch (e) { | |
console.log(chalk.red("!!! Could not log in! Aborting script.")); | |
throw e; | |
} | |
/** | |
* ---------------------------------------------- Create client | |
*/ | |
console.log(chalk.blue("Creating Client . . . \n")); | |
try { | |
await axios.post( | |
`${KEYCLOAK_BASE_URL}/admin/realms/master/clients`, | |
{ | |
clientId: CLIENT_NAME, | |
id: CLIENT_NAME, | |
redirectUris: ["*"], | |
webOrigins: ["*"], | |
publicClient: true, | |
}, | |
{ | |
headers: { | |
"Content-Type": "application/json", | |
Authorization: `Bearer ${access_token}`, | |
}, | |
}, | |
); | |
console.log(chalk.green("Created Client! \n")); | |
} catch (e) { | |
console.error(chalk.red("!!! Couldn't create client \n")); | |
console.error(e.response?.data ?? e.response ?? e.res ?? e); | |
console.log(""); | |
} | |
/** | |
* ---------------------------------------------- Create client roles | |
*/ | |
for (const role of CLIENT_ROLES) { | |
console.log( | |
chalk.blue(`Creating role "${role}" for client: "${CLIENT_NAME}"`), | |
); | |
try { | |
await axios.post( | |
`${KEYCLOAK_BASE_URL}/admin/realms/master/clients/${CLIENT_NAME}/roles`, | |
{ name: role }, | |
{ | |
headers: { | |
"Content-Type": "application/json", | |
Authorization: `Bearer ${access_token}`, | |
}, | |
}, | |
); | |
console.log( | |
chalk.green(`Created role "${role}" for client: "${CLIENT_NAME}"`), | |
); | |
} catch (e) { | |
console.log(chalk.red("!!! Could not create role! \n")); | |
console.error(e.response?.data ?? e.response ?? e.res ?? e); | |
console.log(""); | |
} | |
} | |
/** | |
* ---------------------------------------------- Create client protocol mappers | |
*/ | |
try { | |
for (const mapper of MAPPERS) { | |
console.log(chalk.blue(`Creating mapper: ${mapper.attribute}`)); | |
await axios.post( | |
`${KEYCLOAK_BASE_URL}/admin/realms/master/clients/${CLIENT_NAME}/protocol-mappers/models`, | |
{ | |
protocol: "openid-connect", | |
config: { | |
"id.token.claim": "true", | |
"access.token.claim": "true", | |
"userinfo.token.claim": "true", | |
multivalued: mapper.multivalued, | |
"aggregate.attrs": "", | |
"user.attribute": mapper.attribute, | |
"claim.name": mapper.attribute, | |
"jsonType.label": mapper.jsonLabel, | |
}, | |
name: mapper.attribute, | |
protocolMapper: "oidc-usermodel-attribute-mapper", | |
}, | |
{ | |
headers: { | |
"Content-Type": "application/json", | |
Authorization: `Bearer ${access_token}`, | |
}, | |
}, | |
); | |
console.log(chalk.green(`Created mapper: ${mapper.attribute}`)); | |
} | |
} catch (e) { | |
console.log(chalk.red("!!! Could not create protocol mapper! \n")); | |
console.error(e.response?.data ?? e.response ?? e.res ?? e); | |
console.log(""); | |
} | |
/** | |
* ---------------------------------------------- Create users | |
*/ | |
for (const user of USERS) { | |
console.log(chalk.blue(`Creating user: ${user.username}`)); | |
try { | |
await axios.post( | |
`${KEYCLOAK_BASE_URL}/admin/realms/master/users`, | |
{ | |
username: user.username, | |
firstName: user.firstName, | |
lastName: user.lastName, | |
email: user.email, | |
emailVerified: true, | |
enabled: true, | |
credentials: [ | |
{ | |
temporary: false, | |
type: "password", | |
value: user.username, | |
}, | |
], | |
attributes: { | |
externalUser: user.externalUser, | |
"securityContext.application": "sva", | |
"securityContext.securityRoles": user.securityRoles.map((r) => | |
JSON.stringify(r), | |
), | |
}, | |
}, | |
{ | |
headers: { | |
"Content-Type": "application/json", | |
Authorization: `Bearer ${access_token}`, | |
}, | |
}, | |
); | |
console.log(chalk.green(`Created user: ${user.username}`)); | |
} catch (e) { | |
console.log(chalk.red(`!!! Could not create user: ${user.username} \n`)); | |
console.error(e.response?.data ?? e.response ?? e.res ?? e); | |
console.log(""); | |
} | |
} | |
/** | |
* ---------------------------------------------- Updating realm token lifetime | |
*/ | |
try { | |
console.log(chalk.blue(`Updating realm token lifetime`)); | |
await axios.put( | |
`${KEYCLOAK_BASE_URL}/admin/realms/master`, | |
{ | |
accessTokenLifespan: 86400, | |
sslRequired: "NONE", | |
}, | |
{ | |
headers: { | |
"Content-Type": "application/json", | |
Authorization: `Bearer ${access_token}`, | |
}, | |
}, | |
); | |
console.log(chalk.green(`Updated realm token lifetime`)); | |
} catch (e) { | |
console.log(chalk.red(`!!! Could not update realm token life \n`)); | |
console.error(e.response?.data ?? e.response ?? e.res ?? e); | |
console.log(""); | |
} | |
} | |
main(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment