permission.spec.ts raw

   1  import { Permission, PermissionChecker } from './permission';
   2  import { IdentityId } from '../value-objects';
   3  
   4  describe('Permission Entity', () => {
   5    const testIdentityId = IdentityId.from('identity-1');
   6    const testHost = 'example.com';
   7    const testMethod = 'signEvent';
   8  
   9    describe('allow', () => {
  10      it('should create an allow permission', () => {
  11        const permission = Permission.allow(testIdentityId, testHost, testMethod);
  12  
  13        expect(permission.isAllowed()).toBe(true);
  14      });
  15  
  16      it('should create permission with kind for signEvent', () => {
  17        const permission = Permission.allow(testIdentityId, testHost, testMethod, 1);
  18  
  19        expect(permission.isAllowed()).toBe(true);
  20      });
  21    });
  22  
  23    describe('deny', () => {
  24      it('should create a deny permission', () => {
  25        const permission = Permission.deny(testIdentityId, testHost, testMethod);
  26  
  27        expect(permission.isAllowed()).toBe(false);
  28      });
  29    });
  30  
  31    describe('matches', () => {
  32      it('should match when all parameters are the same', () => {
  33        const permission = Permission.allow(testIdentityId, testHost, testMethod);
  34  
  35        expect(permission.matches(testIdentityId, testHost, testMethod)).toBe(true);
  36      });
  37  
  38      it('should not match when identity differs', () => {
  39        const permission = Permission.allow(testIdentityId, testHost, testMethod);
  40        const differentIdentity = IdentityId.from('identity-2');
  41  
  42        expect(permission.matches(differentIdentity, testHost, testMethod)).toBe(false);
  43      });
  44  
  45      it('should not match when host differs', () => {
  46        const permission = Permission.allow(testIdentityId, testHost, testMethod);
  47  
  48        expect(permission.matches(testIdentityId, 'other.com', testMethod)).toBe(false);
  49      });
  50  
  51      it('should not match when method differs', () => {
  52        const permission = Permission.allow(testIdentityId, testHost, testMethod);
  53  
  54        expect(permission.matches(testIdentityId, testHost, 'getPublicKey')).toBe(false);
  55      });
  56  
  57      it('should match any kind when permission has no kind specified', () => {
  58        const permission = Permission.allow(testIdentityId, testHost, testMethod);
  59  
  60        expect(permission.matches(testIdentityId, testHost, testMethod, 1)).toBe(true);
  61        expect(permission.matches(testIdentityId, testHost, testMethod, 30023)).toBe(true);
  62      });
  63  
  64      it('should only match specific kind when permission has kind', () => {
  65        const permission = Permission.allow(testIdentityId, testHost, testMethod, 1);
  66  
  67        expect(permission.matches(testIdentityId, testHost, testMethod, 1)).toBe(true);
  68        expect(permission.matches(testIdentityId, testHost, testMethod, 30023)).toBe(false);
  69      });
  70    });
  71  
  72    describe('fromSnapshot', () => {
  73      it('should reconstruct permission from snapshot', () => {
  74        const original = Permission.allow(testIdentityId, testHost, testMethod, 1);
  75        const snapshot = original.toSnapshot();
  76  
  77        const restored = Permission.fromSnapshot(snapshot);
  78  
  79        expect(restored.isAllowed()).toBe(true);
  80        expect(restored.matches(testIdentityId, testHost, testMethod, 1)).toBe(true);
  81      });
  82    });
  83  
  84    describe('toSnapshot', () => {
  85      it('should create valid snapshot', () => {
  86        const permission = Permission.allow(testIdentityId, testHost, testMethod, 1);
  87        const snapshot = permission.toSnapshot();
  88  
  89        expect(snapshot.identityId).toEqual(testIdentityId.toString());
  90        expect(snapshot.host).toEqual(testHost);
  91        expect(snapshot.method).toEqual(testMethod);
  92        expect(snapshot.methodPolicy).toEqual('allow');
  93        expect(snapshot.kind).toBe(1);
  94      });
  95    });
  96  });
  97  
  98  describe('PermissionChecker', () => {
  99    const identity1 = IdentityId.from('identity-1');
 100    const identity2 = IdentityId.from('identity-2');
 101  
 102    describe('check', () => {
 103      it('should return true for allowed permission', () => {
 104        const permissions = [
 105          Permission.allow(identity1, 'example.com', 'signEvent'),
 106        ];
 107        const checker = new PermissionChecker(permissions);
 108  
 109        expect(checker.check(identity1, 'example.com', 'signEvent')).toBe(true);
 110      });
 111  
 112      it('should return false for denied permission', () => {
 113        const permissions = [
 114          Permission.deny(identity1, 'example.com', 'signEvent'),
 115        ];
 116        const checker = new PermissionChecker(permissions);
 117  
 118        expect(checker.check(identity1, 'example.com', 'signEvent')).toBe(false);
 119      });
 120  
 121      it('should return undefined when no matching permission exists', () => {
 122        const permissions = [
 123          Permission.allow(identity1, 'example.com', 'signEvent'),
 124        ];
 125        const checker = new PermissionChecker(permissions);
 126  
 127        expect(checker.check(identity2, 'example.com', 'signEvent')).toBeUndefined();
 128      });
 129  
 130      it('should check kind-specific permissions first', () => {
 131        const permissions = [
 132          Permission.deny(identity1, 'example.com', 'signEvent', 1), // Deny kind 1
 133          Permission.allow(identity1, 'example.com', 'signEvent'),   // Allow all others
 134        ];
 135        const checker = new PermissionChecker(permissions);
 136  
 137        expect(checker.check(identity1, 'example.com', 'signEvent', 1)).toBe(false);
 138        expect(checker.check(identity1, 'example.com', 'signEvent', 30023)).toBe(true);
 139      });
 140  
 141      it('should handle multiple identities', () => {
 142        const permissions = [
 143          Permission.allow(identity1, 'example.com', 'signEvent'),
 144          Permission.deny(identity2, 'example.com', 'signEvent'),
 145        ];
 146        const checker = new PermissionChecker(permissions);
 147  
 148        expect(checker.check(identity1, 'example.com', 'signEvent')).toBe(true);
 149        expect(checker.check(identity2, 'example.com', 'signEvent')).toBe(false);
 150      });
 151  
 152      it('should handle multiple hosts', () => {
 153        const permissions = [
 154          Permission.allow(identity1, 'allowed.com', 'signEvent'),
 155          Permission.deny(identity1, 'denied.com', 'signEvent'),
 156        ];
 157        const checker = new PermissionChecker(permissions);
 158  
 159        expect(checker.check(identity1, 'allowed.com', 'signEvent')).toBe(true);
 160        expect(checker.check(identity1, 'denied.com', 'signEvent')).toBe(false);
 161        expect(checker.check(identity1, 'unknown.com', 'signEvent')).toBeUndefined();
 162      });
 163  
 164      it('should handle multiple methods', () => {
 165        const permissions = [
 166          Permission.allow(identity1, 'example.com', 'getPublicKey'),
 167          Permission.deny(identity1, 'example.com', 'signEvent'),
 168        ];
 169        const checker = new PermissionChecker(permissions);
 170  
 171        expect(checker.check(identity1, 'example.com', 'getPublicKey')).toBe(true);
 172        expect(checker.check(identity1, 'example.com', 'signEvent')).toBe(false);
 173      });
 174    });
 175  });
 176