Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
anis
anis-next
Commits
5e02f891
Commit
5e02f891
authored
Apr 25, 2022
by
François Agneray
Browse files
Instance and dataset private => done
parent
cb6185ab
Changes
13
Hide whitespace changes
Inline
Side-by-side
client/src/app/instance/home/components/welcome.component.html
View file @
5e02f891
<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>
client/src/app/instance/instance.component.ts
View file @
5e02f891
...
...
@@ -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
;
}
...
...
client/src/app/instance/search/components/dataset/dataset-card.component.html
View file @
5e02f891
...
...
@@ -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>
...
...
client/src/app/instance/search/components/dataset/dataset-card.component.ts
View file @
5e02f891
...
...
@@ -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.
*
...
...
client/src/app/instance/search/components/dataset/dataset-tabs.component.html
View file @
5e02f891
...
...
@@ -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>
...
...
client/src/app/instance/search/components/dataset/dataset-tabs.component.ts
View file @
5e02f891
...
...
@@ -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
[];
}
client/src/app/instance/search/containers/dataset.component.html
View file @
5e02f891
<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"
>
...
...
client/src/app/instance/search/containers/dataset.component.ts
View file @
5e02f891
...
...
@@ -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
();
...
...
client/src/app/shared/components/navbar.component.html
View file @
5e02f891
...
...
@@ -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 -->
...
...
client/src/app/shared/pipes/auth-image.pipe.ts
View file @
5e02f891
...
...
@@ -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
);
...
...
docker-compose.yml
View file @
5e02f891
...
...
@@ -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
...
...
server/src/Action/DatasetListByInstanceAction.php
View file @
5e02f891
...
...
@@ -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
);
...
...
server/src/Action/InstanceListAction.php
View file @
5e02f891
...
...
@@ -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
*
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment