614 lines
24 KiB
PHP
614 lines
24 KiB
PHP
<?php
|
|
|
|
use App\Filament\Resources\TenantResource\Pages\ViewTenant;
|
|
use App\Http\Controllers\RbacDelegatedAuthController;
|
|
use App\Models\Tenant;
|
|
use App\Models\User;
|
|
use App\Services\Graph\GraphClientInterface;
|
|
use App\Services\Graph\GraphResponse;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Livewire\Livewire;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
beforeEach(function () {
|
|
Cache::flush();
|
|
config()->set('tenantpilot.features.conditional_access', false);
|
|
});
|
|
|
|
function tenantWithApp(): Tenant
|
|
{
|
|
return Tenant::create([
|
|
'tenant_id' => 'tenant-guid',
|
|
'name' => 'Tenant One',
|
|
'app_client_id' => 'client-123',
|
|
'app_client_secret' => 'secret',
|
|
'status' => 'active',
|
|
]);
|
|
}
|
|
|
|
test('rbac action prompts login when no delegated token', function () {
|
|
$tenant = tenantWithApp();
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
Livewire::test(ViewTenant::class, ['record' => $tenant->getRouteKey()])
|
|
->mountAction('setup_rbac')
|
|
->setActionData([
|
|
'role_definition_id' => 'role-1',
|
|
'role_display_name' => 'Policy and Profile Manager',
|
|
'scope' => 'all_devices',
|
|
'group_mode' => 'create',
|
|
])
|
|
->callMountedAction()
|
|
->assertHasNoActionErrors();
|
|
|
|
expect(Cache::get(RbacDelegatedAuthController::cacheKey($tenant, $user->id, session()->getId())))->toBeNull();
|
|
});
|
|
|
|
test('rbac action succeeds and clears token cache', function () {
|
|
$tenant = tenantWithApp();
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$cacheKey = RbacDelegatedAuthController::cacheKey($tenant, $user->id, null);
|
|
Cache::put($cacheKey, 'delegated-token', now()->addMinutes(5));
|
|
|
|
app()->bind(GraphClientInterface::class, function () {
|
|
return new class implements GraphClientInterface
|
|
{
|
|
public function listPolicies(string $policyType, array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
public function getPolicy(string $policyType, string $policyId, array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
public function getOrganization(array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
public function applyPolicy(string $policyType, string $policyId, array $payload, array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
public function getServicePrincipalPermissions(array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
public function request(string $method, string $path, array $options = []): GraphResponse
|
|
{
|
|
$filter = $options['query']['$filter'] ?? '';
|
|
|
|
if ($method === 'GET' && $path === 'servicePrincipals') {
|
|
return new GraphResponse(true, ['value' => [['id' => 'sp-1']]]);
|
|
}
|
|
|
|
if ($method === 'GET' && $path === 'groups' && str_contains($filter, 'displayName eq')) {
|
|
return new GraphResponse(true, ['value' => []]);
|
|
}
|
|
|
|
if ($method === 'POST' && $path === 'groups') {
|
|
return new GraphResponse(true, ['id' => 'group-1']);
|
|
}
|
|
|
|
if ($method === 'POST' && str_contains($path, '/members/$ref')) {
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
if ($method === 'GET' && $path === 'deviceManagement/roleDefinitions') {
|
|
return new GraphResponse(true, ['value' => [['id' => 'role-1', 'displayName' => 'Policy and Profile Manager']]]);
|
|
}
|
|
|
|
if ($method === 'GET' && $path === 'deviceManagement/roleAssignments') {
|
|
return new GraphResponse(true, ['value' => []]);
|
|
}
|
|
|
|
if ($method === 'POST' && $path === 'deviceManagement/roleAssignments') {
|
|
return new GraphResponse(true, ['id' => 'assign-1']);
|
|
}
|
|
|
|
if ($method === 'GET' && str_starts_with($path, 'deviceManagement/deviceConfigurations')) {
|
|
return new GraphResponse(true, ['value' => []]);
|
|
}
|
|
|
|
if ($method === 'GET' && str_starts_with($path, 'deviceManagement/deviceCompliancePolicies')) {
|
|
return new GraphResponse(true, ['value' => []]);
|
|
}
|
|
|
|
return new GraphResponse(true, ['value' => []]);
|
|
}
|
|
};
|
|
});
|
|
|
|
Livewire::test(ViewTenant::class, ['record' => $tenant->getRouteKey()])
|
|
->mountAction('setup_rbac')
|
|
->setActionData([
|
|
'role_definition_id' => 'role-1',
|
|
'role_display_name' => 'Policy and Profile Manager',
|
|
'scope' => 'all_devices',
|
|
'group_mode' => 'create',
|
|
])
|
|
->callMountedAction()
|
|
->assertHasNoActionErrors();
|
|
|
|
$tenant->refresh();
|
|
|
|
expect($tenant->rbac_group_id)->toBe('group-1');
|
|
expect($tenant->rbac_role_assignment_id)->toBe('assign-1');
|
|
expect($tenant->rbac_canary_results)->toMatchArray([
|
|
'deviceConfigurations' => 'ok',
|
|
'deviceCompliancePolicies' => 'ok',
|
|
]);
|
|
expect($tenant->rbac_last_warnings)->toContain('ca_canary_disabled');
|
|
expect(Cache::has($cacheKey))->toBeFalse();
|
|
});
|
|
|
|
test('rbac action is idempotent on rerun', function () {
|
|
$tenant = tenantWithApp();
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$cacheKey = RbacDelegatedAuthController::cacheKey($tenant, $user->id, null);
|
|
Cache::put($cacheKey, 'delegated-token', now()->addMinutes(5));
|
|
|
|
app()->bind(GraphClientInterface::class, function () {
|
|
return new class implements GraphClientInterface
|
|
{
|
|
public function listPolicies(string $policyType, array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
public function getPolicy(string $policyType, string $policyId, array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
public function getOrganization(array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
public function applyPolicy(string $policyType, string $policyId, array $payload, array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
public function getServicePrincipalPermissions(array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
public function request(string $method, string $path, array $options = []): GraphResponse
|
|
{
|
|
if ($method === 'GET' && $path === 'servicePrincipals') {
|
|
return new GraphResponse(true, ['value' => [['id' => 'sp-1']]]);
|
|
}
|
|
|
|
if ($method === 'POST' && str_contains($path, '/members/$ref')) {
|
|
return new GraphResponse(false, [], 400, [
|
|
[
|
|
'error' => [
|
|
'code' => 'Request_BadRequest',
|
|
'message' => 'One or more added object references already exist for the following modified objects',
|
|
],
|
|
],
|
|
]);
|
|
}
|
|
|
|
if ($method === 'GET' && $path === 'deviceManagement/roleDefinitions') {
|
|
return new GraphResponse(true, ['value' => [['id' => 'role-1', 'displayName' => 'Policy and Profile Manager']]]);
|
|
}
|
|
|
|
if ($method === 'GET' && $path === 'deviceManagement/roleAssignments') {
|
|
return new GraphResponse(true, ['value' => [[
|
|
'id' => 'assign-1',
|
|
'members' => ['group-1'],
|
|
'resourceScopes' => ['/'],
|
|
'roleDefinition' => ['id' => 'role-1'],
|
|
]]]);
|
|
}
|
|
|
|
if ($method === 'PATCH' && str_contains($path, 'deviceManagement/roleAssignments/assign-1')) {
|
|
return new GraphResponse(true, ['id' => 'assign-1']);
|
|
}
|
|
|
|
if ($method === 'GET' && str_starts_with($path, 'groups/')) {
|
|
$id = str_replace('groups/', '', $path);
|
|
|
|
return new GraphResponse(true, ['id' => $id, 'displayName' => "Group {$id}"]);
|
|
}
|
|
|
|
if ($method === 'GET' && str_starts_with($path, 'deviceManagement/deviceConfigurations')) {
|
|
return new GraphResponse(true, ['value' => []]);
|
|
}
|
|
|
|
if ($method === 'GET' && str_starts_with($path, 'deviceManagement/deviceCompliancePolicies')) {
|
|
return new GraphResponse(true, ['value' => []]);
|
|
}
|
|
|
|
return new GraphResponse(true, ['value' => []]);
|
|
}
|
|
};
|
|
});
|
|
|
|
Livewire::test(ViewTenant::class, ['record' => $tenant->getRouteKey()])
|
|
->mountAction('setup_rbac')
|
|
->setActionData([
|
|
'role_definition_id' => 'role-1',
|
|
'role_display_name' => 'Policy and Profile Manager',
|
|
'scope' => 'scope_group',
|
|
'scope_group_id' => 'group-scope',
|
|
'group_mode' => 'existing',
|
|
'existing_group_id' => 'group-1',
|
|
])
|
|
->callMountedAction()
|
|
->assertHasNoActionErrors();
|
|
|
|
$tenant->refresh();
|
|
expect($tenant->rbac_group_id)->toBe('group-1');
|
|
expect($tenant->rbac_role_assignment_id)->toBe('assign-1');
|
|
expect($tenant->rbac_scope_mode)->toBe('scope_group');
|
|
expect($tenant->rbac_last_warnings)->toContain('scope_limited');
|
|
expect($tenant->rbac_status)->toBe('ok');
|
|
});
|
|
|
|
test('existing group membership error from Graph json payload is treated idempotently', function () {
|
|
$tenant = tenantWithApp();
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$cacheKey = RbacDelegatedAuthController::cacheKey($tenant, $user->id, null);
|
|
Cache::put($cacheKey, 'delegated-token', now()->addMinutes(5));
|
|
|
|
app()->bind(GraphClientInterface::class, function () {
|
|
return new class implements GraphClientInterface
|
|
{
|
|
public function listPolicies(string $policyType, array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
public function getPolicy(string $policyType, string $policyId, array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
public function getOrganization(array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
public function applyPolicy(string $policyType, string $policyId, array $payload, array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
public function getServicePrincipalPermissions(array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
public function request(string $method, string $path, array $options = []): GraphResponse
|
|
{
|
|
if ($method === 'GET' && $path === 'servicePrincipals') {
|
|
return new GraphResponse(true, ['value' => [['id' => 'sp-1']]]);
|
|
}
|
|
|
|
if ($method === 'GET' && $path === 'groups' && str_contains($options['query']['$filter'] ?? '', 'displayName')) {
|
|
return new GraphResponse(true, ['value' => [['id' => 'group-1', 'displayName' => 'Existing Group']]]);
|
|
}
|
|
|
|
if ($method === 'POST' && str_contains($path, '/members/$ref')) {
|
|
return new GraphResponse(false, [], 400, [
|
|
[
|
|
'error' => [
|
|
'code' => 'Request_BadRequest',
|
|
'message' => 'One or more added object references already exist for the following modified objects',
|
|
],
|
|
],
|
|
]);
|
|
}
|
|
|
|
if ($method === 'GET' && $path === 'deviceManagement/roleDefinitions') {
|
|
return new GraphResponse(true, ['value' => [['id' => 'role-1', 'displayName' => 'Policy and Profile Manager']]]);
|
|
}
|
|
|
|
if ($method === 'GET' && $path === 'deviceManagement/roleAssignments') {
|
|
return new GraphResponse(true, ['value' => []]);
|
|
}
|
|
|
|
if ($method === 'POST' && $path === 'deviceManagement/roleAssignments') {
|
|
return new GraphResponse(true, ['id' => 'assign-1']);
|
|
}
|
|
|
|
if ($method === 'GET' && str_starts_with($path, 'deviceManagement/deviceConfigurations')) {
|
|
return new GraphResponse(true, ['value' => []]);
|
|
}
|
|
|
|
if ($method === 'GET' && str_starts_with($path, 'deviceManagement/deviceCompliancePolicies')) {
|
|
return new GraphResponse(true, ['value' => []]);
|
|
}
|
|
|
|
return new GraphResponse(true, ['value' => []]);
|
|
}
|
|
};
|
|
});
|
|
|
|
Livewire::test(ViewTenant::class, ['record' => $tenant->getRouteKey()])
|
|
->mountAction('setup_rbac')
|
|
->setActionData([
|
|
'role_definition_id' => 'role-1',
|
|
'role_display_name' => 'Policy and Profile Manager',
|
|
'scope' => 'all_devices',
|
|
'group_mode' => 'existing',
|
|
'existing_group_id' => 'group-1',
|
|
])
|
|
->callMountedAction()
|
|
->assertHasNoActionErrors();
|
|
|
|
$tenant->refresh();
|
|
expect($tenant->rbac_group_id)->toBe('group-1');
|
|
expect($tenant->rbac_role_assignment_id)->toBe('assign-1');
|
|
expect($tenant->rbac_status)->toBe('ok');
|
|
});
|
|
|
|
test('group picker is disabled without delegated token', function () {
|
|
$tenant = tenantWithApp();
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
Livewire::test(ViewTenant::class, ['record' => $tenant->getRouteKey()])
|
|
->mountAction('setup_rbac')
|
|
->setActionData([
|
|
'group_mode' => 'existing',
|
|
])
|
|
->assertFormFieldDisabled('existing_group_id');
|
|
|
|
expect(\App\Filament\Resources\TenantResource::groupSearchHelper($tenant))->toBe('Login to search groups');
|
|
});
|
|
|
|
test('group picker toggles when switching modes', function () {
|
|
$tenant = tenantWithApp();
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
Livewire::test(ViewTenant::class, ['record' => $tenant->getRouteKey()])
|
|
->mountAction('setup_rbac')
|
|
->setActionData([
|
|
'group_mode' => 'existing',
|
|
])
|
|
->assertFormFieldVisible('existing_group_id')
|
|
->assertFormFieldHidden('group_name');
|
|
});
|
|
|
|
test('delegated group search returns options and persists selection', function () {
|
|
$tenant = tenantWithApp();
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$cacheKey = RbacDelegatedAuthController::cacheKey($tenant, $user->id, null);
|
|
Cache::put($cacheKey, 'delegated-token', now()->addMinutes(5));
|
|
|
|
app()->bind(GraphClientInterface::class, function () {
|
|
return new class implements GraphClientInterface
|
|
{
|
|
public function listPolicies(string $policyType, array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
public function getPolicy(string $policyType, string $policyId, array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
public function getOrganization(array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
public function applyPolicy(string $policyType, string $policyId, array $payload, array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
public function getServicePrincipalPermissions(array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
public function request(string $method, string $path, array $options = []): GraphResponse
|
|
{
|
|
$filter = $options['query']['$filter'] ?? '';
|
|
|
|
if ($method === 'GET' && $path === 'servicePrincipals') {
|
|
return new GraphResponse(true, ['value' => [['id' => 'sp-1']]]);
|
|
}
|
|
|
|
if ($method === 'GET' && $path === 'groups' && str_contains($filter, 'securityEnabled eq true')) {
|
|
return new GraphResponse(true, ['value' => [
|
|
['id' => 'group-123', 'displayName' => 'Ops Team'],
|
|
['id' => 'group-456', 'displayName' => 'Helpdesk'],
|
|
]]);
|
|
}
|
|
|
|
if ($method === 'GET' && str_starts_with($path, 'groups/')) {
|
|
$id = str_replace('groups/', '', $path);
|
|
|
|
return new GraphResponse(true, ['id' => $id, 'displayName' => 'Ops Team']);
|
|
}
|
|
|
|
if ($method === 'POST' && str_contains($path, '/members/$ref')) {
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
if ($method === 'GET' && $path === 'deviceManagement/roleDefinitions') {
|
|
return new GraphResponse(true, ['value' => [['id' => 'role-1', 'displayName' => 'Policy and Profile Manager']]]);
|
|
}
|
|
|
|
if ($method === 'GET' && $path === 'deviceManagement/roleAssignments') {
|
|
return new GraphResponse(true, ['value' => []]);
|
|
}
|
|
|
|
if ($method === 'POST' && $path === 'deviceManagement/roleAssignments') {
|
|
return new GraphResponse(true, ['id' => 'assign-1']);
|
|
}
|
|
|
|
if ($method === 'GET' && str_starts_with($path, 'deviceManagement/deviceConfigurations')) {
|
|
return new GraphResponse(true, ['value' => []]);
|
|
}
|
|
|
|
if ($method === 'GET' && str_starts_with($path, 'deviceManagement/deviceCompliancePolicies')) {
|
|
return new GraphResponse(true, ['value' => []]);
|
|
}
|
|
|
|
return new GraphResponse(true, ['value' => []]);
|
|
}
|
|
};
|
|
});
|
|
|
|
$options = \App\Filament\Resources\TenantResource::groupSearchOptions($tenant, 'Ops');
|
|
|
|
expect($options)->toHaveKey('group-123');
|
|
expect($options['group-123'])->toContain('Ops Team');
|
|
|
|
Livewire::test(ViewTenant::class, ['record' => $tenant->getRouteKey()])
|
|
->mountAction('setup_rbac')
|
|
->setActionData([
|
|
'group_mode' => 'existing',
|
|
'role_definition_id' => 'role-1',
|
|
'role_display_name' => 'Policy and Profile Manager',
|
|
'scope' => 'all_devices',
|
|
'existing_group_id' => 'group-123',
|
|
])
|
|
->assertFormFieldEnabled('existing_group_id')
|
|
->callMountedAction()
|
|
->assertHasNoActionErrors();
|
|
|
|
$tenant->refresh();
|
|
|
|
expect($tenant->rbac_group_id)->toBe('group-123');
|
|
expect($tenant->rbac_role_assignment_id)->toBe('assign-1');
|
|
expect(Cache::has($cacheKey))->toBeFalse();
|
|
});
|
|
|
|
test('delegated role search returns options and persists role definition id', function () {
|
|
$tenant = tenantWithApp();
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$cacheKey = RbacDelegatedAuthController::cacheKey($tenant, $user->id, null);
|
|
Cache::put($cacheKey, 'delegated-token', now()->addMinutes(5));
|
|
|
|
app()->bind(GraphClientInterface::class, function () {
|
|
return new class implements GraphClientInterface
|
|
{
|
|
public function listPolicies(string $policyType, array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
public function getPolicy(string $policyType, string $policyId, array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
public function getOrganization(array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
public function applyPolicy(string $policyType, string $policyId, array $payload, array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
public function getServicePrincipalPermissions(array $options = []): GraphResponse
|
|
{
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
public function request(string $method, string $path, array $options = []): GraphResponse
|
|
{
|
|
$filter = $options['query']['$filter'] ?? '';
|
|
|
|
if ($method === 'GET' && $path === 'deviceManagement/roleDefinitions') {
|
|
return new GraphResponse(true, ['value' => [
|
|
['id' => 'role-1', 'displayName' => 'Policy and Profile Manager'],
|
|
['id' => 'role-2', 'displayName' => 'Helpdesk Operator'],
|
|
]]);
|
|
}
|
|
|
|
if ($method === 'GET' && $path === 'deviceManagement/roleDefinitions/role-1') {
|
|
return new GraphResponse(true, ['id' => 'role-1', 'displayName' => 'Policy and Profile Manager']);
|
|
}
|
|
|
|
if ($method === 'GET' && $path === 'servicePrincipals') {
|
|
return new GraphResponse(true, ['value' => [['id' => 'sp-1']]]);
|
|
}
|
|
|
|
if ($method === 'GET' && $path === 'groups' && str_contains($filter, 'displayName eq')) {
|
|
return new GraphResponse(true, ['value' => []]);
|
|
}
|
|
|
|
if ($method === 'POST' && $path === 'groups') {
|
|
return new GraphResponse(true, ['id' => 'group-1']);
|
|
}
|
|
|
|
if ($method === 'POST' && str_contains($path, '/members/$ref')) {
|
|
return new GraphResponse(true, []);
|
|
}
|
|
|
|
if ($method === 'GET' && $path === 'deviceManagement/roleAssignments') {
|
|
return new GraphResponse(true, ['value' => []]);
|
|
}
|
|
|
|
if ($method === 'POST' && $path === 'deviceManagement/roleAssignments') {
|
|
return new GraphResponse(true, ['id' => 'assign-1']);
|
|
}
|
|
|
|
if ($method === 'GET' && str_starts_with($path, 'deviceManagement/deviceConfigurations')) {
|
|
return new GraphResponse(true, ['value' => []]);
|
|
}
|
|
|
|
if ($method === 'GET' && str_starts_with($path, 'deviceManagement/deviceCompliancePolicies')) {
|
|
return new GraphResponse(true, ['value' => []]);
|
|
}
|
|
|
|
return new GraphResponse(true, ['value' => []]);
|
|
}
|
|
};
|
|
});
|
|
|
|
$roles = \App\Filament\Resources\TenantResource::roleSearchOptions($tenant, 'Policy');
|
|
|
|
expect($roles)->toHaveKey('role-1');
|
|
expect($roles['role-1'])->toContain('Policy and Profile Manager');
|
|
|
|
Livewire::test(ViewTenant::class, ['record' => $tenant->getRouteKey()])
|
|
->mountAction('setup_rbac')
|
|
->setActionData([
|
|
'role_definition_id' => 'role-1',
|
|
'role_display_name' => 'Policy and Profile Manager',
|
|
'scope' => 'all_devices',
|
|
'group_mode' => 'create',
|
|
])
|
|
->callMountedAction()
|
|
->assertHasNoActionErrors();
|
|
|
|
$tenant->refresh();
|
|
|
|
expect($tenant->rbac_role_definition_id)->toBe('role-1');
|
|
expect($tenant->rbac_role_display_name)->toBe('Policy and Profile Manager');
|
|
expect(Cache::has($cacheKey))->toBeFalse();
|
|
});
|