@continuoussecuritytooling/keycloak-reporter 0.6.0 → 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.
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.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Reporting Tools for Keycloak",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -25,6 +25,7 @@
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
31
  "@slack/webhook": "^7.0.0",
@@ -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/dist/cli.js DELETED
@@ -1,131 +0,0 @@
1
- #!/usr/bin/env node
2
- import { writeFileSync } from 'node:fs';
3
- import path from 'path';
4
- import yargs from 'yargs/yargs';
5
- import { hideBin } from 'yargs/helpers';
6
- import { listUsers, listClients, convertJSON2CSV, post2Webhook } from './index.js';
7
- import config from './src/config.js';
8
- class WebhookConfig {
9
- constructor(type, url, title, message) {
10
- this.type = type;
11
- this.url = url;
12
- this.title = title;
13
- this.message = message;
14
- }
15
- }
16
- class ReportConfig {
17
- }
18
- async function convert(format, output, reports, config, json) {
19
- let outputContent;
20
- switch (format) {
21
- case 'csv':
22
- outputContent = (await convertJSON2CSV(json)).toString();
23
- break;
24
- // defaulting to JSON
25
- default:
26
- outputContent = JSON.stringify(json);
27
- }
28
- if (reports.directory) {
29
- const date = new Date();
30
- writeFileSync(path.join(`${reports.directory}`, `${reports.name}_${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}.${format.toLowerCase()}`), outputContent);
31
- }
32
- switch (output) {
33
- case 'webhook':
34
- try {
35
- console.log(`Sending report via webhook to ${config.type} ....`);
36
- await post2Webhook(config.type, config.url, config.title, outputContent, config.message);
37
- console.log('Done sending.');
38
- }
39
- catch (e) {
40
- switch (e.code || e.message) {
41
- case 'Request failed with status code 400':
42
- console.error('Invalid Teams Webhook Payload. Check your params.');
43
- throw new Error('Invalid Teams Payload');
44
- case 'slack_webhook_http_error':
45
- console.error('Invalid Slack Webhook Payload. Check your params.');
46
- throw new Error('Invalid Slack Payload');
47
- default:
48
- console.error(`Error during sending webhook.(${e === null || e === void 0 ? void 0 : e.code})`, e === null || e === void 0 ? void 0 : e.original);
49
- throw e;
50
- }
51
- }
52
- break;
53
- // defaulting to standard out
54
- default:
55
- console.log(outputContent);
56
- }
57
- }
58
- yargs(hideBin(process.argv))
59
- .command('listUsers [url] [clientId] [clientSecret]', 'fetches all users in the realms.',
60
- // eslint-disable-next-line @typescript-eslint/no-empty-function
61
- () => { }, async (argv) => {
62
- const users = await listUsers({
63
- clientId: config.clientId ? config.clientId : argv.clientId,
64
- clientSecret: config.clientSecret
65
- ? config.clientSecret
66
- : argv.clientSecret,
67
- rootUrl: config.url ? config.url : argv.url
68
- });
69
- await convert(config.format ? config.format : argv.format, config.output ? config.output : argv.output, {
70
- name: 'user_listing',
71
- directory: argv.reports ? argv.reports : config.reports
72
- }, new WebhookConfig(config.webhookType
73
- ? config.webhookType
74
- : argv.webhookType, config.webhookUrl ? config.webhookUrl : argv.webhookUrl, 'User Listing', config.webhookMessage
75
- ? config.webhookMessage
76
- : argv.webhookMessage), users);
77
- })
78
- .command('listClients [url] [clientId] [clientSecret]', 'fetches all clients in the realms.',
79
- // eslint-disable-next-line @typescript-eslint/no-empty-function
80
- () => { }, async (argv) => {
81
- const clients = await listClients({
82
- clientId: config.clientId ? config.clientId : argv.clientId,
83
- clientSecret: config.clientSecret
84
- ? config.clientSecret
85
- : argv.clientSecret,
86
- rootUrl: config.url ? config.url : argv.url
87
- });
88
- await convert(config.format ? config.format : argv.format, config.output ? config.output : argv.output, {
89
- name: 'client_listing',
90
- directory: argv.reports ? argv.reports : config.reports
91
- }, new WebhookConfig(config.webhookType
92
- ? config.webhookType
93
- : argv.webhookType, config.webhookUrl ? config.webhookUrl : argv.webhookUrl, 'Client Listing', config.webhookMessage
94
- ? config.webhookMessage
95
- : argv.webhookMessage), clients);
96
- })
97
- .option('format', {
98
- alias: 'f',
99
- type: 'string',
100
- default: 'json',
101
- description: 'output format, e.g. JSON|CSV'
102
- })
103
- .option('output', {
104
- alias: 'o',
105
- type: 'string',
106
- default: 'stdout',
107
- description: 'output channel'
108
- })
109
- .option('webhookType', {
110
- alias: 'w',
111
- type: 'string',
112
- default: 'slack',
113
- description: 'Webhook Type'
114
- })
115
- .option('webhookMessage', {
116
- alias: 'm',
117
- type: 'string',
118
- description: 'Webhook Message'
119
- })
120
- .option('webhookUrl', {
121
- alias: 't',
122
- type: 'string',
123
- description: 'Webhook URL'
124
- })
125
- .option('reports', {
126
- alias: 'r',
127
- type: 'string',
128
- description: 'Reports directory'
129
- })
130
- .parse();
131
- //# sourceMappingURL=cli.js.map
package/dist/cli.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,KAAK,MAAM,aAAa,CAAC;AAChC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EACL,SAAS,EACT,WAAW,EAEX,eAAe,EACf,YAAY,EACb,MAAM,YAAY,CAAC;AACpB,OAAO,MAAM,MAAM,iBAAiB,CAAC;AAErC,MAAM,aAAa;IAKjB,YAAY,IAAY,EAAE,GAAW,EAAE,KAAa,EAAE,OAAgB;QACpE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF;AAED,MAAM,YAAY;CAGjB;AAED,KAAK,UAAU,OAAO,CACpB,MAAc,EACd,MAAc,EACd,OAAqB,EACrB,MAAqB,EACrB,IAAY;IAEZ,IAAI,aAAqB,CAAC;IAC1B,QAAQ,MAAM,EAAE;QACd,KAAK,KAAK;YACR,aAAa,GAAG,CAAC,MAAM,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;YACzD,MAAM;QACR,qBAAqB;QACrB;YACE,aAAa,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;KACxC;IACD,IAAI,OAAO,CAAC,SAAS,EAAE;QACrB,MAAM,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;QACxB,aAAa,CACX,IAAI,CAAC,IAAI,CACP,GAAG,OAAO,CAAC,SAAS,EAAE,EACtB,GAAG,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,WAAW,EAAE,IACnC,IAAI,CAAC,QAAQ,EAAE,GAAG,CACpB,IAAI,IAAI,CAAC,OAAO,EAAE,IAAI,MAAM,CAAC,WAAW,EAAE,EAAE,CAC7C,EACD,aAAa,CACd,CAAC;KACH;IACD,QAAQ,MAAM,EAAE;QACd,KAAK,SAAS;YACZ,IAAI;gBACF,OAAO,CAAC,GAAG,CAAC,iCAAiC,MAAM,CAAC,IAAI,OAAO,CAAC,CAAC;gBACjE,MAAM,YAAY,CAChB,MAAM,CAAC,IAAI,EACX,MAAM,CAAC,GAAG,EACV,MAAM,CAAC,KAAK,EACZ,aAAa,EACb,MAAM,CAAC,OAAO,CACf,CAAC;gBACF,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;aAC9B;YAAC,OAAO,CAAC,EAAE;gBACV,QAAQ,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,OAAO,EAAE;oBAC3B,KAAK,qCAAqC;wBACxC,OAAO,CAAC,KAAK,CAAC,mDAAmD,CAAC,CAAC;wBACnE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;oBAC3C,KAAK,0BAA0B;wBAC7B,OAAO,CAAC,KAAK,CAAC,mDAAmD,CAAC,CAAC;wBACnE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;oBAC3C;wBACE,OAAO,CAAC,KAAK,CACX,iCAAiC,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,IAAI,GAAG,EAC3C,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,QAAQ,CACZ,CAAC;wBACF,MAAM,CAAC,CAAC;iBACX;aACF;YACD,MAAM;QACR,6BAA6B;QAC7B;YACE,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;KAC9B;AACH,CAAC;AAED,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;KACzB,OAAO,CACN,2CAA2C,EAC3C,kCAAkC;AAClC,gEAAgE;AAChE,GAAG,EAAE,GAAE,CAAC,EACR,KAAK,EAAE,IAAI,EAAE,EAAE;IACb,MAAM,KAAK,GAAG,MAAM,SAAS,CAAU;QACrC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAE,IAAI,CAAC,QAAmB;QACvE,YAAY,EAAE,MAAM,CAAC,YAAY;YAC/B,CAAC,CAAC,MAAM,CAAC,YAAY;YACrB,CAAC,CAAE,IAAI,CAAC,YAAuB;QACjC,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAE,IAAI,CAAC,GAAc;KACxD,CAAC,CAAC;IACH,MAAM,OAAO,CACX,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAE,IAAI,CAAC,MAAiB,EACvD,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAE,IAAI,CAAC,MAAiB,EACvD;QACE,IAAI,EAAE,cAAc;QACpB,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAE,IAAI,CAAC,OAAkB,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO;KACpE,EACD,IAAI,aAAa,CACf,MAAM,CAAC,WAAW;QAChB,CAAC,CAAC,MAAM,CAAC,WAAW;QACpB,CAAC,CAAE,IAAI,CAAC,WAAsB,EAChC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAE,IAAI,CAAC,UAAqB,EACnE,cAAc,EACd,MAAM,CAAC,cAAc;QACnB,CAAC,CAAC,MAAM,CAAC,cAAc;QACvB,CAAC,CAAE,IAAI,CAAC,cAAyB,CACpC,EACD,KAAK,CACN,CAAC;AACJ,CAAC,CACF;KACA,OAAO,CACN,6CAA6C,EAC7C,oCAAoC;AACpC,gEAAgE;AAChE,GAAG,EAAE,GAAE,CAAC,EACR,KAAK,EAAE,IAAI,EAAE,EAAE;IACb,MAAM,OAAO,GAAG,MAAM,WAAW,CAAU;QACzC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAE,IAAI,CAAC,QAAmB;QACvE,YAAY,EAAE,MAAM,CAAC,YAAY;YAC/B,CAAC,CAAC,MAAM,CAAC,YAAY;YACrB,CAAC,CAAE,IAAI,CAAC,YAAuB;QACjC,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAE,IAAI,CAAC,GAAc;KACxD,CAAC,CAAC;IACH,MAAM,OAAO,CACX,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAE,IAAI,CAAC,MAAiB,EACvD,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAE,IAAI,CAAC,MAAiB,EACvD;QACE,IAAI,EAAE,gBAAgB;QACtB,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAE,IAAI,CAAC,OAAkB,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO;KACpE,EACD,IAAI,aAAa,CACf,MAAM,CAAC,WAAW;QAChB,CAAC,CAAC,MAAM,CAAC,WAAW;QACpB,CAAC,CAAE,IAAI,CAAC,WAAsB,EAChC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAE,IAAI,CAAC,UAAqB,EACnE,gBAAgB,EAChB,MAAM,CAAC,cAAc;QACnB,CAAC,CAAC,MAAM,CAAC,cAAc;QACvB,CAAC,CAAE,IAAI,CAAC,cAAyB,CACpC,EACD,OAAO,CACR,CAAC;AACJ,CAAC,CACF;KACA,MAAM,CAAC,QAAQ,EAAE;IAChB,KAAK,EAAE,GAAG;IACV,IAAI,EAAE,QAAQ;IACd,OAAO,EAAE,MAAM;IACf,WAAW,EAAE,8BAA8B;CAC5C,CAAC;KACD,MAAM,CAAC,QAAQ,EAAE;IAChB,KAAK,EAAE,GAAG;IACV,IAAI,EAAE,QAAQ;IACd,OAAO,EAAE,QAAQ;IACjB,WAAW,EAAE,gBAAgB;CAC9B,CAAC;KACD,MAAM,CAAC,aAAa,EAAE;IACrB,KAAK,EAAE,GAAG;IACV,IAAI,EAAE,QAAQ;IACd,OAAO,EAAE,OAAO;IAChB,WAAW,EAAE,cAAc;CAC5B,CAAC;KACD,MAAM,CAAC,gBAAgB,EAAE;IACxB,KAAK,EAAE,GAAG;IACV,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,iBAAiB;CAC/B,CAAC;KACD,MAAM,CAAC,YAAY,EAAE;IACpB,KAAK,EAAE,GAAG;IACV,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,aAAa;CAC3B,CAAC;KACD,MAAM,CAAC,SAAS,EAAE;IACjB,KAAK,EAAE,GAAG;IACV,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,mBAAmB;CACjC,CAAC;KACD,KAAK,EAAE,CAAC"}
@@ -1,65 +0,0 @@
1
- {
2
- "$id": "https://github.com/ContinuousSecurityTooling/keycloak-reporter/blob/main/config/schema.json",
3
- "$schema": "http://json-schema.org/draft-07/schema#",
4
- "title": "Keycloak Reporter Config",
5
- "type": "object",
6
- "definitions": {},
7
- "required": ["url", "clientId", "clientSecret"],
8
- "properties": {
9
- "command": {
10
- "type": "array",
11
- "items": {
12
- "type": "string",
13
- "enum": ["listClients", "listUsers"]
14
- }
15
- },
16
- "url": {
17
- "type": "string",
18
- "description": "Keycloak Server URL"
19
- },
20
- "clientId": {
21
- "type": "string",
22
- "description": "Keycloak Client used for reporting"
23
- },
24
- "clientSecret": {
25
- "type": "string",
26
- "description": "Keycloak Client Secret used for reporting"
27
- },
28
- "output": {
29
- "type": "array",
30
- "items": {
31
- "type": "string",
32
- "enum": ["webhook", "stdout"]
33
- },
34
- "description": "Output channel to use"
35
- },
36
- "format": {
37
- "type": "array",
38
- "items": {
39
- "type": "string",
40
- "enum": ["json", "csv"]
41
- },
42
- "description": "Report format"
43
- },
44
- "webhookType": {
45
- "type": "array",
46
- "items": {
47
- "type": "string",
48
- "enum": ["slack", "teams"]
49
- },
50
- "description": "Type of webhook"
51
- },
52
- "webhookMessage": {
53
- "type": "string",
54
- "description": "Message added to the webhook post"
55
- },
56
- "webhookUrl": {
57
- "type": "string",
58
- "description": "URL of the webhook"
59
- },
60
- "reports": {
61
- "type": "string",
62
- "description": "Reports directory"
63
- }
64
- }
65
- }
package/dist/index.js DELETED
@@ -1,4 +0,0 @@
1
- export { listUsers, listClients } from './src/cli.js';
2
- export { convertJSON2CSV } from './lib/convert.js';
3
- export { post2Webhook } from './lib/output.js';
4
- //# sourceMappingURL=index.js.map
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEtD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC"}
@@ -1,41 +0,0 @@
1
- import { Issuer } from 'openid-client';
2
- import KcAdminClient from '@keycloak/keycloak-admin-client';
3
- // Token refresh interval 60 seconds
4
- const TOKEN_REFRESH = 60;
5
- export async function createClient(options) {
6
- const kcAdminClient = new KcAdminClient({
7
- baseUrl: options.rootUrl,
8
- realmName: 'master',
9
- });
10
- try {
11
- // client login
12
- await kcAdminClient.auth({
13
- clientId: options.clientId,
14
- clientSecret: options.clientSecret,
15
- grantType: 'client_credentials',
16
- });
17
- }
18
- catch (e) {
19
- console.error('Check Client Config:', e.response ? e.response.data.error_description : e);
20
- return Promise.reject();
21
- }
22
- const keycloakIssuer = await Issuer.discover(`${options.rootUrl}/realms/master`);
23
- const client = new keycloakIssuer.Client({
24
- client_id: options.clientId,
25
- token_endpoint_auth_method: 'none', // to send only client_id in the header
26
- });
27
- // Use the grant type 'password'
28
- const tokenSet = await client.grant({
29
- client_id: options.clientId,
30
- client_secret: options.clientSecret,
31
- grant_type: 'client_credentials',
32
- });
33
- /*
34
- // TODO: FIXME - Periodically using refresh_token grant flow to get new access token here
35
- setInterval(async () => {
36
- const refreshToken = tokenSet.refresh_token;
37
- kcAdminClient.setAccessToken((await client.refresh(refreshToken)).access_token);
38
- }, TOKEN_REFRESH * 1000); */
39
- return new Promise((resolve) => resolve(kcAdminClient));
40
- }
41
- //# sourceMappingURL=client.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../lib/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,aAAa,MAAM,iCAAiC,CAAC;AAE5D,oCAAoC;AACpC,MAAM,aAAa,GAAG,EAAE,CAAC;AAQzB,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAgB;IACjD,MAAM,aAAa,GAAG,IAAI,aAAa,CAAC;QACtC,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,SAAS,EAAE,QAAQ;KACpB,CAAC,CAAC;IAEH,IAAI;QACF,eAAe;QACf,MAAM,aAAa,CAAC,IAAI,CAAC;YACvB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,SAAS,EAAE,oBAAoB;SAChC,CAAC,CAAC;KACJ;IAAC,OAAO,CAAC,EAAE;QACV,OAAO,CAAC,KAAK,CACX,sBAAsB,EACtB,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CACnD,CAAC;QACF,OAAO,OAAO,CAAC,MAAM,EAAE,CAAC;KACzB;IAED,MAAM,cAAc,GAAG,MAAM,MAAM,CAAC,QAAQ,CAC1C,GAAG,OAAO,CAAC,OAAO,gBAAgB,CACnC,CAAC;IAEF,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC,MAAM,CAAC;QACvC,SAAS,EAAE,OAAO,CAAC,QAAQ;QAC3B,0BAA0B,EAAE,MAAM,EAAE,uCAAuC;KAC5E,CAAC,CAAC;IAEH,gCAAgC;IAChC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC;QAClC,SAAS,EAAE,OAAO,CAAC,QAAQ;QAC3B,aAAa,EAAE,OAAO,CAAC,YAAY;QACnC,UAAU,EAAE,oBAAoB;KACjC,CAAC,CAAC;IAEH;;;;;gCAK4B;IAE5B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC;AAC1D,CAAC"}
@@ -1,9 +0,0 @@
1
- import { AsyncParser } from '@json2csv/node';
2
- export async function convertJSON2CSV(json) {
3
- const opts = {};
4
- const transformOpts = {};
5
- const asyncOpts = {};
6
- const parser = new AsyncParser(opts, transformOpts, asyncOpts);
7
- return await parser.parse(json).promise();
8
- }
9
- //# sourceMappingURL=convert.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"convert.js","sourceRoot":"","sources":["../../lib/convert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAY;IAChD,MAAM,IAAI,GAAG,EAAE,CAAC;IAChB,MAAM,aAAa,GAAG,EAAE,CAAC;IACzB,MAAM,SAAS,GAAG,EAAE,CAAC;IACrB,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,IAAI,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;IAC/D,OAAO,MAAM,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;AAC5C,CAAC"}