@flowcore/cli-plugin-iam 1.6.1 → 1.8.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 (47) hide show
  1. package/README.md +396 -7
  2. package/bin/dev.js +2 -2
  3. package/bin/run.js +2 -2
  4. package/dist/commands/assign/policy.d.ts +16 -0
  5. package/dist/commands/assign/policy.js +124 -0
  6. package/dist/commands/assign/role.d.ts +15 -0
  7. package/dist/commands/assign/role.js +98 -0
  8. package/dist/commands/create/policy.d.ts +16 -0
  9. package/dist/commands/create/policy.js +110 -0
  10. package/dist/commands/create/role.d.ts +14 -0
  11. package/dist/commands/create/role.js +78 -0
  12. package/dist/commands/edit/policy.js +3 -3
  13. package/dist/commands/edit/role.js +3 -3
  14. package/dist/commands/get/key-policies.d.ts +13 -0
  15. package/dist/commands/get/key-policies.js +79 -0
  16. package/dist/commands/get/key-roles.d.ts +13 -0
  17. package/dist/commands/get/key-roles.js +75 -0
  18. package/dist/commands/get/user-policies.d.ts +14 -0
  19. package/dist/commands/get/user-policies.js +94 -0
  20. package/dist/commands/get/user-roles.d.ts +14 -0
  21. package/dist/commands/get/user-roles.js +90 -0
  22. package/dist/commands/unassign/policy.d.ts +17 -0
  23. package/dist/commands/unassign/policy.js +143 -0
  24. package/dist/commands/unassign/role.d.ts +16 -0
  25. package/dist/commands/unassign/role.js +117 -0
  26. package/dist/commands/validate/key.d.ts +15 -0
  27. package/dist/commands/validate/key.js +106 -0
  28. package/dist/commands/validate/user.d.ts +15 -0
  29. package/dist/commands/validate/user.js +106 -0
  30. package/dist/index.d.ts +1 -1
  31. package/dist/index.js +1 -1
  32. package/dist/resource-types/iam-api-version.js +2 -2
  33. package/dist/resource-types/policy.resource.d.ts +8 -7
  34. package/dist/resource-types/policy.resource.js +7 -4
  35. package/dist/resource-types/role-binding.resource.d.ts +4 -4
  36. package/dist/resource-types/role.resource.d.ts +3 -3
  37. package/dist/resource-types/role.resource.js +2 -2
  38. package/dist/utils/combine-merge.util.d.ts +1 -1
  39. package/dist/utils/combine-merge.util.js +1 -1
  40. package/dist/utils/error-message.util.d.ts +1 -0
  41. package/dist/utils/error-message.util.js +4 -0
  42. package/dist/utils/fetch-manifest.util.js +2 -2
  43. package/dist/utils/read-pipe.util.js +5 -5
  44. package/oclif.manifest.json +964 -69
  45. package/package.json +18 -11
  46. package/.npmrc +0 -1
  47. package/CHANGELOG.md +0 -124
@@ -0,0 +1,98 @@
1
+ import { BaseCommand, ValidateLogin } from "@flowcore/cli-plugin-config";
2
+ import { ClientFactory } from "@flowcore/cli-plugin-core";
3
+ import { Args, Flags } from "@oclif/core";
4
+ import { tryit } from "radash";
5
+ import { OrganizationService } from "../../services/organization.service.js";
6
+ import { Api as IamApi } from "../../utils/clients/iam/Api.js";
7
+ import { getErrorMessage } from "../../utils/error-message.util.js";
8
+ export default class AssignRole extends BaseCommand {
9
+ static args = {
10
+ ROLE_NAME: Args.string({
11
+ description: "The name of the role to assign",
12
+ required: true,
13
+ }),
14
+ };
15
+ static description = "Assign an IAM role to a user or API key. Exactly one of --user or --key must be specified";
16
+ static examples = [
17
+ `$ flowcore iam assign role data-reader --user "auth0|abc123" -t my-org`,
18
+ `$ flowcore iam assign role data-reader --key "550e8400-e29b-41d4-a716-446655440000" -t my-org`,
19
+ `$ flowcore iam assign role data-reader --user "auth0|abc123" -t my-org -j`,
20
+ ];
21
+ static flags = {
22
+ json: Flags.boolean({
23
+ char: "j",
24
+ description: "Output result as JSON",
25
+ required: false,
26
+ }),
27
+ key: Flags.string({
28
+ description: "The API key ID to assign the role to",
29
+ exclusive: ["user"],
30
+ required: false,
31
+ }),
32
+ tenant: Flags.string({
33
+ char: "t",
34
+ description: "The tenant (organization slug) containing the role",
35
+ required: true,
36
+ }),
37
+ user: Flags.string({
38
+ description: "The user ID to assign the role to",
39
+ exclusive: ["key"],
40
+ required: false,
41
+ }),
42
+ };
43
+ async run() {
44
+ const { args, flags } = await this.parse(AssignRole);
45
+ if (!flags.user && !flags.key) {
46
+ this.logger.fatal("Exactly one of --user or --key is required");
47
+ }
48
+ const graphqlClient = await ClientFactory.create(this.cliConfiguration, this.logger, flags.json);
49
+ const organizationService = new OrganizationService(graphqlClient);
50
+ const organizations = await organizationService.getMyOrganizations();
51
+ const iamClient = new IamApi();
52
+ const config = this.cliConfiguration.getConfig();
53
+ const login = new ValidateLogin(config.login.url);
54
+ await login.validate(config, this.cliConfiguration, !flags.json);
55
+ const { auth } = config;
56
+ if (!auth?.accessToken) {
57
+ this.logger.fatal("Not logged in, run 'flowcore login'");
58
+ }
59
+ const organization = organizations.me.organizations.find((org) => org.organization.org === flags.tenant);
60
+ if (!organization) {
61
+ this.logger.fatal(`Organization ${flags.tenant} not found, or you are not a member`);
62
+ }
63
+ const authHeaders = {
64
+ headers: { Authorization: `Bearer ${auth?.accessToken}` },
65
+ };
66
+ // Resolve role name to ID
67
+ const [rolesErr, roles] = await tryit(iamClient.getApiV1RoleAssociationsOrganizationByOrganizationId)(organization.organization.id, authHeaders);
68
+ if (rolesErr) {
69
+ this.logger.fatal(`Failed to get roles: ${rolesErr.message}`);
70
+ }
71
+ const role = roles.data.find((r) => r.name === args.ROLE_NAME);
72
+ if (!role) {
73
+ this.logger.fatal(`Role "${args.ROLE_NAME}" not found in tenant: ${flags.tenant}`);
74
+ }
75
+ let result;
76
+ if (flags.user) {
77
+ const [err, response] = await tryit(iamClient.postApiV1RoleAssociationsUserByUserId)(flags.user, { roleId: role.id }, authHeaders);
78
+ if (err) {
79
+ this.logger.fatal(`Failed to assign role to user: ${getErrorMessage(err)}`);
80
+ }
81
+ result = response.data;
82
+ }
83
+ else if (flags.key) {
84
+ const [err, response] = await tryit(iamClient.postApiV1RoleAssociationsKeyByKeyId)(flags.key, { roleId: role.id }, authHeaders);
85
+ if (err) {
86
+ this.logger.fatal(`Failed to assign role to key: ${getErrorMessage(err)}`);
87
+ }
88
+ result = response.data;
89
+ }
90
+ if (flags.json) {
91
+ console.log(JSON.stringify(result, null, 2));
92
+ }
93
+ else {
94
+ const target = flags.user ? `user ${flags.user}` : `key ${flags.key}`;
95
+ this.logger.info(`Role "${args.ROLE_NAME}" assigned to ${target}`);
96
+ }
97
+ }
98
+ }
@@ -0,0 +1,16 @@
1
+ import { BaseCommand } from "@flowcore/cli-plugin-config";
2
+ export default class CreatePolicy extends BaseCommand<typeof CreatePolicy> {
3
+ static args: {
4
+ NAME: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ static flags: {
9
+ description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ documents: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ tenant: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
13
+ version: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
14
+ };
15
+ run(): Promise<void>;
16
+ }
@@ -0,0 +1,110 @@
1
+ import { BaseCommand, ValidateLogin } from "@flowcore/cli-plugin-config";
2
+ import { ClientFactory } from "@flowcore/cli-plugin-core";
3
+ import { Args, Flags } from "@oclif/core";
4
+ import { tryit } from "radash";
5
+ import { OrganizationService } from "../../services/organization.service.js";
6
+ import { Api as IamApi } from "../../utils/clients/iam/Api.js";
7
+ import { readPipe } from "../../utils/read-pipe.util.js";
8
+ import { getErrorMessage } from "../../utils/error-message.util.js";
9
+ export default class CreatePolicy extends BaseCommand {
10
+ static args = {
11
+ NAME: Args.string({
12
+ description: "The name of the policy to create",
13
+ required: true,
14
+ }),
15
+ };
16
+ static description = "Create a new IAM policy with the specified name, version, and policy documents defining resource access rules";
17
+ static examples = [
18
+ `$ flowcore iam create policy read-access -t my-org --version "2024-01-01" --documents '[{"resource":"frn::my-org:data-core/*","action":["read","fetch"]}]'`,
19
+ `$ cat docs.json | flowcore iam create policy read-access -t my-org --version "2024-01-01" --documents -`,
20
+ `$ flowcore iam create policy admin-access -t my-org --version "2024-01-01" --description "Full admin access" --documents '[{"resource":"frn::my-org:*","action":"*"}]' -j`,
21
+ ];
22
+ static flags = {
23
+ description: Flags.string({
24
+ description: "A description of the policy",
25
+ required: false,
26
+ }),
27
+ documents: Flags.string({
28
+ description: 'JSON array of policy documents, each with "resource" and "action" fields. Use "-" to read from stdin',
29
+ required: true,
30
+ }),
31
+ json: Flags.boolean({
32
+ char: "j",
33
+ description: "Output result as JSON",
34
+ required: false,
35
+ }),
36
+ tenant: Flags.string({
37
+ char: "t",
38
+ description: "The tenant (organization slug) to create the policy in",
39
+ required: true,
40
+ }),
41
+ version: Flags.string({
42
+ description: "The version of the policy (e.g. 2024-01-01)",
43
+ required: true,
44
+ }),
45
+ };
46
+ async run() {
47
+ const { args, flags } = await this.parse(CreatePolicy);
48
+ const graphqlClient = await ClientFactory.create(this.cliConfiguration, this.logger, flags.json);
49
+ const organizationService = new OrganizationService(graphqlClient);
50
+ const organizations = await organizationService.getMyOrganizations();
51
+ const iamClient = new IamApi();
52
+ const config = this.cliConfiguration.getConfig();
53
+ const login = new ValidateLogin(config.login.url);
54
+ await login.validate(config, this.cliConfiguration, !flags.json);
55
+ const { auth } = config;
56
+ if (!auth?.accessToken) {
57
+ this.logger.fatal("Not logged in, run 'flowcore login'");
58
+ }
59
+ const organization = organizations.me.organizations.find((org) => org.organization.org === flags.tenant);
60
+ if (!organization) {
61
+ this.logger.fatal(`Organization ${flags.tenant} not found, or you are not a member`);
62
+ }
63
+ let policyDocuments;
64
+ try {
65
+ if (flags.documents === "-") {
66
+ const stdinData = await readPipe();
67
+ if (!stdinData) {
68
+ this.logger.fatal("No data received from stdin. Pipe JSON documents to stdin when using --documents -");
69
+ }
70
+ policyDocuments = JSON.parse(stdinData);
71
+ }
72
+ else {
73
+ policyDocuments = JSON.parse(flags.documents);
74
+ }
75
+ }
76
+ catch {
77
+ this.logger.fatal('Failed to parse --documents flag. Expected a valid JSON array, e.g. \'[{"resource":"frn::org:data-core/*","action":["read"]}]\'');
78
+ }
79
+ if (!Array.isArray(policyDocuments) || policyDocuments.length === 0) {
80
+ this.logger.fatal("--documents must be a non-empty JSON array of policy documents");
81
+ }
82
+ const [err, response] = await tryit(iamClient.postApiV1Policies)({
83
+ description: flags.description,
84
+ name: args.NAME,
85
+ organizationId: organization.organization.id,
86
+ policyDocuments,
87
+ version: flags.version,
88
+ }, {
89
+ headers: {
90
+ Authorization: `Bearer ${auth?.accessToken}`,
91
+ },
92
+ });
93
+ if (err) {
94
+ this.logger.fatal(`Failed to create policy: ${getErrorMessage(err)}`);
95
+ }
96
+ if (flags.json) {
97
+ console.log(JSON.stringify(response.data, null, 2));
98
+ }
99
+ else {
100
+ this.ui
101
+ .sticker()
102
+ .add(`Policy ${this.ui.colors.green(args.NAME)} created`)
103
+ .add("")
104
+ .add(`ID: ${this.ui.colors.green(response.data.id ?? "unknown")}`)
105
+ .add(`Version: ${this.ui.colors.green(flags.version)}`)
106
+ .add(`Organization: ${this.ui.colors.green(flags.tenant)}`)
107
+ .render();
108
+ }
109
+ }
110
+ }
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from "@flowcore/cli-plugin-config";
2
+ export default class CreateRole extends BaseCommand<typeof CreateRole> {
3
+ static args: {
4
+ NAME: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ static flags: {
9
+ description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ tenant: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
+ };
13
+ run(): Promise<void>;
14
+ }
@@ -0,0 +1,78 @@
1
+ import { BaseCommand, ValidateLogin } from "@flowcore/cli-plugin-config";
2
+ import { ClientFactory } from "@flowcore/cli-plugin-core";
3
+ import { Args, Flags } from "@oclif/core";
4
+ import { tryit } from "radash";
5
+ import { OrganizationService } from "../../services/organization.service.js";
6
+ import { Api as IamApi } from "../../utils/clients/iam/Api.js";
7
+ import { getErrorMessage } from "../../utils/error-message.util.js";
8
+ export default class CreateRole extends BaseCommand {
9
+ static args = {
10
+ NAME: Args.string({
11
+ description: "The name of the role to create",
12
+ required: true,
13
+ }),
14
+ };
15
+ static description = "Create a new IAM role with the specified name and optional description";
16
+ static examples = [
17
+ `$ flowcore iam create role data-reader -t my-org --description "Read-only data access"`,
18
+ "$ flowcore iam create role admin -t my-org -j",
19
+ ];
20
+ static flags = {
21
+ description: Flags.string({
22
+ description: "A description of the role",
23
+ required: false,
24
+ }),
25
+ json: Flags.boolean({
26
+ char: "j",
27
+ description: "Output result as JSON",
28
+ required: false,
29
+ }),
30
+ tenant: Flags.string({
31
+ char: "t",
32
+ description: "The tenant (organization slug) to create the role in",
33
+ required: true,
34
+ }),
35
+ };
36
+ async run() {
37
+ const { args, flags } = await this.parse(CreateRole);
38
+ const graphqlClient = await ClientFactory.create(this.cliConfiguration, this.logger, flags.json);
39
+ const organizationService = new OrganizationService(graphqlClient);
40
+ const organizations = await organizationService.getMyOrganizations();
41
+ const iamClient = new IamApi();
42
+ const config = this.cliConfiguration.getConfig();
43
+ const login = new ValidateLogin(config.login.url);
44
+ await login.validate(config, this.cliConfiguration, !flags.json);
45
+ const { auth } = config;
46
+ if (!auth?.accessToken) {
47
+ this.logger.fatal("Not logged in, run 'flowcore login'");
48
+ }
49
+ const organization = organizations.me.organizations.find((org) => org.organization.org === flags.tenant);
50
+ if (!organization) {
51
+ this.logger.fatal(`Organization ${flags.tenant} not found, or you are not a member`);
52
+ }
53
+ const [err, response] = await tryit(iamClient.postApiV1Roles)({
54
+ description: flags.description,
55
+ name: args.NAME,
56
+ organizationId: organization.organization.id,
57
+ }, {
58
+ headers: {
59
+ Authorization: `Bearer ${auth?.accessToken}`,
60
+ },
61
+ });
62
+ if (err) {
63
+ this.logger.fatal(`Failed to create role: ${getErrorMessage(err)}`);
64
+ }
65
+ if (flags.json) {
66
+ console.log(JSON.stringify(response.data, null, 2));
67
+ }
68
+ else {
69
+ this.ui
70
+ .sticker()
71
+ .add(`Role ${this.ui.colors.green(args.NAME)} created`)
72
+ .add("")
73
+ .add(`ID: ${this.ui.colors.green(response.data.id ?? "unknown")}`)
74
+ .add(`Organization: ${this.ui.colors.green(flags.tenant)}`)
75
+ .render();
76
+ }
77
+ }
78
+ }
@@ -1,10 +1,10 @@
1
- import { BaseCommand, ValidateLogin } from "@flowcore/cli-plugin-config";
2
- import { ClientFactory } from "@flowcore/cli-plugin-core";
3
- import { Args, Flags } from "@oclif/core";
4
1
  import { spawn } from "node:child_process";
5
2
  import { unlink, writeFile } from "node:fs/promises";
6
3
  import { tmpdir } from "node:os";
7
4
  import { join } from "node:path";
5
+ import { BaseCommand, ValidateLogin } from "@flowcore/cli-plugin-config";
6
+ import { ClientFactory } from "@flowcore/cli-plugin-core";
7
+ import { Args, Flags } from "@oclif/core";
8
8
  import { tryit } from "radash";
9
9
  import { v4 as uuidv4 } from "uuid";
10
10
  import { OrganizationService } from "../../services/organization.service.js";
@@ -1,10 +1,10 @@
1
- import { BaseCommand, ValidateLogin } from "@flowcore/cli-plugin-config";
2
- import { ClientFactory } from "@flowcore/cli-plugin-core";
3
- import { Args, Flags } from "@oclif/core";
4
1
  import { spawn } from "node:child_process";
5
2
  import { unlink, writeFile } from "node:fs/promises";
6
3
  import { tmpdir } from "node:os";
7
4
  import { join } from "node:path";
5
+ import { BaseCommand, ValidateLogin } from "@flowcore/cli-plugin-config";
6
+ import { ClientFactory } from "@flowcore/cli-plugin-core";
7
+ import { Args, Flags } from "@oclif/core";
8
8
  import { tryit } from "radash";
9
9
  import { v4 as uuidv4 } from "uuid";
10
10
  import { OrganizationService } from "../../services/organization.service.js";
@@ -0,0 +1,13 @@
1
+ import { BaseCommand } from "@flowcore/cli-plugin-config";
2
+ export default class GetKeyPolicies extends BaseCommand<typeof GetKeyPolicies> {
3
+ static args: {
4
+ KEY_ID: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ static flags: {
9
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ wide: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ };
12
+ run(): Promise<void>;
13
+ }
@@ -0,0 +1,79 @@
1
+ import { BaseCommand, ValidateLogin } from "@flowcore/cli-plugin-config";
2
+ import { ClientFactory } from "@flowcore/cli-plugin-core";
3
+ import { Args, Flags } from "@oclif/core";
4
+ import { tryit } from "radash";
5
+ import { Api as IamApi } from "../../utils/clients/iam/Api.js";
6
+ import { getErrorMessage } from "../../utils/error-message.util.js";
7
+ export default class GetKeyPolicies extends BaseCommand {
8
+ static args = {
9
+ KEY_ID: Args.string({
10
+ description: "The API key ID to get policies for",
11
+ required: true,
12
+ }),
13
+ };
14
+ static description = "List all IAM policies assigned to a specific API key";
15
+ static examples = [
16
+ `$ flowcore iam get key-policies "550e8400-e29b-41d4-a716-446655440000"`,
17
+ `$ flowcore iam get key-policies "550e8400-e29b-41d4-a716-446655440000" -j`,
18
+ `$ flowcore iam get key-policies "550e8400-e29b-41d4-a716-446655440000" -w`,
19
+ ];
20
+ static flags = {
21
+ json: Flags.boolean({
22
+ char: "j",
23
+ description: "Output result as JSON",
24
+ required: false,
25
+ }),
26
+ wide: Flags.boolean({
27
+ char: "w",
28
+ description: "Show additional columns in table output",
29
+ required: false,
30
+ }),
31
+ };
32
+ async run() {
33
+ const { args, flags } = await this.parse(GetKeyPolicies);
34
+ await ClientFactory.create(this.cliConfiguration, this.logger, flags.json);
35
+ const iamClient = new IamApi();
36
+ const config = this.cliConfiguration.getConfig();
37
+ const login = new ValidateLogin(config.login.url);
38
+ await login.validate(config, this.cliConfiguration, !flags.json);
39
+ const { auth } = config;
40
+ if (!auth?.accessToken) {
41
+ this.logger.fatal("Not logged in, run 'flowcore login'");
42
+ }
43
+ const [err, response] = await tryit(iamClient.getApiV1PolicyAssociationsKeyByKeyId)(args.KEY_ID, {
44
+ headers: {
45
+ Authorization: `Bearer ${auth?.accessToken}`,
46
+ },
47
+ });
48
+ if (err) {
49
+ this.logger.fatal(`Failed to get key policies: ${getErrorMessage(err)}`);
50
+ }
51
+ const policies = response.data;
52
+ if (flags.json) {
53
+ console.log(JSON.stringify(policies, null, 2));
54
+ }
55
+ else {
56
+ const headers = [
57
+ "Policy ID",
58
+ "Name",
59
+ "Version",
60
+ "Description",
61
+ "Documents",
62
+ ...(flags.wide ? ["Organization ID"] : []),
63
+ ];
64
+ let table = this.ui.table().head(headers);
65
+ for (const policy of policies) {
66
+ const row = [
67
+ policy.id,
68
+ policy.name,
69
+ policy.version,
70
+ policy.description ?? "",
71
+ String(policy.policyDocuments.length),
72
+ ...(flags.wide ? [policy.organizationId] : []),
73
+ ];
74
+ table = table.row(row);
75
+ }
76
+ table.render();
77
+ }
78
+ }
79
+ }
@@ -0,0 +1,13 @@
1
+ import { BaseCommand } from "@flowcore/cli-plugin-config";
2
+ export default class GetKeyRoles extends BaseCommand<typeof GetKeyRoles> {
3
+ static args: {
4
+ KEY_ID: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ static flags: {
9
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ wide: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ };
12
+ run(): Promise<void>;
13
+ }
@@ -0,0 +1,75 @@
1
+ import { BaseCommand, ValidateLogin } from "@flowcore/cli-plugin-config";
2
+ import { ClientFactory } from "@flowcore/cli-plugin-core";
3
+ import { Args, Flags } from "@oclif/core";
4
+ import { tryit } from "radash";
5
+ import { Api as IamApi } from "../../utils/clients/iam/Api.js";
6
+ import { getErrorMessage } from "../../utils/error-message.util.js";
7
+ export default class GetKeyRoles extends BaseCommand {
8
+ static args = {
9
+ KEY_ID: Args.string({
10
+ description: "The API key ID to get roles for",
11
+ required: true,
12
+ }),
13
+ };
14
+ static description = "List all IAM roles assigned to a specific API key";
15
+ static examples = [
16
+ `$ flowcore iam get key-roles "550e8400-e29b-41d4-a716-446655440000"`,
17
+ `$ flowcore iam get key-roles "550e8400-e29b-41d4-a716-446655440000" -j`,
18
+ `$ flowcore iam get key-roles "550e8400-e29b-41d4-a716-446655440000" -w`,
19
+ ];
20
+ static flags = {
21
+ json: Flags.boolean({
22
+ char: "j",
23
+ description: "Output result as JSON",
24
+ required: false,
25
+ }),
26
+ wide: Flags.boolean({
27
+ char: "w",
28
+ description: "Show additional columns in table output",
29
+ required: false,
30
+ }),
31
+ };
32
+ async run() {
33
+ const { args, flags } = await this.parse(GetKeyRoles);
34
+ await ClientFactory.create(this.cliConfiguration, this.logger, flags.json);
35
+ const iamClient = new IamApi();
36
+ const config = this.cliConfiguration.getConfig();
37
+ const login = new ValidateLogin(config.login.url);
38
+ await login.validate(config, this.cliConfiguration, !flags.json);
39
+ const { auth } = config;
40
+ if (!auth?.accessToken) {
41
+ this.logger.fatal("Not logged in, run 'flowcore login'");
42
+ }
43
+ const [err, response] = await tryit(iamClient.getApiV1RoleAssociationsKeyByKeyId)(args.KEY_ID, {
44
+ headers: {
45
+ Authorization: `Bearer ${auth?.accessToken}`,
46
+ },
47
+ });
48
+ if (err) {
49
+ this.logger.fatal(`Failed to get key roles: ${getErrorMessage(err)}`);
50
+ }
51
+ const roles = response.data;
52
+ if (flags.json) {
53
+ console.log(JSON.stringify(roles, null, 2));
54
+ }
55
+ else {
56
+ const headers = [
57
+ "Role ID",
58
+ "Name",
59
+ "Description",
60
+ ...(flags.wide ? ["Organization ID"] : []),
61
+ ];
62
+ let table = this.ui.table().head(headers);
63
+ for (const role of roles) {
64
+ const row = [
65
+ role.id,
66
+ role.name,
67
+ role.description ?? "",
68
+ ...(flags.wide ? [role.organizationId] : []),
69
+ ];
70
+ table = table.row(row);
71
+ }
72
+ table.render();
73
+ }
74
+ }
75
+ }
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from "@flowcore/cli-plugin-config";
2
+ export default class GetUserPolicies extends BaseCommand<typeof GetUserPolicies> {
3
+ static args: {
4
+ USER_ID: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ static flags: {
9
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ tenant: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ wide: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ };
13
+ run(): Promise<void>;
14
+ }
@@ -0,0 +1,94 @@
1
+ import { BaseCommand, ValidateLogin } from "@flowcore/cli-plugin-config";
2
+ import { ClientFactory } from "@flowcore/cli-plugin-core";
3
+ import { Args, Flags } from "@oclif/core";
4
+ import { tryit } from "radash";
5
+ import { OrganizationService } from "../../services/organization.service.js";
6
+ import { Api as IamApi } from "../../utils/clients/iam/Api.js";
7
+ import { getErrorMessage } from "../../utils/error-message.util.js";
8
+ export default class GetUserPolicies extends BaseCommand {
9
+ static args = {
10
+ USER_ID: Args.string({
11
+ description: "The user ID to get policies for (e.g. auth0|abc123)",
12
+ required: true,
13
+ }),
14
+ };
15
+ static description = "List all IAM policies assigned to a specific user, optionally scoped to a tenant";
16
+ static examples = [
17
+ `$ flowcore iam get user-policies "auth0|abc123" -t my-org`,
18
+ `$ flowcore iam get user-policies "auth0|abc123" -j`,
19
+ `$ flowcore iam get user-policies "auth0|abc123" -t my-org -w`,
20
+ ];
21
+ static flags = {
22
+ json: Flags.boolean({
23
+ char: "j",
24
+ description: "Output result as JSON",
25
+ required: false,
26
+ }),
27
+ tenant: Flags.string({
28
+ char: "t",
29
+ description: "Scope results to a specific tenant (organization slug)",
30
+ required: false,
31
+ }),
32
+ wide: Flags.boolean({
33
+ char: "w",
34
+ description: "Show additional columns in table output",
35
+ required: false,
36
+ }),
37
+ };
38
+ async run() {
39
+ const { args, flags } = await this.parse(GetUserPolicies);
40
+ const graphqlClient = await ClientFactory.create(this.cliConfiguration, this.logger, flags.json);
41
+ const organizationService = new OrganizationService(graphqlClient);
42
+ const organizations = await organizationService.getMyOrganizations();
43
+ const iamClient = new IamApi();
44
+ const config = this.cliConfiguration.getConfig();
45
+ const login = new ValidateLogin(config.login.url);
46
+ await login.validate(config, this.cliConfiguration, !flags.json);
47
+ const { auth } = config;
48
+ if (!auth?.accessToken) {
49
+ this.logger.fatal("Not logged in, run 'flowcore login'");
50
+ }
51
+ const authHeaders = {
52
+ headers: { Authorization: `Bearer ${auth?.accessToken}` },
53
+ };
54
+ let organizationId;
55
+ if (flags.tenant) {
56
+ const organization = organizations.me.organizations.find((org) => org.organization.org === flags.tenant);
57
+ if (!organization) {
58
+ this.logger.fatal(`Organization ${flags.tenant} not found, or you are not a member`);
59
+ }
60
+ organizationId = organization.organization.id;
61
+ }
62
+ const [err, response] = await tryit(iamClient.getApiV1PolicyAssociationsUserByUserId)(args.USER_ID, organizationId ? { organizationId } : undefined, authHeaders);
63
+ if (err) {
64
+ this.logger.fatal(`Failed to get user policies: ${getErrorMessage(err)}`);
65
+ }
66
+ const policies = response.data;
67
+ if (flags.json) {
68
+ console.log(JSON.stringify(policies, null, 2));
69
+ }
70
+ else {
71
+ const headers = [
72
+ "Policy ID",
73
+ "Name",
74
+ "Version",
75
+ "Description",
76
+ "Documents",
77
+ ...(flags.wide ? ["Organization ID"] : []),
78
+ ];
79
+ let table = this.ui.table().head(headers);
80
+ for (const policy of policies) {
81
+ const row = [
82
+ policy.id,
83
+ policy.name,
84
+ policy.version,
85
+ policy.description ?? "",
86
+ String(policy.policyDocuments.length),
87
+ ...(flags.wide ? [policy.organizationId] : []),
88
+ ];
89
+ table = table.row(row);
90
+ }
91
+ table.render();
92
+ }
93
+ }
94
+ }
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from "@flowcore/cli-plugin-config";
2
+ export default class GetUserRoles extends BaseCommand<typeof GetUserRoles> {
3
+ static args: {
4
+ USER_ID: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ static flags: {
9
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ tenant: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ wide: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ };
13
+ run(): Promise<void>;
14
+ }