never(); } public function test_legacy_curation_cannot_restore_a_revoked_or_replaced_legacy_authority(): void { $actor = new User; $actor->id = 17; $record = new Opportunity; $record->owner_user_id = null; $access = Mockery::mock(PortalProfileAccess::class); $access->shouldReceive('canView')->once()->with($actor, $record)->andReturnTrue(); $access->shouldReceive('legacyWriteAllowed')->once()->with($actor)->andReturnFalse(); $management = Mockery::mock(OpportunityManagement::class); $management->shouldReceive('installed')->once()->andReturnTrue(); $this->assertFalse((new OpportunityCuration($access, $management))->allowed($actor, $record)); } public function test_native_assignment_to_another_publisher_is_not_inferred_away(): void { $actor = new User; $actor->id = 17; $record = new Opportunity; $record->id = 1; $record->owner_user_id = 19; $record->organization_id = null; $record->opportunity_category = 'investment_project'; $record->workflow_context = ['approval' => ['actor_id' => 20], 'publication_assignment' => ['assignee_user_id' => 21]]; $access = Mockery::mock(PortalProfileAccess::class); $access->shouldReceive('canView')->once()->with($actor, $record)->andReturnTrue(); $access->shouldReceive('scoped')->once()->with($actor, 'publisher', null, 'investment_project')->andReturnTrue(); $access->shouldReceive('operational')->once()->with($actor, 'publish', 1)->andReturnTrue(); $access->shouldReceive('conflict')->once()->with($actor, $record)->andReturnFalse(); $management = Mockery::mock(OpportunityManagement::class); $management->shouldReceive('installed')->once()->andReturnTrue(); $this->assertFalse((new OpportunityCuration($access, $management))->allowed($actor, $record)); } public function test_native_editorial_authorisation_reuses_the_existing_fresh_mfa_requirement(): void { $actor = new User; $actor->id = 17; $record = new Opportunity; $record->id = 1; $record->owner_user_id = 19; $record->organization_id = null; $record->opportunity_category = 'investment_project'; $access = Mockery::mock(PortalProfileAccess::class); $access->shouldReceive('authorize')->once()->with($actor, 'view', $record); $access->shouldReceive('promoterRecordScope')->once()->with($actor, $record)->andReturnFalse(); $access->shouldReceive('scoped')->once()->with($actor, 'publisher', null, 'investment_project')->andReturnTrue(); $access->shouldReceive('operational')->once()->with($actor, 'publish', 1)->andReturnTrue(); $access->shouldReceive('conflict')->once()->with($actor, $record)->andReturnFalse(); $access->shouldReceive('mfa')->once()->with($actor)->andReturnFalse(); $flow = new OpportunityProfileWorkflow($access, Mockery::mock(OpportunityManagement::class), Mockery::mock(OpportunityCanonicalContract::class), Mockery::mock(OpportunityWorkflowTasks::class)); try { $flow->authorizeEditorialCuration($actor, $record); $this->fail('A stale MFA session was accepted.'); } catch (HttpException $error) { $this->assertSame(403, $error->getStatusCode()); $this->assertSame('required', $error->getHeaders()['X-Portal-Reauthentication']); } } }