createMock(PermissionService::class)); $this->assertTrue($policy->supports(OperationsAuthorizationPolicy::ABILITY_ADMIN_JOBS_VIEW)); $this->assertTrue($policy->supports(OperationsAuthorizationPolicy::ABILITY_API_TENANTS_DELETE)); $this->assertTrue($policy->supports(OperationsAuthorizationPolicy::ABILITY_API_TOKENS_SELF_MANAGE)); $this->assertTrue($policy->supports(OperationsAuthorizationPolicy::ABILITY_ADMIN_USERS_CREATE_EDIT_CUSTOM_FIELDS)); } public function testSupportsReturnsFalseForUnknownAbility(): void { $policy = new OperationsAuthorizationPolicy($this->createMock(PermissionService::class)); $this->assertFalse($policy->supports('ops.nonexistent')); $this->assertFalse($policy->supports('admin.roles.view')); $this->assertFalse($policy->supports('')); } // --- authorize: zero/negative actor_user_id --- public function testAuthorizeDeniesWhenActorUserIdIsZero(): void { $permissionService = $this->createMock(PermissionService::class); $permissionService->expects($this->never())->method('userHas'); $policy = new OperationsAuthorizationPolicy($permissionService); $decision = $policy->authorize(OperationsAuthorizationPolicy::ABILITY_ADMIN_JOBS_VIEW, [ 'actor_user_id' => 0, ]); $this->assertForbiddenDecision($decision); } public function testAuthorizeDeniesWhenActorUserIdIsMissing(): void { $permissionService = $this->createMock(PermissionService::class); $permissionService->expects($this->never())->method('userHas'); $policy = new OperationsAuthorizationPolicy($permissionService); $decision = $policy->authorize(OperationsAuthorizationPolicy::ABILITY_ADMIN_STATS_VIEW, []); $this->assertForbiddenDecision($decision); } public function testAuthorizeDeniesWhenActorUserIdIsNegative(): void { $permissionService = $this->createMock(PermissionService::class); $permissionService->expects($this->never())->method('userHas'); $policy = new OperationsAuthorizationPolicy($permissionService); $decision = $policy->authorize(OperationsAuthorizationPolicy::ABILITY_ADMIN_STATS_VIEW, [ 'actor_user_id' => -1, ]); $this->assertForbiddenDecision($decision); } // --- authorize: unsupported ability --- public function testAuthorizeReturns500ForUnsupportedAbility(): void { $policy = new OperationsAuthorizationPolicy($this->createMock(PermissionService::class)); $decision = $policy->authorize('ops.nonexistent', ['actor_user_id' => 1]); $this->assertDeniedDecision($decision, 500, 'authorization_ability_not_supported'); } // --- authorize: allowIfHas (simple permission check) --- // Address book ability tests moved to AddressBookAuthorizationPolicyTest (module-owned). public function testJobsViewAllowedWithPermission(): void { $permissionService = $this->permissionGatewayAllowing([ 10 => [PermissionService::JOBS_VIEW], ]); $policy = new OperationsAuthorizationPolicy($permissionService); $decision = $policy->authorize(OperationsAuthorizationPolicy::ABILITY_ADMIN_JOBS_VIEW, [ 'actor_user_id' => 10, ]); $this->assertTrue($decision->isAllowed()); } public function testApiTenantsDeleteDeniedWithoutPermission(): void { $permissionService = $this->permissionGatewayAllowing([8 => []]); $policy = new OperationsAuthorizationPolicy($permissionService); $decision = $policy->authorize(OperationsAuthorizationPolicy::ABILITY_API_TENANTS_DELETE, [ 'actor_user_id' => 8, ]); $this->assertForbiddenDecision($decision); } // --- authorize: authorizeUsersCreateCustomFields (requires TWO permissions) --- public function testUsersCreateCustomFieldsAllowedWithBothPermissions(): void { $permissionService = $this->permissionGatewayAllowing([ 7 => [PermissionService::CUSTOM_FIELDS_EDIT_VALUES, PermissionService::USERS_UPDATE_ASSIGNMENTS], ]); $policy = new OperationsAuthorizationPolicy($permissionService); $decision = $policy->authorize(OperationsAuthorizationPolicy::ABILITY_ADMIN_USERS_CREATE_EDIT_CUSTOM_FIELDS, [ 'actor_user_id' => 7, ]); $this->assertTrue($decision->isAllowed()); } public function testUsersCreateCustomFieldsDeniedWithoutCustomFieldsPermission(): void { $permissionService = $this->permissionGatewayAllowing([ 7 => [PermissionService::USERS_UPDATE_ASSIGNMENTS], ]); $policy = new OperationsAuthorizationPolicy($permissionService); $decision = $policy->authorize(OperationsAuthorizationPolicy::ABILITY_ADMIN_USERS_CREATE_EDIT_CUSTOM_FIELDS, [ 'actor_user_id' => 7, ]); $this->assertForbiddenDecision($decision); } public function testUsersCreateCustomFieldsDeniedWithoutAssignmentsPermission(): void { $permissionService = $this->permissionGatewayAllowing([ 7 => [PermissionService::CUSTOM_FIELDS_EDIT_VALUES], ]); $policy = new OperationsAuthorizationPolicy($permissionService); $decision = $policy->authorize(OperationsAuthorizationPolicy::ABILITY_ADMIN_USERS_CREATE_EDIT_CUSTOM_FIELDS, [ 'actor_user_id' => 7, ]); $this->assertForbiddenDecision($decision); } // --- authorize: authorizeApiTokensSelfManage (requires EITHER permission) --- public function testApiTokensSelfManageAllowedWithSelfUpdatePermission(): void { $permissionService = $this->permissionGatewayAllowing([ 9 => [PermissionService::USERS_SELF_UPDATE], ]); $policy = new OperationsAuthorizationPolicy($permissionService); $decision = $policy->authorize(OperationsAuthorizationPolicy::ABILITY_API_TOKENS_SELF_MANAGE, [ 'actor_user_id' => 9, ]); $this->assertTrue($decision->isAllowed()); } public function testApiTokensSelfManageAllowedWithApiTokensManagePermission(): void { $permissionService = $this->permissionGatewayAllowing([ 9 => [PermissionService::API_TOKENS_MANAGE], ]); $policy = new OperationsAuthorizationPolicy($permissionService); $decision = $policy->authorize(OperationsAuthorizationPolicy::ABILITY_API_TOKENS_SELF_MANAGE, [ 'actor_user_id' => 9, ]); $this->assertTrue($decision->isAllowed()); } public function testApiTokensSelfManageDeniedWithoutEitherPermission(): void { $permissionService = $this->permissionGatewayAllowing([9 => []]); $policy = new OperationsAuthorizationPolicy($permissionService); $decision = $policy->authorize(OperationsAuthorizationPolicy::ABILITY_API_TOKENS_SELF_MANAGE, [ 'actor_user_id' => 9, ]); $this->assertForbiddenDecision($decision); } }