diff --git a/lib/classes/JsonApi/RouteMap.php b/lib/classes/JsonApi/RouteMap.php index e779e8b25aee30fd93e362c16153af0205c58317..2eb33a815e2869bf776046c189f7cc93f0b47e91 100644 --- a/lib/classes/JsonApi/RouteMap.php +++ b/lib/classes/JsonApi/RouteMap.php @@ -253,6 +253,7 @@ class RouteMap $group->get('/institutes/{id}', Routes\Institutes\InstitutesShow::class); $group->get('/institutes', Routes\Institutes\InstitutesIndex::class); + $group->get('/institutes/{id}/memberships', Routes\Institutes\InstituteMembershipsIndex::class); $group->get('/institutes/{id}/status-groups', Routes\Institutes\StatusGroupsOfInstitutes::class); } diff --git a/lib/classes/JsonApi/Routes/InstituteMemberships/InstituteMembershipsShow.php b/lib/classes/JsonApi/Routes/InstituteMemberships/InstituteMembershipsShow.php index 7662af34693d8a5e657490583f01b3de234da53f..13e0dd4c41bd18dcdd101b04dcc714385b960dde 100644 --- a/lib/classes/JsonApi/Routes/InstituteMemberships/InstituteMembershipsShow.php +++ b/lib/classes/JsonApi/Routes/InstituteMemberships/InstituteMembershipsShow.php @@ -22,7 +22,7 @@ class InstituteMembershipsShow extends JsonApiController } $user = $this->getUser($request); - if ($user->id !== $membership->user_id) { + if ($user->id !== $membership->user_id && !get_visibility_by_id($membership->user_id)) { throw new AuthorizationFailedException(); } diff --git a/lib/classes/JsonApi/Routes/Institutes/Authority.php b/lib/classes/JsonApi/Routes/Institutes/Authority.php index b3fe9b13b87914d4f1347d7d357f21cd32069472..c6ee43bee90d6f8b8b519d8ff262faa2f96077ec 100644 --- a/lib/classes/JsonApi/Routes/Institutes/Authority.php +++ b/lib/classes/JsonApi/Routes/Institutes/Authority.php @@ -2,10 +2,19 @@ namespace JsonApi\Routes\Institutes; +use Institute; use User; class Authority { + /** + * @SuppressWarnings(PHPMD.Superglobals) + */ + public static function canEditInstitute(User $user, Institute $institute) + { + return $GLOBALS['perm']->have_studip_perm('admin', $institute->id, $user->id); + } + /** * @SuppressWarnings(PHPMD.Superglobals) */ diff --git a/lib/classes/JsonApi/Routes/Institutes/InstituteMembershipsIndex.php b/lib/classes/JsonApi/Routes/Institutes/InstituteMembershipsIndex.php new file mode 100644 index 0000000000000000000000000000000000000000..9184fd94d99f29de3579e7abd61e92987badbdc4 --- /dev/null +++ b/lib/classes/JsonApi/Routes/Institutes/InstituteMembershipsIndex.php @@ -0,0 +1,80 @@ +<?php + +namespace JsonApi\Routes\Institutes; + +use Psr\Http\Message\ServerRequestInterface as Request; +use Psr\Http\Message\ResponseInterface as Response; +use JsonApi\Errors\AuthorizationFailedException; +use JsonApi\Errors\BadRequestException; +use JsonApi\Errors\RecordNotFoundException; +use JsonApi\JsonApiController; +use JsonApi\Schemas\InstituteMember; + +/** + * Returns all institute-memberships of the institute. + */ +class InstituteMembershipsIndex extends JsonApiController +{ + protected $allowedFilteringParameters = ['permission']; + + protected $allowedIncludePaths = [InstituteMember::REL_INSTITUTE, InstituteMember::REL_USER]; + + protected $allowedPagingParameters = ['offset', 'limit']; + + /** + * @SuppressWarnings(PHPMD.UnusedFormalParameters) + */ + public function __invoke(Request $request, Response $response, $args) + { + $institute = \Institute::find($args['id']); + if (!$institute) { + throw new RecordNotFoundException(); + } + + $this->validateFilters(); + + $user = $this->getUser($request); + $memberships = $this->getMemberships($institute, $user, $this->getFilters()); + + list($offset, $limit) = $this->getOffsetAndLimit(); + + return $this->getPaginatedContentResponse($memberships->limit($offset, $limit), count($memberships)); + } + + private function getMemberships(\Institute $institute, \User $user, array $filters) + { + $memberships = $institute->members; + + $visibleMemberships = Authority::canEditInstitute($user, $institute) + ? $memberships + : $memberships->filter(function ($membership) use ($user) { + return $membership->user_id === $user->id || get_visibility_by_id($membership->user_id); + }); + + return isset($filters['permission']) + ? $visibleMemberships->filter(function ($membership) use ($filters) { + return $membership->inst_perms === $filters['permission']; + }) + : $visibleMemberships; + } + + private function validateFilters() + { + $filtering = $this->getQueryParameters()->getFilteringParameters() ?? []; + + if (array_key_exists('permission', $filtering)) { + if (!in_array($filtering['permission'], ['user', 'autor', 'tutor', 'dozent', 'admin'])) { + throw new BadRequestException('Filter `permission` must be one of `user`, `autor`, `tutor`, `dozent`, `admin`.'); + } + } + } + + private function getFilters() + { + $filtering = $this->getQueryParameters()->getFilteringParameters() ?? []; + + $filters['permission'] = $filtering['permission'] ?? null; + + return $filters; + } +} diff --git a/lib/classes/JsonApi/Schemas/Institute.php b/lib/classes/JsonApi/Schemas/Institute.php index d3eda4ac133e20d563dff6894a662e4765427422..c1775b1646db204b47f3d8914078cd8167e4da6b 100644 --- a/lib/classes/JsonApi/Schemas/Institute.php +++ b/lib/classes/JsonApi/Schemas/Institute.php @@ -12,6 +12,7 @@ class Institute extends SchemaProvider const REL_BLUBBER = 'blubber-threads'; const REL_FILES = 'file-refs'; const REL_FOLDERS = 'folders'; + const REL_MEMBERSHIPS = 'memberships'; const REL_STATUS_GROUPS = 'status-groups'; public function getId($institute): ?string @@ -60,6 +61,12 @@ class Institute extends SchemaProvider ], ]; + $relationships[self::REL_MEMBERSHIPS] = [ + self::RELATIONSHIP_LINKS => [ + Link::RELATED => $this->getRelationshipRelatedLink($resource, self::REL_MEMBERSHIPS), + ], + ]; + $relationships = $this->addStatusGroupsRelationship( $relationships, $resource, diff --git a/tests/jsonapi/InstituteMembershipsIndexTest.php b/tests/jsonapi/InstituteMembershipsIndexTest.php new file mode 100644 index 0000000000000000000000000000000000000000..f9ddf9defecd78e63ef9f0ab462f24cf84de53af --- /dev/null +++ b/tests/jsonapi/InstituteMembershipsIndexTest.php @@ -0,0 +1,41 @@ +<?php + +use JsonApi\Routes\Institutes\InstituteMembershipsIndex; + +class InstituteMembershipsIndexTest extends \Codeception\Test\Unit +{ + /** + * @var \UnitTester + */ + protected $tester; + + protected function _before() + { + \DBManager::getInstance()->setConnection('studip', $this->getModule('\\Helper\\StudipDb')->dbh); + } + + protected function _after() + { + } + + public function testIndexMemberships() + { + $credentials = $this->tester->getCredentialsForTestAutor(); + $instituteId = '2560f7c7674942a7dce8eeb238e15d93'; + + $institute = \Institute::find($instituteId); + + $app = $this->tester->createApp($credentials, 'get', '/institutes/{id}/memberships', InstituteMembershipsIndex::class); + + $requestBuilder = $this->tester->createRequestBuilder($credentials); + $requestBuilder->setUri('/institutes/'.$instituteId.'/memberships')->fetch(); + + $response = $this->tester->sendMockRequest($app, $requestBuilder->getRequest()); + + $this->tester->assertTrue($response->isSuccessfulDocument([200])); + $document = $response->document(); + $this->tester->assertTrue($document->isResourceCollectionDocument()); + $resources = $document->primaryResources(); + $this->tester->assertCount(count($institute->members), $resources); + } +}