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-admin
Commits
da7876b6
Commit
da7876b6
authored
Nov 12, 2020
by
François Agneray
Browse files
Keycloak integration => ok
parent
1e9158e5
Changes
17
Hide whitespace changes
Inline
Side-by-side
src/app/app.routing.ts
View file @
da7876b6
import
{
NgModule
}
from
'
@angular/core
'
;
import
{
Routes
,
RouterModule
}
from
'
@angular/router
'
;
import
{
LoginComponent
}
from
'
./core/containers/login.component
'
;
import
{
NotFoundPageComponent
}
from
'
./core/containers/not-found-page.component
'
;
import
{
UnauthorizedComponent
}
from
'
./core/containers/unauthorized.component
'
;
const
routes
:
Routes
=
[
{
path
:
''
,
redirectTo
:
'
instance-list
'
,
pathMatch
:
'
full
'
},
{
path
:
'
login
'
,
component
:
LoginComponent
},
{
path
:
'
unauthorized
'
,
component
:
UnauthorizedComponent
},
{
path
:
''
,
redirectTo
:
'
login
'
,
pathMatch
:
'
full
'
},
{
path
:
'
**
'
,
component
:
NotFoundPageComponent
}
];
...
...
src/app/auth/init.keycloak.ts
View file @
da7876b6
...
...
@@ -23,7 +23,9 @@ function initializeKeycloak(keycloak: KeycloakService, store: Store<{ keycloak:
clientId
:
environment
.
ssoClientId
,
},
initOptions
:
{
onLoad
:
'
login-required
'
onLoad
:
'
check-sso
'
,
silentCheckSsoRedirectUri
:
window
.
location
.
origin
+
'
/assets/silent-check-sso.html
'
},
loadUserProfileAtStartUp
:
true
});
...
...
src/app/auth/store/auth.action.ts
View file @
da7876b6
...
...
@@ -6,6 +6,7 @@ export const LOGIN = '[Auth] Login';
export
const
LOGOUT
=
'
[Auth] Logout
'
;
export
const
AUTH_SUCCESS
=
'
[Auth] Auth Success
'
;
export
const
LOAD_USER_PROFILE_SUCCESS
=
'
[Auth] Load User Profile Success
'
;
export
const
LOAD_USER_ROLES_SUCCESS
=
'
[Auth] Load User Roles Success
'
;
export
const
OPEN_EDIT_PROFILE
=
'
[Auth] Edit Profile
'
;
export
class
LoginAction
implements
Action
{
...
...
@@ -32,6 +33,12 @@ export class LoadUserProfileSuccessAction implements Action {
constructor
(
public
payload
:
UserProfile
)
{
}
}
export
class
LoadUserRolesSuccessAction
implements
Action
{
readonly
type
=
LOAD_USER_ROLES_SUCCESS
;
constructor
(
public
payload
:
string
[])
{
}
}
export
class
OpenEditProfileAction
implements
Action
{
readonly
type
=
OPEN_EDIT_PROFILE
;
...
...
@@ -43,4 +50,5 @@ export type Actions
|
LogoutAction
|
AuthSuccessAction
|
LoadUserProfileSuccessAction
|
LoadUserRolesSuccessAction
|
OpenEditProfileAction
;
src/app/auth/store/auth.effects.ts
View file @
da7876b6
...
...
@@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
import
{
from
}
from
'
rxjs
'
;
import
{
switchMap
,
map
,
tap
}
from
'
rxjs/operators
'
;
import
{
Action
}
from
'
@ngrx/store
'
;
import
{
Effect
,
Actions
,
ofType
}
from
'
@ngrx/effects
'
;
import
{
KeycloakService
}
from
'
keycloak-angular
'
;
...
...
@@ -24,7 +25,7 @@ export class AuthEffects {
@
Effect
({
dispatch
:
false
})
logoutAction$
=
this
.
actions$
.
pipe
(
ofType
(
authActions
.
LOGOUT
),
tap
(
_
=>
this
.
keycloak
.
logout
())
tap
(
_
=>
this
.
keycloak
.
logout
(
window
.
location
.
origin
+
'
/login
'
))
);
@
Effect
()
...
...
@@ -32,7 +33,10 @@ export class AuthEffects {
ofType
(
authActions
.
AUTH_SUCCESS
),
switchMap
(
_
=>
from
(
this
.
keycloak
.
loadUserProfile
()).
pipe
(
map
(
userProfile
=>
new
authActions
.
LoadUserProfileSuccessAction
(
userProfile
))
switchMap
(
userProfile
=>
[
new
authActions
.
LoadUserProfileSuccessAction
(
userProfile
),
new
authActions
.
LoadUserRolesSuccessAction
(
this
.
keycloak
.
getUserRoles
())
])
)
)
);
...
...
src/app/auth/store/auth.reducer.ts
View file @
da7876b6
import
*
as
actions
from
'
./auth.action
'
;
import
{
UserProfile
}
from
'
./user-profile.model
'
;
export
interface
State
{
isAuthenticated
:
boolean
;
userProfile
:
any
;
userProfile
:
UserProfile
;
userRoles
:
string
[];
}
export
const
initialState
:
State
=
{
isAuthenticated
:
false
,
userProfile
:
null
userProfile
:
null
,
userRoles
:
[]
};
export
function
reducer
(
state
:
State
=
initialState
,
action
:
actions
.
Actions
):
State
{
...
...
@@ -24,7 +28,15 @@ export function reducer(state: State = initialState, action: actions.Actions): S
return
{
...
state
,
userProfile
}
};
case
actions
.
LOAD_USER_ROLES_SUCCESS
:
const
userRoles
=
action
.
payload
;
return
{
...
state
,
userRoles
};
default
:
return
state
;
...
...
@@ -33,3 +45,4 @@ export function reducer(state: State = initialState, action: actions.Actions): S
export
const
isAuthenticated
=
(
state
:
State
)
=>
state
.
isAuthenticated
;
export
const
getUserProfile
=
(
state
:
State
)
=>
state
.
userProfile
;
export
const
getUserRoles
=
(
state
:
State
)
=>
state
.
userRoles
;
src/app/auth/store/auth.selector.ts
View file @
da7876b6
...
...
@@ -2,14 +2,19 @@ import { createSelector, createFeatureSelector } from '@ngrx/store';
import
*
as
auth
from
'
./auth.reducer
'
;
export
const
getAuthtate
=
createFeatureSelector
<
auth
.
State
>
(
'
auth
'
);
export
const
getAuth
S
tate
=
createFeatureSelector
<
auth
.
State
>
(
'
auth
'
);
export
const
isAuthenticated
=
createSelector
(
getAuthtate
,
getAuth
S
tate
,
auth
.
isAuthenticated
);
export
const
getUserProfile
=
createSelector
(
getAuthtate
,
getAuth
S
tate
,
auth
.
getUserProfile
);
export
const
getUserRoles
=
createSelector
(
getAuthState
,
auth
.
getUserRoles
);
src/app/core/auth.guard.ts
View file @
da7876b6
...
...
@@ -23,20 +23,26 @@ export class AuthGuard extends KeycloakAuthGuard {
)
{
// Force the user to log in if currently unauthenticated.
if
(
!
this
.
authenticated
)
{
await
this
.
keycloak
.
login
({
redirectUri
:
window
.
location
.
origin
+
state
.
url
,
});
this
.
router
.
navigateByUrl
(
'
/login
'
);
}
// If authenticated but not admin go to unauthorized page.
if
(
!
this
.
roles
.
includes
(
'
anis_admin
'
))
{
this
.
router
.
navigateByUrl
(
'
/unauthorized
'
);
}
// Else return true;
return
true
;
// Get the roles required from the route.
const
requiredRoles
=
route
.
data
.
roles
;
//
const requiredRoles = route.data.roles;
// Allow the user to to proceed if no additional roles are required to access the route.
if
(
!
(
requiredRoles
instanceof
Array
)
||
requiredRoles
.
length
===
0
)
{
/*
if (!(requiredRoles instanceof Array) || requiredRoles.length === 0) {
return true;
}
}
*/
// Allow the user to proceed if all the required roles are present.
return
requiredRoles
.
every
((
role
)
=>
this
.
roles
.
includes
(
role
));
//
return requiredRoles.every((role) => this.roles.includes(role));
}
}
\ No newline at end of file
src/app/core/containers/app.component.html
View file @
da7876b6
<header>
<app-nav
[userProfile]=
"userProfile | async"
(logout)=
"logout()"
(openEditProfile)=
"openEditProfile()"
>
<app-nav
*ngIf=
"(isAuthenticated | async) && (isAnisAdmin() | async)"
[userProfile]=
"userProfile | async"
(logout)=
"logout()"
(openEditProfile)=
"openEditProfile()"
>
</app-nav>
</header>
<main
role=
"main"
class=
"pb-4"
>
<router-outlet></router-outlet>
</main>
<footer
class=
"footer mt-auto bg-light"
>
<footer
*ngIf=
"(isAuthenticated | async) && (isAnisAdmin() | async)"
class=
"footer mt-auto bg-light"
>
<div
class=
"container my-3"
>
<div
class=
"row justify-content-center mb-3"
>
<small>
©
ANIS 2014 - {{ year }}
</small>
...
...
src/app/core/containers/app.component.ts
View file @
da7876b6
...
...
@@ -2,6 +2,7 @@ import { Component, ViewEncapsulation } from '@angular/core';
import
{
Store
}
from
'
@ngrx/store
'
;
import
{
Observable
}
from
'
rxjs
'
;
import
{
map
}
from
'
rxjs/operators
'
;
import
*
as
fromAuth
from
'
../../auth/store/auth.reducer
'
;
import
*
as
authActions
from
'
../../auth/store/auth.action
'
;
...
...
@@ -21,11 +22,13 @@ export class AppComponent {
anisClientVersion
:
string
=
VERSIONS
.
anisClient
;
year
=
(
new
Date
()).
getFullYear
();
isAuthenticated
:
Observable
<
boolean
>
;
userProfile
:
Observable
<
UserProfile
>
userProfile
:
Observable
<
UserProfile
>
;
userRoles
:
Observable
<
string
[]
>
;
constructor
(
private
store
:
Store
<
{
auth
:
fromAuth
.
State
}
>
)
{
this
.
isAuthenticated
=
store
.
select
(
authSelector
.
isAuthenticated
);
this
.
userProfile
=
store
.
select
(
authSelector
.
getUserProfile
);
this
.
userRoles
=
store
.
select
(
authSelector
.
getUserRoles
);
}
logout
():
void
{
...
...
@@ -35,4 +38,10 @@ export class AppComponent {
openEditProfile
():
void
{
this
.
store
.
dispatch
(
new
authActions
.
OpenEditProfileAction
());
}
isAnisAdmin
()
{
return
this
.
userRoles
.
pipe
(
map
(
roles
=>
roles
.
includes
(
'
anis_admin
'
))
);
}
}
src/app/core/containers/login.component.html
0 → 100644
View file @
da7876b6
<div
class=
"text-center"
>
<img
class=
"mb-4"
src=
"assets/anis_adminsi.png"
alt=
""
>
<p
class=
"text-center"
>
You must be logged in and have the right privileges to access the administration interface.
</p>
<p
class=
"text-center"
><button
type=
"button"
class=
"btn btn-outline-primary"
(click)=
"login()"
>
Sign in
</button></p>
<p
class=
"mt-5 mb-3 text-muted"
><small>
©
ANIS ADMIN 2014 - {{ year }}
</small></p>
</div>
src/app/core/containers/login.component.ts
0 → 100644
View file @
da7876b6
import
{
Component
,
OnInit
}
from
'
@angular/core
'
;
import
{
Router
}
from
'
@angular/router
'
;
import
{
Store
}
from
'
@ngrx/store
'
;
import
{
Observable
}
from
'
rxjs
'
;
import
*
as
fromAuth
from
'
../../auth/store/auth.reducer
'
;
import
*
as
authActions
from
'
../../auth/store/auth.action
'
;
import
*
as
authSelector
from
'
../../auth/store/auth.selector
'
;
@
Component
({
selector
:
'
app-login
'
,
templateUrl
:
'
login.component.html
'
})
export
class
LoginComponent
implements
OnInit
{
year
=
(
new
Date
()).
getFullYear
();
public
isAuthenticated
:
Observable
<
boolean
>
;
constructor
(
private
store
:
Store
<
{
auth
:
fromAuth
.
State
}
>
,
private
router
:
Router
)
{
this
.
isAuthenticated
=
store
.
select
(
authSelector
.
isAuthenticated
);
}
ngOnInit
()
{
this
.
isAuthenticated
.
subscribe
(
isAuthenticated
=>
(
isAuthenticated
)
?
this
.
router
.
navigateByUrl
(
'
/instance-list
'
)
:
null
);
}
login
():
void
{
this
.
store
.
dispatch
(
new
authActions
.
LoginAction
());
}
}
src/app/core/containers/unauthorized.component.html
0 → 100644
View file @
da7876b6
<div
class=
"text-center"
>
<img
class=
"mb-4"
src=
"assets/anis_adminsi.png"
alt=
""
>
<p>
You are not authorized to navigate to this interface (403).
<br
/>
Please contact the administrator to increase your access rights.
</p>
<p
class=
"mt-5 mb-3 text-muted"
><small>
©
ANIS ADMIN 2014 - {{ year }}
</small></p>
</div>
\ No newline at end of file
src/app/core/containers/unauthorized.component.ts
0 → 100644
View file @
da7876b6
import
{
Component
}
from
'
@angular/core
'
;
@
Component
({
selector
:
'
app-unauthorized
'
,
templateUrl
:
'
unauthorized.component.html
'
})
export
class
UnauthorizedComponent
{
year
=
(
new
Date
()).
getFullYear
();
}
src/app/core/core.module.ts
View file @
da7876b6
...
...
@@ -6,12 +6,14 @@ import { CollapseModule } from 'ngx-bootstrap/collapse';
import
{
BsDropdownModule
}
from
'
ngx-bootstrap/dropdown
'
;
import
{
AppComponent
}
from
'
./containers/app.component
'
;
import
{
LoginComponent
}
from
'
./containers/login.component
'
;
import
{
NotFoundPageComponent
}
from
'
./containers/not-found-page.component
'
;
import
{
NavComponent
}
from
'
./components/nav.component
'
;
import
{
throwIfAlreadyLoaded
}
from
'
./module-import-guard
'
;
export
const
COMPONENTS
=
[
AppComponent
,
LoginComponent
,
NotFoundPageComponent
,
NavComponent
];
...
...
src/app/metamodel/metamodel.routing.ts
View file @
da7876b6
...
...
@@ -18,28 +18,24 @@ import { AttributeComponent } from './containers/attribute/attribute.component';
import
{
AuthGuard
}
from
'
../core/auth.guard
'
;
const
routes
:
Routes
=
[
{
path
:
'
instance-list
'
,
canActivate
:
[
AuthGuard
],
component
:
InstanceComponent
},
{
path
:
'
new-instance
'
,
canActivate
:
[
AuthGuard
],
component
:
NewInstanceComponent
},
{
path
:
'
edit-instance/:iname
'
,
canActivate
:
[
AuthGuard
],
component
:
EditInstanceComponent
},
{
path
:
'
configure-instance/:iname
'
,
canActivate
:
[
AuthGuard
],
component
:
ConfigureInstanceComponent
},
{
path
:
'
configure-instance/:iname/new-dataset
'
,
canActivate
:
[
AuthGuard
],
component
:
NewDatasetComponent
},
{
path
:
'
configure-instance/:iname/edit-dataset/:dname
'
,
canActivate
:
[
AuthGuard
],
component
:
EditDatasetComponent
},
{
path
:
'
configure-instance/:iname/configure-dataset/:dname
'
,
canActivate
:
[
AuthGuard
],
component
:
AttributeComponent
},
{
path
:
''
,
canActivate
:
[
AuthGuard
],
data
:
{
roles
:
[
'
anis_admin
'
]},
children
:
[
{
path
:
'
instance-list
'
,
component
:
InstanceComponent
},
{
path
:
'
new-instance
'
,
component
:
NewInstanceComponent
},
{
path
:
'
edit-instance/:iname
'
,
component
:
EditInstanceComponent
},
{
path
:
'
configure-instance/:iname
'
,
component
:
ConfigureInstanceComponent
},
{
path
:
'
configure-instance/:iname/new-dataset
'
,
component
:
NewDatasetComponent
},
{
path
:
'
configure-instance/:iname/edit-dataset/:dname
'
,
component
:
EditDatasetComponent
},
{
path
:
'
configure-instance/:iname/configure-dataset/:dname
'
,
component
:
AttributeComponent
},
{
path
:
'
project
'
,
component
:
ProjectPageComponent
,
children
:
[
{
path
:
''
,
redirectTo
:
'
project-list
'
,
pathMatch
:
'
full
'
},
{
path
:
'
project-list
'
,
component
:
ProjectComponent
},
{
path
:
'
database-list
'
,
component
:
DatabaseComponent
}
]
},
{
path
:
'
new-project
'
,
component
:
NewProjectComponent
},
{
path
:
'
edit-project/:name
'
,
component
:
EditProjectComponent
},
{
path
:
'
new-database
'
,
component
:
NewDatabaseComponent
},
{
path
:
'
edit-database/:id
'
,
component
:
EditDatabaseComponent
}
path
:
'
project
'
,
component
:
ProjectPageComponent
,
canActivate
:
[
AuthGuard
],
children
:
[
{
path
:
''
,
redirectTo
:
'
project-list
'
,
pathMatch
:
'
full
'
},
{
path
:
'
project-list
'
,
component
:
ProjectComponent
},
{
path
:
'
database-list
'
,
component
:
DatabaseComponent
}
]
}
},
{
path
:
'
new-project
'
,
canActivate
:
[
AuthGuard
],
component
:
NewProjectComponent
},
{
path
:
'
edit-project/:name
'
,
canActivate
:
[
AuthGuard
],
component
:
EditProjectComponent
},
{
path
:
'
new-database
'
,
canActivate
:
[
AuthGuard
],
component
:
NewDatabaseComponent
},
{
path
:
'
edit-database/:id
'
,
canActivate
:
[
AuthGuard
],
component
:
EditDatabaseComponent
}
];
@
NgModule
({
...
...
src/app/settings/settings.routing.ts
View file @
da7876b6
...
...
@@ -2,10 +2,11 @@ import { NgModule } from '@angular/core';
import
{
Routes
,
RouterModule
}
from
'
@angular/router
'
;
import
{
SettingsComponent
}
from
'
./containers/settings.component
'
;
import
{
AuthGuard
}
from
'
../core/auth.guard
'
;
const
routes
:
Routes
=
[
{
path
:
'
settings
'
,
component
:
SettingsComponent
},
{
path
:
'
settings/:select
'
,
component
:
SettingsComponent
}
{
path
:
'
settings
'
,
canActivate
:
[
AuthGuard
],
component
:
SettingsComponent
},
{
path
:
'
settings/:select
'
,
canActivate
:
[
AuthGuard
],
component
:
SettingsComponent
}
];
@
NgModule
({
...
...
src/assets/silent-check-sso.html
0 → 100644
View file @
da7876b6
<html>
<body>
<script>
parent
.
postMessage
(
location
.
href
,
location
.
origin
);
</script>
</body>
</html>
\ No newline at end of file
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