indraq_cli 2.1.0 → 2.2.1
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/LICENSE +21 -21
- package/README.md +41 -15
- package/cloud-api/.dockerignore +18 -18
- package/cloud-api/.env.example +14 -14
- package/cloud-api/Dockerfile +19 -19
- package/cloud-api/README.md +93 -93
- package/cloud-api/dist/index.js +265 -27
- package/cloud-api/node_modules/.package-lock.json +1 -1
- package/cloud-api/package-lock.json +2 -2
- package/cloud-api/package.json +1 -1
- package/cloud-api/sql/001_init.sql +8 -8
- package/cloud-api/sql/002_user_providers_and_project_resources.sql +40 -40
- package/cloud-api/sql/003_environment_infrastructure.sql +19 -19
- package/cloud-api/sql/004_managed_aws_credentials.sql +20 -20
- package/cloud-api/sql/005_project_members_and_resource_grants.sql +22 -22
- package/cloud-api/sql/006_catalog_identity_password_security.sql +96 -96
- package/cloud-api/sql/007_aws_access_key_rotation.sql +16 -16
- package/cloud-api/sql/008_provider_scopes.sql +91 -91
- package/cloud-api/sql/009_scope_user_secrets_and_project_lifecycle.sql +13 -0
- package/cloud-api/src/index.ts +164 -27
- package/cloud-api/tsconfig.json +18 -18
- package/dist/cli/help-content.d.ts.map +1 -1
- package/dist/cli/help-content.js +29 -7
- package/dist/cli/help-content.js.map +1 -1
- package/dist/modules/access/commands/access.command.js +30 -30
- package/dist/modules/aws/services/route53-lookup.service.js.map +1 -1
- package/dist/modules/cloud/commands/cloud.command.d.ts +4 -0
- package/dist/modules/cloud/commands/cloud.command.d.ts.map +1 -1
- package/dist/modules/cloud/commands/cloud.command.js +52 -1
- package/dist/modules/cloud/commands/cloud.command.js.map +1 -1
- package/dist/modules/cloud/index.d.ts.map +1 -1
- package/dist/modules/cloud/index.js +6 -3
- package/dist/modules/cloud/index.js.map +1 -1
- package/dist/modules/cloud/services/cloud-jenkins.service.js +71 -71
- package/dist/modules/cloud/services/user-provider.service.d.ts +2 -8
- package/dist/modules/cloud/services/user-provider.service.d.ts.map +1 -1
- package/dist/modules/cloud/services/user-provider.service.js +7 -35
- package/dist/modules/cloud/services/user-provider.service.js.map +1 -1
- package/dist/modules/deploy/commands/build.command.d.ts +1 -0
- package/dist/modules/deploy/commands/build.command.d.ts.map +1 -1
- package/dist/modules/deploy/commands/build.command.js +12 -5
- package/dist/modules/deploy/commands/build.command.js.map +1 -1
- package/dist/modules/deploy/index.d.ts.map +1 -1
- package/dist/modules/deploy/index.js +11 -4
- package/dist/modules/deploy/index.js.map +1 -1
- package/dist/modules/prebuild/commands/prebuild.command.d.ts +0 -4
- package/dist/modules/prebuild/commands/prebuild.command.d.ts.map +1 -1
- package/dist/modules/prebuild/commands/prebuild.command.js +34 -38
- package/dist/modules/prebuild/commands/prebuild.command.js.map +1 -1
- package/dist/modules/prebuild/index.d.ts.map +1 -1
- package/dist/modules/prebuild/index.js +4 -8
- package/dist/modules/prebuild/index.js.map +1 -1
- package/dist/modules/scaffold/services/docker-template.service.js +95 -95
- package/dist/modules/scopes/commands/scope.command.d.ts.map +1 -1
- package/dist/modules/scopes/commands/scope.command.js +3 -47
- package/dist/modules/scopes/commands/scope.command.js.map +1 -1
- package/dist/modules/scopes/services/scope-native.service.js +2 -2
- package/dist/modules/scopes/services/scope-native.service.js.map +1 -1
- package/dist/modules/users/commands/user.command.d.ts +1 -0
- package/dist/modules/users/commands/user.command.d.ts.map +1 -1
- package/dist/modules/users/commands/user.command.js +15 -1
- package/dist/modules/users/commands/user.command.js.map +1 -1
- package/dist/modules/users/index.d.ts.map +1 -1
- package/dist/modules/users/index.js +3 -2
- package/dist/modules/users/index.js.map +1 -1
- package/dist/modules/users/providers/jenkins-user.provider.js +95 -95
- package/dist/shared/config/config-files.js +7 -7
- package/dist/shared/jenkins/jenkins.service.d.ts.map +1 -1
- package/dist/shared/jenkins/jenkins.service.js +13 -8
- package/dist/shared/jenkins/jenkins.service.js.map +1 -1
- package/dist/shared/jenkins/role-strategy-groovy.js +84 -84
- package/docs/ARCHITECTURE.md +29 -29
- package/docs/CREATE-DEPLOYMENT-reference.groovy +1105 -1105
- package/package.json +9 -5
- package/templates/jenkins/CREATE-DEPLOYMENT.groovy +1105 -1105
- package/templates/jenkins/Jenkinsfile-Mobile-App +759 -759
- package/CHANGELOG_v2.1.0.md +0 -27
- package/UPGRADE_v2.0_to_v2.1.md +0 -68
- package/cloud-api/dist/index.js.map +0 -1
package/cloud-api/src/index.ts
CHANGED
|
@@ -121,10 +121,10 @@ const listQuery = (req: Request, defaultLimit = 10, maximum = 5000): { search: s
|
|
|
121
121
|
};
|
|
122
122
|
|
|
123
123
|
type ProjectAccess = 'view' | 'write';
|
|
124
|
-
const projectAccessLevel = async (projectIdRaw: string, user: Auth): Promise<ProjectAccess | null> => {
|
|
124
|
+
const projectAccessLevel = async (projectIdRaw: string, user: Auth, includeArchived = false): Promise<ProjectAccess | null> => {
|
|
125
125
|
const projectId = ensureUuid(projectIdRaw, 'Project ID');
|
|
126
126
|
const result = await pool.query(
|
|
127
|
-
`SELECT p.id,p.created_by,pm.role,pm.access_level
|
|
127
|
+
`SELECT p.id,p.created_by,p.archived_at,pm.role,pm.access_level
|
|
128
128
|
FROM projects p
|
|
129
129
|
LEFT JOIN project_members pm ON pm.project_id=p.id AND pm.user_id=$2
|
|
130
130
|
WHERE p.id=$1
|
|
@@ -132,11 +132,12 @@ const projectAccessLevel = async (projectIdRaw: string, user: Auth): Promise<Pro
|
|
|
132
132
|
[projectId, user.id]
|
|
133
133
|
);
|
|
134
134
|
if (!result.rowCount) return null;
|
|
135
|
-
if (user.role === 'admin' || user.role === 'manager') return 'write';
|
|
136
135
|
const row = result.rows[0];
|
|
136
|
+
if (row.archived_at && !includeArchived) return null;
|
|
137
|
+
if (user.role === 'admin') return 'write';
|
|
137
138
|
if (row.created_by === user.id || row.role === 'owner' || row.access_level === 'write') return 'write';
|
|
138
|
-
|
|
139
|
-
return
|
|
139
|
+
if (row.role || row.access_level === 'view') return 'view';
|
|
140
|
+
return null;
|
|
140
141
|
};
|
|
141
142
|
|
|
142
143
|
const hasProjectAccess = async (projectId: string, user: Auth): Promise<boolean> =>
|
|
@@ -147,7 +148,7 @@ const hasProjectWriteAccess = async (projectId: string, user: Auth): Promise<boo
|
|
|
147
148
|
|
|
148
149
|
const hasProjectResourceAccess = async (resourceIdRaw: string, user: Auth, manage = false): Promise<boolean> => {
|
|
149
150
|
const resourceId = ensureUuid(resourceIdRaw, 'Project resource ID');
|
|
150
|
-
if (user.role === 'admin'
|
|
151
|
+
if (user.role === 'admin') return true;
|
|
151
152
|
const resource = await pool.query('SELECT project_id,created_by FROM project_resources WHERE id=$1', [resourceId]);
|
|
152
153
|
if (!resource.rowCount) return false;
|
|
153
154
|
const access = await projectAccessLevel(resource.rows[0].project_id, user);
|
|
@@ -300,7 +301,7 @@ const rotateManagedAwsAccessKey = async (
|
|
|
300
301
|
const operatorUserId = await awsRotationOperatorUserId(actor, row);
|
|
301
302
|
const iam = await awsIamClientForUser(operatorUserId);
|
|
302
303
|
const keys = await iam.send(new ListAccessKeysCommand({ UserName: awsUsername }));
|
|
303
|
-
const keyIds = (keys.AccessKeyMetadata || []).map((item) => item.AccessKeyId).filter((value): value is string => !!value);
|
|
304
|
+
const keyIds = (keys.AccessKeyMetadata || []).map((item: { AccessKeyId?: string }) => item.AccessKeyId).filter((value: string | undefined): value is string => !!value);
|
|
304
305
|
if (keyIds.length >= 2) {
|
|
305
306
|
throw Object.assign(
|
|
306
307
|
new Error(`AWS IAM user ${awsUsername} already has two access keys. Remove the extra/unmanaged key before rotating through IndraQ.`),
|
|
@@ -834,11 +835,11 @@ app.post('/me/aws-credentials/:awsUsername/resolve', auth, async (req: AuthedReq
|
|
|
834
835
|
|
|
835
836
|
app.get('/projects', auth, async (req: AuthedRequest, res) => {
|
|
836
837
|
const { search, limit } = listQuery(req);
|
|
837
|
-
const
|
|
838
|
+
const archivedMode = req.query.archived === 'only' ? 'only' : req.query.archived === 'all' ? 'all' : 'active';
|
|
838
839
|
const result = await pool.query(
|
|
839
840
|
`SELECT p.*,u.email AS creator_email,
|
|
840
841
|
CASE
|
|
841
|
-
WHEN $3
|
|
842
|
+
WHEN $3='admin' THEN 'write'
|
|
842
843
|
WHEN p.created_by=$1 THEN 'write'
|
|
843
844
|
WHEN pm.role='owner' OR pm.access_level='write' THEN 'write'
|
|
844
845
|
ELSE 'view'
|
|
@@ -846,9 +847,11 @@ app.get('/projects', auth, async (req: AuthedRequest, res) => {
|
|
|
846
847
|
FROM projects p
|
|
847
848
|
JOIN users u ON u.id=p.created_by
|
|
848
849
|
LEFT JOIN project_members pm ON pm.project_id=p.id AND pm.user_id=$1
|
|
849
|
-
WHERE ($
|
|
850
|
+
WHERE ($3='admin' OR p.created_by=$1 OR pm.user_id=$1)
|
|
851
|
+
AND ($2='' OR p.name ILIKE '%'||$2||'%' OR p.kind ILIKE '%'||$2||'%' OR u.email ILIKE '%'||$2||'%')
|
|
852
|
+
AND ($4='all' OR ($4='only' AND p.archived_at IS NOT NULL) OR ($4='active' AND p.archived_at IS NULL))
|
|
850
853
|
ORDER BY p.created_at DESC
|
|
851
|
-
LIMIT $
|
|
854
|
+
LIMIT $5`, [req.auth!.id, search, req.auth!.role, archivedMode, limit]
|
|
852
855
|
);
|
|
853
856
|
res.json(result.rows);
|
|
854
857
|
});
|
|
@@ -887,6 +890,7 @@ app.get('/projects/:id', auth, async (req: AuthedRequest, res) => {
|
|
|
887
890
|
app.patch('/projects/:id', ...allow('manager', 'admin'), async (req: AuthedRequest, res) => {
|
|
888
891
|
const reason = requireReason(req, res); if (!reason) return;
|
|
889
892
|
const id = ensureUuid(routeParam(req.params.id), 'Project ID');
|
|
893
|
+
if (!(await hasProjectWriteAccess(id, req.auth!))) { res.status(403).json({ error: 'WRITE access to the project is required.' }); return; }
|
|
890
894
|
if (typeof req.body?.name === 'string' && req.body.name.trim()) {
|
|
891
895
|
const duplicate = await pool.query('SELECT id FROM projects WHERE lower(name)=lower($1) AND id<>$2 LIMIT 1', [req.body.name.trim(), id]);
|
|
892
896
|
if (duplicate.rowCount) { res.status(409).json({ error: `Project name "${req.body.name.trim()}" already exists organization-wide.` }); return; }
|
|
@@ -902,15 +906,37 @@ app.patch('/projects/:id', ...allow('manager', 'admin'), async (req: AuthedReque
|
|
|
902
906
|
|
|
903
907
|
app.delete('/projects/:id', ...allow('manager', 'admin'), async (req: AuthedRequest, res) => {
|
|
904
908
|
const reason = requireReason(req, res); if (!reason) return;
|
|
905
|
-
const id = routeParam(req.params.id);
|
|
909
|
+
const id = ensureUuid(routeParam(req.params.id), 'Project ID');
|
|
910
|
+
if ((await projectAccessLevel(id, req.auth!, true)) !== 'write') { res.status(403).json({ error: 'WRITE access to the project is required.' }); return; }
|
|
906
911
|
const result = await pool.query('DELETE FROM projects WHERE id=$1 RETURNING id,name', [id]);
|
|
907
912
|
if (!result.rowCount) { res.status(404).json({ error: 'Project not found' }); return; }
|
|
908
913
|
await audit(req.auth!.id, 'project.delete', 'project', id, reason, { name: result.rows[0].name });
|
|
909
914
|
res.status(204).end();
|
|
910
915
|
});
|
|
911
916
|
|
|
917
|
+
app.post('/projects/:id/archive', ...allow('manager', 'admin'), async (req: AuthedRequest, res) => {
|
|
918
|
+
const reason = requireReason(req, res); if (!reason) return;
|
|
919
|
+
const id = ensureUuid(routeParam(req.params.id), 'Project ID');
|
|
920
|
+
if ((await projectAccessLevel(id, req.auth!, true)) !== 'write') { res.status(403).json({ error: 'WRITE access to the project is required.' }); return; }
|
|
921
|
+
const result = await pool.query('UPDATE projects SET archived_at=COALESCE(archived_at,now()),archived_by=COALESCE(archived_by,$2),updated_at=now() WHERE id=$1 RETURNING id,name,archived_at,archived_by', [id, req.auth!.id]);
|
|
922
|
+
if (!result.rowCount) { res.status(404).json({ error: 'Project not found' }); return; }
|
|
923
|
+
await audit(req.auth!.id, 'project.archive', 'project', id, reason, { name: result.rows[0].name });
|
|
924
|
+
res.json(result.rows[0]);
|
|
925
|
+
});
|
|
926
|
+
|
|
927
|
+
app.post('/projects/:id/restore', ...allow('manager', 'admin'), async (req: AuthedRequest, res) => {
|
|
928
|
+
const reason = requireReason(req, res); if (!reason) return;
|
|
929
|
+
const id = ensureUuid(routeParam(req.params.id), 'Project ID');
|
|
930
|
+
if ((await projectAccessLevel(id, req.auth!, true)) !== 'write') { res.status(403).json({ error: 'WRITE access to the project is required.' }); return; }
|
|
931
|
+
const result = await pool.query('UPDATE projects SET archived_at=NULL,archived_by=NULL,updated_at=now() WHERE id=$1 RETURNING id,name,archived_at,archived_by', [id]);
|
|
932
|
+
if (!result.rowCount) { res.status(404).json({ error: 'Project not found' }); return; }
|
|
933
|
+
await audit(req.auth!.id, 'project.restore', 'project', id, reason, { name: result.rows[0].name });
|
|
934
|
+
res.json(result.rows[0]);
|
|
935
|
+
});
|
|
936
|
+
|
|
912
937
|
app.get('/projects/:id/members', ...allow('manager', 'admin'), async (req: AuthedRequest, res) => {
|
|
913
|
-
const id = routeParam(req.params.id);
|
|
938
|
+
const id = ensureUuid(routeParam(req.params.id), 'Project ID');
|
|
939
|
+
if (!(await hasProjectWriteAccess(id, req.auth!))) { res.status(403).json({ error: 'WRITE access to the project is required.' }); return; }
|
|
914
940
|
const result = await pool.query(`SELECT pm.project_id,pm.user_id,pm.role,pm.access_level,pm.added_at,u.email,u.name,u.role AS user_role
|
|
915
941
|
FROM project_members pm JOIN users u ON u.id=pm.user_id
|
|
916
942
|
WHERE pm.project_id=$1 AND u.is_active=true ORDER BY u.email`, [id]);
|
|
@@ -919,7 +945,8 @@ app.get('/projects/:id/members', ...allow('manager', 'admin'), async (req: Authe
|
|
|
919
945
|
|
|
920
946
|
app.post('/projects/:id/members', ...allow('manager', 'admin'), async (req: AuthedRequest, res) => {
|
|
921
947
|
const reason = requireReason(req, res); if (!reason) return;
|
|
922
|
-
const id = routeParam(req.params.id);
|
|
948
|
+
const id = ensureUuid(routeParam(req.params.id), 'Project ID');
|
|
949
|
+
if (!(await hasProjectWriteAccess(id, req.auth!))) { res.status(403).json({ error: 'WRITE access to the project is required.' }); return; }
|
|
923
950
|
const userId = String(req.body?.userId || '');
|
|
924
951
|
const project = await pool.query('SELECT id FROM projects WHERE id=$1', [id]);
|
|
925
952
|
if (!project.rowCount) { res.status(404).json({ error: 'Project not found' }); return; }
|
|
@@ -946,7 +973,8 @@ app.post('/projects/:id/members', ...allow('manager', 'admin'), async (req: Auth
|
|
|
946
973
|
|
|
947
974
|
app.delete('/projects/:id/members/:userId', ...allow('manager', 'admin'), async (req: AuthedRequest, res) => {
|
|
948
975
|
const reason = requireReason(req, res); if (!reason) return;
|
|
949
|
-
const id = routeParam(req.params.id);
|
|
976
|
+
const id = ensureUuid(routeParam(req.params.id), 'Project ID');
|
|
977
|
+
if (!(await hasProjectWriteAccess(id, req.auth!))) { res.status(403).json({ error: 'WRITE access to the project is required.' }); return; }
|
|
950
978
|
const userId = routeParam(req.params.userId);
|
|
951
979
|
const target = await pool.query('SELECT email,role FROM users WHERE id=$1', [userId]);
|
|
952
980
|
if (req.auth!.role === 'manager' && target.rows[0]?.role && target.rows[0].role !== 'user') { res.status(403).json({ error: 'Managers may remove only normal users from projects.' }); return; }
|
|
@@ -1058,7 +1086,7 @@ app.get('/scopes', auth, async (req: AuthedRequest, res) => {
|
|
|
1058
1086
|
LIMIT $${offset + 3}`,
|
|
1059
1087
|
params
|
|
1060
1088
|
);
|
|
1061
|
-
res.json(result.rows.map((row) => ({
|
|
1089
|
+
res.json(result.rows.map((row: any) => ({
|
|
1062
1090
|
id: row.id, provider: row.provider, name: row.name, description: row.description, company: row.company,
|
|
1063
1091
|
config: row.config || {}, has_secret: !!row.encrypted_secret, access: row.access || 'manage',
|
|
1064
1092
|
is_default: false, created_at: row.created_at, updated_at: row.updated_at
|
|
@@ -1144,6 +1172,7 @@ app.post('/scopes/:id/resolve', auth, async (req: AuthedRequest, res) => {
|
|
|
1144
1172
|
const id = ensureUuid(routeParam(req.params.id), 'Scope ID');
|
|
1145
1173
|
const access = await scopeAccessFor(id, req.auth!);
|
|
1146
1174
|
if (!access) { res.status(404).json({ error: 'Scope not found or access denied' }); return; }
|
|
1175
|
+
if (access === 'use') { res.status(403).json({ error: 'MANAGE access is required to resolve the provider management credential.' }); return; }
|
|
1147
1176
|
const result = await pool.query('SELECT id,provider,name,description,company,config,encrypted_secret FROM provider_scopes WHERE id=$1 AND is_active=true', [id]);
|
|
1148
1177
|
if (!result.rowCount) { res.status(404).json({ error: 'Scope not found' }); return; }
|
|
1149
1178
|
const row = result.rows[0];
|
|
@@ -1213,6 +1242,18 @@ app.get('/scopes/:id/audit', auth, async (req: AuthedRequest, res) => {
|
|
|
1213
1242
|
res.json(result.rows);
|
|
1214
1243
|
});
|
|
1215
1244
|
|
|
1245
|
+
app.get('/me/scopes', auth, async (req: AuthedRequest, res) => {
|
|
1246
|
+
const result = await pool.query(
|
|
1247
|
+
`SELECT ps.id,ps.provider,ps.name,ps.description,ps.company,ps.config,(ps.encrypted_secret IS NOT NULL) has_secret,
|
|
1248
|
+
CASE WHEN $2='admin' THEN 'manage' ELSE psm.access END AS access
|
|
1249
|
+
FROM provider_scopes ps
|
|
1250
|
+
LEFT JOIN provider_scope_members psm ON psm.scope_id=ps.id AND psm.user_id=$1
|
|
1251
|
+
WHERE ps.is_active=true AND ($2='admin' OR psm.user_id=$1)
|
|
1252
|
+
ORDER BY ps.provider,lower(ps.name)`, [req.auth!.id, req.auth!.role]
|
|
1253
|
+
);
|
|
1254
|
+
res.json(result.rows);
|
|
1255
|
+
});
|
|
1256
|
+
|
|
1216
1257
|
app.get('/me/scope-defaults', auth, async (req: AuthedRequest, res) => {
|
|
1217
1258
|
const result = await pool.query(
|
|
1218
1259
|
`SELECT usd.provider,usd.scope_id,ps.name,ps.company,ps.config,ps.updated_at
|
|
@@ -1261,13 +1302,33 @@ app.post('/me/scopes/:provider/resolve', auth, async (req: AuthedRequest, res) =
|
|
|
1261
1302
|
const visible = req.auth!.role === 'admin'
|
|
1262
1303
|
? await pool.query('SELECT id FROM provider_scopes WHERE provider=$1 AND is_active=true ORDER BY created_at', [provider])
|
|
1263
1304
|
: await pool.query('SELECT ps.id FROM provider_scopes ps JOIN provider_scope_members psm ON psm.scope_id=ps.id WHERE psm.user_id=$1 AND ps.provider=$2 AND ps.is_active=true ORDER BY ps.created_at', [req.auth!.id, provider]);
|
|
1264
|
-
if (visible.
|
|
1265
|
-
else if (visible.
|
|
1305
|
+
if (visible.rows.length === 1) scopeId = visible.rows[0].id;
|
|
1306
|
+
else if (visible.rows.length > 1) { res.status(409).json({ error: `Multiple ${provider} scopes are available. Link one to the project environment or set your default with \`indraq scope default\`.` }); return; }
|
|
1266
1307
|
}
|
|
1267
|
-
|
|
1308
|
+
const access = scopeId ? await scopeAccessFor(scopeId, req.auth!) : null;
|
|
1309
|
+
if (!scopeId || !access) { res.status(404).json({ error: `No ${provider} scope is assigned to your account.` }); return; }
|
|
1268
1310
|
const row = (await pool.query('SELECT id,provider,name,description,company,config,encrypted_secret FROM provider_scopes WHERE id=$1 AND is_active=true', [scopeId])).rows[0];
|
|
1269
|
-
|
|
1270
|
-
|
|
1311
|
+
let secret: any = null;
|
|
1312
|
+
let credentialSource = 'scope-management';
|
|
1313
|
+
if (req.auth!.role !== 'admin' && provider === 'aws') {
|
|
1314
|
+
const own = await pool.query('SELECT access_key_id,encrypted_secret_access_key FROM scope_aws_credentials WHERE scope_id=$1 AND cloud_user_id=$2', [scopeId, req.auth!.id]);
|
|
1315
|
+
if (!own.rowCount) { res.status(404).json({ error: 'No AWS credential is provisioned for you in this scope.' }); return; }
|
|
1316
|
+
secret = { accessKeyId: own.rows[0].access_key_id, secretAccessKey: decrypt(own.rows[0].encrypted_secret_access_key) };
|
|
1317
|
+
credentialSource = 'user-native';
|
|
1318
|
+
} else if (req.auth!.role !== 'admin' && ['npm','jenkins-dev','jenkins-prod'].includes(provider)) {
|
|
1319
|
+
const own = await pool.query('SELECT external_username,encrypted_secret FROM scope_external_accounts WHERE scope_id=$1 AND cloud_user_id=$2', [scopeId, req.auth!.id]);
|
|
1320
|
+
if (!own.rowCount || !own.rows[0].encrypted_secret) { res.status(404).json({ error: `No ${provider} credential is provisioned for you in this scope. Rotate your provider password or ask a scope manager to reprovision you.` }); return; }
|
|
1321
|
+
secret = provider === 'npm'
|
|
1322
|
+
? { identity: own.rows[0].external_username, secret: decrypt(own.rows[0].encrypted_secret) }
|
|
1323
|
+
: { username: own.rows[0].external_username, secret: decrypt(own.rows[0].encrypted_secret) };
|
|
1324
|
+
credentialSource = 'user-native';
|
|
1325
|
+
} else {
|
|
1326
|
+
secret = row.encrypted_secret ? JSON.parse(decrypt(row.encrypted_secret)) : null;
|
|
1327
|
+
}
|
|
1328
|
+
await audit(req.auth!.id, 'scope.credential.resolve', 'provider-scope', scopeId, reason, { provider, projectId: projectId || null, environment: environment || null, credentialSource });
|
|
1329
|
+
res.setHeader('Cache-Control', 'no-store');
|
|
1330
|
+
res.setHeader('Pragma', 'no-cache');
|
|
1331
|
+
res.json({ id: row.id, provider: row.provider, name: row.name, description: row.description, company: row.company, config: row.config || {}, secret, access: access === 'admin' ? 'manage' : access });
|
|
1271
1332
|
});
|
|
1272
1333
|
|
|
1273
1334
|
app.get('/projects/:projectId/environment-scopes', auth, async (req: AuthedRequest, res) => {
|
|
@@ -1378,12 +1439,13 @@ app.put('/scopes/:scopeId/users/:userId/external-account', ...allow('manager','a
|
|
|
1378
1439
|
const externalUsername = String(req.body?.externalUsername || '').trim();
|
|
1379
1440
|
if (!externalUsername) { res.status(400).json({ error: 'externalUsername is required.' }); return; }
|
|
1380
1441
|
const externalEmail = req.body?.externalEmail ? String(req.body.externalEmail).trim() : null;
|
|
1442
|
+
const encryptedSecret = req.body?.secret ? encrypt(String(req.body.secret)) : null;
|
|
1381
1443
|
const result = await pool.query(
|
|
1382
|
-
`INSERT INTO scope_external_accounts(scope_id,cloud_user_id,provider,external_username,external_email,managed_by_user_id,config)
|
|
1383
|
-
VALUES($1,$2,$3,$4,$5,$6,$7)
|
|
1384
|
-
ON CONFLICT(scope_id,cloud_user_id) DO UPDATE SET external_username=excluded.external_username,external_email=excluded.external_email,managed_by_user_id=excluded.managed_by_user_id,config=excluded.config,updated_at=now()
|
|
1444
|
+
`INSERT INTO scope_external_accounts(scope_id,cloud_user_id,provider,external_username,external_email,managed_by_user_id,config,encrypted_secret)
|
|
1445
|
+
VALUES($1,$2,$3,$4,$5,$6,$7,$8)
|
|
1446
|
+
ON CONFLICT(scope_id,cloud_user_id) DO UPDATE SET external_username=excluded.external_username,external_email=excluded.external_email,managed_by_user_id=excluded.managed_by_user_id,config=excluded.config,encrypted_secret=COALESCE(excluded.encrypted_secret,scope_external_accounts.encrypted_secret),updated_at=now()
|
|
1385
1447
|
RETURNING id,scope_id,cloud_user_id,provider,external_username,external_email,config,updated_at`,
|
|
1386
|
-
[scopeId, userId, scope.rows[0].provider, externalUsername, externalEmail, req.auth!.id, req.body?.config || {}]
|
|
1448
|
+
[scopeId, userId, scope.rows[0].provider, externalUsername, externalEmail, req.auth!.id, req.body?.config || {}, encryptedSecret]
|
|
1387
1449
|
);
|
|
1388
1450
|
await audit(req.auth!.id, 'scope.native-account.upsert', 'provider-scope', scopeId, reason, { userId, provider: scope.rows[0].provider, externalUsername, externalEmail });
|
|
1389
1451
|
res.json(result.rows[0]);
|
|
@@ -1490,6 +1552,74 @@ app.post('/me/scopes/:scopeId/aws-credential/resolve', auth, async (req: AuthedR
|
|
|
1490
1552
|
res.json({ awsUsername: row.aws_username, accessKeyId: row.access_key_id, secretAccessKey: decrypt(row.encrypted_secret_access_key), indraqRole: row.indraq_role });
|
|
1491
1553
|
});
|
|
1492
1554
|
|
|
1555
|
+
app.post('/me/scopes/:scopeId/aws-credential/rotate', auth, async (req: AuthedRequest, res) => {
|
|
1556
|
+
const reason = requireReason(req, res); if (!reason) return;
|
|
1557
|
+
const scopeId = ensureUuid(routeParam(req.params.scopeId), 'Scope ID');
|
|
1558
|
+
if (!(await scopeVisible(scopeId, req.auth!))) { res.status(403).json({ error: 'Scope access denied.' }); return; }
|
|
1559
|
+
const own = await pool.query('SELECT aws_username,access_key_id,encrypted_secret_access_key FROM scope_aws_credentials WHERE scope_id=$1 AND cloud_user_id=$2', [scopeId, req.auth!.id]);
|
|
1560
|
+
if (!own.rowCount) { res.status(404).json({ error: 'No AWS credential is stored for you in this scope.' }); return; }
|
|
1561
|
+
const scope = await pool.query('SELECT config,encrypted_secret FROM provider_scopes WHERE id=$1 AND provider=\'aws\' AND is_active=true', [scopeId]);
|
|
1562
|
+
if (!scope.rowCount || !scope.rows[0].encrypted_secret) { res.status(409).json({ error: 'AWS scope management credential is unavailable.' }); return; }
|
|
1563
|
+
const management = JSON.parse(decrypt(scope.rows[0].encrypted_secret));
|
|
1564
|
+
const region = String(scope.rows[0].config?.region || 'us-east-1');
|
|
1565
|
+
const iam = new IAMClient({ region, credentials: { accessKeyId: management.accessKeyId, secretAccessKey: management.secretAccessKey, ...(management.sessionToken ? { sessionToken: management.sessionToken } : {}) } });
|
|
1566
|
+
const created = await iam.send(new CreateAccessKeyCommand({ UserName: own.rows[0].aws_username }));
|
|
1567
|
+
const next = created.AccessKey;
|
|
1568
|
+
if (!next?.AccessKeyId || !next.SecretAccessKey) { res.status(502).json({ error: 'AWS did not return a replacement access key.' }); return; }
|
|
1569
|
+
try {
|
|
1570
|
+
await pool.query('UPDATE scope_aws_credentials SET access_key_id=$1,encrypted_secret_access_key=$2,updated_at=now() WHERE scope_id=$3 AND cloud_user_id=$4', [next.AccessKeyId, encrypt(next.SecretAccessKey), scopeId, req.auth!.id]);
|
|
1571
|
+
await iam.send(new DeleteAccessKeyCommand({ UserName: own.rows[0].aws_username, AccessKeyId: own.rows[0].access_key_id }));
|
|
1572
|
+
} catch (error) {
|
|
1573
|
+
try { await iam.send(new DeleteAccessKeyCommand({ UserName: own.rows[0].aws_username, AccessKeyId: next.AccessKeyId })); } catch { /* best effort */ }
|
|
1574
|
+
throw error;
|
|
1575
|
+
}
|
|
1576
|
+
await audit(req.auth!.id, 'scope.aws-credential.self-rotate', 'provider-scope', scopeId, reason, { awsUsername: own.rows[0].aws_username, previousAccessKeyId: own.rows[0].access_key_id, accessKeyId: next.AccessKeyId });
|
|
1577
|
+
res.setHeader('Cache-Control', 'no-store'); res.setHeader('Pragma', 'no-cache');
|
|
1578
|
+
res.json({ awsUsername: own.rows[0].aws_username, accessKeyId: next.AccessKeyId, secretAccessKey: next.SecretAccessKey });
|
|
1579
|
+
});
|
|
1580
|
+
|
|
1581
|
+
app.post('/me/scopes/:scopeId/external-account/rotate', auth, async (req: AuthedRequest, res) => {
|
|
1582
|
+
const reason = requireReason(req, res); if (!reason) return;
|
|
1583
|
+
const scopeId = ensureUuid(routeParam(req.params.scopeId), 'Scope ID');
|
|
1584
|
+
if (!(await scopeVisible(scopeId, req.auth!))) { res.status(403).json({ error: 'Scope access denied.' }); return; }
|
|
1585
|
+
const password = String(req.body?.password || '');
|
|
1586
|
+
if (password.length < 10) { res.status(400).json({ error: 'Password must be at least 10 characters.' }); return; }
|
|
1587
|
+
const account = await pool.query('SELECT provider,external_username FROM scope_external_accounts WHERE scope_id=$1 AND cloud_user_id=$2', [scopeId, req.auth!.id]);
|
|
1588
|
+
if (!account.rowCount) { res.status(404).json({ error: 'No NPM/Jenkins account is stored for you in this scope.' }); return; }
|
|
1589
|
+
const scope = await pool.query('SELECT provider,config,encrypted_secret FROM provider_scopes WHERE id=$1 AND is_active=true', [scopeId]);
|
|
1590
|
+
if (!scope.rowCount || !scope.rows[0].encrypted_secret) { res.status(409).json({ error: 'Provider management credential is unavailable.' }); return; }
|
|
1591
|
+
const provider = String(account.rows[0].provider);
|
|
1592
|
+
const management = JSON.parse(decrypt(scope.rows[0].encrypted_secret));
|
|
1593
|
+
const username = String(account.rows[0].external_username);
|
|
1594
|
+
if (provider === 'npm') {
|
|
1595
|
+
const base = String(scope.rows[0].config?.url || '').replace(/\/+$/, '');
|
|
1596
|
+
const tokenResponse = await fetch(`${base}/api/tokens`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ identity: management.identity, secret: management.secret }) });
|
|
1597
|
+
if (!tokenResponse.ok) { res.status(502).json({ error: `NPM management authentication failed (HTTP ${tokenResponse.status}).` }); return; }
|
|
1598
|
+
const token = (await tokenResponse.json() as { token?: string }).token;
|
|
1599
|
+
if (!token) { res.status(502).json({ error: 'NPM did not return a management token.' }); return; }
|
|
1600
|
+
const headers = { authorization: `Bearer ${token}`, 'content-type': 'application/json' };
|
|
1601
|
+
const usersResponse = await fetch(`${base}/api/users?expand=permissions`, { headers });
|
|
1602
|
+
if (!usersResponse.ok) { res.status(502).json({ error: `Unable to list NPM users (HTTP ${usersResponse.status}).` }); return; }
|
|
1603
|
+
const users = await usersResponse.json() as Array<{ id: number; email?: string; nickname?: string; name?: string }>;
|
|
1604
|
+
const wanted = username.toLowerCase();
|
|
1605
|
+
const target = users.find((u) => String(u.email || '').toLowerCase() === wanted || String(u.nickname || '').toLowerCase() === wanted || String(u.name || '').toLowerCase() === wanted);
|
|
1606
|
+
if (!target) { res.status(404).json({ error: `NPM user ${username} was not found.` }); return; }
|
|
1607
|
+
const update = await fetch(`${base}/api/users/${target.id}/auth`, { method: 'PUT', headers, body: JSON.stringify({ type: 'password', secret: password }) });
|
|
1608
|
+
if (!update.ok) { res.status(502).json({ error: `NPM password reset failed (HTTP ${update.status}).` }); return; }
|
|
1609
|
+
} else if (provider === 'jenkins-dev' || provider === 'jenkins-prod') {
|
|
1610
|
+
const url = String(scope.rows[0].config?.url || '').replace(/\/+$/, '');
|
|
1611
|
+
const adminUsername = String(management.username || '');
|
|
1612
|
+
const adminSecret = String(management.secret || '');
|
|
1613
|
+
const headers = await jenkinsRequestHeaders(url, adminUsername, adminSecret);
|
|
1614
|
+
const script = `import hudson.security.HudsonPrivateSecurityRealm\ndef user = hudson.model.User.getById(${JSON.stringify(username)}, false)\nif (user == null) throw new IllegalStateException('Jenkins user not found')\nuser.addProperty(HudsonPrivateSecurityRealm.Details.fromPlainPassword(${JSON.stringify(password)}))\nuser.save()\nprintln 'updated'`;
|
|
1615
|
+
const response = await fetch(`${url}/scriptText`, { method: 'POST', headers: { ...headers, 'content-type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ script }).toString() });
|
|
1616
|
+
if (!response.ok) { res.status(502).json({ error: `Jenkins password reset failed (HTTP ${response.status}).` }); return; }
|
|
1617
|
+
} else { res.status(400).json({ error: 'Scope must be NPM or Jenkins.' }); return; }
|
|
1618
|
+
await pool.query('UPDATE scope_external_accounts SET encrypted_secret=$1,updated_at=now() WHERE scope_id=$2 AND cloud_user_id=$3', [encrypt(password), scopeId, req.auth!.id]);
|
|
1619
|
+
await audit(req.auth!.id, 'scope.external-account.self-rotate', 'provider-scope', scopeId, reason, { provider, username });
|
|
1620
|
+
res.status(204).end();
|
|
1621
|
+
});
|
|
1622
|
+
|
|
1493
1623
|
// Reusable provider credentials are scoped to the authenticated IndraQ user.
|
|
1494
1624
|
// A user configures AWS/NPM/Jenkins/GHCR once and can reuse that profile across
|
|
1495
1625
|
// every project they are allowed to access. Secrets are encrypted at rest and
|
|
@@ -1699,18 +1829,23 @@ app.get('/users/:id/resource-grants', ...allow('manager', 'admin'), async (req:
|
|
|
1699
1829
|
const target = await pool.query('SELECT id,email,role,is_active FROM users WHERE id=$1', [userId]);
|
|
1700
1830
|
if (!target.rowCount) { res.status(404).json({ error: 'User not found' }); return; }
|
|
1701
1831
|
if (req.auth!.role === 'manager' && target.rows[0].role !== 'user') { res.status(403).json({ error: 'Managers may inspect grants only for normal users.' }); return; }
|
|
1832
|
+
const managerFilter = req.auth!.role === 'manager'
|
|
1833
|
+
? ` AND (p.created_by=$2 OR EXISTS (SELECT 1 FROM project_members pm WHERE pm.project_id=p.id AND pm.user_id=$2 AND (pm.role='owner' OR pm.access_level='write')))`
|
|
1834
|
+
: '';
|
|
1835
|
+
const params: unknown[] = req.auth!.role === 'manager' ? [userId, req.auth!.id] : [userId];
|
|
1702
1836
|
const result = await pool.query(`SELECT g.id,g.project_resource_id,g.user_id,g.permission,g.created_at,
|
|
1703
1837
|
pr.project_id,p.name AS project_name,pr.resource_type,pr.provider,pr.environment,pr.external_id,pr.config
|
|
1704
1838
|
FROM project_resource_grants g
|
|
1705
1839
|
JOIN project_resources pr ON pr.id=g.project_resource_id
|
|
1706
1840
|
JOIN projects p ON p.id=pr.project_id
|
|
1707
|
-
WHERE g.user_id=$1
|
|
1708
|
-
ORDER BY p.name,pr.provider,pr.resource_type`,
|
|
1841
|
+
WHERE g.user_id=$1 AND p.archived_at IS NULL${managerFilter}
|
|
1842
|
+
ORDER BY p.name,pr.provider,pr.resource_type`, params);
|
|
1709
1843
|
res.json(result.rows);
|
|
1710
1844
|
});
|
|
1711
1845
|
|
|
1712
1846
|
app.get('/projects/:projectId/resource-grants', ...allow('manager', 'admin'), async (req: AuthedRequest, res) => {
|
|
1713
1847
|
const projectId = routeParam(req.params.projectId);
|
|
1848
|
+
if (!(await hasProjectWriteAccess(projectId, req.auth!))) { res.status(403).json({ error: 'WRITE access to the project is required.' }); return; }
|
|
1714
1849
|
const userId = typeof req.query.userId === 'string' ? req.query.userId : undefined;
|
|
1715
1850
|
const result = await pool.query(`SELECT g.id,g.project_resource_id,g.user_id,g.permission,g.created_at,
|
|
1716
1851
|
u.email,u.name,pr.resource_type,pr.provider,pr.environment,pr.external_id,pr.config
|
|
@@ -1725,6 +1860,7 @@ app.get('/projects/:projectId/resource-grants', ...allow('manager', 'admin'), as
|
|
|
1725
1860
|
app.post('/projects/:projectId/resources/:resourceId/grants', ...allow('manager', 'admin'), async (req: AuthedRequest, res) => {
|
|
1726
1861
|
const reason = requireReason(req, res); if (!reason) return;
|
|
1727
1862
|
const projectId = routeParam(req.params.projectId);
|
|
1863
|
+
if (!(await hasProjectWriteAccess(projectId, req.auth!))) { res.status(403).json({ error: 'WRITE access to the project is required.' }); return; }
|
|
1728
1864
|
const resourceId = routeParam(req.params.resourceId);
|
|
1729
1865
|
const userId = String(req.body?.userId || '');
|
|
1730
1866
|
const permission = req.body?.permission === 'view' ? 'view' : 'manage';
|
|
@@ -1743,6 +1879,7 @@ app.post('/projects/:projectId/resources/:resourceId/grants', ...allow('manager'
|
|
|
1743
1879
|
app.delete('/projects/:projectId/resources/:resourceId/grants/:userId', ...allow('manager', 'admin'), async (req: AuthedRequest, res) => {
|
|
1744
1880
|
const reason = requireReason(req, res); if (!reason) return;
|
|
1745
1881
|
const projectId = routeParam(req.params.projectId);
|
|
1882
|
+
if (!(await hasProjectWriteAccess(projectId, req.auth!))) { res.status(403).json({ error: 'WRITE access to the project is required.' }); return; }
|
|
1746
1883
|
const resourceId = routeParam(req.params.resourceId);
|
|
1747
1884
|
const userId = routeParam(req.params.userId);
|
|
1748
1885
|
const target = await pool.query('SELECT email,role FROM users WHERE id=$1', [userId]);
|
package/cloud-api/tsconfig.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "NodeNext",
|
|
5
|
-
"moduleResolution": "NodeNext",
|
|
6
|
-
"rootDir": "src",
|
|
7
|
-
"outDir": "dist",
|
|
8
|
-
"strict": true,
|
|
9
|
-
"esModuleInterop": true,
|
|
10
|
-
"skipLibCheck": true,
|
|
11
|
-
"types": [
|
|
12
|
-
"node"
|
|
13
|
-
]
|
|
14
|
-
},
|
|
15
|
-
"include": [
|
|
16
|
-
"src/**/*.ts"
|
|
17
|
-
]
|
|
18
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"rootDir": "src",
|
|
7
|
+
"outDir": "dist",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"types": [
|
|
12
|
+
"node"
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
"include": [
|
|
16
|
+
"src/**/*.ts"
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"help-content.d.ts","sourceRoot":"","sources":["../../src/cli/help-content.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,WAAW,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,KAAK,GAAG,QAAQ,CAAC;AAMpH,eAAO,MAAM,oBAAoB,QAAO,
|
|
1
|
+
{"version":3,"file":"help-content.d.ts","sourceRoot":"","sources":["../../src/cli/help-content.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,WAAW,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,KAAK,GAAG,QAAQ,CAAC;AAMpH,eAAO,MAAM,oBAAoB,QAAO,MAqGvC,CAAC;AA6IF,eAAO,MAAM,YAAY,GAAI,OAAO,SAAS,KAAG,MAS/C,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,QAAQ,MAAM,KAAG,SAAS,GAAG,SAa3D,CAAC"}
|
package/dist/cli/help-content.js
CHANGED
|
@@ -9,7 +9,7 @@ const heading = (value) => chalk_1.default.bold(value);
|
|
|
9
9
|
const command = (value) => chalk_1.default.cyan(value);
|
|
10
10
|
const note = (value) => chalk_1.default.gray(value);
|
|
11
11
|
const fullCommandReference = () => `
|
|
12
|
-
${heading('IndraQ CLI v2.
|
|
12
|
+
${heading('IndraQ CLI v2.2 — scope-first infrastructure and deployment')}
|
|
13
13
|
|
|
14
14
|
${heading('Start here')}
|
|
15
15
|
${command('indraq login')} Sign in to https://cli.indraq.com
|
|
@@ -34,7 +34,8 @@ ${heading('Scopes — AWS / NPM / Jenkins / GHCR')}
|
|
|
34
34
|
${command('indraq scope rotate-password')} Rotate your own NPM/Jenkins password
|
|
35
35
|
|
|
36
36
|
${heading('Organization users')}
|
|
37
|
-
${command('indraq user list')}
|
|
37
|
+
${command('indraq user list')} List organization users (MANAGER/ADMIN)
|
|
38
|
+
${command('indraq user scopes [email]')} Show your own or a manageable user's scope assignments
|
|
38
39
|
${command('indraq user create [username]')} Create Cloud user and select one or many manageable scopes
|
|
39
40
|
${command('indraq user update [username] --role <role>')} Admin changes organization role; assigned scopes synchronize
|
|
40
41
|
${command('indraq user delete [username]')} Permanently remove identity from scopes, then Cloud
|
|
@@ -49,6 +50,10 @@ ${heading('Projects and project access')}
|
|
|
49
50
|
${command('indraq project scopes --env <env>')} Link provider scopes to this project/environment
|
|
50
51
|
${command('indraq project add user')} Add a user and synchronize required scope/resource access
|
|
51
52
|
${command('indraq project delete user')} Remove project membership
|
|
53
|
+
${command('indraq project rename [name]')} Rename a managed project
|
|
54
|
+
${command('indraq project archive')} Archive a managed project
|
|
55
|
+
${command('indraq project restore')} Restore an archived project
|
|
56
|
+
${command('indraq project delete')} Permanently delete project metadata
|
|
52
57
|
${command('indraq access add user')} Grant selected project resources
|
|
53
58
|
${command('indraq access delete user')} Revoke selected project resources
|
|
54
59
|
|
|
@@ -56,7 +61,8 @@ ${heading('Prebuild and deploy')}
|
|
|
56
61
|
${command('indraq prebuild --env dev')} Jenkins + GHCR required; DNS + NPM optional
|
|
57
62
|
${command('indraq deploy:dev')} Deploy dev
|
|
58
63
|
${command('indraq deploy:prod')} Deploy prod
|
|
59
|
-
${command('indraq deploy
|
|
64
|
+
${command('indraq deploy --env staging')} Deploy a named IndraQ project/resource environment
|
|
65
|
+
${command('indraq deploy --env dev --select-scopes')} Explicitly review/change saved scope bindings before deploy
|
|
60
66
|
${command('indraq deployment create --env dev')} Reconcile deployment infrastructure
|
|
61
67
|
${command('indraq deployment run --env dev')} Run deployment
|
|
62
68
|
|
|
@@ -129,12 +135,21 @@ ${heading('Commands')}
|
|
|
129
135
|
${command('indraq scope remove-user')}
|
|
130
136
|
${command('indraq scope default aws')}
|
|
131
137
|
${command('indraq scope audit')}
|
|
138
|
+
${command('indraq scope credentials')} Own AWS IAM credential for an assigned AWS scope
|
|
139
|
+
${command('indraq scope rotate-key')} Rotate your own AWS access key
|
|
140
|
+
${command('indraq scope rotate-password')} Rotate your own NPM/Jenkins password
|
|
132
141
|
|
|
133
|
-
${note(
|
|
142
|
+
${note("scope describe never prints passwords, API tokens, AWS secret keys, or GHCR tokens. Normal USE members never receive the organization provider-management secret; AWS/NPM/Jenkins resolve to the member's own native identity.")}
|
|
134
143
|
`;
|
|
135
144
|
const usersHelp = () => `
|
|
136
145
|
${heading('Users and scope membership')}
|
|
137
146
|
|
|
147
|
+
${command('indraq user list')}
|
|
148
|
+
Lists organization users for MANAGER/ADMIN accounts.
|
|
149
|
+
|
|
150
|
+
${command('indraq user scopes [email]')}
|
|
151
|
+
Shows your own scope assignments, or a user you are authorized to manage.
|
|
152
|
+
|
|
138
153
|
${command('indraq user create')}
|
|
139
154
|
Creates the Cloud identity first, then asks the admin/manager which MANAGE scopes
|
|
140
155
|
should receive the user. Multiple AWS/NPM/Jenkins/GHCR scopes can be selected.
|
|
@@ -154,6 +169,9 @@ ${command('indraq user update <user> --role manager')}
|
|
|
154
169
|
${command('indraq user delete')}
|
|
155
170
|
Removes every manageable provider-scope identity first. Cloud deletion happens only
|
|
156
171
|
after provider cleanup succeeds, so failed cleanup can be retried safely.
|
|
172
|
+
|
|
173
|
+
${command('indraq password reset')}
|
|
174
|
+
Resets your own IndraQ Cloud password.
|
|
157
175
|
`;
|
|
158
176
|
const deployHelp = () => `
|
|
159
177
|
${heading('Project scopes, prebuild and deployment')}
|
|
@@ -167,14 +185,18 @@ ${command('indraq prebuild --env dev')}
|
|
|
167
185
|
Optional Route53: Skip | Create/reconcile | Link existing
|
|
168
186
|
Optional NPM: Skip | Create/reconcile | Link existing
|
|
169
187
|
|
|
170
|
-
${command('indraq deploy
|
|
188
|
+
${command('indraq deploy --env dev')}
|
|
171
189
|
Builds the Docker image, pushes it to the GHCR scope linked/defaulted for dev, then
|
|
172
|
-
runs the Jenkins deployment associated with the dev project environment.
|
|
190
|
+
runs the Jenkins deployment associated with the dev project environment. Normal
|
|
191
|
+
deployments reuse the saved project/environment scope bindings without prompting.
|
|
192
|
+
|
|
193
|
+
${command('indraq deploy --env dev --select-scopes')}
|
|
194
|
+
Explicitly review or change the saved scope bindings before this deployment.
|
|
173
195
|
|
|
174
196
|
${note('--env is an IndraQ environment selector. Application environment files are part of your own Docker/build process and are not selected by this flag.')}
|
|
175
197
|
`;
|
|
176
198
|
const configureHelp = () => `
|
|
177
|
-
${heading('Configuration in v2.
|
|
199
|
+
${heading('Configuration in v2.2')}
|
|
178
200
|
|
|
179
201
|
Organization provider credentials are configured once as named scopes by an ADMIN.
|
|
180
202
|
Managers and users do not create organization AWS/NPM/Jenkins/GHCR credentials.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"help-content.js","sourceRoot":"","sources":["../../src/cli/help-content.ts"],"names":[],"mappings":";;;;;;AAAA,kDAA0B;AAI1B,MAAM,OAAO,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,eAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC7D,MAAM,OAAO,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,eAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC7D,MAAM,IAAI,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,eAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAEnD,MAAM,oBAAoB,GAAG,GAAW,EAAE,CAAC;EAChD,OAAO,CAAC,6DAA6D,CAAC;;EAEtE,OAAO,CAAC,YAAY,CAAC;IACnB,OAAO,CAAC,cAAc,CAAC;IACvB,OAAO,CAAC,mBAAmB,CAAC;IAC5B,OAAO,CAAC,kCAAkC,CAAC;IAC3C,OAAO,CAAC,uBAAuB,CAAC;IAChC,OAAO,CAAC,iCAAiC,CAAC;IAC1C,OAAO,CAAC,2BAA2B,CAAC;IACpC,OAAO,CAAC,mBAAmB,CAAC;;EAE9B,OAAO,CAAC,qCAAqC,CAAC;IAC5C,OAAO,CAAC,mBAAmB,CAAC;IAC5B,OAAO,CAAC,gCAAgC,CAAC;IACzC,OAAO,CAAC,kCAAkC,CAAC;IAC3C,OAAO,CAAC,sBAAsB,CAAC;IAC/B,OAAO,CAAC,uBAAuB,CAAC;IAChC,OAAO,CAAC,0BAA0B,CAAC;IACnC,OAAO,CAAC,iCAAiC,CAAC;IAC1C,OAAO,CAAC,oBAAoB,CAAC;IAC7B,OAAO,CAAC,0BAA0B,CAAC;IACnC,OAAO,CAAC,yBAAyB,CAAC;IAClC,OAAO,CAAC,8BAA8B,CAAC;;EAEzC,OAAO,CAAC,oBAAoB,CAAC;IAC3B,OAAO,CAAC,kBAAkB,CAAC;IAC3B,OAAO,CAAC,+BAA+B,CAAC;IACxC,OAAO,CAAC,6CAA6C,CAAC;IACtD,OAAO,CAAC,+BAA+B,CAAC;IACxC,OAAO,CAAC,uBAAuB,CAAC;;EAElC,OAAO,CAAC,6BAA6B,CAAC;IACpC,OAAO,CAAC,yBAAyB,CAAC;IAClC,OAAO,CAAC,8BAA8B,CAAC;IACvC,OAAO,CAAC,qBAAqB,CAAC;IAC9B,OAAO,CAAC,qBAAqB,CAAC;IAC9B,OAAO,CAAC,uBAAuB,CAAC;IAChC,OAAO,CAAC,mCAAmC,CAAC;IAC5C,OAAO,CAAC,yBAAyB,CAAC;IAClC,OAAO,CAAC,4BAA4B,CAAC;IACrC,OAAO,CAAC,wBAAwB,CAAC;IACjC,OAAO,CAAC,2BAA2B,CAAC;;EAEtC,OAAO,CAAC,qBAAqB,CAAC;IAC5B,OAAO,CAAC,2BAA2B,CAAC;IACpC,OAAO,CAAC,mBAAmB,CAAC;IAC5B,OAAO,CAAC,oBAAoB,CAAC;IAC7B,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"help-content.js","sourceRoot":"","sources":["../../src/cli/help-content.ts"],"names":[],"mappings":"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"}
|
|
@@ -139,36 +139,36 @@ const syncAwsNativeAccess = async (user) => {
|
|
|
139
139
|
const jenkinsRoleScript = (username, roleName, jobs) => {
|
|
140
140
|
const pattern = jobs.length ? `^(?:${jobs.map(escapeRegex).join('|')})$` : 'a^';
|
|
141
141
|
const enc = (v) => Buffer.from(v, 'utf8').toString('base64');
|
|
142
|
-
return `
|
|
143
|
-
import hudson.model.Item
|
|
144
|
-
import hudson.model.Run
|
|
145
|
-
import hudson.security.Permission
|
|
146
|
-
import jenkins.model.Jenkins
|
|
147
|
-
|
|
148
|
-
${role_strategy_groovy_1.jenkinsRoleStrategyCompatGroovy}
|
|
149
|
-
def decode = { String value -> new String(value.decodeBase64(), 'UTF-8') }
|
|
150
|
-
def username = decode('${enc(username)}')
|
|
151
|
-
def roleName = decode('${enc(roleName)}')
|
|
152
|
-
def pattern = decode('${enc(pattern)}')
|
|
153
|
-
def j = Jenkins.get()
|
|
154
|
-
def strategy = j.authorizationStrategy
|
|
155
|
-
indraqRequireRoleStrategy(strategy)
|
|
156
|
-
def map = strategy.getRoleMap(indraqRoleType('Project'))
|
|
157
|
-
def old = map.getRole(roleName)
|
|
158
|
-
if (old != null) map.removeRole(old)
|
|
159
|
-
if (pattern != 'a^') {
|
|
160
|
-
Set<Permission> permissions = new HashSet<Permission>()
|
|
161
|
-
permissions.add(Item.DISCOVER)
|
|
162
|
-
permissions.add(Item.READ)
|
|
163
|
-
permissions.add(Item.BUILD)
|
|
164
|
-
permissions.add(Item.CANCEL)
|
|
165
|
-
permissions.add(Item.WORKSPACE)
|
|
166
|
-
def role = indraqNewRole(roleName, pattern, permissions)
|
|
167
|
-
map.addRole(role)
|
|
168
|
-
indraqAssignRole(map, role, username)
|
|
169
|
-
}
|
|
170
|
-
j.save()
|
|
171
|
-
println 'OK|' + roleName + '|' + pattern
|
|
142
|
+
return `
|
|
143
|
+
import hudson.model.Item
|
|
144
|
+
import hudson.model.Run
|
|
145
|
+
import hudson.security.Permission
|
|
146
|
+
import jenkins.model.Jenkins
|
|
147
|
+
|
|
148
|
+
${role_strategy_groovy_1.jenkinsRoleStrategyCompatGroovy}
|
|
149
|
+
def decode = { String value -> new String(value.decodeBase64(), 'UTF-8') }
|
|
150
|
+
def username = decode('${enc(username)}')
|
|
151
|
+
def roleName = decode('${enc(roleName)}')
|
|
152
|
+
def pattern = decode('${enc(pattern)}')
|
|
153
|
+
def j = Jenkins.get()
|
|
154
|
+
def strategy = j.authorizationStrategy
|
|
155
|
+
indraqRequireRoleStrategy(strategy)
|
|
156
|
+
def map = strategy.getRoleMap(indraqRoleType('Project'))
|
|
157
|
+
def old = map.getRole(roleName)
|
|
158
|
+
if (old != null) map.removeRole(old)
|
|
159
|
+
if (pattern != 'a^') {
|
|
160
|
+
Set<Permission> permissions = new HashSet<Permission>()
|
|
161
|
+
permissions.add(Item.DISCOVER)
|
|
162
|
+
permissions.add(Item.READ)
|
|
163
|
+
permissions.add(Item.BUILD)
|
|
164
|
+
permissions.add(Item.CANCEL)
|
|
165
|
+
permissions.add(Item.WORKSPACE)
|
|
166
|
+
def role = indraqNewRole(roleName, pattern, permissions)
|
|
167
|
+
map.addRole(role)
|
|
168
|
+
indraqAssignRole(map, role, username)
|
|
169
|
+
}
|
|
170
|
+
j.save()
|
|
171
|
+
println 'OK|' + roleName + '|' + pattern
|
|
172
172
|
`;
|
|
173
173
|
};
|
|
174
174
|
const syncJenkinsNativeAccess = async (user) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route53-lookup.service.js","sourceRoot":"","sources":["../../../../src/modules/aws/services/route53-lookup.service.ts"],"names":[],"mappings":";;;AAAA,8DAAyH;AACzH,8EAA+E;AAC/E,6EAA6E;AAC7E,sFAAkF;AAElF,MAAM,SAAS,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAC3F,MAAM,MAAM,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;AAQxE,MAAM,sBAAsB,GAAG,KAAK,EAAE,MAAc,EAAsC,EAAE;IACjG,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IACjC,2EAA2E;IAC3E,4EAA4E;IAC5E,yCAAyC;IACzC,IAAI,IAAA,sCAAmB,GAAE,EAAE,SAAS,EAAE,CAAC;QACrC,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,MAAM,IAAA,4CAAoB,GAAE,CAAC;YAC/C,MAAM,UAAU,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,KAAK,KAAK,IAAI,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,SAAS,CAAC,MAAM,CAAE,QAAQ,CAAC,MAAc,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC;YACtM,IAAI,UAAU,EAAE,CAAC;gBACf,MAAM,MAAM,GAAG,UAAU,CAAC,MAAmE,CAAC;gBAC9F,OAAO;oBACL,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC;oBACnC,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;oBAC9C,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,EAAE,IAAI,EAAE,CAAC,MAAM,CAAC,UAAU,IAAI,GAAG,CAA2C,EAAE;iBAC3I,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,MAAM,CAAC,CAAC,kEAAkE,CAAC,CAAC;IAChF,CAAC;IACD,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,IAAA,yCAAqB,EAAC,qDAAqD,CAAC,CAAC;IACvG,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,wCAAsB,CAAC,EAAE,CAAC,CAAC,CAAC;IACvE,MAAM,KAAK,GAAG,CAAC,WAAW,CAAC,WAAW,IAAI,EAAE,CAAC;SAC1C,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC;SAC7B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,EAAG,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,IAAK,CAAC,EAAE,CAAC,CAAC;SAC7D,MAAM,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"route53-lookup.service.js","sourceRoot":"","sources":["../../../../src/modules/aws/services/route53-lookup.service.ts"],"names":[],"mappings":";;;AAAA,8DAAyH;AACzH,8EAA+E;AAC/E,6EAA6E;AAC7E,sFAAkF;AAElF,MAAM,SAAS,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAC3F,MAAM,MAAM,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;AAQxE,MAAM,sBAAsB,GAAG,KAAK,EAAE,MAAc,EAAsC,EAAE;IACjG,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IACjC,2EAA2E;IAC3E,4EAA4E;IAC5E,yCAAyC;IACzC,IAAI,IAAA,sCAAmB,GAAE,EAAE,SAAS,EAAE,CAAC;QACrC,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,MAAM,IAAA,4CAAoB,GAAE,CAAC;YAC/C,MAAM,UAAU,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,KAAK,KAAK,IAAI,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,SAAS,CAAC,MAAM,CAAE,QAAQ,CAAC,MAAc,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC;YACtM,IAAI,UAAU,EAAE,CAAC;gBACf,MAAM,MAAM,GAAG,UAAU,CAAC,MAAmE,CAAC;gBAC9F,OAAO;oBACL,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC;oBACnC,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;oBAC9C,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,EAAE,IAAI,EAAE,CAAC,MAAM,CAAC,UAAU,IAAI,GAAG,CAA2C,EAAE;iBAC3I,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,MAAM,CAAC,CAAC,kEAAkE,CAAC,CAAC;IAChF,CAAC;IACD,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,IAAA,yCAAqB,EAAC,qDAAqD,CAAC,CAAC;IACvG,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,wCAAsB,CAAC,EAAE,CAAC,CAAC,CAAC;IACvE,MAAM,KAAK,GAAG,CAAC,WAAW,CAAC,WAAW,IAAI,EAAE,CAAC;SAC1C,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC;SAC7B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,EAAG,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,IAAK,CAAC,EAAE,CAAC,CAAC;SAC7D,MAAM,CAAC,CAAC,CAA+B,EAAE,EAAE,CAAC,MAAM,KAAK,CAAC,CAAC,IAAI,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;SAC/F,IAAI,CAAC,CAAC,CAA+B,EAAE,CAA+B,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAE7G,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,+CAA6B,CAAC;YAClE,YAAY,EAAE,IAAI,CAAC,EAAE;YACrB,eAAe,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG;YAC7D,QAAQ,EAAE,EAAE;SACb,CAAC,CAAC,CAAC;QACJ,MAAM,MAAM,GAAG,CAAC,MAAM,CAAC,kBAAkB,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAoB,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC,CAAC;QACpH,IAAI,MAAM;YAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IACtE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AArCW,QAAA,sBAAsB,0BAqCjC;AAEK,MAAM,oBAAoB,GAAG,KAAK,EAAE,MAAc,EAA+B,EAAE;IACxF,MAAM,KAAK,GAAG,MAAM,IAAA,8BAAsB,EAAC,MAAM,CAAC,CAAC;IACnD,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,MAAM,8DAA8D,MAAM,qDAAqD,CAAC,CAAC;IACtM,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAJW,QAAA,oBAAoB,wBAI/B"}
|
|
@@ -34,5 +34,9 @@ export declare const listProjects: (options: ListCliOptions) => Promise<void>;
|
|
|
34
34
|
export declare const linkProject: (options?: ListCliOptions) => Promise<void>;
|
|
35
35
|
export declare const addProjectUser: () => Promise<void>;
|
|
36
36
|
export declare const deleteProjectUser: () => Promise<void>;
|
|
37
|
+
export declare const renameProject: (newNameArg?: string) => Promise<void>;
|
|
38
|
+
export declare const archiveProject: (yes?: boolean) => Promise<void>;
|
|
39
|
+
export declare const restoreProject: () => Promise<void>;
|
|
40
|
+
export declare const deleteProject: (yes?: boolean) => Promise<void>;
|
|
37
41
|
export {};
|
|
38
42
|
//# sourceMappingURL=cloud.command.d.ts.map
|