diff --git a/.github/workflows/check-coding-standards.yml b/.github/workflows/check-coding-standards.yml deleted file mode 100644 index 9d19705..0000000 --- a/.github/workflows/check-coding-standards.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Check Coding Standards - -on: - push: - paths: - - '**.php' - -jobs: - ci: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 #https://github.com/shivammathur/setup-php - with: - php-version: '8.3' - extensions: mbstring, dom, fileinfo - - - name: Install Composer dependencies - run: composer update --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist - - - name: Coding Style Checks - run: composer test:lint - - - name: Type Checks - run: composer test:types - - - name: Type Coverage Checks - run: composer test:type-coverage diff --git a/.github/workflows/laravel.yml b/.github/workflows/laravel.yml new file mode 100644 index 0000000..45cff04 --- /dev/null +++ b/.github/workflows/laravel.yml @@ -0,0 +1,74 @@ +name: CI + +on: + push: + branches: [ 3.x, 4.x ] + pull_request: + branches: [ 3.x, 4.x ] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + laravel: + runs-on: ubuntu-latest + + services: + postgres: + image: postgres:17 + env: + POSTGRES_USER: seatplus + POSTGRES_PASSWORD: secret + POSTGRES_DB: laravel + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + redis: + image: redis:7 + ports: + - 6379:6379 + options: >- + --health-cmd "redis-cli ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + extensions: mbstring, dom, fileinfo, pgsql, pdo_pgsql, redis + coverage: xdebug + + - name: Cache Composer dependencies + uses: actions/cache@v4 + with: + path: vendor + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install Dependencies + run: composer install --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist + + - name: Check Coding Standards + run: composer run test:lint + + - name: Static Analysis + run: composer run test:types + + - name: Type Coverage + run: composer run test:type-coverage + + - name: Run Tests + env: + XDEBUG_MODE: coverage + run: vendor/bin/pest --coverage --min=100 --colors=always diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml deleted file mode 100644 index 8a5d175..0000000 --- a/.github/workflows/tests.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: Tests - -on: - push: - branches: [ 3.x, 4.x ] - pull_request: - branches: [ 3.x, 4.x ] - -jobs: - laravel: - - runs-on: ubuntu-latest - - services: - postgres: - image: postgres:17 - env: - POSTGRES_USER: seatplus - POSTGRES_PASSWORD: secret # required for the default postgres image but not needed if connected from localhost - POSTGRES_DB: laravel - ports: - - 5432:5432 - options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 - redis: - image: redis:7 - ports: - - 6379:6379 - options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5 - - steps: - - uses: actions/checkout@v2 - - name: Setup PHP, with composer and extensions - uses: shivammathur/setup-php@v2 #https://github.com/shivammathur/setup-php - with: - php-version: '8.3' - extensions: mbstring, dom, fileinfo - coverage: xdebug #optional - - name: Install Dependencies - run: composer install --no-ansi --no-interaction --no-scripts --prefer-dist - - name: Test & publish code coverage - uses: paambaati/codeclimate-action@v2.6.0 - env: - CC_TEST_REPORTER_ID: dfdd27f143f73ad7911f5393e3378b9b989cfed884bf522fa3b0d9fbc2b8d4c1 - with: - coverageCommand: vendor/bin/pest --coverage --ci --min=100 - debug: false diff --git a/.gitignore b/.gitignore index 1c18527..3a1a1c8 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,6 @@ build/ .php_cs .php_cs.cache .phpunit.result.cache +.phpunit.cache .php-cs-fixer.cache diff --git a/README.md b/README.md index 95f13e6..4505257 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,87 @@ -# auth -handels authentication for web and eveapi +# seatplus/auth -# Usage +[![CI](https://github.com/seatplus/auth/actions/workflows/laravel.yml/badge.svg)](https://github.com/seatplus/auth/actions/workflows/laravel.yml) -## Add more scopes -By default the minimal scopes are requested for users. However one might add scopes to an existing user by adding -a query parameters stating comma separated which scopes should be add: +Handles authentication, authorisation, and SSO scope compliance for the seatplus EVE Online management platform. This is the core package — `seatplus/eveapi` and `seatplus/web` both depend on it. + +## Overview + +### Role system + +Four role types with distinct membership and permission semantics: + +| Type | Membership | Use case | +|------|-----------|---------| +| `automatic` | Auto-assigned when a character belongs to a configured corporation or alliance | Fleet / alliance access | +| `on-request` | User applies, moderator approves or denies | Corp-specific elevated access | +| `manual` | Admin explicitly adds / removes individual users | One-off grants | +| `opt-in` | User self-joins if they meet the criteria | Opt-in programmes | + +### Affiliation system + +Every role has `Affiliation` records that define **permission scope** (which EVE entities the role holder can access data for), not membership. Three types: + +- `allowed` — these corporations / alliances / characters are in scope +- `inverse` — everyone *except* these is in scope +- `forbidden` — always excluded, overrides `allowed` / `inverse` + +### SSO scope compliance + +`IsUserCompliantService` checks whether every character owned by a user has all required OAuth scopes. Required scopes are aggregated from global settings, corporation-level `SsoScopes` records, and alliance-level records. Non-compliant users have their role memberships set to `inactive` automatically on the next `handleMembers()` call. + +### Permission checking + +`CanUserService::check()` runs a Laravel Pipeline to validate a set of EVE entity IDs against a user's permissions. The pipeline strips IDs the user owns, IDs covered by in-game corporation roles (e.g. Director), and IDs covered by Spatie permissions. Any remaining IDs are denied. The `superuser` permission bypasses all checks. + +## Installation + +```bash +composer require seatplus/auth +``` + +Publish and run migrations: + +```bash +php artisan vendor:publish --provider="Seatplus\Auth\AuthServiceProvider" +php artisan migrate ``` -/eve/sso/{character_id?}/step_up?add_scopes=scope1,scope2 + +## Usage + +### Add OAuth scopes to a character + +By default the minimal scopes are requested. To step up a character to additional scopes, redirect to: + +``` +/eve/sso/{character_id}/step_up?add_scopes=esi-skills.read_skills.v1,esi-wallet.read_character_wallet.v1 +``` + +### Check permissions + +```php +use Seatplus\Auth\Services\Dtos\ValidateIdsDTO; +use Seatplus\Auth\Services\CanUserService; + +$dto = ValidateIdsDTO::make(entity_ids: [12345678], user: $user); +CanUserService::check($user, $dto, permissions: ['view member tracking']); +``` + +## Development + +### Requirements + +- PHP 8.3+ +- PostgreSQL (user `seatplus`, password `secret`, database `laravel` @ `127.0.0.1:5432`) +- Redis @ `127.0.0.1:6379` + +### Running the test suite + +```bash +composer run test # lint + PHPStan + type-coverage + unit tests +composer run test:unit # unit tests only +composer run test:lint # Pint formatting check +composer run lint # auto-fix formatting with Pint +composer run test:types # PHPStan static analysis +composer run test:type-coverage # 100% type coverage check ``` + diff --git a/composer.json b/composer.json index 2f7cae9..70d0bca 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,8 @@ "nunomaduro/collision": "^8.1", "pestphp/pest": "^3.0", "pestphp/pest-plugin-laravel": "^3.0", - "pestphp/pest-plugin-type-coverage": "^3.1", + "pestphp/pest-plugin-type-coverage": "3.5.1", + "phpstan/phpstan": "1.12.24", "rector/rector": "^1.2", "driftingly/rector-laravel": "^1.2", "larastan/larastan": "^2.9", diff --git a/config/permission.php b/config/permission.php index 90ce7bc..3f3c943 100644 --- a/config/permission.php +++ b/config/permission.php @@ -1,5 +1,8 @@ [ @@ -13,7 +16,7 @@ * `Spatie\Permission\Contracts\Permission` contract. */ - 'permission' => Seatplus\Auth\Models\Permissions\Permission::class, + 'permission' => Permission::class, /* * When using the "HasRoles" trait from this package, we need to know which @@ -24,7 +27,7 @@ * `Spatie\Permission\Contracts\Role` contract. */ - 'role' => Seatplus\Auth\Models\Permissions\Role::class, + 'role' => Role::class, ], @@ -167,7 +170,7 @@ * When permissions or roles are updated the cache is flushed automatically. */ - 'expiration_time' => \DateInterval::createFromDateString('24 hours'), + 'expiration_time' => DateInterval::createFromDateString('24 hours'), /* * The cache key used to store all permissions. diff --git a/src/AuthenticationServiceProvider.php b/src/AuthenticationServiceProvider.php index 1f8bf7d..40d9d88 100644 --- a/src/AuthenticationServiceProvider.php +++ b/src/AuthenticationServiceProvider.php @@ -32,6 +32,8 @@ use Laravel\Socialite\SocialiteManager; use Seatplus\Auth\Listeners\ReactOnFreshRefreshToken; use Seatplus\Auth\Listeners\UpdatingRefreshTokenListener; +use Seatplus\Auth\Models\Permissions\Permission; +use Seatplus\Auth\Models\Permissions\Role; use Seatplus\Auth\Models\User; use Seatplus\Auth\Observers\ApplicationObserver; use Seatplus\Auth\Observers\CharacterAffiliationObserver; @@ -102,8 +104,8 @@ function (Container $app) use ($socialite) { $this->mergeConfigFrom(__DIR__.'/../config/auth.services.php', 'services'); config()->set('permission.models', [ - 'permission' => \Seatplus\Auth\Models\Permissions\Permission::class, - 'role' => \Seatplus\Auth\Models\Permissions\Role::class, + 'permission' => Permission::class, + 'role' => Role::class, ]); $this->setUserModel(); diff --git a/src/Http/Actions/Roles/ManageAutomaticRoleAction.php b/src/Http/Actions/Roles/ManageAutomaticRoleAction.php index 982b8b0..4d21560 100644 --- a/src/Http/Actions/Roles/ManageAutomaticRoleAction.php +++ b/src/Http/Actions/Roles/ManageAutomaticRoleAction.php @@ -1,11 +1,15 @@ checkPermission(); $validated = $request->validated(); - $this->baseRoleService->for($validated['role_id']); + $roleService = $this->baseRoleService->for($validated['role_id'])->automatic(); - $roleService = $this->baseRoleService->automatic(); + // setRoleType first: if the type changes it calls resetRoleMemberships(), + // which would wipe any criteria written below. + $roleService->setRoleType(RoleType::AUTOMATIC); if ($name = Arr::get($validated, 'name')) { $roleService->updateRoleName($name); } - if ($affiliated = Arr::get($validated, 'affiliated')) { - $roleService->syncAffiliateManyEntities($affiliated); + if (is_array($affiliated = Arr::get($validated, 'affiliated'))) { + $roleService->syncAffiliateManyEntities( + ...array_map(fn (array $affiliationData) => AffiliationData::fromArray($affiliationData), $affiliated) + ); } - if ($assigned = Arr::get($validated, 'assigned')) { - $roleService->automaticallyAssignRoleTo($assigned); + if (is_array($assigned = Arr::get($validated, 'assigned'))) { + $roleService->automaticallyAssignRoleTo( + ...array_map(fn (array $criteriaData) => CriteriaData::fromArray($criteriaData), $assigned) + ); } - $roleService->setRoleType(RoleType::AUTOMATIC); + $roleService->handleMembers(); } private function checkPermission(): void { - $auth = auth()->user(); throw_unless($auth, \Exception::class, 'User not authenticated'); diff --git a/src/Http/Actions/Roles/Manual/ManageManualRoleAction.php b/src/Http/Actions/Roles/Manual/ManageManualRoleAction.php index 44eab01..70952d7 100644 --- a/src/Http/Actions/Roles/Manual/ManageManualRoleAction.php +++ b/src/Http/Actions/Roles/Manual/ManageManualRoleAction.php @@ -1,11 +1,14 @@ validated(); $roleService = $this->baseRoleService->for($validated['role_id'])->manual(); - if ($affiliated = Arr::get($validated, 'affiliated')) { - $roleService->syncAffiliateManyEntities($affiliated); - } + $roleService->setRoleType(RoleType::MANUAL); if ($name = Arr::get($validated, 'name')) { $roleService->updateRoleName($name); } - $roleService->setRoleType(RoleType::MANUAL); + if (is_array($affiliated = Arr::get($validated, 'affiliated'))) { + $roleService->syncAffiliateManyEntities( + ...array_map(fn (array $affiliationData) => AffiliationData::fromArray($affiliationData), $affiliated) + ); + } + + $roleService->handleMembers(); } } diff --git a/src/Http/Actions/Roles/OnRequest/ManageOnRequestRoleAction.php b/src/Http/Actions/Roles/OnRequest/ManageOnRequestRoleAction.php index 836f0a1..efe1df3 100644 --- a/src/Http/Actions/Roles/OnRequest/ManageOnRequestRoleAction.php +++ b/src/Http/Actions/Roles/OnRequest/ManageOnRequestRoleAction.php @@ -1,11 +1,15 @@ checkPermission(); $validated = $request->validated(); - $this->baseRoleService->for($validated['role_id']); + $roleService = $this->baseRoleService->for($validated['role_id'])->onRequest(); - $roleService = $this->baseRoleService->onRequest(); + $roleService->setRoleType(RoleType::ON_REQUEST); - if ($affiliated = Arr::get($validated, 'affiliated')) { - $roleService->syncAffiliateManyEntities($affiliated); + if ($name = Arr::get($validated, 'name')) { + $roleService->updateRoleName($name); } - if ($assigned = Arr::get($validated, 'assigned')) { - $roleService->addCriteriaForRoleApplication($assigned); + if (is_array($affiliated = Arr::get($validated, 'affiliated'))) { + $roleService->syncAffiliateManyEntities( + ...array_map(fn (array $affiliationData) => AffiliationData::fromArray($affiliationData), $affiliated) + ); } - if ($name = Arr::get($validated, 'name')) { - $roleService->updateRoleName($name); + if (is_array($assigned = Arr::get($validated, 'assigned'))) { + $roleService->addCriteriaForRoleApplication( + ...array_map(fn (array $criteriaData) => CriteriaData::fromArray($criteriaData), $assigned) + ); } - $roleService->setRoleType(RoleType::ON_REQUEST); + $roleService->handleMembers(); } private function checkPermission(): void diff --git a/src/Http/Actions/Roles/OptIn/ManageOptInRoleAction.php b/src/Http/Actions/Roles/OptIn/ManageOptInRoleAction.php index e445038..325b350 100644 --- a/src/Http/Actions/Roles/OptIn/ManageOptInRoleAction.php +++ b/src/Http/Actions/Roles/OptIn/ManageOptInRoleAction.php @@ -1,11 +1,15 @@ checkPermission(); $validated = $request->validated(); - $roleService = $this->baseRoleService->for($validated['role_id'])->optIn(); + $roleService->setRoleType(RoleType::OPT_IN); + if ($name = Arr::get($validated, 'name')) { $roleService->updateRoleName($name); } - if ($affiliated = Arr::get($validated, 'affiliated')) { - $roleService->syncAffiliateManyEntities($affiliated); + if (is_array($affiliated = Arr::get($validated, 'affiliated'))) { + $roleService->syncAffiliateManyEntities( + ...array_map(fn (array $affiliationData) => AffiliationData::fromArray($affiliationData), $affiliated) + ); } - if ($assigned = Arr::get($validated, 'assigned')) { - $roleService->addCriteriaForRole($assigned); + if (is_array($assigned = Arr::get($validated, 'assigned'))) { + $roleService->addCriteriaForRole( + ...array_map(fn (array $criteriaData) => CriteriaData::fromArray($criteriaData), $assigned) + ); } - $roleService->setRoleType(RoleType::OPT_IN); + $roleService->handleMembers(); } private function checkPermission(): void diff --git a/src/Http/Controllers/SwitchMainCharacterController.php b/src/Http/Controllers/SwitchMainCharacterController.php index f761ebd..6896724 100644 --- a/src/Http/Controllers/SwitchMainCharacterController.php +++ b/src/Http/Controllers/SwitchMainCharacterController.php @@ -27,11 +27,12 @@ namespace Seatplus\Auth\Http\Controllers; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Http\RedirectResponse; use Seatplus\Auth\Models\User; class SwitchMainCharacterController extends Controller { - public function __invoke(int $new_character_id): \Illuminate\Http\RedirectResponse + public function __invoke(int $new_character_id): RedirectResponse { $user = User::whereHas('character_users', fn (Builder $query) => $query->where('character_id', $new_character_id)) ->firstWhere('id', auth()->user()->getAuthIdentifier()); diff --git a/src/Http/Middleware/CheckRequiredScopes.php b/src/Http/Middleware/CheckRequiredScopes.php index 9d521da..07f7850 100644 --- a/src/Http/Middleware/CheckRequiredScopes.php +++ b/src/Http/Middleware/CheckRequiredScopes.php @@ -27,7 +27,6 @@ namespace Seatplus\Auth\Http\Middleware; use Closure; -use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Seatplus\Auth\Models\User; use Seatplus\Auth\Services\SsoScopes\IsUserCompliantService; diff --git a/src/Services/Roles/AbstractRoleService.php b/src/Services/Roles/AbstractRoleService.php index 8557644..adb814a 100644 --- a/src/Services/Roles/AbstractRoleService.php +++ b/src/Services/Roles/AbstractRoleService.php @@ -5,8 +5,6 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Relations\MorphTo; -use Illuminate\Validation\Rule; -use Illuminate\Validation\ValidationException; use Seatplus\Auth\Enums\AffiliationType; use Seatplus\Auth\Enums\RoleMembershipStatus; use Seatplus\Auth\Enums\RoleType; @@ -14,9 +12,10 @@ use Seatplus\Auth\Models\Permissions\Affiliation; use Seatplus\Auth\Models\Permissions\Role; use Seatplus\Auth\Models\User; +use Seatplus\Auth\Services\Roles\DTO\AffiliationData; +use Seatplus\Auth\Services\Roles\DTO\CriteriaData; use Seatplus\Auth\Services\SsoScopes\IsUserCompliantService; use Seatplus\Eveapi\Models\Alliance\AllianceInfo; -use Seatplus\Eveapi\Models\Character\CharacterInfo; use Seatplus\Eveapi\Models\Corporation\CorporationInfo; abstract class AbstractRoleService implements RoleServiceInterface @@ -45,32 +44,16 @@ private function resetAffiliation(): void /** * @throws \Throwable */ - private function validateAffiliationEntities(array $entity_sets): void + protected function addCriteria(CriteriaData ...$entities): void { - $validator = validator($entity_sets, [ - '*.0' => 'required|integer', - '*.1' => ['required', 'string', Rule::in(['character', 'corporation', 'alliance'])], - '*.2' => [ - 'required', - 'string', - Rule::in(array_map(fn (AffiliationType $affiliationType) => $affiliationType->value, AffiliationType::cases())), - ], - ]); - - throw_if($validator->fails(), ValidationException::withMessages($validator->errors()->toArray())); - } - - /** - * @throws \Throwable - */ - private function validateCriteria(array $entities): void - { - $validator = validator($entities, [ - '*.0' => 'required|integer', - '*.1' => ['required', 'string', Rule::in(['corporation', 'alliance'])], - ]); + $this->resetCriteria(); - throw_if($validator->fails(), ValidationException::withMessages($validator->errors()->toArray())); + foreach ($entities as $entity) { + $this->setRoleMembership( + entity_id: $entity->entity_id, + entity_type: $entity->entityClass() + ); + } } private function resetCriteria(): void @@ -81,29 +64,6 @@ private function resetCriteria(): void ->delete(); } - /** - * @throws \Throwable - */ - protected function addCriteria(array $entities, RoleType $roleType): void - { - $this->validateCriteria($entities); - - $this->resetCriteria(); - - foreach ($entities as $entity) { - - $entity_type = match ($entity[1]) { - 'corporation' => CorporationInfo::class, - 'alliance' => AllianceInfo::class, - }; - - $this->setRoleMembership( - entity_id: $entity[0], - entity_type: $entity_type - ); - } - } - private function revokeTheRolesFromUsersThatAreNotInMembers(\Illuminate\Support\Collection $member_ids): void { User::query() @@ -245,23 +205,12 @@ protected function isUserCompliant(User $user): bool /** * @throws \Throwable */ - public function syncAffiliateManyEntities(array $entity_sets): void + public function syncAffiliateManyEntities(AffiliationData ...$entity_sets): void { - $this->validateAffiliationEntities($entity_sets); - $this->resetAffiliation(); foreach ($entity_sets as $entity_set) { - - [$entity_id, $entity_type, $affiliation_type] = $entity_set; - - $entity_type = match ($entity_type) { - 'character' => CharacterInfo::class, - 'corporation' => CorporationInfo::class, - 'alliance' => AllianceInfo::class, - }; - - $this->affiliateEntity($entity_id, $entity_type, AffiliationType::from($affiliation_type)); + $this->affiliateEntity($entity_set->entity_id, $entity_set->entityClass(), $entity_set->affiliation_type); } } @@ -271,6 +220,7 @@ public function updateMemberStatusBasedOnUserCompliance(): void ->where('role_id', $this->role->id) ->where('entity_type', User::class) ->whereIn('status', [RoleMembershipStatus::ACTIVE->value, RoleMembershipStatus::INACTIVE->value]) + ->with('entity') ->get() ->each(fn (RoleMembership $role_membership) => $role_membership->updateOrFail([ 'status' => $this->isUserCompliant($role_membership->entity) ? RoleMembershipStatus::ACTIVE : RoleMembershipStatus::INACTIVE, diff --git a/src/Services/Roles/AutomaticRoleService.php b/src/Services/Roles/AutomaticRoleService.php index af2d566..6331b37 100644 --- a/src/Services/Roles/AutomaticRoleService.php +++ b/src/Services/Roles/AutomaticRoleService.php @@ -1,20 +1,22 @@ addCriteria($entities, RoleType::AUTOMATIC); + $this->addCriteria(...$entities); $this->handleMembers(); } diff --git a/src/Services/Roles/DTO/AffiliationData.php b/src/Services/Roles/DTO/AffiliationData.php new file mode 100644 index 0000000..57e4fa9 --- /dev/null +++ b/src/Services/Roles/DTO/AffiliationData.php @@ -0,0 +1,38 @@ +entity_type) { + 'character' => CharacterInfo::class, + 'corporation' => CorporationInfo::class, + 'alliance' => AllianceInfo::class, + default => throw new \ValueError("Unknown entity type: {$this->entity_type}"), + }; + } +} diff --git a/src/Services/Roles/DTO/CriteriaData.php b/src/Services/Roles/DTO/CriteriaData.php new file mode 100644 index 0000000..3353d5b --- /dev/null +++ b/src/Services/Roles/DTO/CriteriaData.php @@ -0,0 +1,33 @@ +entity_type) { + 'corporation' => CorporationInfo::class, + 'alliance' => AllianceInfo::class, + default => throw new \ValueError("Unknown entity type: {$this->entity_type}"), + }; + } +} diff --git a/src/Services/Roles/OnRequestRoleService.php b/src/Services/Roles/OnRequestRoleService.php index 9c7a0bf..978557d 100644 --- a/src/Services/Roles/OnRequestRoleService.php +++ b/src/Services/Roles/OnRequestRoleService.php @@ -1,19 +1,21 @@ addCriteria($entities, RoleType::ON_REQUEST); + $this->addCriteria(...$entities); $this->syncMembers(); } diff --git a/src/Services/Roles/OptInRoleService.php b/src/Services/Roles/OptInRoleService.php index 9e8ec11..cc74081 100644 --- a/src/Services/Roles/OptInRoleService.php +++ b/src/Services/Roles/OptInRoleService.php @@ -1,19 +1,21 @@ addCriteria($entities, RoleType::OPT_IN); + $this->addCriteria(...$entities); $this->syncMembers(); } diff --git a/src/Services/Roles/RoleServiceInterface.php b/src/Services/Roles/RoleServiceInterface.php index 583ffca..b999a0c 100644 --- a/src/Services/Roles/RoleServiceInterface.php +++ b/src/Services/Roles/RoleServiceInterface.php @@ -1,8 +1,11 @@ test_user->assignRole(test()->role); - app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions(); + app()[PermissionRegistrar::class]->forgetCachedPermissions(); Route::middleware([CheckAuthorization::class.":$this->permission_name"]) ->prefix('character') @@ -98,6 +101,10 @@ it('checks owned character ids', function (string $method, string $route, array|int $route_param, string $status = 'ok') { expect(test()->test_user->can('superuser'))->toBeFalse(); + // Ensure no stale character roles from other test runs pollute the permission check. + // This test verifies that owning a character does NOT grant corporation-level access. + CharacterRole::query()->delete(); + test()->actingAs(test()->test_user); // Act @@ -164,8 +171,8 @@ createAffiliation( test()->role, test()->secondary_character->alliance->alliance_id, - \Seatplus\Eveapi\Models\Alliance\AllianceInfo::class, - \Seatplus\Auth\Enums\AffiliationType::ALLOWED + AllianceInfo::class, + AffiliationType::ALLOWED ); test()->actingAs(test()->test_user); @@ -214,7 +221,7 @@ test()->role, test()->secondary_character->character_id, CharacterInfo::class, - \Seatplus\Auth\Enums\AffiliationType::FORBIDDEN + AffiliationType::FORBIDDEN ); test()->actingAs(test()->test_user); @@ -305,7 +312,7 @@ }); }); -function createAffiliation(Role $role, int|string $affiliatable_id, string $affiliatable_type, \Seatplus\Auth\Enums\AffiliationType $type): Affiliation +function createAffiliation(Role $role, int|string $affiliatable_id, string $affiliatable_type, AffiliationType $type): Affiliation { /** @var Affiliation $affiliation */ $affiliation = Affiliation::query()->create([ diff --git a/tests/Feature/Routes/SsoControllerTest.php b/tests/Feature/Routes/SsoControllerTest.php index 4f9d951..1301d7b 100644 --- a/tests/Feature/Routes/SsoControllerTest.php +++ b/tests/Feature/Routes/SsoControllerTest.php @@ -28,6 +28,7 @@ use Illuminate\Support\Facades\Queue; use Laravel\Socialite\Contracts\Provider; use Laravel\Socialite\Facades\Socialite; +use Seatplus\Auth\Jobs\RoleMemberSync; it('works for non authed users', function () { $abstractUser = createSocialiteUser(); @@ -101,7 +102,7 @@ $result = test()->get(route('auth.eve.callback')); // assert no UserRolesSync job has been dispatched - Queue::assertPushedOn('high', \Seatplus\Auth\Jobs\RoleMemberSync::class); + Queue::assertPushedOn('high', RoleMemberSync::class); // assert that no error is present expect(session('error'))->toBeNull(); diff --git a/tests/Feature/Services/RoleAffiliatedIdsServiceTest.php b/tests/Feature/Services/RoleAffiliatedIdsServiceTest.php index f19562d..64453b5 100644 --- a/tests/Feature/Services/RoleAffiliatedIdsServiceTest.php +++ b/tests/Feature/Services/RoleAffiliatedIdsServiceTest.php @@ -1,13 +1,16 @@ secondary_character = CharacterInfo::factory()->create(); @@ -15,7 +18,7 @@ test()->role = Role::create(['name' => 'derp']); - $this->service = new \Seatplus\Auth\Services\Roles\AutomaticRoleService($this->role); + $this->service = new AutomaticRoleService($this->role); }); dataset('entity_types', [ @@ -46,10 +49,10 @@ function getId(string $entity_type, int $character_level) $secondary_id = getId($entity_type, 2); - $this->service->syncAffiliateManyEntities([ - [$primaray_id, $entity_type, $affiliation_type], - [$secondary_id, $entity_type, $affiliation_type], - ]); + $this->service->syncAffiliateManyEntities( + new AffiliationData($primaray_id, $entity_type, AffiliationType::from($affiliation_type)), + new AffiliationData($secondary_id, $entity_type, AffiliationType::from($affiliation_type)), + ); $affiliated_ids = (new RoleAffiliatedIdsService)->get(test()->role); @@ -67,9 +70,9 @@ function getId(string $entity_type, int $character_level) $secondary_id = getId($entity_type, 2); $tertiary_id = getId($entity_type, 3); - $this->service->syncAffiliateManyEntities([ - [$tertiary_id, $entity_type, $affiliation_type], - ]); + $this->service->syncAffiliateManyEntities( + new AffiliationData($tertiary_id, $entity_type, AffiliationType::from($affiliation_type)), + ); $affiliated_ids = (new RoleAffiliatedIdsService)->get(test()->role); @@ -89,9 +92,9 @@ function getId(string $entity_type, int $character_level) $secondary_id = getId($entity_type, 2); $tertiary_id = getId($entity_type, 3); - $this->service->syncAffiliateManyEntities([ - [$tertiary_id, $entity_type, $affiliation_type], - ]); + $this->service->syncAffiliateManyEntities( + new AffiliationData($tertiary_id, $entity_type, AffiliationType::from($affiliation_type)), + ); $affiliated_ids = (new RoleAffiliatedIdsService)->get(test()->role); @@ -111,10 +114,10 @@ function getId(string $entity_type, int $character_level) $secondary_id = getId($entity_type, 2); $tertiary_id = getId($entity_type, 3); - $this->service->syncAffiliateManyEntities([ - [test()->test_character->character_id, 'character', AffiliationType::ALLOWED->value], - [$primary_id, $entity_type, AffiliationType::INVERSE->value], - ]); + $this->service->syncAffiliateManyEntities( + new AffiliationData(test()->test_character->character_id, 'character', AffiliationType::ALLOWED), + new AffiliationData($primary_id, $entity_type, AffiliationType::INVERSE), + ); $affiliated_ids = (new RoleAffiliatedIdsService)->get(test()->role); @@ -133,10 +136,10 @@ function getId(string $entity_type, int $character_level) $primary_id = getId($entity_type, 1); - $this->service->syncAffiliateManyEntities([ - [test()->test_character->character_id, 'character', AffiliationType::FORBIDDEN->value], - [$primary_id, $entity_type, AffiliationType::ALLOWED->value], - ]); + $this->service->syncAffiliateManyEntities( + new AffiliationData(test()->test_character->character_id, 'character', AffiliationType::FORBIDDEN), + new AffiliationData($primary_id, $entity_type, AffiliationType::ALLOWED), + ); $affiliated_ids = (new RoleAffiliatedIdsService)->get(test()->role); @@ -158,10 +161,10 @@ function getId(string $entity_type, int $character_level) $primary_id = getId($entity_type, 1); $secondary_id = getId($entity_type, 2); - $this->service->syncAffiliateManyEntities([ - [test()->test_character->character_id, 'character', AffiliationType::FORBIDDEN->value], - [$secondary_id, $entity_type, AffiliationType::INVERSE->value], - ]); + $this->service->syncAffiliateManyEntities( + new AffiliationData(test()->test_character->character_id, 'character', AffiliationType::FORBIDDEN), + new AffiliationData($secondary_id, $entity_type, AffiliationType::INVERSE), + ); $affiliated_ids = (new RoleAffiliatedIdsService)->get(test()->role); @@ -180,11 +183,11 @@ function getId(string $entity_type, int $character_level) $primary_id = getId($entity_type, 1); $secondary_id = getId($entity_type, 2); - $this->service->syncAffiliateManyEntities([ - [test()->test_character->character_id, 'character', AffiliationType::FORBIDDEN->value], - [$primary_id, $entity_type, AffiliationType::ALLOWED->value], - [$secondary_id, $entity_type, AffiliationType::INVERSE->value], - ]); + $this->service->syncAffiliateManyEntities( + new AffiliationData(test()->test_character->character_id, 'character', AffiliationType::FORBIDDEN), + new AffiliationData($primary_id, $entity_type, AffiliationType::ALLOWED), + new AffiliationData($secondary_id, $entity_type, AffiliationType::INVERSE), + ); $affiliated_ids = (new RoleAffiliatedIdsService)->get(test()->role); diff --git a/tests/Pest.php b/tests/Pest.php index c293401..3d107f5 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -1,13 +1,16 @@ in('Unit', 'Feature'); -uses(\Illuminate\Foundation\Testing\LazilyRefreshDatabase::class)->in('Unit', 'Feature'); +uses(TestCase::class)->in('Unit', 'Feature'); +uses(LazilyRefreshDatabase::class)->in('Unit', 'Feature'); /* |-------------------------------------------------------------------------- @@ -132,5 +135,5 @@ function assignPermissionToTestUser(array|string $permission_strings) } // now re-register all the roles and permissions - app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions(); + app()[PermissionRegistrar::class]->forgetCachedPermissions(); } diff --git a/tests/TestCase.php b/tests/TestCase.php index 9b7ba7a..fe18cd0 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -28,6 +28,7 @@ use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Foundation\Application; use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Queue; use Illuminate\Support\Str; @@ -72,7 +73,7 @@ protected function setUp(): void /** * Get application providers. * - * @param \Illuminate\Foundation\Application $app + * @param Application $app * @return array */ protected function getPackageProviders($app) @@ -85,7 +86,7 @@ protected function getPackageProviders($app) } /** - * @param \Illuminate\Foundation\Application $app + * @param Application $app */ private function setupDatabase($app) { @@ -97,7 +98,7 @@ private function setupDatabase($app) /** * Define environment setup. * - * @param \Illuminate\Foundation\Application $app + * @param Application $app * @return void */ protected function getEnvironmentSetUp($app) diff --git a/tests/Unit/Actions/AddMemberActionTest.php b/tests/Unit/Actions/AddMemberActionTest.php index d94e7c6..71e5e6f 100644 --- a/tests/Unit/Actions/AddMemberActionTest.php +++ b/tests/Unit/Actions/AddMemberActionTest.php @@ -1,11 +1,14 @@ mock(\Seatplus\Auth\Http\Actions\Roles\Manual\SetMemberAction::class, function ($mock) { + $this->mock(SetMemberAction::class, function ($mock) { $mock->shouldReceive('execute')->with(1, 2, true)->once(); }); - $action = app(\Seatplus\Auth\Http\Actions\Roles\Manual\AddMemberAction::class); + $action = app(AddMemberAction::class); $action->execute(1, 2); }); diff --git a/tests/Unit/Actions/AddModeratorRoleActionTest.php b/tests/Unit/Actions/AddModeratorRoleActionTest.php index 1ed6c5c..c1585f9 100644 --- a/tests/Unit/Actions/AddModeratorRoleActionTest.php +++ b/tests/Unit/Actions/AddModeratorRoleActionTest.php @@ -2,6 +2,7 @@ namespace Seatplus\Auth\Tests\Unit\Actions; +use Seatplus\Auth\Http\Actions\Roles\AddModeratorRoleAction; use Seatplus\Auth\Http\Actions\Roles\SetModeratorAction; it('adds a moderator role', function () { @@ -9,7 +10,7 @@ $mock->shouldReceive('execute')->with(1, 2, true)->once(); }); - $action = app(\Seatplus\Auth\Http\Actions\Roles\AddModeratorRoleAction::class); + $action = app(AddModeratorRoleAction::class); $action->execute(1, 2); }); diff --git a/tests/Unit/Actions/ApplyActionTest.php b/tests/Unit/Actions/ApplyActionTest.php index fc87b52..7df7322 100644 --- a/tests/Unit/Actions/ApplyActionTest.php +++ b/tests/Unit/Actions/ApplyActionTest.php @@ -1,17 +1,19 @@ mock(BaseRoleService::class, function (\Mockery\MockInterface $mock) { + $this->mock(BaseRoleService::class, function (MockInterface $mock) { $mock->shouldReceive('for')->with(1) ->andReturnSelf(); $mock->shouldReceive('onRequest') ->once() - ->andReturn(mock(\Seatplus\Auth\Services\Roles\OnRequestRoleService::class, function (\Mockery\MockInterface $mock) { + ->andReturn(mock(OnRequestRoleService::class, function (MockInterface $mock) { $mock->shouldReceive('onRequest')->andReturnSelf(); $mock->shouldReceive('submitApplicationForRole')->once(); })); @@ -27,13 +29,13 @@ }); it('throws exception if user not found', function () { - $this->mock(BaseRoleService::class, function (\Mockery\MockInterface $mock) { + $this->mock(BaseRoleService::class, function (MockInterface $mock) { $mock->shouldReceive('for')->with(1) ->andReturnSelf(); $mock->shouldReceive('onRequest') ->once() - ->andReturn(mock(\Seatplus\Auth\Services\Roles\OnRequestRoleService::class, function (\Mockery\MockInterface $mock) { + ->andReturn(mock(OnRequestRoleService::class, function (MockInterface $mock) { $mock->shouldReceive('onRequest')->andReturnSelf(); $mock->shouldReceive('submitApplicationForRole')->never(); })); diff --git a/tests/Unit/Actions/ApproveActionTest.php b/tests/Unit/Actions/ApproveActionTest.php index 39d4ced..39d81d0 100644 --- a/tests/Unit/Actions/ApproveActionTest.php +++ b/tests/Unit/Actions/ApproveActionTest.php @@ -1,15 +1,17 @@ mock(BaseRoleService::class, function (\Mockery\MockInterface $mock) { + $this->mock(BaseRoleService::class, function (MockInterface $mock) { $mock->shouldReceive('for')->with(1)->andReturnSelf(); - $mock->shouldReceive('onRequest')->andReturn(mock(\Seatplus\Auth\Services\Roles\OnRequestRoleService::class, function (\Mockery\MockInterface $mock) { + $mock->shouldReceive('onRequest')->andReturn(mock(OnRequestRoleService::class, function (MockInterface $mock) { $mock->shouldReceive('approveApplicationForRole')->once(); })); }); @@ -23,7 +25,7 @@ it('throws exception if user not found during approval', function () { $roleServiceMock = $this->mock(BaseRoleService::class, function ($mock) { $mock->shouldReceive('for')->with(1)->andReturnSelf(); - $mock->shouldReceive('onRequest')->andReturn(mock(\Seatplus\Auth\Services\Roles\OnRequestRoleService::class, function ($mock) { + $mock->shouldReceive('onRequest')->andReturn(mock(OnRequestRoleService::class, function ($mock) { $mock->shouldReceive('approveApplicationForRole')->never(); })); }); diff --git a/tests/Unit/Actions/DenyActionTest.php b/tests/Unit/Actions/DenyActionTest.php index 6d622f9..52a8cf0 100644 --- a/tests/Unit/Actions/DenyActionTest.php +++ b/tests/Unit/Actions/DenyActionTest.php @@ -4,11 +4,12 @@ use Seatplus\Auth\Http\Actions\Roles\OnRequest\DenyAction; use Seatplus\Auth\Models\User; use Seatplus\Auth\Services\Roles\BaseRoleService; +use Seatplus\Auth\Services\Roles\OnRequestRoleService; it('denies role application for user successfully', function () { $this->mock(BaseRoleService::class, function ($mock) { $mock->shouldReceive('for')->with(1)->andReturnSelf(); - $mock->shouldReceive('onRequest')->andReturn(mock(\Seatplus\Auth\Services\Roles\OnRequestRoleService::class, function ($mock) { + $mock->shouldReceive('onRequest')->andReturn(mock(OnRequestRoleService::class, function ($mock) { $mock->shouldReceive('denyApplication')->once(); })); }); @@ -22,7 +23,7 @@ it('throws exception if user not found during application', function () { $this->mock(BaseRoleService::class, function ($mock) { $mock->shouldReceive('for')->with(1)->andReturnSelf(); - $mock->shouldReceive('onRequest')->andReturn(mock(\Seatplus\Auth\Services\Roles\OnRequestRoleService::class, function ($mock) { + $mock->shouldReceive('onRequest')->andReturn(mock(OnRequestRoleService::class, function ($mock) { $mock->shouldReceive('submitApplicationForRole')->never(); })); }); diff --git a/tests/Unit/Actions/JoinActionTest.php b/tests/Unit/Actions/JoinActionTest.php index 3c9b662..c94d202 100644 --- a/tests/Unit/Actions/JoinActionTest.php +++ b/tests/Unit/Actions/JoinActionTest.php @@ -1,20 +1,22 @@ mock(BaseRoleService::class, function ($mock) { $mock->shouldReceive('for')->with(1)->andReturnSelf(); - $mock->shouldReceive('optIn')->andReturn(mock(\Seatplus\Auth\Services\Roles\OptInRoleService::class, function ($mock) { + $mock->shouldReceive('optIn')->andReturn(mock(OptInRoleService::class, function ($mock) { $mock->shouldReceive('joinRole')->once(); })); }); $user = User::factory()->create(); - $action = app(\Seatplus\Auth\Http\Actions\Roles\OptIn\JoinAction::class); + $action = app(JoinAction::class); $action->execute(1, $user->id); expect(true)->toBeTrue(); // Just to ensure the test runs without exceptions @@ -23,24 +25,24 @@ it('throws exception if user not found during join', function () { $this->mock(BaseRoleService::class, function ($mock) { $mock->shouldReceive('for')->with(1)->andReturnSelf(); - $mock->shouldReceive('optIn')->andReturn(mock(\Seatplus\Auth\Services\Roles\OptInRoleService::class, function ($mock) { + $mock->shouldReceive('optIn')->andReturn(mock(OptInRoleService::class, function ($mock) { $mock->shouldReceive('joinRole')->never(); })); }); - $action = app(\Seatplus\Auth\Http\Actions\Roles\OptIn\JoinAction::class); + $action = app(JoinAction::class); expect(fn () => $action->execute(1, 999))->toThrow(ModelNotFoundException::class); }); it('throws exception if role service not found during join', function () { $this->mock(BaseRoleService::class, function ($mock) { - $mock->shouldReceive('for')->with(1)->andThrow(new \Exception('Role service not found')); + $mock->shouldReceive('for')->with(1)->andThrow(new Exception('Role service not found')); }); $user = User::factory()->create(); - $action = app(\Seatplus\Auth\Http\Actions\Roles\OptIn\JoinAction::class); + $action = app(JoinAction::class); - expect(fn () => $action->execute(1, $user->id))->toThrow(\Exception::class); + expect(fn () => $action->execute(1, $user->id))->toThrow(Exception::class); }); diff --git a/tests/Unit/Actions/LeaveActionTest.php b/tests/Unit/Actions/LeaveActionTest.php index 2d4dc84..a9d84b6 100644 --- a/tests/Unit/Actions/LeaveActionTest.php +++ b/tests/Unit/Actions/LeaveActionTest.php @@ -1,6 +1,7 @@ create(); - $action = app(\Seatplus\Auth\Http\Actions\Roles\OptIn\LeaveAction::class); + $action = app(LeaveAction::class); $action->execute(1, $user->id); expect(true)->toBeTrue(); // Just to ensure the test runs without exceptions @@ -29,7 +30,7 @@ })); }); - $action = app(\Seatplus\Auth\Http\Actions\Roles\OptIn\LeaveAction::class); + $action = app(LeaveAction::class); expect(fn () => $action->execute(1, 999))->toThrow(ModelNotFoundException::class); }); diff --git a/tests/Unit/Actions/LoginAssetActionTest.php b/tests/Unit/Actions/LoginAssetActionTest.php index c260cae..89c8043 100644 --- a/tests/Unit/Actions/LoginAssetActionTest.php +++ b/tests/Unit/Actions/LoginAssetActionTest.php @@ -2,12 +2,13 @@ use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Session; +use Seatplus\Auth\Http\Actions\LoginAssetsAction; it('returns assets needed for the login page', function () { Config::set('services.eveonline.client_id', 'valid_client_id'); Config::set('services.eveonline.client_secret', 'valid_client_secret'); - $action = new \Seatplus\Auth\Http\Actions\LoginAssetsAction; + $action = new LoginAssetsAction; $result = $action(); expect($result)->toBe([ @@ -20,7 +21,7 @@ Config::set('services.eveonline.client_id', '1234'); Config::set('services.eveonline.client_secret', '1234'); - $action = new \Seatplus\Auth\Http\Actions\LoginAssetsAction; + $action = new LoginAssetsAction; $action(); expect(Session::get('warning'))->toBe(trans('auth::auth.sso_config_warning')); @@ -30,7 +31,7 @@ Config::set('services.eveonline.client_id', 'valid_client_id'); Config::set('services.eveonline.client_secret', 'valid_client_secret'); - $action = new \Seatplus\Auth\Http\Actions\LoginAssetsAction; + $action = new LoginAssetsAction; $action(); expect(Session::get('warning'))->toBeNull(); diff --git a/tests/Unit/Actions/LogoutActionTest.php b/tests/Unit/Actions/LogoutActionTest.php index 34cc3cf..a10dad5 100644 --- a/tests/Unit/Actions/LogoutActionTest.php +++ b/tests/Unit/Actions/LogoutActionTest.php @@ -1,11 +1,12 @@ test_user->id; - $test_user = \Seatplus\Auth\Models\User::find($test_user_id)->makeVisible(['remember_token']); + $test_user = User::find($test_user_id)->makeVisible(['remember_token']); $this->actingAs($test_user); diff --git a/tests/Unit/Actions/ManageAutomaticRoleActionTest.php b/tests/Unit/Actions/ManageAutomaticRoleActionTest.php index c232845..668fe15 100644 --- a/tests/Unit/Actions/ManageAutomaticRoleActionTest.php +++ b/tests/Unit/Actions/ManageAutomaticRoleActionTest.php @@ -1,19 +1,24 @@ actingAs(test()->test_user); - $action = app(\Seatplus\Auth\Http\Actions\Roles\ManageAutomaticRoleAction::class); + $action = app(ManageAutomaticRoleAction::class); $action->execute($request); -})->throws(\Exception::class, 'You are not allowed to administrate access control groups'); +})->throws(Exception::class, 'You are not allowed to administrate access control groups'); it('invokes role service with valid role id', function () { $role = Role::create(['name' => 'test']); @@ -33,42 +38,51 @@ ->and(auth()->user()->hasPermissionTo($admin_permission))->toBeTrue() // ok ->and(auth()->user()->can($admin_permission))->toBeTrue(); // fails - $action = app(\Seatplus\Auth\Http\Actions\Roles\ManageAutomaticRoleAction::class); + $action = app(ManageAutomaticRoleAction::class); $action->execute($request); }); it('invokes role service with affiliated entities', function () { $request = mock(RoleRequest::class, function (MockInterface $mock) { - $mock->shouldReceive('validated')->andReturn(['role_id' => 1, 'affiliated' => [['entity_id' => 1, 'entity_type' => 'corporation', 'type' => 'member']], 'assigned' => []]); + $mock->shouldReceive('validated')->andReturn(['role_id' => 1, 'affiliated' => [['entity_id' => 1, 'entity_type' => 'corporation', 'affiliation_type' => 'allowed']], 'assigned' => []]); }); $this->mock(BaseRoleService::class, function (MockInterface $mock) { - $mock->shouldReceive('for')->with(1); + $mock->shouldReceive('for')->with(1)->andReturn($mock); $mock->shouldReceive('automatic')->andReturn(mock(AutomaticRoleService::class, function (MockInterface $mock) { - $mock->shouldReceive('syncAffiliateManyEntities')->once()->with([['entity_id' => 1, 'entity_type' => 'corporation', 'type' => 'member']]); - $mock->shouldReceive('setRoleType')->once()->with(\Seatplus\Auth\Enums\RoleType::AUTOMATIC); + $mock->shouldReceive('setRoleType')->once()->with(RoleType::AUTOMATIC); + $mock->shouldReceive('syncAffiliateManyEntities')->once()->withArgs(function (AffiliationData $entity) { + return $entity->entity_id === 1 && $entity->entity_type === 'corporation' && $entity->affiliation_type === AffiliationType::ALLOWED; + }); + // assigned: [] → empty array → automaticallyAssignRoleTo called with 0 args (clears criteria) + $mock->shouldReceive('automaticallyAssignRoleTo')->once()->withNoArgs(); + $mock->shouldReceive('handleMembers')->once(); })); - }); $this->actingAs(test()->test_user); // give the user the permission to administrate access control groups assignPermissionToTestUser('administrate access control groups'); - $action = app(\Seatplus\Auth\Http\Actions\Roles\ManageAutomaticRoleAction::class); + $action = app(ManageAutomaticRoleAction::class); $action->execute($request); }); it('invokes role service with assigned entities', function () { $request = mock(RoleRequest::class, function (MockInterface $mock) { - $mock->shouldReceive('validated')->andReturn(['role_id' => 1, 'affiliated' => [], 'assigned' => [['entity_id' => 1, 'entity_type' => 'corporation', 'type' => 'member']]]); + $mock->shouldReceive('validated')->andReturn(['role_id' => 1, 'affiliated' => [], 'assigned' => [['entity_id' => 1, 'entity_type' => 'corporation']]]); }); $this->mock(BaseRoleService::class, function (MockInterface $mock) { - $mock->shouldReceive('for')->once()->with(1); + $mock->shouldReceive('for')->once()->with(1)->andReturn($mock); $mock->shouldReceive('automatic')->andReturn(mock(AutomaticRoleService::class, function (MockInterface $mock) { - $mock->shouldReceive('automaticallyAssignRoleTo')->once(); - $mock->shouldReceive('setRoleType')->once()->with(\Seatplus\Auth\Enums\RoleType::AUTOMATIC); + $mock->shouldReceive('setRoleType')->once()->with(RoleType::AUTOMATIC); + // affiliated: [] → empty array → syncAffiliateManyEntities called with 0 args (clears scope) + $mock->shouldReceive('syncAffiliateManyEntities')->once()->withNoArgs(); + $mock->shouldReceive('automaticallyAssignRoleTo')->once()->withArgs(function (CriteriaData $entity) { + return $entity->entity_id === 1 && $entity->entity_type === 'corporation'; + }); + $mock->shouldReceive('handleMembers')->once(); })); }); @@ -76,7 +90,7 @@ // give the user the permission to administrate access control groups assignPermissionToTestUser('administrate access control groups'); - $action = app(\Seatplus\Auth\Http\Actions\Roles\ManageAutomaticRoleAction::class); + $action = app(ManageAutomaticRoleAction::class); $action->execute($request); }); @@ -86,10 +100,14 @@ }); $this->mock(BaseRoleService::class, function (MockInterface $mock) { - $mock->shouldReceive('for')->once()->with(1); + $mock->shouldReceive('for')->once()->with(1)->andReturn($mock); $mock->shouldReceive('automatic')->andReturn(mock(AutomaticRoleService::class, function (MockInterface $mock) { + $mock->shouldReceive('setRoleType')->once()->with(RoleType::AUTOMATIC); $mock->shouldReceive('updateRoleName')->once()->with('new name'); - $mock->shouldReceive('setRoleType')->once()->with(\Seatplus\Auth\Enums\RoleType::AUTOMATIC); + // affiliated: [] and assigned: [] → both called with 0 args + $mock->shouldReceive('syncAffiliateManyEntities')->once()->withNoArgs(); + $mock->shouldReceive('automaticallyAssignRoleTo')->once()->withNoArgs(); + $mock->shouldReceive('handleMembers')->once(); })); }); @@ -97,6 +115,6 @@ // give the user the permission to administrate access control groups assignPermissionToTestUser('administrate access control groups'); - $action = app(\Seatplus\Auth\Http\Actions\Roles\ManageAutomaticRoleAction::class); + $action = app(ManageAutomaticRoleAction::class); $action->execute($request); }); diff --git a/tests/Unit/Actions/ManageManualRoleActionTest.php b/tests/Unit/Actions/ManageManualRoleActionTest.php index 09bfd47..a4db0bf 100644 --- a/tests/Unit/Actions/ManageManualRoleActionTest.php +++ b/tests/Unit/Actions/ManageManualRoleActionTest.php @@ -1,26 +1,34 @@ 'test_role']); - $role_request = mock(RoleRequest::class, function ($mock) use ($role) { + $role_request = mock(RoleRequest::class, function (MockInterface $mock) use ($role) { $mock->shouldReceive('validated') ->andReturn(['role_id' => $role->id]); }); - $this->mock(BaseRoleService::class, function ($mock) use ($role) { + $this->mock(BaseRoleService::class, function (MockInterface $mock) use ($role) { $mock->shouldReceive('for') ->with($role->id) ->andReturn($mock); $mock->shouldReceive('manual') - ->andReturn(mock(\Seatplus\Auth\Services\Roles\ManualRoleService::class, function (\Mockery\MockInterface $mock) { - $mock->shouldReceive('setRoleType'); + ->andReturn(mock(ManualRoleService::class, function (MockInterface $mock) { + $mock->shouldReceive('setRoleType')->once(); + $mock->shouldReceive('handleMembers')->once(); })); }); @@ -28,26 +36,27 @@ $action->execute($role_request); - expect($role->refresh()->type)->toBe(\Seatplus\Auth\Enums\RoleType::MANUAL); + expect($role->refresh()->type)->toBe(RoleType::MANUAL); }); it('updates the role name', function () { $role = Role::create(['name' => 'test_role']); - $role_request = mock(RoleRequest::class, function ($mock) use ($role) { + $role_request = mock(RoleRequest::class, function (MockInterface $mock) use ($role) { $mock->shouldReceive('validated') ->andReturn(['role_id' => $role->id, 'name' => 'new_name']); }); - $this->mock(BaseRoleService::class, function ($mock) use ($role) { + $this->mock(BaseRoleService::class, function (MockInterface $mock) use ($role) { $mock->shouldReceive('for') ->with($role->id) ->andReturn($mock); $mock->shouldReceive('manual') - ->andReturn(mock(\Seatplus\Auth\Services\Roles\ManualRoleService::class, function (\Mockery\MockInterface $mock) { - $mock->shouldReceive('setRoleType'); - $mock->shouldReceive('updateRoleName')->once(); + ->andReturn(mock(ManualRoleService::class, function (MockInterface $mock) { + $mock->shouldReceive('setRoleType')->once(); + $mock->shouldReceive('updateRoleName')->once()->with('new_name'); + $mock->shouldReceive('handleMembers')->once(); })); }); @@ -59,20 +68,25 @@ it('affiliates many entities', function () { $role = Role::create(['name' => 'test_role']); - $role_request = mock(RoleRequest::class, function ($mock) use ($role) { + $role_request = mock(RoleRequest::class, function (MockInterface $mock) use ($role) { $mock->shouldReceive('validated') - ->andReturn(['role_id' => $role->id, 'affiliated' => [['entity_id' => 1, 'entity_type' => 'corporation', 'type' => 'member']]]); + ->andReturn(['role_id' => $role->id, 'affiliated' => [['entity_id' => 1, 'entity_type' => 'corporation', 'affiliation_type' => 'allowed']]]); }); - $this->mock(BaseRoleService::class, function ($mock) use ($role) { + $this->mock(BaseRoleService::class, function (MockInterface $mock) use ($role) { $mock->shouldReceive('for') ->with($role->id) ->andReturn($mock); $mock->shouldReceive('manual') - ->andReturn(mock(\Seatplus\Auth\Services\Roles\ManualRoleService::class, function (\Mockery\MockInterface $mock) { - $mock->shouldReceive('setRoleType'); - $mock->shouldReceive('syncAffiliateManyEntities')->once(); + ->andReturn(mock(ManualRoleService::class, function (MockInterface $mock) { + $mock->shouldReceive('setRoleType')->once(); + $mock->shouldReceive('syncAffiliateManyEntities')->once()->withArgs(function (AffiliationData $affiliationData) { + return $affiliationData->entity_id === 1 + && $affiliationData->entity_type === 'corporation' + && $affiliationData->affiliation_type === AffiliationType::ALLOWED; + }); + $mock->shouldReceive('handleMembers')->once(); })); }); diff --git a/tests/Unit/Actions/ManageOnRequestRoleActionTest.php b/tests/Unit/Actions/ManageOnRequestRoleActionTest.php index ec4c7ab..889fdac 100644 --- a/tests/Unit/Actions/ManageOnRequestRoleActionTest.php +++ b/tests/Unit/Actions/ManageOnRequestRoleActionTest.php @@ -1,11 +1,16 @@ shouldReceive('validated')->once()->andReturn([ 'role_id' => $role->refresh()->id, - 'affiliated' => ['entity1', 'entity2'], - 'assigned' => ['criteria1', 'criteria2'], + 'affiliated' => [['entity_id' => 1, 'entity_type' => 'corporation', 'affiliation_type' => 'allowed']], + 'assigned' => [['entity_id' => 5, 'entity_type' => 'character']], 'name' => 'New Role Name', ]); }); @@ -27,10 +32,15 @@ ->andReturn($mock); $mock->shouldReceive('onRequest')->andReturn(mock(OnRequestRoleService::class, function ($mock) { - $mock->shouldReceive('syncAffiliateManyEntities')->once()->with(['entity1', 'entity2']); - $mock->shouldReceive('addCriteriaForRoleApplication')->once(); + $mock->shouldReceive('setRoleType')->with(RoleType::ON_REQUEST)->once(); $mock->shouldReceive('updateRoleName')->once(); - $mock->shouldReceive('setRoleType')->with(\Seatplus\Auth\Enums\RoleType::ON_REQUEST)->once(); + $mock->shouldReceive('syncAffiliateManyEntities')->once()->withArgs(function (AffiliationData $entity) { + return $entity->entity_id === 1 && $entity->entity_type === 'corporation' && $entity->affiliation_type === AffiliationType::ALLOWED; + }); + $mock->shouldReceive('addCriteriaForRoleApplication')->once()->withArgs(function (CriteriaData $entity) { + return $entity->entity_id === 5 && $entity->entity_type === 'character'; + }); + $mock->shouldReceive('handleMembers')->once(); })); }); @@ -52,7 +62,7 @@ $this->actingAs($this->test_user); - $request = \Mockery::mock(RoleRequest::class); + $request = Mockery::mock(RoleRequest::class); $request->shouldReceive('validated')->andReturn([ 'role_id' => 1, 'affiliated' => ['entity1', 'entity2'], @@ -62,5 +72,5 @@ $action = app(ManageOnRequestRoleAction::class); - expect(fn () => $action->execute($request))->toThrow(\Symfony\Component\HttpKernel\Exception\HttpException::class); + expect(fn () => $action->execute($request))->toThrow(HttpException::class); }); diff --git a/tests/Unit/Actions/ManageOptInRoleActionTest.php b/tests/Unit/Actions/ManageOptInRoleActionTest.php index da66121..0307164 100644 --- a/tests/Unit/Actions/ManageOptInRoleActionTest.php +++ b/tests/Unit/Actions/ManageOptInRoleActionTest.php @@ -1,11 +1,16 @@ shouldReceive('validated')->once()->andReturn([ 'role_id' => $role->refresh()->id, - 'affiliated' => ['entity1', 'entity2'], - 'assigned' => ['criteria1', 'criteria2'], + 'affiliated' => [['entity_id' => 1, 'entity_type' => 'corporation', 'affiliation_type' => 'allowed']], + 'assigned' => [['entity_id' => 5, 'entity_type' => 'character']], 'name' => 'New Role Name', ]); }); @@ -27,10 +32,15 @@ ->andReturn($mock); $mock->shouldReceive('optIn')->andReturn(mock(OptInRoleService::class, function ($mock) { - $mock->shouldReceive('syncAffiliateManyEntities')->once()->with(['entity1', 'entity2']); - $mock->shouldReceive('addCriteriaForRole')->once(); + $mock->shouldReceive('setRoleType')->with(RoleType::OPT_IN)->once(); $mock->shouldReceive('updateRoleName')->once(); - $mock->shouldReceive('setRoleType')->with(\Seatplus\Auth\Enums\RoleType::OPT_IN)->once(); + $mock->shouldReceive('syncAffiliateManyEntities')->once()->withArgs(function (AffiliationData $entity) { + return $entity->entity_id === 1 && $entity->entity_type === 'corporation' && $entity->affiliation_type === AffiliationType::ALLOWED; + }); + $mock->shouldReceive('addCriteriaForRole')->once()->withArgs(function (CriteriaData $entity) { + return $entity->entity_id === 5 && $entity->entity_type === 'character'; + }); + $mock->shouldReceive('handleMembers')->once(); })); }); @@ -52,7 +62,7 @@ $this->actingAs($this->test_user); - $request = \Mockery::mock(RoleRequest::class); + $request = Mockery::mock(RoleRequest::class); $request->shouldReceive('validated')->andReturn([ 'role_id' => 1, 'affiliated' => ['entity1', 'entity2'], @@ -62,5 +72,5 @@ $action = app(ManageOptInRoleAction::class); - expect(fn () => $action->execute($request))->toThrow(\Symfony\Component\HttpKernel\Exception\HttpException::class); + expect(fn () => $action->execute($request))->toThrow(HttpException::class); }); diff --git a/tests/Unit/Actions/OptOutActionTest.php b/tests/Unit/Actions/OptOutActionTest.php index 3a5d3dc..3ca3cf7 100644 --- a/tests/Unit/Actions/OptOutActionTest.php +++ b/tests/Unit/Actions/OptOutActionTest.php @@ -1,6 +1,7 @@ create(); - $action = app(\Seatplus\Auth\Http\Actions\Roles\OnRequest\OptOutAction::class); + $action = app(OptOutAction::class); $action->execute(1, $user->id); expect(true)->toBeTrue(); // Just to ensure the test runs without exceptions @@ -29,7 +30,7 @@ })); }); - $action = app(\Seatplus\Auth\Http\Actions\Roles\OnRequest\OptOutAction::class); + $action = app(OptOutAction::class); expect(fn () => $action->execute(1, 999))->toThrow(ModelNotFoundException::class); }); diff --git a/tests/Unit/Actions/RemoveMemberActionTest.php b/tests/Unit/Actions/RemoveMemberActionTest.php index 221da89..2eaae4c 100644 --- a/tests/Unit/Actions/RemoveMemberActionTest.php +++ b/tests/Unit/Actions/RemoveMemberActionTest.php @@ -1,12 +1,16 @@ 'test']); + $role = Role::create(['name' => 'test']); - $this->mock(\Seatplus\Auth\Http\Actions\Roles\Manual\SetMemberAction::class, function ($mock) use ($role) { + $this->mock(SetMemberAction::class, function ($mock) use ($role) { $mock->shouldReceive('execute')->with($role->id, 1, false)->once(); }); - $action = app(\Seatplus\Auth\Http\Actions\Roles\Manual\RemoveMemberAction::class); + $action = app(RemoveMemberAction::class); $action->execute($role->id, 1); }); diff --git a/tests/Unit/Actions/RemoveModeratorRoleActionTest.php b/tests/Unit/Actions/RemoveModeratorRoleActionTest.php index 94837ab..6cdc147 100644 --- a/tests/Unit/Actions/RemoveModeratorRoleActionTest.php +++ b/tests/Unit/Actions/RemoveModeratorRoleActionTest.php @@ -2,6 +2,7 @@ namespace Seatplus\Auth\Tests\Unit\Actions; +use Seatplus\Auth\Http\Actions\Roles\RemoveModeratorRoleAction; use Seatplus\Auth\Http\Actions\Roles\SetModeratorAction; it('adds a moderator role', function () { @@ -9,7 +10,7 @@ $mock->shouldReceive('execute')->with(1, 2, false)->once(); }); - $action = app(\Seatplus\Auth\Http\Actions\Roles\RemoveModeratorRoleAction::class); + $action = app(RemoveModeratorRoleAction::class); $action->execute(1, 2); }); diff --git a/tests/Unit/Actions/SetMemberActionTest.php b/tests/Unit/Actions/SetMemberActionTest.php index 1f4509e..76c7b1e 100644 --- a/tests/Unit/Actions/SetMemberActionTest.php +++ b/tests/Unit/Actions/SetMemberActionTest.php @@ -1,11 +1,13 @@ mock(BaseRoleService::class, function (\Mockery\MockInterface $mock) { + $this->mock(BaseRoleService::class, function (MockInterface $mock) { $mock->shouldReceive('for') ->once() @@ -24,7 +26,7 @@ it('sets member', function (bool $is_member) { - $this->mock(BaseRoleService::class, function (\Mockery\MockInterface $mock) use ($is_member) { + $this->mock(BaseRoleService::class, function (MockInterface $mock) use ($is_member) { $mock->shouldReceive('for') ->once() @@ -36,7 +38,7 @@ $mock->shouldReceive('manual') ->once() - ->andReturn(mock(\Seatplus\Auth\Services\Roles\ManualRoleService::class, function (\Mockery\MockInterface $mock) use ($is_member) { + ->andReturn(mock(ManualRoleService::class, function (MockInterface $mock) use ($is_member) { if ($is_member) { $mock->shouldReceive('addMember') diff --git a/tests/Unit/Actions/SetModeratorActionTest.php b/tests/Unit/Actions/SetModeratorActionTest.php index ff2653f..68694f3 100644 --- a/tests/Unit/Actions/SetModeratorActionTest.php +++ b/tests/Unit/Actions/SetModeratorActionTest.php @@ -1,11 +1,15 @@ actingAs(test()->test_user); - $this->mock(\Seatplus\Auth\Services\Roles\BaseRoleService::class, function (\Mockery\MockInterface $mock) { + $this->mock(BaseRoleService::class, function (MockInterface $mock) { $mock->shouldReceive('for')->with(1); $mock->shouldReceive('canModerate')->andReturn(false); }); @@ -14,16 +18,16 @@ $action->execute(1, 1, true); -})->throws(\Exception::class, 'You are not allowed to add moderators'); +})->throws(Exception::class, 'You are not allowed to add moderators'); it('sets moderator role', function () { $this->actingAs(test()->test_user); - $this->mock(\Seatplus\Auth\Services\Roles\BaseRoleService::class, function (\Mockery\MockInterface $mock) { + $this->mock(BaseRoleService::class, function (MockInterface $mock) { $mock->shouldReceive('for')->with(1); $mock->shouldReceive('canModerate')->andReturn(true); $mock->shouldReceive('getTypeService')->andReturn( - mock(\Seatplus\Auth\Services\Roles\OnRequestRoleService::class, function (\Mockery\MockInterface $mock) { + mock(OnRequestRoleService::class, function (MockInterface $mock) { $mock->shouldReceive('setModerator')->once()->andReturn(); }) ); @@ -37,15 +41,15 @@ it('throws exception if role type is not manual or on request', function () { $this->actingAs(test()->test_user); - $this->mock(\Seatplus\Auth\Services\Roles\BaseRoleService::class, function (\Mockery\MockInterface $mock) { + $this->mock(BaseRoleService::class, function (MockInterface $mock) { $mock->shouldReceive('for')->with(1); $mock->shouldReceive('canModerate')->andReturn(true); $mock->shouldReceive('getTypeService')->andReturn( - mock(\Seatplus\Auth\Services\Roles\AutomaticRoleService::class) + mock(AutomaticRoleService::class) ); }); $action = app(SetModeratorAction::class); $action->execute(1, 1, true); -})->throws(\Exception::class, 'This action is not allowed'); +})->throws(Exception::class, 'This action is not allowed'); diff --git a/tests/Unit/AuthenticationServiceProviderTest.php b/tests/Unit/AuthenticationServiceProviderTest.php index dcc08a1..9e821dc 100644 --- a/tests/Unit/AuthenticationServiceProviderTest.php +++ b/tests/Unit/AuthenticationServiceProviderTest.php @@ -1,9 +1,11 @@ driver('eveonline'); - expect($driver)->toBeInstanceOf(\SocialiteProviders\Eveonline\Provider::class); + expect($driver)->toBeInstanceOf(Provider::class); }); diff --git a/tests/Unit/Controllers/RedirectSSOControllerTest.php b/tests/Unit/Controllers/RedirectSSOControllerTest.php index a2ff6a5..87ae16b 100644 --- a/tests/Unit/Controllers/RedirectSSOControllerTest.php +++ b/tests/Unit/Controllers/RedirectSSOControllerTest.php @@ -38,5 +38,5 @@ it('throws exception when user is already authenticated', function () { $this->authenticationServiceMock->shouldReceive('isUserAuthenticated')->andReturn(true); - expect(fn () => $this->controller->__invoke($this->socialiteMock))->toThrow(\Exception::class, 'You are already authenticated'); + expect(fn () => $this->controller->__invoke($this->socialiteMock))->toThrow(Exception::class, 'You are already authenticated'); }); diff --git a/tests/Unit/Middleware/CheckRequiredScopesTest.php b/tests/Unit/Middleware/CheckRequiredScopesTest.php index 18ad790..910cd97 100644 --- a/tests/Unit/Middleware/CheckRequiredScopesTest.php +++ b/tests/Unit/Middleware/CheckRequiredScopesTest.php @@ -24,8 +24,10 @@ * SOFTWARE. */ +use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Event; +use Illuminate\Support\Str; use Seatplus\Auth\Http\Middleware\CheckRequiredScopes; use Seatplus\Auth\Models\CharacterUser; use Seatplus\Auth\Models\User; @@ -155,7 +157,7 @@ createRefreshTokenWithScopes(['a', 'b']); // 2. create user application - test()->test_user->application()->create(['id' => \Illuminate\Support\Str::uuid(), 'corporation_id' => test()->test_character->corporation->corporation_id]); + test()->test_user->application()->create(['id' => Str::uuid(), 'corporation_id' => test()->test_character->corporation->corporation_id]); // 3. create required corp scopes createCorporationSsoScope(['c']); @@ -303,7 +305,7 @@ createRefreshTokenWithScopes(['a', 'b']); // 2. create user application - test()->test_user->application()->create(['id' => \Illuminate\Support\Str::uuid(), 'corporation_id' => test()->test_character->corporation->corporation_id]); + test()->test_user->application()->create(['id' => Str::uuid(), 'corporation_id' => test()->test_character->corporation->corporation_id]); // TestingTime @@ -322,7 +324,7 @@ createRefreshTokenWithScopes(['a', 'b']); // 2. create user application - test()->test_user->application()->create(['id' => \Illuminate\Support\Str::uuid(), 'corporation_id' => test()->test_character->corporation->corporation_id]); + test()->test_user->application()->create(['id' => Str::uuid(), 'corporation_id' => test()->test_character->corporation->corporation_id]); // 3. create required corp scopes createCorporationSsoScope(['a']); @@ -346,7 +348,7 @@ $mock->shouldReceive('getMissingScopes')->with(Mockery::type(User::class))->andReturn(['scope1', 'scope2']); }); - $middleware = new \Seatplus\Auth\Http\Middleware\CheckRequiredScopes(app(IsUserCompliantService::class)); + $middleware = new CheckRequiredScopes(app(IsUserCompliantService::class)); $request = Mockery::mock(Request::class); $request->shouldReceive('user')->andReturn(new User); @@ -356,7 +358,7 @@ $response = $middleware->handle($request, $next); - expect($response)->toBeInstanceOf(\Illuminate\Http\RedirectResponse::class) + expect($response)->toBeInstanceOf(RedirectResponse::class) ->and($response->getTargetUrl())->toBe('http://localhost'); }); diff --git a/tests/Unit/Models/RoleMembershipTest.php b/tests/Unit/Models/RoleMembershipTest.php index c835270..4c2250e 100644 --- a/tests/Unit/Models/RoleMembershipTest.php +++ b/tests/Unit/Models/RoleMembershipTest.php @@ -1,20 +1,22 @@ 'test role']); + $role = Role::create(['name' => 'test role']); - \Seatplus\Auth\Models\AccessControl\RoleMembership::query()->create([ + RoleMembership::query()->create([ 'role_id' => $role->id, 'entity_id' => test()->test_character->corporation_id, 'entity_type' => CorporationInfo::class, ]); // Act - $role_membership = \Seatplus\Auth\Models\AccessControl\RoleMembership::first(); + $role_membership = RoleMembership::first(); // Assert expect($role_membership->role->name)->toEqual('test role'); diff --git a/tests/Unit/Models/RoleModelTest.php b/tests/Unit/Models/RoleModelTest.php index bd764cb..c1232d4 100644 --- a/tests/Unit/Models/RoleModelTest.php +++ b/tests/Unit/Models/RoleModelTest.php @@ -24,6 +24,8 @@ * SOFTWARE. */ +use Seatplus\Auth\Enums\RoleType; +use Seatplus\Auth\Models\AccessControl\RoleMembership; use Seatplus\Auth\Models\Permissions\Affiliation; use Seatplus\Auth\Models\Permissions\Permission; use Seatplus\Auth\Models\Permissions\Role; @@ -84,12 +86,12 @@ }); it('has default type attribute', function () { - expect(test()->role->fresh()->type)->toEqual(\Seatplus\Auth\Enums\RoleType::MANUAL); + expect(test()->role->fresh()->type)->toEqual(RoleType::MANUAL); }); it('has role memberships', function () { - \Seatplus\Auth\Models\AccessControl\RoleMembership::query()->create([ + RoleMembership::query()->create([ 'role_id' => test()->role->id, 'entity_id' => test()->test_character->corporation_id, 'entity_type' => CorporationInfo::class, diff --git a/tests/Unit/Observers/CharacterAffiliationObserverTest.php b/tests/Unit/Observers/CharacterAffiliationObserverTest.php index 2eebaba..364e1de 100644 --- a/tests/Unit/Observers/CharacterAffiliationObserverTest.php +++ b/tests/Unit/Observers/CharacterAffiliationObserverTest.php @@ -1,11 +1,15 @@ test_user) ->active->toBeTrue() ->characters->toHaveCount(1); - $character_affiliation = \Seatplus\Eveapi\Models\Character\CharacterAffiliation::firstWhere('character_id', $this->test_user->characters->first()->character_id); + $character_affiliation = CharacterAffiliation::firstWhere('character_id', $this->test_user->characters->first()->character_id); // doomheim the character $character_affiliation->corporation_id = 1000001; @@ -20,7 +24,7 @@ $user->main_character_id = test()->test_character->character_id; $user->save(); - $character_user = \Seatplus\Auth\Models\CharacterUser::factory() + $character_user = CharacterUser::factory() ->create(['user_id' => test()->test_user->id]); expect(test()->test_user->refresh()) @@ -30,7 +34,7 @@ ->main_character_id->not()->toBe($character_user->character_id); // doomheim the character - $character_affiliation = \Seatplus\Eveapi\Models\Character\CharacterAffiliation::firstWhere('character_id', $character_user->character_id); + $character_affiliation = CharacterAffiliation::firstWhere('character_id', $character_user->character_id); $character_affiliation->corporation_id = 1000001; $character_affiliation->save(); @@ -46,7 +50,7 @@ $user->main_character_id = test()->test_character->character_id; $user->save(); - $character_user = \Seatplus\Auth\Models\CharacterUser::factory() + $character_user = CharacterUser::factory() ->create(['user_id' => test()->test_user->id]); expect(test()->test_user->refresh()) @@ -55,14 +59,14 @@ ->main_character_id->toBeInt()->toBe(test()->test_character->character_id) ->main_character_id->not()->toBe($character_user->character_id); - expect(\Seatplus\Auth\Models\User::all())->toHaveCount(1); + expect(User::all())->toHaveCount(1); // doomheim the character - $character_affiliation = \Seatplus\Eveapi\Models\Character\CharacterAffiliation::firstWhere('character_id', test()->test_character->character_id); + $character_affiliation = CharacterAffiliation::firstWhere('character_id', test()->test_character->character_id); $character_affiliation->corporation_id = 1000001; $character_affiliation->save(); - expect(\Seatplus\Auth\Models\User::all())->toHaveCount(2); + expect(User::all())->toHaveCount(2); // original user should still be active expect($user->refresh()) @@ -71,7 +75,7 @@ ->main_character_id->toBeInt()->toBe($character_user->character_id) ->characters->toHaveCount(1); - expect(\Seatplus\Auth\Models\User::firstWhere('main_character_id', '<>', $character_user->character_id)) + expect(User::firstWhere('main_character_id', '<>', $character_user->character_id)) ->active->toBeFalsy() ->main_character_id->toBeInt()->toBe(test()->test_character->character_id) ->main_character_id->toBeInt()->not()->toBe($character_user->character_id) diff --git a/tests/Unit/Services/AuthenticationServiceTest.php b/tests/Unit/Services/AuthenticationServiceTest.php index 901b48c..9abb303 100644 --- a/tests/Unit/Services/AuthenticationServiceTest.php +++ b/tests/Unit/Services/AuthenticationServiceTest.php @@ -26,7 +26,7 @@ it('fails to log in user and reports exception', function () { $user = mock(User::class); - $this->authMock->shouldReceive('login')->with($user, true)->andThrow(new \Exception); + $this->authMock->shouldReceive('login')->with($user, true)->andThrow(new Exception); $result = $this->authenticationService->loginUser($user); diff --git a/tests/Unit/Services/Permissions/UserPermissionServiceTest.php b/tests/Unit/Services/Permissions/UserPermissionServiceTest.php index e462c61..864c8d2 100644 --- a/tests/Unit/Services/Permissions/UserPermissionServiceTest.php +++ b/tests/Unit/Services/Permissions/UserPermissionServiceTest.php @@ -1,5 +1,6 @@ assignRole([$role1, $role2]); - $role_permission_object_service = mock(RolePermissionObjectService::class, function (\Mockery\MockInterface $mock) use ($permissions) { + $role_permission_object_service = mock(RolePermissionObjectService::class, function (MockInterface $mock) use ($permissions) { $result1 = collect([ $permissions[0]->name => [1, 2, 3], diff --git a/tests/Unit/Services/Roles/AbstractRoleServiceTest.php b/tests/Unit/Services/Roles/AbstractRoleServiceTest.php index c261d5c..0651c1f 100644 --- a/tests/Unit/Services/Roles/AbstractRoleServiceTest.php +++ b/tests/Unit/Services/Roles/AbstractRoleServiceTest.php @@ -1,27 +1,34 @@ role = Role::create(['name' => 'test']); $this->role = $this->role->refresh(); - $this->service = new class($this->role) extends \Seatplus\Auth\Services\Roles\AbstractRoleService + $this->service = new class($this->role) extends AbstractRoleService { public function syncMembers(): void {} - public function canView(\Seatplus\Auth\Models\User $user): bool + public function canView(User $user): bool { return false; } - public function canJoin(\Seatplus\Auth\Models\User $user): bool + public function canJoin(User $user): bool { return false; } - public function canModerate(\Seatplus\Auth\Models\User $user): bool + public function canModerate(User $user): bool { return false; } @@ -38,17 +45,17 @@ public function canModerate(\Seatplus\Auth\Models\User $user): bool expect(Affiliation::count())->toEqual(0); // act - $this->service->syncAffiliateManyEntities([ - [$corporation_id, 'corporation', \Seatplus\Auth\Enums\AffiliationType::ALLOWED->value], - [$test_character->character_id, 'character', \Seatplus\Auth\Enums\AffiliationType::ALLOWED->value], - [$alliance_id, 'alliance', \Seatplus\Auth\Enums\AffiliationType::ALLOWED->value], - ]); + $this->service->syncAffiliateManyEntities( + new AffiliationData($corporation_id, 'corporation', AffiliationType::ALLOWED), + new AffiliationData($test_character->character_id, 'character', AffiliationType::ALLOWED), + new AffiliationData($alliance_id, 'alliance', AffiliationType::ALLOWED), + ); // Test expect(Affiliation::count())->toEqual(3) ->and(Affiliation::first()->affiliatable_id)->toEqual($corporation_id) - ->and(Affiliation::first()->affiliatable_type)->toEqual(\Seatplus\Eveapi\Models\Corporation\CorporationInfo::class) - ->and(Affiliation::first()->type)->toEqual(\Seatplus\Auth\Enums\AffiliationType::ALLOWED->value); + ->and(Affiliation::first()->affiliatable_type)->toEqual(CorporationInfo::class) + ->and(Affiliation::first()->type)->toEqual(AffiliationType::ALLOWED->value); }); it('returns early when setting same role type', function () { @@ -58,10 +65,10 @@ public function canModerate(\Seatplus\Auth\Models\User $user): bool $this->role->save(); // Act - $automated_role_service = new \Seatplus\Auth\Services\Roles\AutomaticRoleService($this->role); - $automated_role_service->automaticallyAssignRoleTo([ - [1, 'corporation'], - ]); + $automated_role_service = new AutomaticRoleService($this->role); + $automated_role_service->automaticallyAssignRoleTo( + new CriteriaData(1, 'corporation'), + ); // Assert expect($this->role->refresh()->type)->toEqual(RoleType::AUTOMATIC); diff --git a/tests/Unit/Services/Roles/AutomaticRoleServiceTest.php b/tests/Unit/Services/Roles/AutomaticRoleServiceTest.php index 9808686..8769a5a 100644 --- a/tests/Unit/Services/Roles/AutomaticRoleServiceTest.php +++ b/tests/Unit/Services/Roles/AutomaticRoleServiceTest.php @@ -5,6 +5,7 @@ use Seatplus\Auth\Models\Permissions\Role; use Seatplus\Auth\Models\User; use Seatplus\Auth\Services\Roles\AutomaticRoleService; +use Seatplus\Auth\Services\Roles\DTO\CriteriaData; beforeEach(function () { $this->role = Role::create(['name' => 'test']); @@ -20,9 +21,9 @@ expect(test()->test_user->refresh()->hasRole($this->role->name))->toBeFalse(); - $this->service->automaticallyAssignRoleTo([ - [$corporation_id, 'corporation'], - ]); + $this->service->automaticallyAssignRoleTo( + new CriteriaData($corporation_id, 'corporation'), + ); expect(RoleMembership::get())->toHaveCount(2) // User and Corporation ->and(test()->test_user->refresh()->hasRole($this->role->name))->toBeTrue(); @@ -34,9 +35,9 @@ $test_character = test()->test_character; $alliance_id = $test_character->alliance_id; - $this->service->automaticallyAssignRoleTo([ - [$alliance_id, 'alliance'], - ]); + $this->service->automaticallyAssignRoleTo( + new CriteriaData($alliance_id, 'alliance'), + ); expect(test()->test_user->refresh()->hasRole($this->role->name))->toBeTrue(); }); @@ -47,10 +48,10 @@ $corporation_id = $test_character->corporation_id; $alliance_id = $test_character->alliance_id; - $this->service->automaticallyAssignRoleTo([ - [$corporation_id, 'corporation'], - [$alliance_id, 'alliance'], - ]); + $this->service->automaticallyAssignRoleTo( + new CriteriaData($corporation_id, 'corporation'), + new CriteriaData($alliance_id, 'alliance'), + ); expect(RoleMembership::get())->toHaveCount(3) // User, Corporation and Alliance ->and(test()->test_user->refresh()->hasRole($this->role->name))->toBeTrue(); @@ -66,7 +67,7 @@ expect(test()->test_user->refresh()->hasRole($this->role->name))->toBeTrue(); - $this->service->automaticallyAssignRoleTo([]); + $this->service->automaticallyAssignRoleTo(); expect(test()->test_user->refresh()->hasRole($this->role->name))->toBeFalse() ->and(RoleMembership::query()->count())->toBe(0); @@ -81,9 +82,9 @@ $test_character = test()->test_character; $corporation_id = $test_character->corporation_id; - $service->automaticallyAssignRoleTo([ - [$corporation_id, 'corporation'], - ]); + $service->automaticallyAssignRoleTo( + new CriteriaData($corporation_id, 'corporation'), + ); expect(RoleMembership::get())->toHaveCount(2) // User and Corporation ->and(test()->test_user->refresh()->hasRole($role->name))->toBeTrue(); @@ -107,9 +108,9 @@ $test_character = test()->test_character; $corporation_id = $test_character->corporation_id; - $this->service->automaticallyAssignRoleTo([ - [$corporation_id, 'corporation'], - ]); + $this->service->automaticallyAssignRoleTo( + new CriteriaData($corporation_id, 'corporation'), + ); expect($this->service->canView(test()->test_user))->toBeTrue(); }); diff --git a/tests/Unit/Services/Roles/BaseRoleServiceTest.php b/tests/Unit/Services/Roles/BaseRoleServiceTest.php index 90ee732..7641d08 100644 --- a/tests/Unit/Services/Roles/BaseRoleServiceTest.php +++ b/tests/Unit/Services/Roles/BaseRoleServiceTest.php @@ -2,7 +2,9 @@ use Seatplus\Auth\Enums\RoleType; use Seatplus\Auth\Models\Permissions\Role; +use Seatplus\Auth\Services\Roles\AutomaticRoleService; use Seatplus\Auth\Services\Roles\BaseRoleService; +use Spatie\Permission\Exceptions\RoleDoesNotExist; beforeEach(function () { $this->role = Role::create(['name' => faker()->name()]); @@ -28,14 +30,14 @@ it('throws exception if role not found', function () { BaseRoleService::make('abc'); - })->expectException(\Spatie\Permission\Exceptions\RoleDoesNotExist::class); + })->expectException(RoleDoesNotExist::class); }); it('can get automatic role service', function () { $service = BaseRoleService::make($this->role)->automatic(); - expect($service)->toBeInstanceOf(\Seatplus\Auth\Services\Roles\AutomaticRoleService::class); + expect($service)->toBeInstanceOf(AutomaticRoleService::class); }); it('work with the various role types', function (RoleType $role_type) { diff --git a/tests/Unit/Services/Roles/OnRequestRoleServiceTest.php b/tests/Unit/Services/Roles/OnRequestRoleServiceTest.php index 199ba18..ebee34d 100644 --- a/tests/Unit/Services/Roles/OnRequestRoleServiceTest.php +++ b/tests/Unit/Services/Roles/OnRequestRoleServiceTest.php @@ -1,15 +1,16 @@ role = Role::create(['name' => 'test', 'type' => \Seatplus\Auth\Enums\RoleType::ON_REQUEST->value]); + $this->role = Role::create(['name' => 'test', 'type' => RoleType::ON_REQUEST->value]); $this->role = $this->role->refresh(); $this->service = new OnRequestRoleService($this->role); @@ -18,28 +19,26 @@ describe('adding criteria for role application', function () { it('adds criteria for role application with valid entities', function () { // Arrange - $entities = [ - [test()->test_character->corporation_id, 'corporation'], - [test()->test_character->alliance_id, 'alliance'], - ]; + $corporation_id = test()->test_character->corporation_id; + $alliance_id = test()->test_character->alliance_id; // Act - $this->service->addCriteriaForRoleApplication($entities); + $this->service->addCriteriaForRoleApplication( + new CriteriaData($corporation_id, 'corporation'), + new CriteriaData($alliance_id, 'alliance'), + ); // Assert expect(RoleMembership::query()->count())->toBe(2); }); it('throws validation exception for invalid entities', function () { - // Arrange - $entities = [ - [test()->test_character->corporation_id, 'corporation'], - [test()->test_character->alliance_id, 'invalid'], - ]; - // Act - $this->service->addCriteriaForRoleApplication($entities); - })->expectException(ValidationException::class); + $this->service->addCriteriaForRoleApplication( + new CriteriaData(test()->test_character->corporation_id, 'corporation'), + new CriteriaData(test()->test_character->alliance_id, 'invalid'), + ); + })->throws(ValueError::class); it('resets criterias', function () { // Arrange @@ -58,13 +57,11 @@ 'entity_type' => User::class, ]); - $entities = [ - [test()->test_character->corporation_id, 'corporation'], - [test()->test_character->alliance_id, 'alliance'], - ]; - // Act - $this->service->addCriteriaForRoleApplication($entities); + $this->service->addCriteriaForRoleApplication( + new CriteriaData(test()->test_character->corporation_id, 'corporation'), + new CriteriaData(test()->test_character->alliance_id, 'alliance'), + ); // Assert expect(RoleMembership::query()->count())->toBe(3) @@ -82,15 +79,15 @@ // assert -})->throws(\Exception::class, 'User does not meet criteria to join role'); +})->throws(Exception::class, 'User does not meet criteria to join role'); it('submits application for role', function () { // arrange $user = test()->test_user; - $this->service->addCriteriaForRoleApplication([ - [test()->test_character->corporation_id, 'corporation'], - ]); + $this->service->addCriteriaForRoleApplication( + new CriteriaData(test()->test_character->corporation_id, 'corporation'), + ); // act $this->service->submitApplicationForRole($user); @@ -105,9 +102,9 @@ it('approving application for role', function () { // arrange $user = test()->test_user; - $this->service->addCriteriaForRoleApplication([ - [test()->test_character->corporation_id, 'corporation'], - ]); + $this->service->addCriteriaForRoleApplication( + new CriteriaData(test()->test_character->corporation_id, 'corporation'), + ); // act $this->service->approveApplicationForRole($user); @@ -127,7 +124,7 @@ $this->service->approveApplicationForRole($user); // assert -})->throws(\Exception::class, 'User does not meet criteria to join role'); +})->throws(Exception::class, 'User does not meet criteria to join role'); it('denies application for role', function () { // arrange @@ -233,12 +230,10 @@ describe('can', function () { beforeEach(function () { - $entities = [ - [test()->test_character->corporation_id, 'corporation'], - [test()->test_character->alliance_id, 'alliance'], - ]; - - $this->service->addCriteriaForRoleApplication($entities); + $this->service->addCriteriaForRoleApplication( + new CriteriaData(test()->test_character->corporation_id, 'corporation'), + new CriteriaData(test()->test_character->alliance_id, 'alliance'), + ); }); it('can view', function () { diff --git a/tests/Unit/Services/Roles/OptInRoleServiceTest.php b/tests/Unit/Services/Roles/OptInRoleServiceTest.php index ac935e9..19a18b6 100644 --- a/tests/Unit/Services/Roles/OptInRoleServiceTest.php +++ b/tests/Unit/Services/Roles/OptInRoleServiceTest.php @@ -1,34 +1,36 @@ role = Role::create(['name' => 'test']); $this->role = $this->role->refresh(); - $this->service = new \Seatplus\Auth\Services\Roles\OptInRoleService($this->role); + $this->service = new OptInRoleService($this->role); }); it('can add criteria', function () { - $entities = [ - [1, 'corporation'], - ]; - - $this->service->addCriteriaForRole($entities); + $this->service->addCriteriaForRole( + new CriteriaData(1, 'corporation'), + ); expect(RoleMembership::query()->count())->toBe(1) - ->and(RoleMembership::first())->entity_type->toBe(\Seatplus\Eveapi\Models\Corporation\CorporationInfo::class); + ->and(RoleMembership::first())->entity_type->toBe(CorporationInfo::class); }); it('can join role', function () { $test_user = test()->test_user; - $this->service->addCriteriaForRole([ - [test()->test_character->corporation_id, 'corporation'], - ]); + $this->service->addCriteriaForRole( + new CriteriaData(test()->test_character->corporation_id, 'corporation'), + ); $this->service->joinRole($test_user); @@ -37,9 +39,9 @@ it('can leave role', function () { $test_user = test()->test_user; - $this->service->addCriteriaForRole([ - [test()->test_character->corporation_id, 'corporation'], - ]); + $this->service->addCriteriaForRole( + new CriteriaData(test()->test_character->corporation_id, 'corporation'), + ); $this->service->joinRole($test_user); @@ -52,9 +54,9 @@ it('syncs members', function () { $test_user = test()->test_user; - $this->service->addCriteriaForRole([ - [test()->test_character->corporation_id, 'corporation'], - ]); + $this->service->addCriteriaForRole( + new CriteriaData(test()->test_character->corporation_id, 'corporation'), + ); $this->service->joinRole($test_user); @@ -62,12 +64,12 @@ $role_member = RoleMembership::query()->where('entity_type', User::class)->get(); expect($role_member->count())->toBe(1) - ->and($role_member->first())->status->toBe(\Seatplus\Auth\Enums\RoleMembershipStatus::ACTIVE->value); + ->and($role_member->first())->status->toBe(RoleMembershipStatus::ACTIVE->value); // remove criteria makes the user not meet the criteria anymore - $this->service->addCriteriaForRole([ - [1234, 'corporation'], - ]); + $this->service->addCriteriaForRole( + new CriteriaData(1234, 'corporation'), + ); $this->service->syncMembers(); @@ -76,11 +78,9 @@ describe('it can', function () { beforeEach(function () { - $entities = [ - [test()->test_character->corporation_id, 'corporation'], - ]; - - $this->service->addCriteriaForRole($entities); + $this->service->addCriteriaForRole( + new CriteriaData(test()->test_character->corporation_id, 'corporation'), + ); }); it('can view', function () { diff --git a/tests/Unit/Services/SsoScopes/GlobalSsoScopesServiceTest.php b/tests/Unit/Services/SsoScopes/GlobalSsoScopesServiceTest.php index 464be98..43803f2 100644 --- a/tests/Unit/Services/SsoScopes/GlobalSsoScopesServiceTest.php +++ b/tests/Unit/Services/SsoScopes/GlobalSsoScopesServiceTest.php @@ -1,12 +1,15 @@ set($scopes); - $sso_scopes = \Seatplus\Eveapi\Models\SsoScopes::query() + $sso_scopes = SsoScopes::query() ->where('type', 'global') ->first();