diff --git a/.gitignore b/.gitignore index 136c551..64f7bff 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,8 @@ package-lock.json .pmdCache .sf + +# Claude Code files +CLAUDE.md +.claude/ +settings.local.json diff --git a/trigger-actions-framework/main/default/classes/FinalizerHandler.cls b/trigger-actions-framework/main/default/classes/FinalizerHandler.cls index 61b81f4..8c9780f 100644 --- a/trigger-actions-framework/main/default/classes/FinalizerHandler.cls +++ b/trigger-actions-framework/main/default/classes/FinalizerHandler.cls @@ -194,11 +194,50 @@ public with sharing virtual class FinalizerHandler { if (permissionName != null && !permissionMap.containsKey(permissionName)) { permissionMap.put( permissionName, - FeatureManagement.checkPermission(permissionName) + checkPermissionNamespaceAware(permissionName) ); } } + /** + * @description Check permission in a namespace-aware manner. + * Handles both namespaced (namespace__permission) and non-namespaced permissions. + * When a namespaced permission is detected, tries both the original name and the + * permission name without namespace to ensure compatibility across package contexts. + * + * @param permissionName The permission name, which may include a namespace prefix + * @return True if the user has the permission, false otherwise + */ + @TestVisible + private Boolean checkPermissionNamespaceAware(String permissionName) { + String doubleUnderscore = '__'; + + // First try the permission as provided + try { + if (FeatureManagement.checkPermission(permissionName)) { + return true; + } + } catch (Exception e) { + // Permission doesn't exist in current context, continue to try alternatives + } + + // If the permission contains namespace prefix, try without it + if (permissionName.contains(doubleUnderscore)) { + List parts = permissionName.split(doubleUnderscore, 2); + if (parts.size() == 2) { + String permission = parts[1]; + try { + return FeatureManagement.checkPermission(permission); + } catch (Exception e) { + // Permission doesn't exist without namespace either + } + } + } + + // Default to false if permission cannot be found in any context + return false; + } + @TestVisible private List allFinalizers { get { diff --git a/trigger-actions-framework/main/default/classes/FinalizerHandlerTest.cls b/trigger-actions-framework/main/default/classes/FinalizerHandlerTest.cls index 637fbb8..60adab0 100644 --- a/trigger-actions-framework/main/default/classes/FinalizerHandlerTest.cls +++ b/trigger-actions-framework/main/default/classes/FinalizerHandlerTest.cls @@ -299,6 +299,35 @@ private with sharing class FinalizerHandlerTest { } } + @IsTest + private static void checkPermissionNamespaceAwareShouldHandleNamespacedPermissions() { + FinalizerHandler testHandler = new FinalizerHandler(); + + // Test non-namespaced permission (should work as normal) + Boolean result1 = testHandler.checkPermissionNamespaceAware('SimplePermission'); + System.Assert.areEqual( + false, // Permissions typically return false in test context unless specifically set up + result1, + 'Non-namespaced permission should work correctly' + ); + + // Test namespaced permission (should try both formats) + Boolean result2 = testHandler.checkPermissionNamespaceAware('MyNamespace__MyPermission'); + System.Assert.areEqual( + false, // Will return false since permissions don't exist in test context + result2, + 'Namespaced permission should handle gracefully when permission does not exist' + ); + + // Test permission with multiple underscores (should split at first occurrence) + Boolean result3 = testHandler.checkPermissionNamespaceAware('MyNamespace__Complex__Permission'); + System.Assert.areEqual( + false, // Will return false since permissions don't exist in test context + result3, + 'Permission with multiple underscores should handle gracefully' + ); + } + public class MyClass { } diff --git a/trigger-actions-framework/main/default/classes/MetadataTriggerHandler.cls b/trigger-actions-framework/main/default/classes/MetadataTriggerHandler.cls index a4fa549..4dcb77a 100644 --- a/trigger-actions-framework/main/default/classes/MetadataTriggerHandler.cls +++ b/trigger-actions-framework/main/default/classes/MetadataTriggerHandler.cls @@ -237,11 +237,48 @@ public inherited sharing class MetadataTriggerHandler extends TriggerBase implem if (permissionName != null && !permissionMap.containsKey(permissionName)) { permissionMap.put( permissionName, - FeatureManagement.checkPermission(permissionName) + checkPermissionNamespaceAware(permissionName) ); } } + /** + * @description Check permission in a namespace-aware manner. + * Handles both namespaced (namespace__permission) and non-namespaced permissions. + * When a namespaced permission is detected, tries both the original name and the + * permission name without namespace to ensure compatibility across package contexts. + * + * @param permissionName The permission name, which may include a namespace prefix + * @return True if the user has the permission, false otherwise + */ + @TestVisible + private Boolean checkPermissionNamespaceAware(String permissionName) { + // First try the permission as provided + try { + if (FeatureManagement.checkPermission(permissionName)) { + return true; + } + } catch (Exception e) { + // Permission doesn't exist in current context, continue to try alternatives + } + + // If the permission contains namespace prefix, try without it + if (permissionName.contains(DOUBLE_UNDERSCORE)) { + List parts = permissionName.split(DOUBLE_UNDERSCORE, 2); + if (parts.size() == 2) { + String permission = parts[1]; + try { + return FeatureManagement.checkPermission(permission); + } catch (Exception e) { + // Permission doesn't exist without namespace either + } + } + } + + // Default to false if permission cannot be found in any context + return false; + } + /** * @description Get the Trigger Action metadata. * diff --git a/trigger-actions-framework/main/default/classes/MetadataTriggerHandlerTest.cls b/trigger-actions-framework/main/default/classes/MetadataTriggerHandlerTest.cls index 20e394b..970eebb 100644 --- a/trigger-actions-framework/main/default/classes/MetadataTriggerHandlerTest.cls +++ b/trigger-actions-framework/main/default/classes/MetadataTriggerHandlerTest.cls @@ -789,6 +789,35 @@ private class MetadataTriggerHandlerTest { MetadataTriggerHandlerTest.executed = true; } } + @IsTest + private static void checkPermissionNamespaceAwareShouldHandleNamespacedPermissions() { + MetadataTriggerHandler testHandler = new MetadataTriggerHandler(); + + // Test non-namespaced permission (should work as normal) + Boolean result1 = testHandler.checkPermissionNamespaceAware('SimplePermission'); + System.Assert.areEqual( + false, // Permissions typically return false in test context unless specifically set up + result1, + 'Non-namespaced permission should work correctly' + ); + + // Test namespaced permission (should try both formats) + Boolean result2 = testHandler.checkPermissionNamespaceAware('MyNamespace__MyPermission'); + System.Assert.areEqual( + false, // Will return false since permissions don't exist in test context + result2, + 'Namespaced permission should handle gracefully when permission does not exist' + ); + + // Test permission with multiple underscores (should split at first occurrence) + Boolean result3 = testHandler.checkPermissionNamespaceAware('MyNamespace__Complex__Permission'); + System.Assert.areEqual( + false, // Will return false since permissions don't exist in test context + result3, + 'Permission with multiple underscores should handle gracefully' + ); + } + public class TestFinalizerHandler extends FinalizerHandler { public override void handleDynamicFinalizers() { MetadataTriggerHandlerTest.executed = true;