Commit 5e02f891 authored by François Agneray's avatar François Agneray
Browse files

Instance and dataset private => done

parent cb6185ab
<div class="row align-items-center jumbotron">
<div class="col-6 col-md-4 order-md-2 mx-auto text-center">
<img class="img-fluid mb-3 mb-md-0" [src]="getLogoSrc() | authImage | async" alt="Instance logo">
<img *ngIf="instance.public" class="img-fluid mb-3 mb-md-0" [src]="getLogoSrc()" alt="Instance logo">
<img *ngIf="!instance.public" class="img-fluid mb-3 mb-md-0" [src]="getLogoSrc() | authImage | async" alt="Instance logo">
</div>
<div class="col-md-8 order-md-1 text-justify pr-md-5" [innerHtml]="instance.home_component_config.home_component_text"></div>
</div>
......@@ -8,6 +8,7 @@
*/
import { Component, OnDestroy, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Store } from '@ngrx/store';
import { Observable, Subscription } from 'rxjs';
......@@ -20,6 +21,7 @@ import * as authSelector from 'src/app/auth/auth.selector';
import * as datasetActions from 'src/app/metamodel/actions/dataset.actions';
import * as datasetFamilyActions from 'src/app/metamodel/actions/dataset-family.actions';
import * as instanceSelector from 'src/app/metamodel/selectors/instance.selector';
import * as datasetGroupActions from 'src/app/metamodel/actions/dataset-group.actions';
import { AppConfigService } from 'src/app/app-config.service';
/**
......@@ -47,7 +49,7 @@ export class InstanceComponent implements OnInit, OnDestroy {
public url: Observable<string>;
public instanceSubscription: Subscription;
constructor(private store: Store<{ }>, private config: AppConfigService) {
constructor(private store: Store<{ }>, private config: AppConfigService, private http: HttpClient) {
this.instance = store.select(instanceSelector.selectInstanceByRouteName);
this.isAuthenticated = store.select(authSelector.selectIsAuthenticated);
this.userProfile = store.select(authSelector.selectUserProfile);
......@@ -60,6 +62,7 @@ export class InstanceComponent implements OnInit, OnDestroy {
// This micro task prevent the expression has changed after view init error
Promise.resolve(null).then(() => this.store.dispatch(datasetFamilyActions.loadDatasetFamilyList()));
Promise.resolve(null).then(() => this.store.dispatch(datasetActions.loadDatasetList()));
Promise.resolve(null).then(() => this.store.dispatch(datasetGroupActions.loadDatasetGroupList()));
this.instanceSubscription = this.instance.subscribe(instance => {
if (instance) {
if (instance.search_by_criteria_allowed) {
......@@ -72,7 +75,7 @@ export class InstanceComponent implements OnInit, OnDestroy {
this.links.push({ label: instance.documentation_label, icon: 'fas fa-question', routerLink: 'documentation' });
}
if (instance.design_favicon !== '') {
this.favIcon.href = `${this.config.apiUrl}/instance/${instance.name}/file-explorer${instance.design_favicon}`;
this.setFaviconHref(instance);
}
this.title.innerHTML = instance.label;
this.body.style.backgroundColor = instance.design_background_color;
......@@ -80,6 +83,22 @@ export class InstanceComponent implements OnInit, OnDestroy {
})
}
setFaviconHref(instance: Instance) {
const src = `${this.config.apiUrl}/instance/${instance.name}/file-explorer${instance.design_favicon}`;
if (instance.public) {
this.favIcon.href = src;
} else {
this.http.get(src, { responseType: 'blob' }).subscribe(data => {
const reader = new FileReader();
reader.readAsDataURL(data);
reader.onloadend = () => {
const base64data = reader.result;
this.favIcon.href = base64data as string;
}
});
}
}
/**
* Returns application base href.
*
......@@ -98,6 +117,11 @@ export class InstanceComponent implements OnInit, OnDestroy {
return this.config.authenticationEnabled;
}
/**
* Returns admin roles list
*
* @returns string[]
*/
getAdminRoles(): string[] {
return this.config.adminRoles;
}
......
......@@ -12,11 +12,16 @@
</div>
</div>
<div class="col-auto">
<button *ngIf="dataset.name !== datasetSelected"
<button *ngIf="dataset.name !== datasetSelected && isDatasetAccessible()"
(click)="selectDataset(dataset.name)"
class="btn btn-outline-secondary">
<span *ngIf="!dataset.public" class="fa-solid fa-lock-open"></span>
<span *ngIf="dataset.public" class="fa-solid fa-globe"></span>
Select
</button>
<button *ngIf="!isDatasetAccessible()" class="btn btn-outline-danger disabled" title="You are not authorized to access this dataset">
<span class="fa-solid fa-lock"></span> Private
</button>
<span *ngIf="dataset.name === datasetSelected">
<span class="far fa-check-circle fa-2x text-success"></span>
</span>
......
......@@ -10,7 +10,7 @@
import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
import { Router } from '@angular/router';
import { Dataset } from 'src/app/metamodel/models';
import { Dataset, DatasetGroup } from 'src/app/metamodel/models';
/**
* @class
......@@ -25,9 +25,45 @@ export class DatasetCardComponent {
@Input() dataset: Dataset;
@Input() instanceSelected: string;
@Input() datasetSelected: string;
@Input() authenticationEnabled: boolean;
@Input() isAuthenticated: boolean;
@Input() userRoles: string[];
@Input() adminRoles: string[];
@Input() datasetGroupList: DatasetGroup[];
constructor(private router: Router) { }
isDatasetAccessible() {
let accessible = true;
if (this.authenticationEnabled && !this.dataset.public && !this.isAdmin()) {
accessible = false;
if (this.isAuthenticated) {
accessible = this.datasetGroupList
.filter(datasetGroup => datasetGroup.datasets.includes(this.dataset.name))
.filter(datasetGroup => this.userRoles.includes(datasetGroup.role))
.length > 0;
}
}
return accessible;
}
/**
* Returns true if user is admin
*
* @returns boolean
*/
isAdmin() {
let admin = false;
for (let i = 0; i < this.adminRoles.length; i++) {
admin = this.userRoles.includes(this.adminRoles[i]);
if (admin) break;
}
return admin;
}
/**
* Navigates to search form corresponding to the given dataset.
*
......
......@@ -17,7 +17,13 @@
<app-dataset-card
[dataset]="dataset"
[instanceSelected]="instanceSelected"
[datasetSelected]="datasetSelected">
[datasetSelected]="datasetSelected"
[datasetSelected]="datasetSelected"
[authenticationEnabled]="authenticationEnabled"
[isAuthenticated]="isAuthenticated"
[userRoles]="userRoles"
[adminRoles]="adminRoles"
[datasetGroupList]="datasetGroupList">
</app-dataset-card>
<hr *ngIf="!isLast">
</div>
......
......@@ -9,7 +9,7 @@
import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
import { Dataset, DatasetFamily } from 'src/app/metamodel/models';
import { Dataset, DatasetFamily, DatasetGroup } from 'src/app/metamodel/models';
/**
* @class
......@@ -26,4 +26,9 @@ export class DatasetTabsComponent {
@Input() datasetFamilyList: DatasetFamily[];
@Input() instanceSelected: string;
@Input() datasetSelected: string;
@Input() authenticationEnabled: boolean;
@Input() isAuthenticated: boolean;
@Input() userRoles: string[];
@Input() adminRoles: string[];
@Input() datasetGroupList: DatasetGroup[];
}
<app-spinner *ngIf="(datasetFamilyListIsLoading | async)
|| (datasetListIsLoading | async)">
|| (datasetListIsLoading | async)
|| (datasetGroupListIsLoading | async)">
</app-spinner>
<div *ngIf="(datasetFamilyListIsLoaded | async)
&& (datasetListIsLoaded | async)" class="row mt-4">
&& (datasetListIsLoaded | async)
&& (datasetGroupListIsLoaded | async)" class="row mt-4">
<ng-container *ngIf="(datasetList | async).length === 0">
<div class="col-12 lead text-center">
Oops! No dataset available...
......@@ -18,7 +20,12 @@
[datasetList]="datasetList | async"
[datasetFamilyList]="datasetFamilyList | async"
[instanceSelected]="instanceSelected | async"
[datasetSelected]="datasetSelected | async">
[datasetSelected]="datasetSelected | async"
[authenticationEnabled]="getAuthenticationEnabled()"
[isAuthenticated]="isAuthenticated | async"
[userRoles]="userRoles | async"
[adminRoles]="getAdminRoles()"
[datasetGroupList]="datasetGroupList | async">
</app-dataset-tabs>
</div>
<div class="col-12 col-md-4 col-lg-3 pt-2">
......
......@@ -13,10 +13,12 @@ import { Store } from '@ngrx/store';
import { Observable, Subscription } from 'rxjs';
import { AbstractSearchComponent } from './abstract-search.component';
import { DatasetFamily } from 'src/app/metamodel/models';
import { DatasetFamily, DatasetGroup } from 'src/app/metamodel/models';
import * as searchActions from '../../store/actions/search.actions';
import * as authSelector from 'src/app/auth/auth.selector';
import * as datasetFamilySelector from 'src/app/metamodel/selectors/dataset-family.selector';
import * as datasetGroupSelector from 'src/app/metamodel/selectors/dataset-group.selector';
import { AppConfigService } from 'src/app/app-config.service';
/**
* @class
......@@ -31,14 +33,22 @@ export class DatasetComponent extends AbstractSearchComponent {
public datasetFamilyListIsLoading: Observable<boolean>;
public datasetFamilyListIsLoaded: Observable<boolean>;
public datasetFamilyList: Observable<DatasetFamily[]>;
public userRoles: Observable<string[]>;
public datasetGroupList: Observable<DatasetGroup[]>;
public datasetGroupListIsLoading: Observable<boolean>;
public datasetGroupListIsLoaded: Observable<boolean>;
public datasetSelectedSubscription: Subscription;
constructor(protected store: Store<{ }>) {
constructor(protected store: Store<{ }>, private config: AppConfigService) {
super(store);
this.isAuthenticated = store.select(authSelector.selectIsAuthenticated);
this.datasetFamilyListIsLoading = store.select(datasetFamilySelector.selectDatasetFamilyListIsLoading);
this.datasetFamilyListIsLoaded = store.select(datasetFamilySelector.selectDatasetFamilyListIsLoaded);
this.datasetFamilyList = store.select(datasetFamilySelector.selectAllDatasetFamilies);
this.userRoles = store.select(authSelector.selectUserRoles);
this.datasetGroupList = store.select(datasetGroupSelector.selectAllDatasetGroups);
this.datasetGroupListIsLoading = store.select(datasetGroupSelector.selectDatasetGroupListIsLoading);
this.datasetGroupListIsLoaded = store.select(datasetGroupSelector.selectDatasetGroupListIsLoaded);
}
ngOnInit(): void {
......@@ -53,6 +63,24 @@ export class DatasetComponent extends AbstractSearchComponent {
super.ngOnInit();
}
/**
* Checks if authentication is enabled.
*
* @return boolean
*/
getAuthenticationEnabled(): boolean {
return this.config.authenticationEnabled;
}
/**
* Returns admin roles list
*
* @returns string[]
*/
getAdminRoles(): string[] {
return this.config.adminRoles;
}
ngOnDestroy(): void {
if (this.datasetSelectedSubscription) this.datasetSelectedSubscription.unsubscribe();
super.ngOnDestroy();
......
......@@ -5,7 +5,8 @@
</a>
<a *ngIf="instance" routerLink="/instance/{{ instance.name }}" class="navbar-brand">
<img [src]="getLogoHref() | authImage | async" alt="Instance logo" />
<img *ngIf="instance.public" [src]="getLogoHref()" alt="Instance logo" />
<img *ngIf="!instance.public" [src]="getLogoHref() | authImage | async" alt="Instance logo" />
</a>
<!-- Right Navigation -->
......
......@@ -10,13 +10,16 @@
import { Pipe, PipeTransform } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { lastValueFrom } from 'rxjs';
@Pipe({ name: 'authImage' })
export class AuthImagePipe implements PipeTransform {
constructor(private http: HttpClient) { }
async transform(src: string): Promise<string> {
try {
const imageBlob = await this.http.get(src, { responseType: 'blob' }).toPromise();
const get$ = this.http.get(src, { responseType: 'blob' });
const imageBlob = await lastValueFrom(get$);
const reader = new FileReader();
return new Promise((resolve, reject) => {
reader.onloadend = () => resolve(reader.result as string);
......
......@@ -35,7 +35,7 @@ services:
SSO_AUTH_URL: "http://localhost:8180/auth"
SSO_REALM: "anis"
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_ADMIN_ROLES: anis_admin,superuser
RMQ_HOST: rmq
......
......@@ -71,35 +71,12 @@ final class DatasetListByInstanceAction extends AbstractAction
}
if ($request->getMethod() === GET) {
$token = $request->getAttribute('token');
$qb = $this->em->createQueryBuilder();
$qb->select('d')
->from('App\Entity\Dataset', 'd')
->join('d.datasetFamily', 'f')
->where($qb->expr()->eq('IDENTITY(f.instance)', ':instanceName'));
if (boolval($this->settings['enabled'])) {
if (!$token) {
// If user is not connected return public datasets
$qb->andWhere($qb->expr()->eq('d.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('d.public', 'true'));
$qb2 = $this->em->createQueryBuilder();
$qb2->select('d2.name')
->from('App\Entity\DatasetGroup', 'g')
->join('g.datasets', 'd2')
->where($qb2->expr()->in('g.role', $roles));
$qb->orWhere($qb->expr()->in('d.name', $qb2->getDQL()));
}
}
}
$qb->setParameter('instanceName', $instance->getName());
$datasets = $qb->getQuery()->getResult();
$payload = json_encode($datasets);
......
......@@ -64,7 +64,6 @@ final class InstanceListAction extends AbstractAction
if ($request->getMethod() === GET) {
$instances = $this->em->getRepository('App\Entity\Instance')->findAll();
//$instances = $this->getInstanceList($request->getAttribute('token'));
$payload = json_encode($instances);
}
......@@ -90,35 +89,6 @@ final class InstanceListAction extends AbstractAction
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
*
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment