// setup_realm.js
import dotenv from 'dotenv';
import axios from 'axios';
// Lade Umgebungsvariablen
dotenv.config();
// Konfigurationskonstanten
const KEYCLOAK_URL = process.env.KEYCLOAK_URL || 'https://auth.mrx8086.com';
const ADMIN_USERNAME = process.env.KEYCLOAK_ADMIN_USER;
const ADMIN_PASSWORD = process.env.KEYCLOAK_ADMIN_PASSWORD;
const REALM_NAME = 'office-automation';
// Client IDs aus Umgebungsvariablen
const NEXTCLOUD_CLIENT_ID = process.env.NEXTCLOUD_CLIENT_ID || 'nextcloud';
const PAPERLESS_CLIENT_ID = process.env.PAPERLESS_CLIENT_ID || 'paperless';
const NODERED_CLIENT_ID = process.env.NODERED_CLIENT_ID || 'nodered';
// Hilfsfunktion für API-Fehlerbehandlung
const handleAxiosError = (error, operation) => {
if (error.response) {
console.error(`Error during ${operation}:`, {
status: error.response.status,
data: error.response.data
});
} else {
console.error(`Error during ${operation}:`, error.message);
}
throw error;
};
// Admin Token abrufen
async function getAdminToken() {
try {
const response = await axios.post(
`${KEYCLOAK_URL}/realms/master/protocol/openid-connect/token`,
new URLSearchParams({
'client_id': 'admin-cli',
'username': ADMIN_USERNAME,
'password': ADMIN_PASSWORD,
'grant_type': 'password'
}),
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
);
return response.data.access_token;
} catch (error) {
handleAxiosError(error, 'getting admin token');
}
}
// Prüfen ob Realm existiert
async function checkRealmExists(token) {
try {
await axios.get(
`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}`,
{
headers: {
'Authorization': `Bearer ${token}`
}
}
);
return true;
} catch (error) {
if (error.response?.status === 404) {
return false;
}
handleAxiosError(error, 'checking realm existence');
}
}
// Prüfen ob Client existiert
async function checkClientExists(token, clientId) {
try {
const response = await axios.get(
`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients`,
{
headers: {
'Authorization': `Bearer ${token}`
},
params: {
clientId: clientId
}
}
);
return response.data.length > 0;
} catch (error) {
handleAxiosError(error, `checking client existence: ${clientId}`);
}
}
// Realm erstellen
async function createRealm(token) {
const realmConfig = {
realm: REALM_NAME,
enabled: true,
displayName: "Office Automation",
displayNameHtml: "
Office Automation
",
sslRequired: "external",
registrationAllowed: false,
loginWithEmailAllowed: true,
duplicateEmailsAllowed: false,
resetPasswordAllowed: true,
editUsernameAllowed: false,
bruteForceProtected: true,
permanentLockout: false,
maxFailureWaitSeconds: 900,
minimumQuickLoginWaitSeconds: 60,
waitIncrementSeconds: 60,
quickLoginCheckMilliSeconds: 1000,
maxDeltaTimeSeconds: 43200,
failureFactor: 3,
defaultSignatureAlgorithm: "RS256",
offlineSessionMaxLifespan: 5184000,
offlineSessionMaxLifespanEnabled: true,
webAuthnPolicySignatureAlgorithms: ["ES256"],
webAuthnPolicyAttestationConveyancePreference: "none",
webAuthnPolicyAuthenticatorAttachment: "cross-platform",
webAuthnPolicyRequireResidentKey: "not specified",
webAuthnPolicyUserVerificationRequirement: "preferred",
webAuthnPolicyCreateTimeout: 0,
webAuthnPolicyAvoidSameAuthenticatorRegister: false,
defaultDefaultClientScopes: [
"email",
"profile",
"roles",
"web-origins"
],
defaultOptionalClientScopes: [
"address",
"phone",
"offline_access",
"microprofile-jwt"
]
};
try {
await axios.post(
`${KEYCLOAK_URL}/admin/realms`,
realmConfig,
{
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
}
);
console.log('Realm created successfully');
} catch (error) {
handleAxiosError(error, 'creating realm');
}
}
// Client erstellen
async function createClient(token, clientId, clientName, redirectUris) {
const clientConfig = {
clientId: clientId,
name: clientName,
enabled: true,
protocol: "openid-connect",
publicClient: false,
authorizationServicesEnabled: true,
serviceAccountsEnabled: true,
standardFlowEnabled: true,
implicitFlowEnabled: false,
directAccessGrantsEnabled: true,
redirectUris: redirectUris,
webOrigins: ["+"],
defaultClientScopes: [
"email",
"profile",
"roles",
"web-origins"
],
optionalClientScopes: [
"address",
"phone",
"offline_access",
"microprofile-jwt"
]
};
try {
await axios.post(
`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients`,
clientConfig,
{
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
}
);
console.log(`Client ${clientId} created successfully`);
} catch (error) {
handleAxiosError(error, `creating client: ${clientId}`);
}
}
// Gruppen erstellen
async function createDefaultGroups(token) {
const groups = [
{
name: "Administrators",
path: "/Administrators",
attributes: {
"description": ["Full system access"]
}
},
{
name: "Users",
path: "/Users",
attributes: {
"description": ["Regular system users"]
}
}
];
for (const group of groups) {
try {
await axios.post(
`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/groups`,
group,
{
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
}
);
console.log(`Group ${group.name} created successfully`);
} catch (error) {
if (error.response?.status === 409) {
console.log(`Group ${group.name} already exists`);
} else {
handleAxiosError(error, `creating group: ${group.name}`);
}
}
}
}
// Test-User erstellen
async function createInitialUsers(token) {
const users = [
{
username: "testadmin",
enabled: true,
emailVerified: true,
firstName: "Test",
lastName: "Admin",
email: "testadmin@mrx8086.com",
credentials: [{
type: "password",
value: process.env.TESTADMIN_PASSWORD || "initial123!",
temporary: true
}],
groups: ["/Administrators"]
},
{
username: "testuser",
enabled: true,
emailVerified: true,
firstName: "Test",
lastName: "User",
email: "testuser@mrx8086.com",
credentials: [{
type: "password",
value: process.env.TESTUSER_PASSWORD || "initial123!",
temporary: true
}],
groups: ["/Users"]
}
];
for (const user of users) {
try {
// Prüfen ob User existiert
const existingUsers = await axios.get(
`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/users`,
{
headers: {
'Authorization': `Bearer ${token}`
},
params: {
username: user.username,
exact: true
}
}
);
if (existingUsers.data.length > 0) {
console.log(`User ${user.username} already exists`);
continue;
}
// User erstellen
await axios.post(
`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/users`,
user,
{
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
}
);
console.log(`User ${user.username} created successfully`);
} catch (error) {
handleAxiosError(error, `creating user: ${user.username}`);
}
}
}
// Hauptfunktion
async function setupRealm() {
try {
console.log('Starting Keycloak setup...');
const token = await getAdminToken();
// Prüfe ob Realm existiert
const realmExists = await checkRealmExists(token);
if (!realmExists) {
console.log('Creating new realm...');
await createRealm(token);
// Clients erstellen
const clients = [
{ id: NEXTCLOUD_CLIENT_ID, name: "Nextcloud", redirectUris: ["https://cloud.mrx8086.com/*"] },
{ id: PAPERLESS_CLIENT_ID, name: "Paperless", redirectUris: ["https://docs.mrx8086.com/*"] },
{ id: NODERED_CLIENT_ID, name: "Node-RED", redirectUris: ["https://automate.mrx8086.com/*"] }
];
for (const client of clients) {
const clientExists = await checkClientExists(token, client.id);
if (!clientExists) {
await createClient(token, client.id, client.name, client.redirectUris);
} else {
console.log(`Client ${client.id} already exists`);
}
}
// Gruppen erstellen
await createDefaultGroups(token);
} else {
console.log('Realm already exists, skipping base setup');
}
// User erstellen/aktualisieren
await createInitialUsers(token);
console.log('Setup completed successfully');
} catch (error) {
console.error('Setup failed:', error);
process.exit(1);
}
}
// Script ausführen
setupRealm();