Skip to content

Commit a48424d

Browse files
blaissao : Enforce 'DeleteSandbox' Permission Set requirement for Sandbox Deletion
1 parent 85721af commit a48424d

2 files changed

Lines changed: 71 additions & 0 deletions

File tree

messages/delete_sandbox.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,11 @@ Ensure the CLI has authenticated with the sandbox's production org.
5454
# error.missingUsername
5555

5656
Unable to determine the username of the org to delete. Specify the username with the --target-org | -o flag.
57+
58+
# error.insufficientPermissions
59+
60+
You do not have the required permission to delete this sandbox: %s. Contact your administrator to assign you the "DeleteSandbox" PermissionSet.
61+
62+
# warning.couldNotVerifyPermissions
63+
64+
Could not verify permissions in sandbox: %s. The delete operation will proceed, but may fail if you do not have the required permissions.

src/commands/org/delete/sandbox.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,29 @@ export default class DeleteSandbox extends SfCommand<SandboxDeleteResponse> {
6161
throw messages.createError('error.unknownSandbox', [username]);
6262
}
6363

64+
// Check if user has the DeleteSandbox PermissionSet in the sandbox
65+
try {
66+
const sandboxOrg = await Org.create({ aliasOrUsername: username });
67+
const hasDeleteSandboxPermission = await this.hasPermission(sandboxOrg, 'DeleteSandbox');
68+
this.debug('hasDeleteSandboxPermission %s ', hasDeleteSandboxPermission);
69+
if (!hasDeleteSandboxPermission) {
70+
throw messages.createError('error.insufficientPermissions', [username]);
71+
}
72+
} catch (error) {
73+
// If it's a permission error we created, re-throw it
74+
if (error instanceof SfError) {
75+
const errorMessage = error.message || '';
76+
if (errorMessage.includes('required permission') || errorMessage.includes('DeleteSandbox')) {
77+
throw error;
78+
}
79+
}
80+
// For other errors (e.g., org connection issues), log a warning but continue
81+
// The actual delete operation will fail if permissions are truly insufficient
82+
if (error instanceof Error) {
83+
this.warn(messages.getMessage('warning.couldNotVerifyPermissions', [username]));
84+
}
85+
}
86+
6487
if (flags['no-prompt'] || (await this.confirm({ message: messages.getMessage('prompt.confirm', [username]) }))) {
6588
try {
6689
const org = await Org.create({ aliasOrUsername: username });
@@ -82,4 +105,44 @@ export default class DeleteSandbox extends SfCommand<SandboxDeleteResponse> {
82105
}
83106
return { username, orgId };
84107
}
108+
109+
/**
110+
* Checks if the current user has a PermissionSet with the specified name assigned.
111+
*
112+
* @param org The org to check permissions in
113+
* @param permissionSetName The name of the PermissionSet to check (e.g., 'DeleteSandbox')
114+
* @returns True if the user has the PermissionSet assigned, false otherwise
115+
*/
116+
// eslint-disable-next-line class-methods-use-this
117+
private async hasPermission(org: Org, permissionSetName: string): Promise<boolean> {
118+
try {
119+
const connection = org.getConnection();
120+
await org.refreshAuth();
121+
// try to get it from Identity API
122+
const identity = await connection.identity();
123+
const userId = identity.user_id;
124+
125+
if (!userId) {
126+
return false;
127+
}
128+
129+
// Check if user has the PermissionSet assigned
130+
const permissionSetAssignmentQuery = `
131+
SELECT Id
132+
FROM PermissionSetAssignment
133+
WHERE AssigneeId = '${userId.replace(/'/g, "\\'")}'
134+
AND PermissionSet.Name = '${permissionSetName.replace(/'/g, "\\'")}'
135+
`;
136+
137+
try {
138+
const permissionSetResult = await connection.query(permissionSetAssignmentQuery);
139+
return permissionSetResult.totalSize > 0;
140+
} catch {
141+
// If query fails, return false
142+
return false;
143+
}
144+
} catch {
145+
return false;
146+
}
147+
}
85148
}

0 commit comments

Comments
 (0)