-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
executable file
Β·165 lines (147 loc) Β· 5.31 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#!/usr/bin/env node
import { ArgumentParser } from 'argparse'
import { Keycloak } from "./keycloak.js"
import * as dotenv from 'dotenv'
import { buildTemplate, parseTemplate, validateTemplate } from "./template-parser.js"
// Driver for kraxen CLI
// Set up Arguments
const parser = new ArgumentParser({
description: "KRAXENπ¦ -- Infrastructure as Code for Keycloak"
})
parser.add_argument('-t', '--template', { help: 'Specify a template to deploy' });
const args = parser.parse_args()
// For config-- check if dotenv exists- if not, use env vars. If errors, alert in CLI.
// Get user information
// TODO: Add way to pass in dotenv (maybe .kconfig)
dotenv.config()
// Get template path and parse template
// TODO: Check that path actually exists and inst fake
if ('template' in args){
const templatePath = args.template
const templateData = parseTemplate(templatePath)
const isValid = validateTemplate(templateData)
if (isValid){
const confData = templateData.keycloak
const clientData = flattenObject(templateData.client)
const clientRequestBody = representClient(clientData)
const kc = new Keycloak(confData.realm)
// Check if client exists, if it does, update it.
const clientList = await kc.getClientList()
for (const client of clientList){
if (client.clientId == clientData.clientId){
const clientUuid = client.id
let status = await kc.updateClient(clientUuid, clientRequestBody)
console.log("Updating Client...")
if (status == 204 || status == 200){
console.log("Client Updated Successfully β
")
process.exit(0)
} else {
console.error("Uh Oh! The client could not be updated β")
process.exit(1)
}
}
}
// Client does not exist yet, we need to create it.
let status = await kc.createClient(clientRequestBody)
console.log("Creating Client...")
if (status == 201 || status == 204 || status == 200){
console.log("Client Created Successfully β
")
process.exit(0)
} else {
console.error("Uh Oh! The client could not be created β")
process.exit(1)
}
} else {
console.error("Provided template contains errors that must be fixed before proceeding πͺ")
process.exit(1)
}
}
// NEED to exfiltrate the "smart defaults bit into separate function for use by CLI"
function flattenObject(ob){
let result = {};
// loop through the object "ob"
for (const i in ob) {
// We check the type of the i using
// typeof() function and recursively
// call the function again
if ((typeof ob[i]) === 'object' && !Array.isArray(ob[i])) {
const temp = flattenObject(ob[i]);
for (const j in temp) {
// Store temp in result
result[j] = temp[j];
}
}
// Else store ob[i] in result directly
else {
result[i] = ob[i];
}
}
return result;
}
function representClient(clientData){
let clientRepresentation = {}
// Loop through list of keys- if in template data, use template value. Else, use smart default value.
const configOptions = [
'adminUrl',
'alwaysDisplayInConsole',
'authenticationFlowBindingOverrides',
'authorizationServicesEnabled',
'authorizationSettings',
'baseUrl',
'bearerOnly',
'clientAuthenticatorType',
'clientId',
'consentRequired',
'defaultClientScopes',
'description',
'directAccessGrantsEnabled',
'enabled',
'frontchannelLogout',
'fullScopeAllowed',
'id',
'implicitFlowEnabled',
'name',
'oauth2DeviceAuthorizationGrantEnabled',
'optionalClientScopes',
'origin',
'protocol',
'protocolMappers',
'publicClient',
'redirectUris',
'registeredNodes',
'registrationAccessToken',
'rootUrl',
'secret',
'serviceAccountsEnabled',
'standardFlowEnabled',
'surrogateAuthRequired',
'webOrigins'
]
const smartDefaults = {
access: { view: true, configure: true, manage: true },
alwaysDisplayInConsole: false,
authorizationServicesEnabled: false,
bearerOnly: false,
clientAuthenticatorType: 'client-secret',
consentRequired: false,
defaultClientScopes: [ 'web-origins', 'acr', 'roles', 'profile', 'email' ],
directAccessGrantsEnabled: false,
enabled: true,
frontchannelLogout: true,
fullScopeAllowed: true,
implicitFlowEnabled: false,
optionalClientScopes: [ 'address', 'phone', 'offline_access', 'microprofile-jwt' ],
publicClient: true,
serviceAccountsEnabled: false,
standardFlowEnabled: true,
surrogateAuthRequired: false,
}
for (let key of configOptions){
if(clientData[key] != undefined){
clientRepresentation[key] = clientData[key]
} else if (smartDefaults[key] != undefined){
clientRepresentation[key] = smartDefaults[key]
}
}
return clientRepresentation
}