@credibledata/cred-cli 0.0.102 → 0.0.103
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/README.md +1 -1
- package/coverage/coverage-summary.json +40 -0
- package/coverage/tmp/coverage-2152-1789496974457-0.json +1 -0
- package/coverage/tmp/coverage-2153-1789496954404-0.json +1 -0
- package/dist/client/api.js +4193 -1342
- package/dist/client/api.js.map +1 -1
- package/dist/clientconfiguration.js +57 -4
- package/dist/clientconfiguration.js.map +1 -1
- package/dist/clientconfiguration.spec.js +90 -0
- package/dist/clientconfiguration.spec.js.map +1 -1
- package/dist/commands/add.js +18 -0
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/environment/updateenvironment.js +63 -1
- package/dist/commands/environment/updateenvironment.js.map +1 -1
- package/dist/commands/environment/updateenvironment.spec.js +84 -0
- package/dist/commands/environment/updateenvironment.spec.js.map +1 -1
- package/dist/commands/get.js +52 -1
- package/dist/commands/get.js.map +1 -1
- package/dist/commands/list.js +78 -0
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/materialization/cancelrun.js +62 -0
- package/dist/commands/materialization/cancelrun.js.map +1 -0
- package/dist/commands/materialization/getindex.js +52 -0
- package/dist/commands/materialization/getindex.js.map +1 -0
- package/dist/commands/materialization/getmaterialization.js +54 -0
- package/dist/commands/materialization/getmaterialization.js.map +1 -0
- package/dist/commands/materialization/getpreaggregate.js +55 -0
- package/dist/commands/materialization/getpreaggregate.js.map +1 -0
- package/dist/commands/materialization/getrun.js +54 -0
- package/dist/commands/materialization/getrun.js.map +1 -0
- package/dist/commands/materialization/listindexes.js +58 -0
- package/dist/commands/materialization/listindexes.js.map +1 -0
- package/dist/commands/materialization/listmaterializations.js +52 -0
- package/dist/commands/materialization/listmaterializations.js.map +1 -0
- package/dist/commands/materialization/listpreaggregates.js +61 -0
- package/dist/commands/materialization/listpreaggregates.js.map +1 -0
- package/dist/commands/materialization/listruns.js +60 -0
- package/dist/commands/materialization/listruns.js.map +1 -0
- package/dist/commands/materialization/rerun.js +111 -0
- package/dist/commands/materialization/rerun.js.map +1 -0
- package/dist/commands/misc/login.js +14 -0
- package/dist/commands/misc/login.js.map +1 -1
- package/dist/commands/misc/login.spec.js +92 -0
- package/dist/commands/misc/login.spec.js.map +1 -1
- package/dist/commands/package/listpackages.js +7 -0
- package/dist/commands/package/listpackages.js.map +1 -1
- package/dist/commands/package/listpackages.spec.js +12 -0
- package/dist/commands/package/listpackages.spec.js.map +1 -1
- package/dist/commands/package/publish.js +34 -11
- package/dist/commands/package/publish.js.map +1 -1
- package/dist/commands/package/publish.spec.js +26 -28
- package/dist/commands/package/publish.spec.js.map +1 -1
- package/dist/commands/permission/createpermission.js +123 -0
- package/dist/commands/permission/createpermission.js.map +1 -0
- package/dist/commands/permission/createpermission.spec.js +360 -0
- package/dist/commands/permission/createpermission.spec.js.map +1 -0
- package/dist/commands/permission/deletepermission.js +86 -0
- package/dist/commands/permission/deletepermission.js.map +1 -0
- package/dist/commands/permission/getpermission.js +85 -0
- package/dist/commands/permission/getpermission.js.map +1 -0
- package/dist/commands/permission/listpermissions.js +75 -0
- package/dist/commands/permission/listpermissions.js.map +1 -0
- package/dist/commands/permission/scope.js +140 -0
- package/dist/commands/permission/scope.js.map +1 -0
- package/dist/commands/rm.js +11 -0
- package/dist/commands/rm.js.map +1 -1
- package/dist/commands/update.js +18 -12
- package/dist/commands/update.js.map +1 -1
- package/dist/common/UserGroupId.js +1 -1
- package/dist/common/UserGroupId.js.map +1 -1
- package/dist/common/UserGroupId.spec.js +2 -4
- package/dist/common/UserGroupId.spec.js.map +1 -1
- package/dist/constants.js +48 -0
- package/dist/constants.js.map +1 -1
- package/dist/index.js +61 -6
- package/dist/index.js.map +1 -1
- package/dist/logger.js +5 -1
- package/dist/logger.js.map +1 -1
- package/openapitools.json +4 -1
- package/package.json +5 -3
- package/src/client/api.ts +6433 -1533
- package/src/clientconfiguration.spec.ts +126 -0
- package/src/clientconfiguration.ts +64 -4
- package/src/commands/add.ts +43 -0
- package/src/commands/environment/updateenvironment.spec.ts +167 -0
- package/src/commands/environment/updateenvironment.ts +85 -1
- package/src/commands/get.ts +110 -1
- package/src/commands/list.ts +171 -0
- package/src/commands/materialization/cancelrun.ts +70 -0
- package/src/commands/materialization/getindex.ts +55 -0
- package/src/commands/materialization/getmaterialization.ts +57 -0
- package/src/commands/materialization/getpreaggregate.ts +58 -0
- package/src/commands/materialization/getrun.ts +55 -0
- package/src/commands/materialization/listindexes.ts +69 -0
- package/src/commands/materialization/listmaterializations.ts +53 -0
- package/src/commands/materialization/listpreaggregates.ts +74 -0
- package/src/commands/materialization/listruns.ts +77 -0
- package/src/commands/materialization/rerun.ts +123 -0
- package/src/commands/misc/login.spec.ts +118 -2
- package/src/commands/misc/login.ts +14 -0
- package/src/commands/package/listpackages.spec.ts +16 -0
- package/src/commands/package/listpackages.ts +7 -0
- package/src/commands/package/publish.spec.ts +45 -30
- package/src/commands/package/publish.ts +31 -10
- package/src/commands/permission/createpermission.spec.ts +507 -0
- package/src/commands/permission/createpermission.ts +181 -0
- package/src/commands/permission/deletepermission.ts +117 -0
- package/src/commands/permission/getpermission.ts +115 -0
- package/src/commands/permission/listpermissions.ts +101 -0
- package/src/commands/permission/scope.ts +180 -0
- package/src/commands/rm.ts +29 -0
- package/src/commands/update.ts +50 -27
- package/src/common/UserGroupId.spec.ts +2 -4
- package/src/common/UserGroupId.ts +1 -1
- package/src/constants.ts +50 -1
- package/src/index.ts +94 -11
- package/src/logger.ts +5 -1
- package/bun.lock +0 -934
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EnvironmentPermissionsApi,
|
|
3
|
+
OrganizationPermissionsApi,
|
|
4
|
+
PackagePermissionsApi,
|
|
5
|
+
} from "../../client/api";
|
|
6
|
+
import { Configuration } from "../../client/configuration";
|
|
7
|
+
import { ClientConfiguration } from "../../clientconfiguration";
|
|
8
|
+
import { UserGroupId } from "../../common/UserGroupId";
|
|
9
|
+
import { logger } from "../../logger";
|
|
10
|
+
import { getConfirmation, printOutError } from "../../utils";
|
|
11
|
+
import {
|
|
12
|
+
describeScope,
|
|
13
|
+
PermissionScope,
|
|
14
|
+
resolveScope,
|
|
15
|
+
ScopeOptions,
|
|
16
|
+
} from "./scope";
|
|
17
|
+
|
|
18
|
+
const organizationPermissionsApi = new OrganizationPermissionsApi(
|
|
19
|
+
new Configuration(),
|
|
20
|
+
);
|
|
21
|
+
const environmentPermissionsApi = new EnvironmentPermissionsApi(
|
|
22
|
+
new Configuration(),
|
|
23
|
+
);
|
|
24
|
+
const packagePermissionsApi = new PackagePermissionsApi(new Configuration());
|
|
25
|
+
|
|
26
|
+
export async function deletePermission(
|
|
27
|
+
controlPlane: string,
|
|
28
|
+
userGroupIdValue: string,
|
|
29
|
+
debug: boolean,
|
|
30
|
+
autoConfirm: boolean,
|
|
31
|
+
options: ScopeOptions,
|
|
32
|
+
) {
|
|
33
|
+
const clientConfig = new ClientConfiguration();
|
|
34
|
+
|
|
35
|
+
if (!clientConfig.doesConfigExists()) {
|
|
36
|
+
logger.error("Please, make sure that you are logged in");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const organizationName = clientConfig.getOrganization();
|
|
41
|
+
if (!organizationName) {
|
|
42
|
+
logger.error("No organization set. Please login or set an organization.");
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const resolved = resolveScope(options, clientConfig.getProject());
|
|
47
|
+
if ("error" in resolved) {
|
|
48
|
+
logger.error(resolved.error);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let userGroupId: string;
|
|
54
|
+
try {
|
|
55
|
+
userGroupId = new UserGroupId(userGroupIdValue).getValue();
|
|
56
|
+
} catch (e) {
|
|
57
|
+
logger.error((e as Error).message);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const target = describeScope(organizationName, resolved);
|
|
63
|
+
|
|
64
|
+
if (!autoConfirm) {
|
|
65
|
+
const confirmation = await getConfirmation(
|
|
66
|
+
`Are you sure that you want to revoke ${userGroupId}'s permission on ${target}?`,
|
|
67
|
+
);
|
|
68
|
+
if (!confirmation) {
|
|
69
|
+
logger.info("Operation cancelled.");
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
logger.info(`Revoking permission for ${userGroupId} on ${target}`);
|
|
75
|
+
|
|
76
|
+
const requestOptions = {
|
|
77
|
+
baseURL: controlPlane,
|
|
78
|
+
headers: {
|
|
79
|
+
Authorization: clientConfig.getAuthenticationHeader(),
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
switch (resolved.scope) {
|
|
85
|
+
case PermissionScope.PACKAGE:
|
|
86
|
+
await packagePermissionsApi.deletePackagePermission(
|
|
87
|
+
organizationName,
|
|
88
|
+
resolved.environmentName as string,
|
|
89
|
+
resolved.packageName as string,
|
|
90
|
+
userGroupId,
|
|
91
|
+
requestOptions,
|
|
92
|
+
);
|
|
93
|
+
break;
|
|
94
|
+
case PermissionScope.ENVIRONMENT:
|
|
95
|
+
await environmentPermissionsApi.deleteEnvironmentPermission(
|
|
96
|
+
organizationName,
|
|
97
|
+
resolved.environmentName as string,
|
|
98
|
+
userGroupId,
|
|
99
|
+
requestOptions,
|
|
100
|
+
);
|
|
101
|
+
break;
|
|
102
|
+
default:
|
|
103
|
+
await organizationPermissionsApi.deleteOrganizationPermission(
|
|
104
|
+
organizationName,
|
|
105
|
+
userGroupId,
|
|
106
|
+
requestOptions,
|
|
107
|
+
);
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
logger.info("Permission revoked successfully");
|
|
112
|
+
} catch (error) {
|
|
113
|
+
logger.error("Failed to revoke permission");
|
|
114
|
+
printOutError(error, debug);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EnvironmentPermissionsApi,
|
|
3
|
+
OrganizationPermissionsApi,
|
|
4
|
+
PackagePermissionsApi,
|
|
5
|
+
} from "../../client/api";
|
|
6
|
+
import { Configuration } from "../../client/configuration";
|
|
7
|
+
import { ClientConfiguration } from "../../clientconfiguration";
|
|
8
|
+
import { UserGroupId } from "../../common/UserGroupId";
|
|
9
|
+
import { logger } from "../../logger";
|
|
10
|
+
import { printOutError } from "../../utils";
|
|
11
|
+
import {
|
|
12
|
+
describeScope,
|
|
13
|
+
PermissionScope,
|
|
14
|
+
resolveScope,
|
|
15
|
+
ScopeOptions,
|
|
16
|
+
} from "./scope";
|
|
17
|
+
|
|
18
|
+
const organizationPermissionsApi = new OrganizationPermissionsApi(
|
|
19
|
+
new Configuration(),
|
|
20
|
+
);
|
|
21
|
+
const environmentPermissionsApi = new EnvironmentPermissionsApi(
|
|
22
|
+
new Configuration(),
|
|
23
|
+
);
|
|
24
|
+
const packagePermissionsApi = new PackagePermissionsApi(new Configuration());
|
|
25
|
+
|
|
26
|
+
export async function getPermission(
|
|
27
|
+
controlPlane: string,
|
|
28
|
+
userGroupIdValue: string,
|
|
29
|
+
debug: boolean,
|
|
30
|
+
options: ScopeOptions,
|
|
31
|
+
) {
|
|
32
|
+
const clientConfig = new ClientConfiguration();
|
|
33
|
+
|
|
34
|
+
if (!clientConfig.doesConfigExists()) {
|
|
35
|
+
logger.error("Please, make sure that you are logged in");
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const organizationName = clientConfig.getOrganization();
|
|
40
|
+
if (!organizationName) {
|
|
41
|
+
logger.error("No organization set. Please login or set an organization.");
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const resolved = resolveScope(options, clientConfig.getProject());
|
|
46
|
+
if ("error" in resolved) {
|
|
47
|
+
logger.error(resolved.error);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let userGroupId: string;
|
|
53
|
+
try {
|
|
54
|
+
userGroupId = new UserGroupId(userGroupIdValue).getValue();
|
|
55
|
+
} catch (e) {
|
|
56
|
+
logger.error((e as Error).message);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
logger.info(
|
|
62
|
+
`Getting permission for ${userGroupId} on ${describeScope(organizationName, resolved)}`,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const requestOptions = {
|
|
66
|
+
baseURL: controlPlane,
|
|
67
|
+
headers: {
|
|
68
|
+
Authorization: clientConfig.getAuthenticationHeader(),
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
let data: unknown;
|
|
74
|
+
switch (resolved.scope) {
|
|
75
|
+
case PermissionScope.PACKAGE: {
|
|
76
|
+
const response = await packagePermissionsApi.getPackagePermission(
|
|
77
|
+
organizationName,
|
|
78
|
+
resolved.environmentName as string,
|
|
79
|
+
resolved.packageName as string,
|
|
80
|
+
userGroupId,
|
|
81
|
+
requestOptions,
|
|
82
|
+
);
|
|
83
|
+
data = response.data;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
case PermissionScope.ENVIRONMENT: {
|
|
87
|
+
const response =
|
|
88
|
+
await environmentPermissionsApi.getEnvironmentPermission(
|
|
89
|
+
organizationName,
|
|
90
|
+
resolved.environmentName as string,
|
|
91
|
+
userGroupId,
|
|
92
|
+
requestOptions,
|
|
93
|
+
);
|
|
94
|
+
data = response.data;
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
default: {
|
|
98
|
+
const response =
|
|
99
|
+
await organizationPermissionsApi.getOrganizationPermission(
|
|
100
|
+
organizationName,
|
|
101
|
+
userGroupId,
|
|
102
|
+
requestOptions,
|
|
103
|
+
);
|
|
104
|
+
data = response.data;
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
console.log(JSON.stringify(data, null, 2));
|
|
110
|
+
} catch (error) {
|
|
111
|
+
logger.error("Failed to get permission");
|
|
112
|
+
printOutError(error, debug);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EnvironmentPermissionsApi,
|
|
3
|
+
OrganizationPermissionsApi,
|
|
4
|
+
PackagePermissionsApi,
|
|
5
|
+
} from "../../client/api";
|
|
6
|
+
import { Configuration } from "../../client/configuration";
|
|
7
|
+
import { ClientConfiguration } from "../../clientconfiguration";
|
|
8
|
+
import { logger } from "../../logger";
|
|
9
|
+
import { printOutError } from "../../utils";
|
|
10
|
+
import {
|
|
11
|
+
describeScope,
|
|
12
|
+
PermissionScope,
|
|
13
|
+
resolveScope,
|
|
14
|
+
ScopeOptions,
|
|
15
|
+
} from "./scope";
|
|
16
|
+
|
|
17
|
+
const organizationPermissionsApi = new OrganizationPermissionsApi(
|
|
18
|
+
new Configuration(),
|
|
19
|
+
);
|
|
20
|
+
const environmentPermissionsApi = new EnvironmentPermissionsApi(
|
|
21
|
+
new Configuration(),
|
|
22
|
+
);
|
|
23
|
+
const packagePermissionsApi = new PackagePermissionsApi(new Configuration());
|
|
24
|
+
|
|
25
|
+
export async function listPermissions(
|
|
26
|
+
controlPlane: string,
|
|
27
|
+
debug: boolean,
|
|
28
|
+
options: ScopeOptions,
|
|
29
|
+
) {
|
|
30
|
+
const clientConfig = new ClientConfiguration();
|
|
31
|
+
|
|
32
|
+
if (!clientConfig.doesConfigExists()) {
|
|
33
|
+
logger.error("Please, make sure that you are logged in");
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const organizationName = clientConfig.getOrganization();
|
|
38
|
+
if (!organizationName) {
|
|
39
|
+
logger.error("No organization set. Please login or set an organization.");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const resolved = resolveScope(options, clientConfig.getProject());
|
|
44
|
+
if ("error" in resolved) {
|
|
45
|
+
logger.error(resolved.error);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
logger.info(
|
|
51
|
+
`Listing permissions on ${describeScope(organizationName, resolved)}`,
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const requestOptions = {
|
|
55
|
+
baseURL: controlPlane,
|
|
56
|
+
headers: {
|
|
57
|
+
Authorization: clientConfig.getAuthenticationHeader(),
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
let data: unknown;
|
|
63
|
+
switch (resolved.scope) {
|
|
64
|
+
case PermissionScope.PACKAGE: {
|
|
65
|
+
const response = await packagePermissionsApi.listPackagePermissions(
|
|
66
|
+
organizationName,
|
|
67
|
+
resolved.environmentName as string,
|
|
68
|
+
resolved.packageName as string,
|
|
69
|
+
requestOptions,
|
|
70
|
+
);
|
|
71
|
+
data = response.data;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
case PermissionScope.ENVIRONMENT: {
|
|
75
|
+
const response =
|
|
76
|
+
await environmentPermissionsApi.listEnvironmentPermissions(
|
|
77
|
+
organizationName,
|
|
78
|
+
resolved.environmentName as string,
|
|
79
|
+
requestOptions,
|
|
80
|
+
);
|
|
81
|
+
data = response.data;
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
default: {
|
|
85
|
+
const response =
|
|
86
|
+
await organizationPermissionsApi.listOrganizationPermissions(
|
|
87
|
+
organizationName,
|
|
88
|
+
requestOptions,
|
|
89
|
+
);
|
|
90
|
+
data = response.data;
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
console.log(JSON.stringify(data, null, 2));
|
|
96
|
+
} catch (error) {
|
|
97
|
+
logger.error("Failed to list permissions");
|
|
98
|
+
printOutError(error, debug);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EnvironmentPermissionPermissionEnum,
|
|
3
|
+
OrganizationPermissionPermissionEnum,
|
|
4
|
+
PackagePermissionPermissionEnum,
|
|
5
|
+
} from "../../client/api";
|
|
6
|
+
import { UserGroupId } from "../../common/UserGroupId";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Which resource a permission is granted on. The organization is the default
|
|
10
|
+
* because it is the only scope that names no resource of its own.
|
|
11
|
+
*/
|
|
12
|
+
export enum PermissionScope {
|
|
13
|
+
ORGANIZATION = "organization",
|
|
14
|
+
ENVIRONMENT = "environment",
|
|
15
|
+
PACKAGE = "package",
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The roles each scope accepts.
|
|
20
|
+
*
|
|
21
|
+
* The organization is the odd one out: it ends in `member` where the other two
|
|
22
|
+
* end in `viewer`. They are not interchangeable, so the sets are listed per
|
|
23
|
+
* scope rather than shared.
|
|
24
|
+
*
|
|
25
|
+
* A package takes `viewer` alone, which is narrower than its schema enum. The
|
|
26
|
+
* enum carries admin and modeler because a package reports them -- they are
|
|
27
|
+
* inherited from the environment -- but only viewer is directly assignable, and
|
|
28
|
+
* the server rejects the other two. Deriving this set from the enum would let
|
|
29
|
+
* the CLI accept a grant that cannot be written.
|
|
30
|
+
*/
|
|
31
|
+
const ROLES_BY_SCOPE: Record<PermissionScope, readonly string[]> = {
|
|
32
|
+
[PermissionScope.ORGANIZATION]: Object.values(
|
|
33
|
+
OrganizationPermissionPermissionEnum,
|
|
34
|
+
),
|
|
35
|
+
[PermissionScope.ENVIRONMENT]: Object.values(
|
|
36
|
+
EnvironmentPermissionPermissionEnum,
|
|
37
|
+
),
|
|
38
|
+
[PermissionScope.PACKAGE]: [PackagePermissionPermissionEnum.Viewer],
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type ScopeOptions = {
|
|
42
|
+
environment?: string;
|
|
43
|
+
package?: string;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The resource a command addresses: the scope plus the names needed to reach
|
|
48
|
+
* it. `environmentName` is set for both the environment and package scopes,
|
|
49
|
+
* since a package is addressed through its environment.
|
|
50
|
+
*/
|
|
51
|
+
export type ResolvedScope = {
|
|
52
|
+
scope: PermissionScope;
|
|
53
|
+
environmentName?: string;
|
|
54
|
+
packageName?: string;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export function rolesFor(scope: PermissionScope): readonly string[] {
|
|
58
|
+
return ROLES_BY_SCOPE[scope];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Works out which resource the options address, reading the environment from
|
|
63
|
+
* the config when a package is named without one.
|
|
64
|
+
*
|
|
65
|
+
* Returns an error rather than guessing when the environment cannot be
|
|
66
|
+
* resolved: silently falling back to the organization would grant a wider
|
|
67
|
+
* permission than the caller asked for.
|
|
68
|
+
*/
|
|
69
|
+
export function resolveScope(
|
|
70
|
+
options: ScopeOptions,
|
|
71
|
+
configuredEnvironment: string | undefined,
|
|
72
|
+
): ResolvedScope | { error: string } {
|
|
73
|
+
// An option that was passed but is empty -- `--environment "$UNSET_VAR"` --
|
|
74
|
+
// is a mistake, not an omission. Treating it as absent would widen the grant
|
|
75
|
+
// to the next scope out, which is the outcome this function exists to refuse,
|
|
76
|
+
// so it is reported rather than interpreted.
|
|
77
|
+
for (const name of ["environment", "package"] as const) {
|
|
78
|
+
const value = options[name];
|
|
79
|
+
if (value !== undefined && value.trim() === "") {
|
|
80
|
+
return { error: `--${name} was given an empty value.` };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (options.package) {
|
|
85
|
+
const environmentName = options.environment || configuredEnvironment;
|
|
86
|
+
if (!environmentName) {
|
|
87
|
+
return {
|
|
88
|
+
error: "A package permission needs an environment. Pass --environment, or set one with 'cred set environment <name>'.",
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
scope: PermissionScope.PACKAGE,
|
|
93
|
+
environmentName,
|
|
94
|
+
packageName: options.package,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (options.environment) {
|
|
99
|
+
return {
|
|
100
|
+
scope: PermissionScope.ENVIRONMENT,
|
|
101
|
+
environmentName: options.environment,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return { scope: PermissionScope.ORGANIZATION };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Names the resource in log lines, so a message says what was addressed. */
|
|
109
|
+
export function describeScope(
|
|
110
|
+
organizationName: string,
|
|
111
|
+
resolved: ResolvedScope,
|
|
112
|
+
): string {
|
|
113
|
+
switch (resolved.scope) {
|
|
114
|
+
case PermissionScope.PACKAGE:
|
|
115
|
+
return `package ${organizationName}/${resolved.environmentName}/${resolved.packageName}`;
|
|
116
|
+
case PermissionScope.ENVIRONMENT:
|
|
117
|
+
return `environment ${organizationName}/${resolved.environmentName}`;
|
|
118
|
+
default:
|
|
119
|
+
return `organization ${organizationName}`;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Checks a role against the scope it is granted on, so a role the API would
|
|
125
|
+
* reject fails before the request instead of as an opaque 400.
|
|
126
|
+
*/
|
|
127
|
+
export function validateRole(
|
|
128
|
+
scope: PermissionScope,
|
|
129
|
+
role: string,
|
|
130
|
+
): string | null {
|
|
131
|
+
const allowed = rolesFor(scope);
|
|
132
|
+
if (!allowed.includes(role)) {
|
|
133
|
+
return `Invalid role '${role}' for a ${scope} permission. Expected one of: ${allowed.join(", ")}`;
|
|
134
|
+
}
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* The roles the authorization model accepts for public access, per scope.
|
|
140
|
+
*
|
|
141
|
+
* Narrower than the roles a named subject can hold, and not derivable from
|
|
142
|
+
* them: the model lists `user:*` on exactly three relations. Anything else
|
|
143
|
+
* reaches OpenFGA and fails there as a model validation error, so it is listed
|
|
144
|
+
* rather than special-cased.
|
|
145
|
+
*/
|
|
146
|
+
const PUBLIC_ACCESS_ROLES_BY_SCOPE: Record<PermissionScope, readonly string[]> =
|
|
147
|
+
{
|
|
148
|
+
[PermissionScope.ORGANIZATION]: ["modeler"],
|
|
149
|
+
[PermissionScope.ENVIRONMENT]: ["viewer"],
|
|
150
|
+
[PermissionScope.PACKAGE]: ["viewer"],
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Parses the subject and checks the roles public access may hold. Everything
|
|
155
|
+
* else about the subject -- whether a user's email is well formed, whether a
|
|
156
|
+
* group exists -- is left to the server, which is the authority on it.
|
|
157
|
+
*/
|
|
158
|
+
export function parseSubject(
|
|
159
|
+
scope: PermissionScope,
|
|
160
|
+
role: string,
|
|
161
|
+
value: string,
|
|
162
|
+
): { userGroupId: UserGroupId } | { error: string } {
|
|
163
|
+
let userGroupId: UserGroupId;
|
|
164
|
+
try {
|
|
165
|
+
userGroupId = new UserGroupId(value);
|
|
166
|
+
} catch (e) {
|
|
167
|
+
return { error: (e as Error).message };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (userGroupId.isPublicAccess()) {
|
|
171
|
+
const allowed = PUBLIC_ACCESS_ROLES_BY_SCOPE[scope];
|
|
172
|
+
if (!allowed.includes(role)) {
|
|
173
|
+
return {
|
|
174
|
+
error: `Public access cannot be granted '${role}' on a ${scope}. Expected one of: ${allowed.join(", ")}`,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return { userGroupId };
|
|
180
|
+
}
|
package/src/commands/rm.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { deleteGroupAccessToken } from "./groupaccesstoken/deleteToken";
|
|
|
7
7
|
import { deletePackage } from "./package/deletepackage";
|
|
8
8
|
import { deleteEnvironment } from "./environment/deleteenvironment";
|
|
9
9
|
import { warnProjectDeprecated } from "./legacy";
|
|
10
|
+
import { deletePermission } from "./permission/deletepermission";
|
|
10
11
|
|
|
11
12
|
export const registerRmCommands = (cli: Command, environment: Environment) => {
|
|
12
13
|
const rm = cli
|
|
@@ -14,6 +15,34 @@ export const registerRmCommands = (cli: Command, environment: Environment) => {
|
|
|
14
15
|
.description("Remove a resource")
|
|
15
16
|
.argument("<resource>", "The resource to remove");
|
|
16
17
|
|
|
18
|
+
rm.command("permission")
|
|
19
|
+
.description(
|
|
20
|
+
"Revoke a user or group's permission on the organization, an environment, or a package.",
|
|
21
|
+
)
|
|
22
|
+
.argument(
|
|
23
|
+
"<userGroupId>",
|
|
24
|
+
"Whose permission to revoke [user:{email}|group:{groupName}|user:*]",
|
|
25
|
+
)
|
|
26
|
+
.option(
|
|
27
|
+
"--environment <environmentName>",
|
|
28
|
+
"Revoke the permission on this environment instead of the organization.",
|
|
29
|
+
)
|
|
30
|
+
.option(
|
|
31
|
+
"--package <packageName>",
|
|
32
|
+
"Revoke the permission on this package. Uses --environment, or the current environment.",
|
|
33
|
+
)
|
|
34
|
+
.option("--debug")
|
|
35
|
+
.option("-y, --yes", "Skip confirmation.")
|
|
36
|
+
.action((userGroupId, options) => {
|
|
37
|
+
deletePermission(
|
|
38
|
+
controlPlanesByEnv[environment],
|
|
39
|
+
userGroupId,
|
|
40
|
+
options.debug || false,
|
|
41
|
+
options.yes === true,
|
|
42
|
+
{ environment: options.environment, package: options.package },
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
|
|
17
46
|
rm.command("package")
|
|
18
47
|
.description("Delete a package.")
|
|
19
48
|
.argument("<packageName>", "The name of the package to delete.")
|
package/src/commands/update.ts
CHANGED
|
@@ -20,40 +20,63 @@ export const registerUpdateCommands = (
|
|
|
20
20
|
environmentName,
|
|
21
21
|
{
|
|
22
22
|
replicationCount: options.replication,
|
|
23
|
+
gitModeling: options.gitModeling,
|
|
24
|
+
gitRepositoryOwner: options.gitRepositoryOwner,
|
|
25
|
+
gitRepositoryName: options.gitRepositoryName,
|
|
26
|
+
gitBaseBranch: options.gitBaseBranch,
|
|
23
27
|
},
|
|
24
28
|
options.debug,
|
|
25
29
|
options.yes,
|
|
26
30
|
);
|
|
27
31
|
};
|
|
28
32
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
// Declared once and applied to both the environment command and its deprecated
|
|
34
|
+
// project alias, so the alias cannot drift into accepting a different set.
|
|
35
|
+
const environmentOptions = (command: Command) =>
|
|
36
|
+
command
|
|
37
|
+
.option(
|
|
38
|
+
"--replication <replicationCount>",
|
|
39
|
+
"The new replication count (must be at least 1).",
|
|
40
|
+
parseInt,
|
|
41
|
+
)
|
|
42
|
+
.option(
|
|
43
|
+
"--git-modeling <true|false>",
|
|
44
|
+
"Enroll (or un-enroll) this environment for git-backed modeling drafts.",
|
|
45
|
+
)
|
|
46
|
+
.option(
|
|
47
|
+
"--git-repository-owner <owner>",
|
|
48
|
+
"GitHub owner of the repository holding this environment's draft branches.",
|
|
49
|
+
)
|
|
50
|
+
.option(
|
|
51
|
+
"--git-repository-name <name>",
|
|
52
|
+
"GitHub repository name, paired with --git-repository-owner.",
|
|
53
|
+
)
|
|
54
|
+
.option(
|
|
55
|
+
"--git-base-branch <branch>",
|
|
56
|
+
'Branch draft branches fork from. Pass "" to all three git-repository options to unset the repository.',
|
|
57
|
+
)
|
|
58
|
+
.option("--debug")
|
|
59
|
+
.option("-y, --yes", "Skip confirmation.");
|
|
41
60
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
environmentOptions(
|
|
62
|
+
update
|
|
63
|
+
.command("environment")
|
|
64
|
+
.description("Update an environment.")
|
|
65
|
+
.argument(
|
|
66
|
+
"<environmentName>",
|
|
67
|
+
"The name of the environment to update.",
|
|
68
|
+
),
|
|
69
|
+
).action(updateEnvironmentAction);
|
|
70
|
+
|
|
71
|
+
environmentOptions(
|
|
72
|
+
update
|
|
73
|
+
.command("project")
|
|
74
|
+
.description("[deprecated] Alias for 'update environment'.")
|
|
75
|
+
.argument("<projectName>", "The name of the environment to update."),
|
|
76
|
+
).action((projectName, options) => {
|
|
77
|
+
warnProjectDeprecated("update environment");
|
|
78
|
+
updateEnvironmentAction(projectName, options);
|
|
79
|
+
});
|
|
57
80
|
|
|
58
81
|
update
|
|
59
82
|
.command("package")
|
|
@@ -107,11 +107,9 @@ describe("UserGroupId", () => {
|
|
|
107
107
|
expect(id.isPublicAccess()).to.be.false;
|
|
108
108
|
});
|
|
109
109
|
|
|
110
|
-
it("should return
|
|
111
|
-
// isPublicAccess checks this.originalValue === "*" which is never true
|
|
112
|
-
// since originalValue is the full string "user:*"
|
|
110
|
+
it("should return true for user:* (the public-access wildcard)", () => {
|
|
113
111
|
const id = UserGroupId.forPublicAccess();
|
|
114
|
-
expect(id.isPublicAccess()).to.be.
|
|
112
|
+
expect(id.isPublicAccess()).to.be.true;
|
|
115
113
|
});
|
|
116
114
|
});
|
|
117
115
|
});
|