@continuoussecuritytooling/keycloak-reporter 0.5.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/.eslintrc.cjs +4 -3
  2. package/.github/workflows/pipeline.yml +37 -10
  3. package/.github/workflows/release.yml +1 -1
  4. package/.prettierrc +2 -2
  5. package/Dockerfile +19 -2
  6. package/README.md +4 -3
  7. package/artifacthub-repo.yml +6 -0
  8. package/charts/keycloak-reporter/Chart.yaml +9 -3
  9. package/charts/keycloak-reporter/README.md +7 -21
  10. package/charts/keycloak-reporter/templates/_helpers.tpl +8 -8
  11. package/charts/keycloak-reporter/templates/cronjob.yaml +21 -16
  12. package/charts/keycloak-reporter/templates/secret.yaml +6 -8
  13. package/charts/keycloak-reporter/values.yaml +42 -39
  14. package/cli.ts +54 -87
  15. package/config/schema.json +6 -1
  16. package/index.ts +1 -1
  17. package/lib/client.ts +10 -37
  18. package/lib/output.ts +2 -2
  19. package/lib/user.ts +86 -49
  20. package/package.json +5 -4
  21. package/renovate.json +12 -5
  22. package/src/commands.ts +27 -0
  23. package/src/config.ts +6 -18
  24. package/config.json +0 -9
  25. package/dist/cli.js +0 -130
  26. package/dist/cli.js.map +0 -1
  27. package/dist/config/schema.json +0 -65
  28. package/dist/index.js +0 -4
  29. package/dist/index.js.map +0 -1
  30. package/dist/lib/client.js +0 -41
  31. package/dist/lib/client.js.map +0 -1
  32. package/dist/lib/convert.js +0 -9
  33. package/dist/lib/convert.js.map +0 -1
  34. package/dist/lib/output.js +0 -113
  35. package/dist/lib/output.js.map +0 -1
  36. package/dist/lib/user.js +0 -75
  37. package/dist/lib/user.js.map +0 -1
  38. package/dist/src/cli.js +0 -19
  39. package/dist/src/cli.js.map +0 -1
  40. package/dist/src/config.js +0 -57
  41. package/dist/src/config.js.map +0 -1
  42. package/k8s.yaml +0 -51
  43. package/keycloak-reporter-0.5.0.tgz +0 -0
  44. package/src/cli.ts +0 -26
  45. package/test.values.yaml +0 -8
package/cli.ts CHANGED
@@ -4,15 +4,8 @@ import { writeFileSync } from 'node:fs';
4
4
  import path from 'path';
5
5
  import yargs from 'yargs/yargs';
6
6
  import { hideBin } from 'yargs/helpers';
7
- import {
8
- listUsers,
9
- listClients,
10
- Options,
11
- convertJSON2CSV,
12
- post2Webhook
13
- } from './index.js';
7
+ import { listUsers, listClients, Options, convertJSON2CSV, post2Webhook } from './index.js';
14
8
  import config from './src/config.js';
15
-
16
9
  class WebhookConfig {
17
10
  type: string;
18
11
  url: string;
@@ -31,13 +24,34 @@ class ReportConfig {
31
24
  directory: string;
32
25
  }
33
26
 
34
- async function convert(
35
- format: string,
36
- output: string,
37
- reports: ReportConfig,
38
- config: WebhookConfig,
39
- json: object
40
- ) {
27
+ function getKeycloakConfig(config, argv): Options {
28
+ return {
29
+ clientId: config.clientId ? config.clientId : (argv.clientId as string),
30
+ clientSecret: config.clientSecret ? config.clientSecret : (argv.clientSecret as string),
31
+ rootUrl: config.url ? config.url : (argv.url as string),
32
+ useAuditingEndpoint: argv.useAuditingEndpoint == true || config.useAuditingEndpoint.toLowerCase() == 'true',
33
+ };
34
+ }
35
+
36
+ function convertData(config, argv, name: string, title: string, json: object) {
37
+ convert(
38
+ config.format ? config.format : (argv.format as string),
39
+ config.output ? config.output : (argv.output as string),
40
+ {
41
+ name,
42
+ directory: argv.reports ? (argv.reports as string) : config.reports,
43
+ },
44
+ new WebhookConfig(
45
+ config.webhookType ? config.webhookType : (argv.webhookType as string),
46
+ config.webhookUrl ? config.webhookUrl : (argv.webhookUrl as string),
47
+ title,
48
+ config.webhookMessage ? config.webhookMessage : (argv.webhookMessage as string)
49
+ ),
50
+ json
51
+ );
52
+ }
53
+
54
+ async function convert(format: string, output: string, reports: ReportConfig, config: WebhookConfig, json: object) {
41
55
  let outputContent: string;
42
56
  switch (format) {
43
57
  case 'csv':
@@ -52,23 +66,21 @@ async function convert(
52
66
  writeFileSync(
53
67
  path.join(
54
68
  `${reports.directory}`,
55
- `${reports.name}_${date.getFullYear()}-${
56
- date.getMonth() + 1
57
- }-${date.getDate()}.${format.toLowerCase()}`
69
+ `${reports.name}_${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}.${format.toLowerCase()}`
58
70
  ),
59
71
  outputContent
60
72
  );
61
73
  }
62
74
  switch (output) {
63
75
  case 'webhook':
76
+ if (!config.url) {
77
+ console.error('No valid Webhook URL given');
78
+ throw new Error('Please provide a valid --webhookUrl parameter');
79
+ }
64
80
  try {
65
- await post2Webhook(
66
- config.type,
67
- config.url,
68
- config.title,
69
- outputContent,
70
- config.message
71
- );
81
+ console.log(`Sending report via webhook to ${config.type} ....`);
82
+ await post2Webhook(config.type, config.url, config.title, outputContent, config.message);
83
+ console.log('Done sending.');
72
84
  } catch (e) {
73
85
  switch (e.code || e.message) {
74
86
  case 'Request failed with status code 400':
@@ -78,10 +90,7 @@ async function convert(
78
90
  console.error('Invalid Slack Webhook Payload. Check your params.');
79
91
  throw new Error('Invalid Slack Payload');
80
92
  default:
81
- console.error(
82
- `Error during sending webhook.(${e?.code})`,
83
- e?.original
84
- );
93
+ console.error(`Error during sending webhook.(${e?.code})`, e?.original);
85
94
  throw e;
86
95
  }
87
96
  }
@@ -99,32 +108,8 @@ yargs(hideBin(process.argv))
99
108
  // eslint-disable-next-line @typescript-eslint/no-empty-function
100
109
  () => {},
101
110
  async (argv) => {
102
- const users = await listUsers(<Options>{
103
- clientId: config.clientId ? config.clientId : (argv.clientId as string),
104
- clientSecret: config.clientSecret
105
- ? config.clientSecret
106
- : (argv.clientSecret as string),
107
- rootUrl: config.url ? config.url : (argv.url as string)
108
- });
109
- await convert(
110
- config.format ? config.format : (argv.format as string),
111
- config.output ? config.output : (argv.output as string),
112
- {
113
- name: 'user_listing',
114
- directory: argv.reports ? (argv.reports as string) : config.reports
115
- },
116
- new WebhookConfig(
117
- config.webhookType
118
- ? config.webhookType
119
- : (argv.webhookType as string),
120
- config.webhookUrl ? config.webhookUrl : (argv.webhookUrl as string),
121
- 'User Listing',
122
- config.webhookMessage
123
- ? config.webhookMessage
124
- : (argv.webhookMessage as string)
125
- ),
126
- users
127
- );
111
+ const users = await listUsers(getKeycloakConfig(config, argv));
112
+ convertData(config, argv, 'user_listing', 'User Listing', users);
128
113
  }
129
114
  )
130
115
  .command(
@@ -133,65 +118,47 @@ yargs(hideBin(process.argv))
133
118
  // eslint-disable-next-line @typescript-eslint/no-empty-function
134
119
  () => {},
135
120
  async (argv) => {
136
- const clients = await listClients(<Options>{
137
- clientId: config.clientId ? config.clientId : (argv.clientId as string),
138
- clientSecret: config.clientSecret
139
- ? config.clientSecret
140
- : (argv.clientSecret as string),
141
- rootUrl: config.url ? config.url : (argv.url as string)
142
- });
143
- await convert(
144
- config.format ? config.format : (argv.format as string),
145
- config.output ? config.output : (argv.output as string),
146
- {
147
- name: 'client_listing',
148
- directory: argv.reports ? (argv.reports as string) : config.reports
149
- },
150
- new WebhookConfig(
151
- config.webhookType
152
- ? config.webhookType
153
- : (argv.webhookType as string),
154
- config.webhookUrl ? config.webhookUrl : (argv.webhookUrl as string),
155
- 'Client Listing',
156
- config.webhookMessage
157
- ? config.webhookMessage
158
- : (argv.webhookMessage as string)
159
- ),
160
- clients
161
- );
121
+ const clients = await listClients(getKeycloakConfig(config, argv));
122
+ convertData(config, argv, 'client_listing', 'Client Listing', clients);
162
123
  }
163
124
  )
164
125
  .option('format', {
165
126
  alias: 'f',
166
127
  type: 'string',
167
128
  default: 'json',
168
- description: 'output format, e.g. JSON|CSV'
129
+ description: 'output format, e.g. JSON|CSV',
169
130
  })
170
131
  .option('output', {
171
132
  alias: 'o',
172
133
  type: 'string',
173
134
  default: 'stdout',
174
- description: 'output channel'
135
+ description: 'output channel',
175
136
  })
176
137
  .option('webhookType', {
177
138
  alias: 'w',
178
139
  type: 'string',
179
140
  default: 'slack',
180
- description: 'Webhook Type'
141
+ description: 'Webhook Type',
181
142
  })
182
143
  .option('webhookMessage', {
183
144
  alias: 'm',
184
145
  type: 'string',
185
- description: 'Webhook Message'
146
+ description: 'Webhook Message',
186
147
  })
187
148
  .option('webhookUrl', {
188
149
  alias: 't',
189
150
  type: 'string',
190
- description: 'Webhook URL'
151
+ description: 'Webhook URL',
191
152
  })
192
153
  .option('reports', {
193
154
  alias: 'r',
194
155
  type: 'string',
195
- description: 'Reports directory'
156
+ description: 'Reports directory',
157
+ })
158
+ .option('useAuditingEndpoint', {
159
+ alias: 'a',
160
+ type: 'boolean',
161
+ default: false,
162
+ description: 'use auditior rest endpoint',
196
163
  })
197
164
  .parse();
@@ -60,6 +60,11 @@
60
60
  "reports": {
61
61
  "type": "string",
62
62
  "description": "Reports directory"
63
+ },
64
+ "useAuditingEndpoint": {
65
+ "type": "boolean",
66
+ "default": "false",
67
+ "description": "Enable usage of keycloak reporter auditing endpoint"
63
68
  }
64
69
  }
65
- }
70
+ }
package/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { listUsers, listClients } from './src/cli.js';
1
+ export { listUsers, listClients } from './src/commands.js';
2
2
  export { Options } from './lib/client.js';
3
3
  export { convertJSON2CSV } from './lib/convert.js';
4
4
  export { post2Webhook } from './lib/output.js';
package/lib/client.ts CHANGED
@@ -1,21 +1,20 @@
1
- import { Issuer } from 'openid-client';
2
1
  import KcAdminClient from '@keycloak/keycloak-admin-client';
3
-
4
- // Token refresh interval 60 seconds
5
- const TOKEN_REFRESH = 60;
2
+ import { AuditClient } from '@continuoussecuritytooling/keycloak-auditor';
6
3
 
7
4
  export interface Options {
8
5
  clientId: string;
9
6
  clientSecret: string;
10
7
  rootUrl: string;
8
+ useAuditingEndpoint: boolean;
11
9
  }
12
10
 
13
- export async function createClient(options: Options): Promise<KcAdminClient> {
14
- const kcAdminClient = new KcAdminClient({
15
- baseUrl: options.rootUrl,
16
- realmName: 'master',
17
- });
18
-
11
+ export async function createClient(options: Options): Promise<KcAdminClient | AuditClient> {
12
+ const kcAdminClient = options.useAuditingEndpoint
13
+ ? new AuditClient(options.rootUrl, 'master')
14
+ : new KcAdminClient({
15
+ baseUrl: options.rootUrl,
16
+ realmName: 'master',
17
+ });
19
18
  try {
20
19
  // client login
21
20
  await kcAdminClient.auth({
@@ -24,35 +23,9 @@ export async function createClient(options: Options): Promise<KcAdminClient> {
24
23
  grantType: 'client_credentials',
25
24
  });
26
25
  } catch (e) {
27
- console.error(
28
- 'Check Client Config:',
29
- e.response ? e.response.data.error_description : e
30
- );
26
+ console.error('Check Client Config:', e.response ? e.response.data.error_description : e);
31
27
  return Promise.reject();
32
28
  }
33
29
 
34
- const keycloakIssuer = await Issuer.discover(
35
- `${options.rootUrl}/realms/master`
36
- );
37
-
38
- const client = new keycloakIssuer.Client({
39
- client_id: options.clientId,
40
- token_endpoint_auth_method: 'none', // to send only client_id in the header
41
- });
42
-
43
- // Use the grant type 'password'
44
- const tokenSet = await client.grant({
45
- client_id: options.clientId,
46
- client_secret: options.clientSecret,
47
- grant_type: 'client_credentials',
48
- });
49
-
50
- /*
51
- // TODO: FIXME - Periodically using refresh_token grant flow to get new access token here
52
- setInterval(async () => {
53
- const refreshToken = tokenSet.refresh_token;
54
- kcAdminClient.setAccessToken((await client.refresh(refreshToken)).access_token);
55
- }, TOKEN_REFRESH * 1000); */
56
-
57
30
  return new Promise((resolve) => resolve(kcAdminClient));
58
31
  }
package/lib/output.ts CHANGED
@@ -29,7 +29,7 @@ export async function post2Webhook(
29
29
  {
30
30
  contentType: 'application/vnd.microsoft.card.adaptive',
31
31
  content: {
32
- $schema: 'http://adaptivecards.io/schemas/adaptive-card.json',
32
+ $schema: 'https://adaptivecards.io/schemas/adaptive-card.json',
33
33
  type: 'AdaptiveCard',
34
34
  version: '1.2',
35
35
  body: [
@@ -68,7 +68,7 @@ export async function post2Webhook(
68
68
  }
69
69
  ],
70
70
  $schema:
71
- 'http://adaptivecards.io/schemas/adaptive-card.json'
71
+ 'https://adaptivecards.io/schemas/adaptive-card.json'
72
72
  }
73
73
  }
74
74
  ]
package/lib/user.ts CHANGED
@@ -1,4 +1,9 @@
1
1
  import KcAdminClient from '@keycloak/keycloak-admin-client';
2
+ import {
3
+ AuditClient,
4
+ AuditedClientRepresentation,
5
+ AuditedUserRepresentation,
6
+ } from '@continuoussecuritytooling/keycloak-auditor';
2
7
 
3
8
  export interface User {
4
9
  username: string;
@@ -21,79 +26,111 @@ export interface Client {
21
26
  }
22
27
 
23
28
  export async function clientListing(
24
- client: KcAdminClient
25
- ): Promise<Array<Client>> {
26
- const currentRealm = client.realmName;
27
- let realms;
28
- try {
29
- // iterate over realms
30
- realms = await client.realms.find();
31
- } catch (e) {
32
- console.error('Check Client role:', e.response.statusText);
33
- return Promise.reject();
34
- }
35
- let allClients = new Array<Client>();
36
- for (const realm of realms) {
37
- // switch realm
29
+ client: KcAdminClient | AuditClient
30
+ ): Promise<Array<Client | AuditedClientRepresentation>> {
31
+ let allClients = new Array<Client | AuditedClientRepresentation>();
32
+ if (client instanceof KcAdminClient) {
33
+ const currentRealm = client.realmName;
34
+ let realms;
35
+ try {
36
+ // iterate over realms
37
+ realms = await client.realms.find();
38
+ } catch (e) {
39
+ console.error('Check Client role:', e.response.statusText);
40
+ return Promise.reject();
41
+ }
42
+ for (const realm of realms) {
43
+ // switch realm
44
+ client.setConfig({
45
+ realmName: realm.realm,
46
+ });
47
+ const realmClients = new Array<Client>();
48
+ for (const user of await client.clients.find()) {
49
+ realmClients.push({
50
+ client: user.clientId,
51
+ id: user.id,
52
+ description: user.description,
53
+ realm: realm.realm,
54
+ enabled: user.enabled,
55
+ public: user.publicClient,
56
+ allowedOrigins: JSON.stringify(user.webOrigins),
57
+ });
58
+ }
59
+ allClients = [...allClients, ...realmClients];
60
+ }
61
+ // switch back to realm
38
62
  client.setConfig({
39
- realmName: realm.realm,
63
+ realmName: currentRealm,
40
64
  });
41
- const realmClients = new Array<Client>();
42
- for (const user of await client.clients.find()) {
43
- realmClients.push({
65
+ } else {
66
+ const clients = await client.clientListing();
67
+ for (const user of clients) {
68
+ allClients.push({
44
69
  client: user.clientId,
45
70
  id: user.id,
46
71
  description: user.description,
47
- realm: realm.realm,
72
+ realm: user.realm,
48
73
  enabled: user.enabled,
49
74
  public: user.publicClient,
75
+ lastLogin: user.lastLogin,
50
76
  allowedOrigins: JSON.stringify(user.webOrigins),
51
77
  });
52
78
  }
53
- allClients = [...allClients, ...realmClients];
54
79
  }
55
- // switch back to realm
56
- client.setConfig({
57
- realmName: currentRealm,
58
- });
59
-
60
80
  return new Promise((resolve) => resolve(allClients));
61
81
  }
62
82
 
63
- export async function userListing(client: KcAdminClient): Promise<Array<User>> {
64
- const currentRealm = client.realmName;
65
- let realms;
66
- // iterate over realms
67
- try {
68
- realms = await client.realms.find();
69
- } catch (e) {
70
- console.error('Check Client role:', e.response.statusText);
71
- return Promise.reject();
72
- }
73
- let allUsers = new Array<User>();
74
- for (const realm of realms) {
75
- // switch realm
83
+ export async function userListing(
84
+ client: KcAdminClient | AuditClient
85
+ ): Promise<Array<User | AuditedUserRepresentation>> {
86
+ let allUsers = new Array<User | AuditedUserRepresentation>();
87
+ if (client instanceof KcAdminClient) {
88
+ const currentRealm = client.realmName;
89
+ let realms;
90
+ // iterate over realms
91
+ try {
92
+ realms = await client.realms.find();
93
+ } catch (e) {
94
+ console.error('Check Client role:', e.response.statusText);
95
+ return Promise.reject();
96
+ }
97
+ for (const realm of realms) {
98
+ // switch realm
99
+ client.setConfig({
100
+ realmName: realm.realm,
101
+ });
102
+ const realmUsers = new Array<User>();
103
+ for (const user of await client.users.find()) {
104
+ realmUsers.push({
105
+ username: user.username,
106
+ id: user.id,
107
+ firstName: user.firstName,
108
+ lastName: user.lastName,
109
+ email: user.email,
110
+ realm: realm.realm,
111
+ enabled: user.enabled,
112
+ });
113
+ }
114
+ allUsers = [...allUsers, ...realmUsers];
115
+ }
116
+ // switch back to realm
76
117
  client.setConfig({
77
- realmName: realm.realm,
118
+ realmName: currentRealm,
78
119
  });
79
- const realmUsers = new Array<User>();
80
- for (const user of await client.users.find()) {
81
- realmUsers.push({
120
+ } else {
121
+ const users = await client.userListing();
122
+ for (const user of users) {
123
+ allUsers.push({
82
124
  username: user.username,
83
125
  id: user.id,
84
126
  firstName: user.firstName,
85
127
  lastName: user.lastName,
86
128
  email: user.email,
87
- realm: realm.realm,
129
+ realm: user.realm,
130
+ lastLogin: user.lastLogin,
88
131
  enabled: user.enabled,
89
132
  });
90
133
  }
91
- allUsers = [...allUsers, ...realmUsers];
92
134
  }
93
- // switch back to realm
94
- client.setConfig({
95
- realmName: currentRealm,
96
- });
97
-
98
135
  return new Promise((resolve) => resolve(allUsers));
99
136
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@continuoussecuritytooling/keycloak-reporter",
3
- "version": "0.5.1",
3
+ "version": "0.7.0",
4
4
  "description": "Reporting Tools for Keycloak",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -25,9 +25,10 @@
25
25
  },
26
26
  "homepage": "https://github.com/ContinuousSecurityTooling/keycloak-reporter#readme",
27
27
  "dependencies": {
28
+ "@continuoussecuritytooling/keycloak-auditor": "^1.1.0",
28
29
  "@json2csv/node": "^7.0.0",
29
30
  "@keycloak/keycloak-admin-client": "^22.0.0",
30
- "@slack/webhook": "^6.1.0",
31
+ "@slack/webhook": "^7.0.0",
31
32
  "ajv": "^8.12.0",
32
33
  "install": "^0.13.0",
33
34
  "ms-teams-webhook": "^2.0.2",
@@ -41,8 +42,8 @@
41
42
  "@types/jest": "^29.5.1",
42
43
  "@types/node": "^20.1.5",
43
44
  "@types/yargs": "^17.0.24",
44
- "@typescript-eslint/eslint-plugin": "^5.59.6",
45
- "@typescript-eslint/parser": "^5.59.6",
45
+ "@typescript-eslint/eslint-plugin": "^6.0.0",
46
+ "@typescript-eslint/parser": "^6.0.0",
46
47
  "eslint": "^8.40.0",
47
48
  "eslint-config-prettier": "^9.0.0",
48
49
  "eslint-plugin-prettier": "^5.0.0",
package/renovate.json CHANGED
@@ -1,6 +1,9 @@
1
1
  {
2
2
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3
- "extends": ["config:base", ":rebaseStalePrs"],
3
+ "labels": ["dependencies"],
4
+ "extends": ["config:base", ":dependencyDashboard", ":rebaseStalePrs"],
5
+ "automergeSchedule": ["after 5am and before 5pm every weekday"],
6
+ "separateMinorPatch": true,
4
7
  "platformAutomerge": true,
5
8
  "packageRules": [
6
9
  {
@@ -12,12 +15,16 @@
12
15
  ],
13
16
  "regexManagers": [
14
17
  {
15
- "description": "Update version entries in YAML files",
16
- "fileMatch": [".*y[a]?ml$"],
18
+ "description": "Update image variables in YAML files",
19
+ "fileMatch": ["\\.y[a]?ml$", "\\.y[a]?ml\\.tpl$"],
17
20
  "matchStrings": [
18
- "# renovate: datasource=(?<datasource>[a-z-]+?)(?: lookupName=(?<lookupName>.+?))?(?: versioning=(?<versioning>[a-z-]+?))?\\sRUN install-[a-z]+? (?<depName>[a-z-]+?) (?<currentValue>.+?)\\s"
21
+ "# renovate: datasource=(?<datasource>[a-z-]+?) depName=(?<depName>[^\\s]+?)(?: (lookupName|packageName)=(?<packageName>[^\\s]+?))?(?: versioning=(?<versioning>[^\\s]+?))?\\s.+?_version: (?<currentValue>.+?)\\s",
22
+ "# renovate: datasource=(?<datasource>[a-z-]+?) depName=(?<depName>[^\\s]+?)(?: (lookupName|packageName)=(?<packageName>[^\\s]+?))?(?: versioning=(?<versioning>[^\\s]+?))?\\s.+?-version: (?<currentValue>.+?)\\s",
23
+ "# renovate: datasource=(?<datasource>[a-z-]+?) depName=(?<depName>[^\\s]+?)(?: (lookupName|packageName)=(?<packageName>[^\\s]+?))?(?: versioning=(?<versioning>[^\\s]+?))?\\simage: (?<currentValue>.+?)\\s",
24
+ "# renovate: datasource=(?<datasource>[a-z-]+?) depName=(?<depName>.+?) versioning=(?<versioning>.+?)\\s\\s*image: (?<currentValue>.*)",
25
+ "# renovate: datasource=(?<datasource>[a-z-]+?) depName=(?<depName>.+?) \\s\\s*image: (?<currentValue>.*)"
19
26
  ],
20
- "versioningTemplate": "{{#if versioning}}{{versioning}}{{else}}semver{{/if}}"
27
+ "extractVersionTemplate": "{{#if extractVersion}}{{{extractVersion}}}{{else}}^v?(?<version>.+)${{/if}}"
21
28
  }
22
29
  ],
23
30
  "prHourlyLimit": 10
@@ -0,0 +1,27 @@
1
+ import KcAdminClient from '@keycloak/keycloak-admin-client';
2
+ import {
3
+ AuditClient,
4
+ AuditedClientRepresentation,
5
+ AuditedUserRepresentation,
6
+ } from '@continuoussecuritytooling/keycloak-auditor';
7
+ import { Options, createClient } from '../lib/client.js';
8
+ import { User, userListing, clientListing, Client } from '../lib/user.js';
9
+
10
+ function kcClient(options: Options): Promise<KcAdminClient | AuditClient> {
11
+ return createClient({
12
+ clientId: options.clientId,
13
+ clientSecret: options.clientSecret,
14
+ rootUrl: options.rootUrl,
15
+ useAuditingEndpoint: options.useAuditingEndpoint,
16
+ });
17
+ }
18
+
19
+ export async function listUsers(options: Options): Promise<Array<User | AuditedUserRepresentation>> {
20
+ const users = await userListing(await kcClient(options));
21
+ return new Promise((resolve) => resolve(users));
22
+ }
23
+
24
+ export async function listClients(options: Options): Promise<Array<Client | AuditedClientRepresentation>> {
25
+ const clients = await clientListing(await kcClient(options));
26
+ return new Promise((resolve) => resolve(clients));
27
+ }
package/src/config.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { assoc, pick, mergeAll, mergeDeepRight } from 'ramda';
2
- import Ajv from 'ajv';
3
2
  import path from 'path';
4
3
  import { fileURLToPath } from 'url';
5
4
  import fs from 'fs';
@@ -8,16 +7,15 @@ const schema = JSON.parse(
8
7
  fs.readFileSync(fileURLToPath(path.join(import.meta.url, '../../config/schema.json')), 'utf8')
9
8
  );
10
9
 
11
- const ajv = new Ajv.default();
12
- const ajvValidate = ajv.compile(schema);
13
-
14
10
  // import the config file
15
11
  function buildConfigFromFile(filePath) {
16
12
  if (!filePath) return {};
17
13
  const isAbsolutePath = filePath.charAt(0) === '/';
18
- return JSON.parse(isAbsolutePath
19
- ? fs.readFileSync(filePath, 'utf8')
20
- : fs.readFileSync(fileURLToPath(path.join(import.meta.url, '../config', filePath)), 'utf8'));
14
+ return JSON.parse(
15
+ isAbsolutePath
16
+ ? fs.readFileSync(filePath, 'utf8')
17
+ : fs.readFileSync(fileURLToPath(path.join(import.meta.url, '../config', filePath)), 'utf8')
18
+ );
21
19
  }
22
20
  // build an object using the defaults in the schema
23
21
  function buildDefaults(schema, definitions) {
@@ -50,19 +48,9 @@ function buildEnvironmentVariablesConfig(schema) {
50
48
  }
51
49
  }, {});
52
50
  }
53
-
54
- function validate(data) {
55
- const valid = ajvValidate(data);
56
- if (valid) return true;
57
- throw new Error(ajv.errorsText());
58
- }
59
-
60
51
  // merge the environment variables, config file values, and defaults
61
52
  const config = mergeAll(
62
- mergeDeepRight(
63
- buildDefaults(schema, schema.definitions),
64
- buildConfigFromFile(process.env.CONFIG_FILE)
65
- ),
53
+ mergeDeepRight(buildDefaults(schema, schema.definitions), buildConfigFromFile(process.env.CONFIG_FILE)),
66
54
  buildEnvironmentVariablesConfig(schema)
67
55
  );
68
56
 
package/config.json DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "url": "https://id.m13t.de",
3
- "clientId": "admin-cli",
4
- "clientSecret": "PWkJ98Atq36QFP5Z25YXJDWs4tvsGvkI",
5
- "output": "webhook",
6
- "webhookType": "teams",
7
- "webhookUrl": "https://m13t4mgmt.webhook.office.com/webhookb2/02950819-c8ca-4c83-9751-808d801e8810@09f6f098-3af9-474c-a398-d17786fff1bf/IncomingWebhook/b06222e267a04255aaa32a341acb1749/a4f92b5b-01c7-40a8-91ff-0695e08d76ff",
8
- "webhookMessage": "TEST"
9
- }