@@ -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