@flowcore/cli-plugin-iam 1.4.0 → 1.6.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/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.6.0](https://github.com/flowcore-io/cli-plugin-iam/compare/v1.5.0...v1.6.0) (2025-02-16)
4
+
5
+
6
+ ### Features
7
+
8
+ * add edit role command to modify role details interactively ([d67eb3a](https://github.com/flowcore-io/cli-plugin-iam/commit/d67eb3a28d25ca7cac0683fb9aeb6f478386f8e8))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * :rotating_light: fixed linting errors ([7f481bd](https://github.com/flowcore-io/cli-plugin-iam/commit/7f481bd90bf67edfbd483dbd351b52ebba0d0bd2))
14
+
15
+ ## [1.5.0](https://github.com/flowcore-io/cli-plugin-iam/compare/v1.4.0...v1.5.0) (2025-02-16)
16
+
17
+
18
+ ### Features
19
+
20
+ * add edit policy command to modify policy details interactively ([061e036](https://github.com/flowcore-io/cli-plugin-iam/commit/061e036e07fb076017543b8ed232f9077d615a72))
21
+
22
+
23
+ ### Bug Fixes
24
+
25
+ * :rotating_light: fixed linting errors ([ff60a54](https://github.com/flowcore-io/cli-plugin-iam/commit/ff60a54131d35bfbc7f8b0ca3985e6a6528be8ae))
26
+ * update GitHub Actions runners to use blacksmith infrastructure ([826d577](https://github.com/flowcore-io/cli-plugin-iam/commit/826d577d8145442b1e35b7268a6f7a5482047509))
27
+
3
28
  ## [1.4.0](https://github.com/flowcore-io/cli-plugin-iam/compare/v1.3.1...v1.4.0) (2024-11-08)
4
29
 
5
30
 
package/README.md CHANGED
@@ -18,7 +18,7 @@ $ npm install -g @flowcore/cli-plugin-iam
18
18
  $ iam COMMAND
19
19
  running command...
20
20
  $ iam (--version)
21
- @flowcore/cli-plugin-iam/1.4.0 linux-x64 node-v22.10.0
21
+ @flowcore/cli-plugin-iam/1.6.0 linux-x64 node-v20.16.0
22
22
  $ iam --help [COMMAND]
23
23
  USAGE
24
24
  $ iam COMMAND
@@ -29,6 +29,8 @@ USAGE
29
29
  <!-- commands -->
30
30
  * [`iam delete policy NAME`](#iam-delete-policy-name)
31
31
  * [`iam delete role NAME`](#iam-delete-role-name)
32
+ * [`iam edit policy NAME`](#iam-edit-policy-name)
33
+ * [`iam edit role NAME`](#iam-edit-role-name)
32
34
  * [`iam get policy [NAME]`](#iam-get-policy-name)
33
35
  * [`iam get role [NAME]`](#iam-get-role-name)
34
36
 
@@ -53,7 +55,7 @@ DESCRIPTION
53
55
  Delete a policy
54
56
  ```
55
57
 
56
- _See code: [src/commands/delete/policy.ts](https://github.com/flowcore-io/cli-plugin-iam/blob/v1.4.0/src/commands/delete/policy.ts)_
58
+ _See code: [src/commands/delete/policy.ts](https://github.com/flowcore-io/cli-plugin-iam/blob/v1.6.0/src/commands/delete/policy.ts)_
57
59
 
58
60
  ## `iam delete role NAME`
59
61
 
@@ -76,7 +78,59 @@ DESCRIPTION
76
78
  Delete a role
77
79
  ```
78
80
 
79
- _See code: [src/commands/delete/role.ts](https://github.com/flowcore-io/cli-plugin-iam/blob/v1.4.0/src/commands/delete/role.ts)_
81
+ _See code: [src/commands/delete/role.ts](https://github.com/flowcore-io/cli-plugin-iam/blob/v1.6.0/src/commands/delete/role.ts)_
82
+
83
+ ## `iam edit policy NAME`
84
+
85
+ Edit a policy in your preferred editor
86
+
87
+ ```
88
+ USAGE
89
+ $ iam edit policy NAME -t <value> [--profile <value>]
90
+
91
+ ARGUMENTS
92
+ NAME name
93
+
94
+ FLAGS
95
+ -t, --tenant=<value> (required) tenant
96
+ --profile=<value> Specify the configuration profile to use
97
+
98
+ DESCRIPTION
99
+ Edit a policy in your preferred editor
100
+
101
+ EXAMPLES
102
+ $ flowcore iam edit policy my-policy -t my-tenant
103
+
104
+ $ FC_EDITOR=code flowcore iam edit policy my-policy -t my-tenant
105
+ ```
106
+
107
+ _See code: [src/commands/edit/policy.ts](https://github.com/flowcore-io/cli-plugin-iam/blob/v1.6.0/src/commands/edit/policy.ts)_
108
+
109
+ ## `iam edit role NAME`
110
+
111
+ Edit a role in your preferred editor
112
+
113
+ ```
114
+ USAGE
115
+ $ iam edit role NAME -t <value> [--profile <value>]
116
+
117
+ ARGUMENTS
118
+ NAME name
119
+
120
+ FLAGS
121
+ -t, --tenant=<value> (required) tenant
122
+ --profile=<value> Specify the configuration profile to use
123
+
124
+ DESCRIPTION
125
+ Edit a role in your preferred editor
126
+
127
+ EXAMPLES
128
+ $ flowcore iam edit role my-role -t my-tenant
129
+
130
+ $ FC_EDITOR=code flowcore iam edit role my-role -t my-tenant
131
+ ```
132
+
133
+ _See code: [src/commands/edit/role.ts](https://github.com/flowcore-io/cli-plugin-iam/blob/v1.6.0/src/commands/edit/role.ts)_
80
134
 
81
135
  ## `iam get policy [NAME]`
82
136
 
@@ -99,7 +153,7 @@ DESCRIPTION
99
153
  Get a policy
100
154
  ```
101
155
 
102
- _See code: [src/commands/get/policy.ts](https://github.com/flowcore-io/cli-plugin-iam/blob/v1.4.0/src/commands/get/policy.ts)_
156
+ _See code: [src/commands/get/policy.ts](https://github.com/flowcore-io/cli-plugin-iam/blob/v1.6.0/src/commands/get/policy.ts)_
103
157
 
104
158
  ## `iam get role [NAME]`
105
159
 
@@ -122,5 +176,5 @@ DESCRIPTION
122
176
  Get a role
123
177
  ```
124
178
 
125
- _See code: [src/commands/get/role.ts](https://github.com/flowcore-io/cli-plugin-iam/blob/v1.4.0/src/commands/get/role.ts)_
179
+ _See code: [src/commands/get/role.ts](https://github.com/flowcore-io/cli-plugin-iam/blob/v1.6.0/src/commands/get/role.ts)_
126
180
  <!-- commandsstop -->
@@ -0,0 +1,16 @@
1
+ import { BaseCommand } from "@flowcore/cli-plugin-config";
2
+ export declare const EDIT_POLICY_ARGS: {
3
+ NAME: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
4
+ };
5
+ export default class EditPolicy extends BaseCommand<typeof EditPolicy> {
6
+ static args: {
7
+ NAME: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
8
+ };
9
+ static description: string;
10
+ static examples: string[];
11
+ static flags: {
12
+ tenant: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
13
+ };
14
+ run(): Promise<void>;
15
+ private spawnEditor;
16
+ }
@@ -0,0 +1,118 @@
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 { spawn } from "node:child_process";
5
+ import { unlink, writeFile } from "node:fs/promises";
6
+ import { tmpdir } from "node:os";
7
+ import { join } from "node:path";
8
+ import { tryit } from "radash";
9
+ import { v4 as uuidv4 } from "uuid";
10
+ import { OrganizationService } from "../../services/organization.service.js";
11
+ import { Api as IamApi } from "../../utils/clients/iam/Api.js";
12
+ export const EDIT_POLICY_ARGS = {
13
+ NAME: Args.string({ description: "name", required: true }),
14
+ };
15
+ export default class EditPolicy extends BaseCommand {
16
+ static args = EDIT_POLICY_ARGS;
17
+ static description = "Edit a policy in your preferred editor";
18
+ static examples = [
19
+ "$ flowcore iam edit policy my-policy -t my-tenant",
20
+ "$ FC_EDITOR=code flowcore iam edit policy my-policy -t my-tenant",
21
+ ];
22
+ static flags = {
23
+ tenant: Flags.string({
24
+ char: "t",
25
+ description: "tenant",
26
+ required: true,
27
+ }),
28
+ };
29
+ async run() {
30
+ const { args, flags } = await this.parse(EditPolicy);
31
+ const graphqlClient = await ClientFactory.create(this.cliConfiguration, this.logger);
32
+ const organizationService = new OrganizationService(graphqlClient);
33
+ const organizations = await organizationService.getMyOrganizations();
34
+ const iamClient = new IamApi();
35
+ const config = this.cliConfiguration.getConfig();
36
+ const login = new ValidateLogin(config.login.url);
37
+ await login.validate(config, this.cliConfiguration, true);
38
+ const { auth } = config;
39
+ if (!auth?.accessToken) {
40
+ this.logger.fatal("Not logged in, run 'flowcore login'");
41
+ }
42
+ const organization = organizations.me.organizations.find((org) => org.organization.org === flags.tenant);
43
+ if (!organization) {
44
+ this.logger.fatal(`Organization ${flags.tenant} not found, or you are not a member`);
45
+ }
46
+ const [policiesErr, policies] = await tryit(iamClient.getApiV1PolicyAssociationsOrganizationByOrganizationId)(organization.organization.id, {
47
+ headers: {
48
+ Authorization: `Bearer ${auth?.accessToken}`,
49
+ },
50
+ });
51
+ if (policiesErr) {
52
+ this.logger.fatal(`Failed to get policies: ${policiesErr.message}`);
53
+ }
54
+ const policy = policies.data.find((policy) => policy.name === args.NAME);
55
+ if (!policy) {
56
+ this.logger.fatal(`Policy ${args.NAME} not found in tenant: ${flags.tenant}`);
57
+ }
58
+ // Create a temporary file with the policy content
59
+ const tmpFile = join(tmpdir(), `policy-${uuidv4()}.json`);
60
+ const policyContent = JSON.stringify({
61
+ description: policy.description,
62
+ documents: policy.policyDocuments,
63
+ name: policy.name,
64
+ version: policy.version,
65
+ }, null, 2);
66
+ try {
67
+ await writeFile(tmpFile, policyContent, "utf8");
68
+ await this.spawnEditor(tmpFile);
69
+ // Read and parse the edited content
70
+ const editedContent = await import(tmpFile, {
71
+ assert: { type: "json" },
72
+ });
73
+ // Update the policy
74
+ const [updateErr] = await tryit(iamClient.patchApiV1PoliciesById)(policy.id, {
75
+ description: editedContent.default.description,
76
+ name: editedContent.default.name,
77
+ organizationId: organization.organization.id,
78
+ policyDocuments: editedContent.default.documents,
79
+ version: editedContent.default.version,
80
+ }, {
81
+ headers: {
82
+ Authorization: `Bearer ${auth?.accessToken}`,
83
+ },
84
+ });
85
+ if (updateErr) {
86
+ this.logger.fatal(`Failed to update policy: ${updateErr.message}`);
87
+ }
88
+ this.logger.info(`Policy ${policy.name} updated successfully`);
89
+ }
90
+ catch (error) {
91
+ this.logger.fatal(`Error editing policy: ${error}`);
92
+ }
93
+ finally {
94
+ await unlink(tmpFile).catch(() => {
95
+ // Ignore error if file doesn't exist
96
+ });
97
+ }
98
+ }
99
+ async spawnEditor(filePath) {
100
+ return new Promise((resolve, reject) => {
101
+ const editor = process.env.FC_EDITOR || process.env.EDITOR || "vim";
102
+ const child = spawn(editor, [filePath], {
103
+ stdio: "inherit",
104
+ });
105
+ child.on("exit", (code) => {
106
+ if (code === 0) {
107
+ resolve();
108
+ }
109
+ else {
110
+ reject(new Error(`Editor process exited with code ${code}`));
111
+ }
112
+ });
113
+ child.on("error", (err) => {
114
+ reject(err);
115
+ });
116
+ });
117
+ }
118
+ }
@@ -0,0 +1,16 @@
1
+ import { BaseCommand } from "@flowcore/cli-plugin-config";
2
+ export declare const EDIT_ROLE_ARGS: {
3
+ NAME: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
4
+ };
5
+ export default class EditRole extends BaseCommand<typeof EditRole> {
6
+ static args: {
7
+ NAME: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
8
+ };
9
+ static description: string;
10
+ static examples: string[];
11
+ static flags: {
12
+ tenant: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
13
+ };
14
+ run(): Promise<void>;
15
+ private spawnEditor;
16
+ }
@@ -0,0 +1,115 @@
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 { spawn } from "node:child_process";
5
+ import { unlink, writeFile } from "node:fs/promises";
6
+ import { tmpdir } from "node:os";
7
+ import { join } from "node:path";
8
+ import { tryit } from "radash";
9
+ import { v4 as uuidv4 } from "uuid";
10
+ import { OrganizationService } from "../../services/organization.service.js";
11
+ import { Api as IamApi } from "../../utils/clients/iam/Api.js";
12
+ export const EDIT_ROLE_ARGS = {
13
+ NAME: Args.string({ description: "name", required: true }),
14
+ };
15
+ export default class EditRole extends BaseCommand {
16
+ static args = EDIT_ROLE_ARGS;
17
+ static description = "Edit a role in your preferred editor";
18
+ static examples = [
19
+ "$ flowcore iam edit role my-role -t my-tenant",
20
+ "$ FC_EDITOR=code flowcore iam edit role my-role -t my-tenant",
21
+ ];
22
+ static flags = {
23
+ tenant: Flags.string({
24
+ char: "t",
25
+ description: "tenant",
26
+ required: true,
27
+ }),
28
+ };
29
+ async run() {
30
+ const { args, flags } = await this.parse(EditRole);
31
+ const graphqlClient = await ClientFactory.create(this.cliConfiguration, this.logger);
32
+ const organizationService = new OrganizationService(graphqlClient);
33
+ const organizations = await organizationService.getMyOrganizations();
34
+ const iamClient = new IamApi();
35
+ const config = this.cliConfiguration.getConfig();
36
+ const login = new ValidateLogin(config.login.url);
37
+ await login.validate(config, this.cliConfiguration, true);
38
+ const { auth } = config;
39
+ if (!auth?.accessToken) {
40
+ this.logger.fatal("Not logged in, run 'flowcore login'");
41
+ }
42
+ const organization = organizations.me.organizations.find((org) => org.organization.org === flags.tenant);
43
+ if (!organization) {
44
+ this.logger.fatal(`Organization ${flags.tenant} not found, or you are not a member`);
45
+ }
46
+ const [rolesErr, roles] = await tryit(iamClient.getApiV1RoleAssociationsOrganizationByOrganizationId)(organization.organization.id, {
47
+ headers: {
48
+ Authorization: `Bearer ${auth?.accessToken}`,
49
+ },
50
+ });
51
+ if (rolesErr) {
52
+ this.logger.fatal(`Failed to get roles: ${rolesErr.message}`);
53
+ }
54
+ const role = roles.data.find((role) => role.name === args.NAME);
55
+ if (!role) {
56
+ this.logger.fatal(`Role ${args.NAME} not found in tenant: ${flags.tenant}`);
57
+ }
58
+ // Create a temporary file with the role content
59
+ const tmpFile = join(tmpdir(), `role-${uuidv4()}.json`);
60
+ const roleContent = JSON.stringify({
61
+ description: role.description,
62
+ name: role.name,
63
+ }, null, 2);
64
+ try {
65
+ await writeFile(tmpFile, roleContent, "utf8");
66
+ await this.spawnEditor(tmpFile);
67
+ // Read and parse the edited content
68
+ const editedContent = await import(tmpFile, {
69
+ assert: { type: "json" },
70
+ });
71
+ // Update the role
72
+ const [updateErr] = await tryit(iamClient.patchApiV1RolesById)(role.id, {
73
+ description: editedContent.default.description,
74
+ id: role.id,
75
+ name: editedContent.default.name,
76
+ organizationId: organization.organization.id,
77
+ }, {
78
+ headers: {
79
+ Authorization: `Bearer ${auth?.accessToken}`,
80
+ },
81
+ });
82
+ if (updateErr) {
83
+ this.logger.fatal(`Failed to update role: ${updateErr.message}`);
84
+ }
85
+ this.logger.info(`Role ${role.name} updated successfully`);
86
+ }
87
+ catch (error) {
88
+ this.logger.fatal(`Error editing role: ${error}`);
89
+ }
90
+ finally {
91
+ await unlink(tmpFile).catch(() => {
92
+ // Ignore error if file doesn't exist
93
+ });
94
+ }
95
+ }
96
+ async spawnEditor(filePath) {
97
+ return new Promise((resolve, reject) => {
98
+ const editor = process.env.FC_EDITOR || process.env.EDITOR || "vim";
99
+ const child = spawn(editor, [filePath], {
100
+ stdio: "inherit",
101
+ });
102
+ child.on("exit", (code) => {
103
+ if (code === 0) {
104
+ resolve();
105
+ }
106
+ else {
107
+ reject(new Error(`Editor process exited with code ${code}`));
108
+ }
109
+ });
110
+ child.on("error", (err) => {
111
+ reject(err);
112
+ });
113
+ });
114
+ }
115
+ }
@@ -122,7 +122,7 @@ export default class GetPolicy extends BaseCommand {
122
122
  const row = [
123
123
  document.statementId,
124
124
  document.resource,
125
- document.action,
125
+ String(document.action),
126
126
  ];
127
127
  documentTable = documentTable.row(row);
128
128
  }
@@ -143,7 +143,9 @@ export default class GetPolicy extends BaseCommand {
143
143
  .table()
144
144
  .head(userAssociationHeaders);
145
145
  const keyAssociationTable = this.ui.table().head(keyAssociationHeaders);
146
- const roleAssociationTable = this.ui.table().head(roleAssociationHeaders);
146
+ const roleAssociationTable = this.ui
147
+ .table()
148
+ .head(roleAssociationHeaders);
147
149
  for (const association of associations.data.keys) {
148
150
  keyAssociationTable.row([association.keyId]);
149
151
  }
@@ -1,5 +1,101 @@
1
1
  {
2
2
  "commands": {
3
+ "edit:policy": {
4
+ "aliases": [],
5
+ "args": {
6
+ "NAME": {
7
+ "description": "name",
8
+ "name": "NAME",
9
+ "required": true
10
+ }
11
+ },
12
+ "description": "Edit a policy in your preferred editor",
13
+ "examples": [
14
+ "$ flowcore iam edit policy my-policy -t my-tenant",
15
+ "$ FC_EDITOR=code flowcore iam edit policy my-policy -t my-tenant"
16
+ ],
17
+ "flags": {
18
+ "profile": {
19
+ "description": "Specify the configuration profile to use",
20
+ "name": "profile",
21
+ "hasDynamicHelp": false,
22
+ "multiple": false,
23
+ "type": "option"
24
+ },
25
+ "tenant": {
26
+ "char": "t",
27
+ "description": "tenant",
28
+ "name": "tenant",
29
+ "required": true,
30
+ "hasDynamicHelp": false,
31
+ "multiple": false,
32
+ "type": "option"
33
+ }
34
+ },
35
+ "hasDynamicHelp": false,
36
+ "hiddenAliases": [],
37
+ "id": "edit:policy",
38
+ "pluginAlias": "@flowcore/cli-plugin-iam",
39
+ "pluginName": "@flowcore/cli-plugin-iam",
40
+ "pluginType": "core",
41
+ "strict": true,
42
+ "enableJsonFlag": false,
43
+ "isESM": true,
44
+ "relativePath": [
45
+ "dist",
46
+ "commands",
47
+ "edit",
48
+ "policy.js"
49
+ ]
50
+ },
51
+ "edit:role": {
52
+ "aliases": [],
53
+ "args": {
54
+ "NAME": {
55
+ "description": "name",
56
+ "name": "NAME",
57
+ "required": true
58
+ }
59
+ },
60
+ "description": "Edit a role in your preferred editor",
61
+ "examples": [
62
+ "$ flowcore iam edit role my-role -t my-tenant",
63
+ "$ FC_EDITOR=code flowcore iam edit role my-role -t my-tenant"
64
+ ],
65
+ "flags": {
66
+ "profile": {
67
+ "description": "Specify the configuration profile to use",
68
+ "name": "profile",
69
+ "hasDynamicHelp": false,
70
+ "multiple": false,
71
+ "type": "option"
72
+ },
73
+ "tenant": {
74
+ "char": "t",
75
+ "description": "tenant",
76
+ "name": "tenant",
77
+ "required": true,
78
+ "hasDynamicHelp": false,
79
+ "multiple": false,
80
+ "type": "option"
81
+ }
82
+ },
83
+ "hasDynamicHelp": false,
84
+ "hiddenAliases": [],
85
+ "id": "edit:role",
86
+ "pluginAlias": "@flowcore/cli-plugin-iam",
87
+ "pluginName": "@flowcore/cli-plugin-iam",
88
+ "pluginType": "core",
89
+ "strict": true,
90
+ "enableJsonFlag": false,
91
+ "isESM": true,
92
+ "relativePath": [
93
+ "dist",
94
+ "commands",
95
+ "edit",
96
+ "role.js"
97
+ ]
98
+ },
3
99
  "delete:policy": {
4
100
  "aliases": [],
5
101
  "args": {
@@ -245,5 +341,5 @@
245
341
  ]
246
342
  }
247
343
  },
248
- "version": "1.4.0"
344
+ "version": "1.6.0"
249
345
  }
package/package.json CHANGED
@@ -91,7 +91,7 @@
91
91
  "version": "oclif readme && git add README.md",
92
92
  "update-schema": "rover graph introspect https://graph.api.flowcore.io/graphql -o schema.gql"
93
93
  },
94
- "version": "1.4.0",
94
+ "version": "1.6.0",
95
95
  "bugs": "https://github.com/flowcore-io/cli-plugin-iam/issues",
96
96
  "keywords": [
97
97
  "oclif"