Skip to content
Snippets Groups Projects
Commit d71fbf28 authored by François Agneray's avatar François Agneray
Browse files

#27 => done

parent 50229fbd
No related branches found
No related tags found
2 merge requests!72Develop,!34New features
Showing
with 214 additions and 14 deletions
...@@ -15,6 +15,15 @@ ...@@ -15,6 +15,15 @@
</header> </header>
<main role="main" class="container-fluid pb-4"> <main role="main" class="container-fluid pb-4">
<div class="container"> <div class="container">
<ng-container *ngIf="(instanceList | async).length === 0">
<div class="col-12 lead text-center font-weight-bold">
Oops! No instances available...
<span *ngIf="!(isAuthenticated | async)">
Try to sign in to access to protected instances.
</span>
</div>
</ng-container>
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-auto mb-3" *ngFor="let instance of (instanceList | async)"> <div class="col-auto mb-3" *ngFor="let instance of (instanceList | async)">
<app-instance-card <app-instance-card
......
...@@ -36,7 +36,7 @@ services: ...@@ -36,7 +36,7 @@ services:
SSO_AUTH_URL: "http://localhost:8180/auth" SSO_AUTH_URL: "http://localhost:8180/auth"
SSO_REALM: "anis" SSO_REALM: "anis"
SSO_CLIENT_ID: "anis-client" SSO_CLIENT_ID: "anis-client"
TOKEN_ENABLED: 0 TOKEN_ENABLED: 1
TOKEN_JWKS_URL: "http://keycloak:8180/auth/realms/anis/protocol/openid-connect/certs" TOKEN_JWKS_URL: "http://keycloak:8180/auth/realms/anis/protocol/openid-connect/certs"
TOKEN_ADMIN_ROLES: anis_admin,superuser TOKEN_ADMIN_ROLES: anis_admin,superuser
RMQ_HOST: rmq RMQ_HOST: rmq
......
...@@ -138,7 +138,7 @@ $container->set('App\Action\GroupAction', function (ContainerInterface $c) { ...@@ -138,7 +138,7 @@ $container->set('App\Action\GroupAction', function (ContainerInterface $c) {
}); });
$container->set('App\Action\InstanceListAction', function (ContainerInterface $c) { $container->set('App\Action\InstanceListAction', function (ContainerInterface $c) {
return new App\Action\InstanceListAction($c->get('em')); return new App\Action\InstanceListAction($c->get('em'), $c->get(SETTINGS)['token']);
}); });
$container->set('App\Action\InstanceAction', function (ContainerInterface $c) { $container->set('App\Action\InstanceAction', function (ContainerInterface $c) {
......
...@@ -85,6 +85,40 @@ abstract class AbstractAction ...@@ -85,6 +85,40 @@ abstract class AbstractAction
} }
} }
/**
* @param ServerRequestInterface $request PSR-7 This object represents the HTTP request
* @param string $instanceName
* @param array $adminRoles
*/
protected function verifyInstanceAuthorization(
ServerRequestInterface $request,
string $instanceName,
array $adminRoles
) {
$token = $request->getAttribute('token');
if (!$token) {
// The user is not connected (401)
throw new HttpUnauthorizedException($request);
}
$roles = $token->realm_access->roles;
if (!$this->isAdmin($adminRoles, $roles)) {
$qb = $this->em->createQueryBuilder();
$qb->select('i.name')
->from('App\Entity\InstanceGroup', 'ig')
->join('ig.instances', 'i')
->where($qb->expr()->in('ig.role', $roles))
->andWhere($qb->expr()->eq('i.name', ':iname'));
$qb->setParameter('iname', $instanceName);
$r = $qb->getQuery()->getResult();
if (count($r) < 1) {
throw new HttpForbiddenException(
$request,
'You do not have the permission to access the instance : ' . $instanceName
);
}
}
}
protected function isAdmin(array $adminRoles, $roles) protected function isAdmin(array $adminRoles, $roles)
{ {
$admin = false; $admin = false;
......
...@@ -82,6 +82,16 @@ final class DatasetFileExplorerAction extends AbstractAction ...@@ -82,6 +82,16 @@ final class DatasetFileExplorerAction extends AbstractAction
); );
} }
// If instance is private and authorization enabled
$instance = $dataset->getDatasetFamily()->getInstance();
if (!$instance->getPublic() && boolval($this->settings['enabled'])) {
$this->verifyInstanceAuthorization(
$request,
$instance->getName(),
explode(',', $this->settings['admin_roles'])
);
}
// If dataset is private and authorization enabled // If dataset is private and authorization enabled
if (!$dataset->getPublic() && boolval($this->settings['enabled'])) { if (!$dataset->getPublic() && boolval($this->settings['enabled'])) {
$this->verifyDatasetAuthorization( $this->verifyDatasetAuthorization(
......
...@@ -90,6 +90,16 @@ final class DownloadArchiveAction extends AbstractAction ...@@ -90,6 +90,16 @@ final class DownloadArchiveAction extends AbstractAction
); );
} }
// If instance is private and authorization enabled
$instance = $dataset->getDatasetFamily()->getInstance();
if (!$instance->getPublic() && boolval($this->settings['enabled'])) {
$this->verifyInstanceAuthorization(
$request,
$instance->getName(),
explode(',', $this->settings['admin_roles'])
);
}
// If dataset is private and authorization enabled // If dataset is private and authorization enabled
if (!$dataset->getPublic() && boolval($this->settings['enabled'])) { if (!$dataset->getPublic() && boolval($this->settings['enabled'])) {
$this->verifyDatasetAuthorization( $this->verifyDatasetAuthorization(
......
...@@ -82,6 +82,16 @@ final class DownloadFileAction extends AbstractAction ...@@ -82,6 +82,16 @@ final class DownloadFileAction extends AbstractAction
); );
} }
// If instance is private and authorization enabled
$instance = $dataset->getDatasetFamily()->getInstance();
if (!$instance->getPublic() && boolval($this->settings['enabled'])) {
$this->verifyInstanceAuthorization(
$request,
$instance->getName(),
explode(',', $this->settings['admin_roles'])
);
}
// If dataset is private and authorization enabled // If dataset is private and authorization enabled
if (!$dataset->getPublic() && boolval($this->settings['enabled'])) { if (!$dataset->getPublic() && boolval($this->settings['enabled'])) {
$this->verifyDatasetAuthorization( $this->verifyDatasetAuthorization(
......
...@@ -90,6 +90,16 @@ final class DownloadResultAction extends AbstractAction ...@@ -90,6 +90,16 @@ final class DownloadResultAction extends AbstractAction
); );
} }
// If instance is private and authorization enabled
$instance = $dataset->getDatasetFamily()->getInstance();
if (!$instance->getPublic() && boolval($this->settings['enabled'])) {
$this->verifyInstanceAuthorization(
$request,
$instance->getName(),
explode(',', $this->settings['admin_roles'])
);
}
// If dataset is private and authorization enabled // If dataset is private and authorization enabled
if (!$dataset->getPublic() && boolval($this->settings['enabled'])) { if (!$dataset->getPublic() && boolval($this->settings['enabled'])) {
$this->verifyDatasetAuthorization( $this->verifyDatasetAuthorization(
......
...@@ -14,6 +14,7 @@ namespace App\Action; ...@@ -14,6 +14,7 @@ namespace App\Action;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use Doctrine\ORM\EntityManagerInterface;
use Slim\Exception\HttpBadRequestException; use Slim\Exception\HttpBadRequestException;
use App\Entity\Instance; use App\Entity\Instance;
...@@ -23,6 +24,25 @@ use App\Entity\Instance; ...@@ -23,6 +24,25 @@ use App\Entity\Instance;
*/ */
final class InstanceListAction extends AbstractAction final class InstanceListAction extends AbstractAction
{ {
/**
* Contains settings to handle Json Web Token
*
* @var array
*/
private $settings;
/**
* Create the classe before call __invoke to execute the action
*
* @param EntityManagerInterface $em Doctrine Entity Manager Interface
* @param array $settings Settings about token
*/
public function __construct(EntityManagerInterface $em, array $settings)
{
parent::__construct($em);
$this->settings = $settings;
}
/** /**
* `GET` Returns a list of all instances listed in the metamodel * `GET` Returns a list of all instances listed in the metamodel
* `POST` Add a new instance * `POST` Add a new instance
...@@ -43,7 +63,8 @@ final class InstanceListAction extends AbstractAction ...@@ -43,7 +63,8 @@ final class InstanceListAction extends AbstractAction
} }
if ($request->getMethod() === GET) { if ($request->getMethod() === GET) {
$instances = $this->em->getRepository('App\Entity\Instance')->findAll(); //$instances = $this->em->getRepository('App\Entity\Instance')->findAll();
$instances = $this->getInstanceList($request->getAttribute('token'));
$payload = json_encode($instances); $payload = json_encode($instances);
} }
...@@ -69,6 +90,35 @@ final class InstanceListAction extends AbstractAction ...@@ -69,6 +90,35 @@ final class InstanceListAction extends AbstractAction
return $response; return $response;
} }
private function getInstanceList($token)
{
$qb = $this->em->createQueryBuilder();
$qb->select('i')->from('App\Entity\Instance', 'i');
if (boolval($this->settings['enabled'])) {
if (!$token) {
// If user is not connected return public instances
$qb->andWhere($qb->expr()->eq('i.public', 'true'));
} else {
$adminRoles = explode(',', $this->settings['admin_roles']);
$roles = $token->realm_access->roles;
if (!$this->isAdmin($adminRoles, $roles)) {
// If user is not an admin return public datasets
// And returns datasets from user's groups
$qb->andWhere($qb->expr()->eq('i.public', 'true'));
$qb2 = $this->em->createQueryBuilder();
$qb2->select('i2.name')
->from('App\Entity\InstanceGroup', 'ig')
->join('ig.instances', 'i2')
->where($qb2->expr()->in('ig.role', $roles));
$qb->orWhere($qb->expr()->in('i.name', $qb2->getDQL()));
}
}
}
return $qb->getQuery()->getResult();
}
/** /**
* Add a new instance into the metamodel * Add a new instance into the metamodel
* *
......
...@@ -105,6 +105,16 @@ final class SearchAction extends AbstractAction ...@@ -105,6 +105,16 @@ final class SearchAction extends AbstractAction
); );
} }
// If instance is private and authorization enabled
$instance = $dataset->getDatasetFamily()->getInstance();
if (!$instance->getPublic() && boolval($this->settings['enabled'])) {
$this->verifyInstanceAuthorization(
$request,
$instance->getName(),
explode(',', $this->settings['admin_roles'])
);
}
// If dataset is private and authorization enabled // If dataset is private and authorization enabled
if (!$dataset->getPublic() && boolval($this->settings['enabled'])) { if (!$dataset->getPublic() && boolval($this->settings['enabled'])) {
$this->verifyDatasetAuthorization( $this->verifyDatasetAuthorization(
......
...@@ -87,8 +87,20 @@ final class StartTaskCreateArchiveAction extends AbstractAction ...@@ -87,8 +87,20 @@ final class StartTaskCreateArchiveAction extends AbstractAction
); );
} }
// If dataset is private and authorization enabled
$token = ''; $token = '';
// If instance is private and authorization enabled
$instance = $dataset->getDatasetFamily()->getInstance();
if (!$instance->getPublic() && boolval($this->settings['enabled'])) {
$this->verifyInstanceAuthorization(
$request,
$instance->getName(),
explode(',', $this->settings['admin_roles'])
);
$token = $request->getHeader('Authorization')[0];
}
// If dataset is private and authorization enabled
if (!$dataset->getPublic() && boolval($this->settings['enabled'])) { if (!$dataset->getPublic() && boolval($this->settings['enabled'])) {
$this->verifyDatasetAuthorization( $this->verifyDatasetAuthorization(
$request, $request,
......
...@@ -87,8 +87,20 @@ final class StartTaskCreateResultAction extends AbstractAction ...@@ -87,8 +87,20 @@ final class StartTaskCreateResultAction extends AbstractAction
); );
} }
// If dataset is private and authorization enabled
$token = ''; $token = '';
// If instance is private and authorization enabled
$instance = $dataset->getDatasetFamily()->getInstance();
if (!$instance->getPublic() && boolval($this->settings['enabled'])) {
$this->verifyInstanceAuthorization(
$request,
$instance->getName(),
explode(',', $this->settings['admin_roles'])
);
$token = $request->getHeader('Authorization')[0];
}
// If dataset is private and authorization enabled
if (!$dataset->getPublic() && boolval($this->settings['enabled'])) { if (!$dataset->getPublic() && boolval($this->settings['enabled'])) {
$this->verifyDatasetAuthorization( $this->verifyDatasetAuthorization(
$request, $request,
...@@ -97,7 +109,7 @@ final class StartTaskCreateResultAction extends AbstractAction ...@@ -97,7 +109,7 @@ final class StartTaskCreateResultAction extends AbstractAction
); );
$token = $request->getHeader('Authorization')[0]; $token = $request->getHeader('Authorization')[0];
} }
$queryParams = $request->getQueryParams(); $queryParams = $request->getQueryParams();
// The parameter "a" is mandatory // The parameter "a" is mandatory
......
...@@ -82,6 +82,7 @@ final class InstanceActionTest extends TestCase ...@@ -82,6 +82,7 @@ final class InstanceActionTest extends TestCase
'description' => 'Test', 'description' => 'Test',
'display' => 10, 'display' => 10,
'data_path' => '/DEFAULT', 'data_path' => '/DEFAULT',
'public' => true,
'portal_logo' => '', 'portal_logo' => '',
'design_color' => '#7AC29A', 'design_color' => '#7AC29A',
'design_background_color' => '#FFFFFF', 'design_background_color' => '#FFFFFF',
......
...@@ -17,7 +17,9 @@ use Nyholm\Psr7\ServerRequest; ...@@ -17,7 +17,9 @@ use Nyholm\Psr7\ServerRequest;
use Nyholm\Psr7\Response; use Nyholm\Psr7\Response;
use Slim\Exception\HttpBadRequestException; use Slim\Exception\HttpBadRequestException;
use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManager;
use Doctrine\Persistence\ObjectRepository; use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\Query\Expr;
final class InstanceListActionTest extends TestCase final class InstanceListActionTest extends TestCase
{ {
...@@ -26,8 +28,12 @@ final class InstanceListActionTest extends TestCase ...@@ -26,8 +28,12 @@ final class InstanceListActionTest extends TestCase
protected function setUp(): void protected function setUp(): void
{ {
$settings = array(
'enabled' => '0',
'admin_role' => 'anis_admin'
);
$this->entityManager = $this->createMock(EntityManager::class); $this->entityManager = $this->createMock(EntityManager::class);
$this->action = new \App\Action\InstanceListAction($this->entityManager); $this->action = new \App\Action\InstanceListAction($this->entityManager, $settings);
} }
public function testOptionsHttpMethod(): void public function testOptionsHttpMethod(): void
...@@ -39,9 +45,18 @@ final class InstanceListActionTest extends TestCase ...@@ -39,9 +45,18 @@ final class InstanceListActionTest extends TestCase
public function testGetAllInstances(): void public function testGetAllInstances(): void
{ {
$repository = $this->getObjectRepositoryMock(); $expr = $this->getExprMock();
$repository->expects($this->once())->method('findAll'); $query = $this->getAbstractQueryMock();
$this->entityManager->method('getRepository')->with('App\Entity\Instance')->willReturn($repository); $query->expects($this->once())->method('getResult');
$queryBuilder = $this->getQueryBuilderMock();
$queryBuilder->method('select')->willReturn($queryBuilder);
$queryBuilder->method('from')->willReturn($queryBuilder);
$queryBuilder->method('join')->willReturn($queryBuilder);
$queryBuilder->method('expr')->willReturn($expr);
$queryBuilder->expects($this->once())->method('getQuery')->willReturn($query);
$this->entityManager->method('createQueryBuilder')->willReturn($queryBuilder);
$request = $this->getRequest('GET'); $request = $this->getRequest('GET');
($this->action)($request, new Response(), array()); ($this->action)($request, new Response(), array());
...@@ -66,6 +81,7 @@ final class InstanceListActionTest extends TestCase ...@@ -66,6 +81,7 @@ final class InstanceListActionTest extends TestCase
'description' => 'Test', 'description' => 'Test',
'display' => 10, 'display' => 10,
'data_path' => '/DEFAULT', 'data_path' => '/DEFAULT',
'public' => true,
'portal_logo' => '', 'portal_logo' => '',
'design_color' => '#7AC29A', 'design_color' => '#7AC29A',
'design_background_color' => '#FFFFFF', 'design_background_color' => '#FFFFFF',
...@@ -96,10 +112,26 @@ final class InstanceListActionTest extends TestCase ...@@ -96,10 +112,26 @@ final class InstanceListActionTest extends TestCase
} }
/** /**
* @return ObjectRepository|\PHPUnit\Framework\MockObject\MockObject * @return Expr|\PHPUnit\Framework\MockObject\MockObject
*/
private function getExprMock()
{
return $this->createMock(Expr::class);
}
/**
* @return AbstractQuery|\PHPUnit\Framework\MockObject\MockObject
*/
private function getAbstractQueryMock()
{
return $this->createMock(AbstractQuery::class);
}
/**
* @return QueryBuilder|\PHPUnit\Framework\MockObject\MockObject
*/ */
private function getObjectRepositoryMock() private function getQueryBuilderMock()
{ {
return $this->createMock(ObjectRepository::class); return $this->createMock(QueryBuilder::class);
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment