Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
7
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Open sidebar
anis
anis-client
Commits
9d5fa6a3
Commit
9d5fa6a3
authored
Nov 20, 2020
by
Tifenn Guillas
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add comments and fix tests
parent
0fd4ad19
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
232 additions
and
41 deletions
+232
-41
karma.conf.js
karma.conf.js
+6
-2
src/app/auth/auth.action.spec.ts
src/app/auth/auth.action.spec.ts
+34
-0
src/app/auth/auth.action.ts
src/app/auth/auth.action.ts
+31
-3
src/app/auth/auth.effects.ts
src/app/auth/auth.effects.ts
+19
-4
src/app/auth/auth.reducer.spec.ts
src/app/auth/auth.reducer.spec.ts
+41
-0
src/app/auth/auth.reducer.ts
src/app/auth/auth.reducer.ts
+2
-6
src/app/auth/auth.selector.spec.ts
src/app/auth/auth.selector.spec.ts
+10
-0
src/app/auth/auth.service.ts
src/app/auth/auth.service.ts
+43
-10
src/app/auth/config-auth.ts
src/app/auth/config-auth.ts
+29
-14
src/app/auth/token-interceptor.ts
src/app/auth/token-interceptor.ts
+17
-2
No files found.
karma.conf.js
View file @
9d5fa6a3
...
...
@@ -8,7 +8,7 @@ module.exports = function (config) {
plugins
:
[
require
(
'
karma-jasmine
'
),
require
(
'
karma-chrome-launcher
'
),
require
(
'
karma-spec-reporter
'
),
//
require('karma-spec-reporter'),
require
(
'
karma-jasmine-html-reporter
'
),
require
(
'
karma-coverage-istanbul-reporter
'
),
require
(
'
@angular-devkit/build-angular/plugins/karma
'
)
...
...
@@ -21,7 +21,11 @@ module.exports = function (config) {
reports
:
[
'
html
'
,
'
lcovonly
'
,
'
text-summary
'
],
fixWebpackSourcePaths
:
true
},
reporters
:
[
'
progress
'
,
'
kjhtml
'
,
'
spec
'
],
reporters
:
[
'
progress
'
,
'
kjhtml
'
,
// 'spec'
],
port
:
9876
,
colors
:
true
,
logLevel
:
config
.
LOG_INFO
,
...
...
src/app/auth/auth.action.spec.ts
View file @
9d5fa6a3
import
*
as
authActions
from
'
./auth.action
'
;
describe
(
'
[Auth] Action
'
,
()
=>
{
it
(
'
should create CheckAuthAction
'
,
()
=>
{
const
action
=
new
authActions
.
CheckAuthAction
();
expect
(
action
.
type
).
toEqual
(
authActions
.
CHECK_AUTH
);
});
it
(
'
should create CheckAuthCompleteAction
'
,
()
=>
{
const
action
=
new
authActions
.
CheckAuthCompleteAction
(
true
);
expect
(
action
.
type
).
toEqual
(
authActions
.
CHECK_AUTH_COMPLETE
);
expect
(
action
.
payload
).
toBeTruthy
();
});
it
(
'
should create LoginAction
'
,
()
=>
{
const
action
=
new
authActions
.
LoginAction
();
expect
(
action
.
type
).
toEqual
(
authActions
.
LOGIN
);
});
it
(
'
should create LoginCompleteAction
'
,
()
=>
{
const
action
=
new
authActions
.
LoginCompleteAction
(
'
toto
'
);
expect
(
action
.
type
).
toEqual
(
authActions
.
LOGIN_COMPLETE
);
expect
(
action
.
payload
).
toEqual
(
'
toto
'
);
});
it
(
'
should create LogoutAction
'
,
()
=>
{
const
action
=
new
authActions
.
LogoutAction
();
expect
(
action
.
type
).
toEqual
(
authActions
.
LOGOUT
);
});
it
(
'
should create OpenEditProfileAction
'
,
()
=>
{
const
action
=
new
authActions
.
OpenEditProfileAction
();
expect
(
action
.
type
).
toEqual
(
authActions
.
OPEN_EDIT_PROFILE
);
});
it
(
'
should create ParseJwtAction
'
,
()
=>
{
const
action
=
new
authActions
.
ParseJwtAction
(
'
toto
'
);
expect
(
action
.
type
).
toEqual
(
authActions
.
PARSE_JWT
);
expect
(
action
.
payload
).
toEqual
(
'
toto
'
);
});
it
(
'
should create LoadUserRolesAction
'
,
()
=>
{
const
action
=
new
authActions
.
LoadUserRolesAction
([
'
toto
'
]);
expect
(
action
.
type
).
toEqual
(
authActions
.
LOAD_USER_ROLES
);
expect
(
action
.
payload
).
toEqual
([
'
toto
'
]);
});
});
src/app/auth/auth.action.ts
View file @
9d5fa6a3
...
...
@@ -9,22 +9,31 @@
import
{
Action
}
from
'
@ngrx/store
'
;
export
const
CHECK_AUTH
=
'
[Auth]
c
heck Auth
'
;
export
const
CHECK_AUTH
=
'
[Auth]
C
heck Auth
'
;
export
const
CHECK_AUTH_COMPLETE
=
'
[Auth] Check Auth Complete
'
;
export
const
LOGIN
=
'
[Auth] Login
'
;
export
const
LOGIN_COMPLETE
=
'
[Auth] Login Complete
'
;
export
const
LOGOUT
=
'
[Auth] Logout
'
;
export
const
OPEN_EDIT_PROFILE
=
'
[Auth] Edit Profile
'
;
export
const
GET_USER_ROLES
=
'
[Auth] Get User Roles
'
;
export
const
PARSE_JWT
=
'
[Auth] Parse JWT
'
;
export
const
LOAD_USER_ROLES
=
'
[Auth] Load User Roles
'
;
/**
* @class
* @classdesc CheckAuthAction action.
* @readonly
*/
export
class
CheckAuthAction
implements
Action
{
readonly
type
=
CHECK_AUTH
;
constructor
(
public
payload
:
{}
=
null
)
{
}
}
/**
* @class
* @classdesc CheckAuthCompleteAction action.
* @readonly
*/
export
class
CheckAuthCompleteAction
implements
Action
{
readonly
type
=
CHECK_AUTH_COMPLETE
;
...
...
@@ -42,13 +51,17 @@ export class LoginAction implements Action {
constructor
(
public
payload
:
string
=
null
)
{
}
}
/**
* @class
* @classdesc LoginCompleteAction action.
* @readonly
*/
export
class
LoginCompleteAction
implements
Action
{
readonly
type
=
LOGIN_COMPLETE
;
constructor
(
public
payload
:
any
)
{
}
}
/**
* @class
* @classdesc LogoutAction action.
...
...
@@ -60,18 +73,33 @@ export class LogoutAction implements Action {
constructor
(
public
payload
:
{}
=
null
)
{
}
}
/**
* @class
* @classdesc OpenEditProfileAction action.
* @readonly
*/
export
class
OpenEditProfileAction
implements
Action
{
readonly
type
=
OPEN_EDIT_PROFILE
;
constructor
(
public
payload
:
{}
=
null
)
{
}
}
/**
* @class
* @classdesc ParseJwtAction action.
* @readonly
*/
export
class
ParseJwtAction
implements
Action
{
readonly
type
=
PARSE_JWT
;
constructor
(
public
payload
:
string
)
{
}
}
/**
* @class
* @classdesc LoadUserRolesAction action.
* @readonly
*/
export
class
LoadUserRolesAction
implements
Action
{
readonly
type
=
LOAD_USER_ROLES
;
...
...
src/app/auth/auth.effects.ts
View file @
9d5fa6a3
...
...
@@ -10,9 +10,9 @@
import
{
Injectable
}
from
'
@angular/core
'
;
import
{
Router
}
from
'
@angular/router
'
;
import
{
Effect
,
Actions
,
ofType
}
from
'
@ngrx/effects
'
;
import
{
of
}
from
'
rxjs
'
;
import
{
switchMap
,
map
,
tap
}
from
'
rxjs/operators
'
;
import
{
Effect
,
Actions
,
ofType
}
from
'
@ngrx/effects
'
;
import
jwt_decode
from
'
jwt-decode
'
;
import
*
as
authActions
from
'
./auth.action
'
;
...
...
@@ -32,7 +32,7 @@ export class AuthEffects {
)
{
}
/**
* Execute
L
ogin (OIDC Login)
* Execute
s l
og
in (OIDC Login)
.
*/
@
Effect
({
dispatch
:
false
})
loginAction$
=
this
.
actions$
.
pipe
(
...
...
@@ -46,6 +46,9 @@ export class AuthEffects {
})
);
/**
* Checks if user is authenticated.
*/
@
Effect
()
checkAuthAction$
=
this
.
actions$
.
pipe
(
ofType
(
authActions
.
CHECK_AUTH
),
...
...
@@ -58,12 +61,15 @@ export class AuthEffects {
)
);
/**
* Completes authentication if not complete.
*/
@
Effect
()
checkAuthCompleteAction$
=
this
.
actions$
.
pipe
(
ofType
(
authActions
.
CHECK_AUTH_COMPLETE
),
switchMap
((
action
:
authActions
.
CheckAuthCompleteAction
)
=>
{
const
isAuthenticated
=
action
.
payload
;
if
(
isAuthenticated
)
{
return
this
.
authService
.
userData
.
pipe
(
switchMap
((
profile
)
=>
[
...
...
@@ -77,6 +83,9 @@ export class AuthEffects {
})
);
/**
* Redirects user after login completes.
*/
@
Effect
({
dispatch
:
false
})
loginCompleteAction$
=
this
.
actions$
.
pipe
(
ofType
(
authActions
.
LOGIN_COMPLETE
),
...
...
@@ -89,6 +98,9 @@ export class AuthEffects {
})
);
/**
* Parses token to get user roles.
*/
@
Effect
()
parseJWTAction$
=
this
.
actions$
.
pipe
(
ofType
(
authActions
.
PARSE_JWT
),
...
...
@@ -102,6 +114,9 @@ export class AuthEffects {
})
);
/**
* Executes log out.
*/
@
Effect
({
dispatch
:
false
})
logoutAction$
=
this
.
actions$
.
pipe
(
ofType
(
authActions
.
LOGOUT
),
...
...
@@ -112,7 +127,7 @@ export class AuthEffects {
* Opens edit profile page.
*/
@
Effect
({
dispatch
:
false
})
O
penEditProfileAction$
=
this
.
actions$
.
pipe
(
o
penEditProfileAction$
=
this
.
actions$
.
pipe
(
ofType
(
authActions
.
OPEN_EDIT_PROFILE
),
tap
(
_
=>
window
.
open
(
environment
.
ssoAuthUrl
+
'
/account
'
,
'
_blank
'
))
);
...
...
src/app/auth/auth.reducer.spec.ts
View file @
9d5fa6a3
...
...
@@ -10,6 +10,40 @@ describe('[Auth] Reducer', () => {
expect
(
state
).
toBe
(
initialState
);
});
it
(
'
should set isAuthenticated to true and set userProfile
'
,
()
=>
{
const
{
initialState
}
=
fromAuth
;
const
action
=
new
authActions
.
LoginCompleteAction
(
'
toto
'
);
const
state
=
fromAuth
.
reducer
(
initialState
,
action
);
expect
(
state
.
isAuthenticated
).
toBeTruthy
();
expect
(
state
.
userProfile
).
toEqual
(
'
toto
'
);
expect
(
state
.
userRoles
.
length
).
toEqual
(
0
);
expect
(
state
).
not
.
toEqual
(
initialState
);
});
it
(
'
should set userRoles
'
,
()
=>
{
const
{
initialState
}
=
fromAuth
;
const
action
=
new
authActions
.
LoadUserRolesAction
([
'
toto
'
]);
const
state
=
fromAuth
.
reducer
(
initialState
,
action
);
expect
(
state
.
isAuthenticated
).
toBeFalsy
();
expect
(
state
.
userProfile
).
toBeNull
();
expect
(
state
.
userRoles
.
length
).
toEqual
(
1
);
expect
(
state
.
userRoles
[
0
]).
toEqual
(
'
toto
'
);
expect
(
state
).
not
.
toEqual
(
initialState
);
});
it
(
'
should set isAuthenticated to false and unset userRoles
'
,
()
=>
{
const
initialState
=
{
...
fromAuth
.
initialState
,
isAuthenticated
:
true
,
userProfile
:
'
toto
'
};
const
action
=
new
authActions
.
LogoutAction
();
const
state
=
fromAuth
.
reducer
(
initialState
,
action
);
expect
(
state
.
isAuthenticated
).
toBeFalsy
();
expect
(
state
.
userProfile
).
toBeNull
();
expect
(
state
.
userRoles
.
length
).
toEqual
(
0
);
expect
(
state
).
not
.
toEqual
(
initialState
);
});
it
(
'
should get isAuthenticated
'
,
()
=>
{
const
action
=
{}
as
authActions
.
Actions
;
const
state
=
fromAuth
.
reducer
(
undefined
,
action
);
...
...
@@ -23,4 +57,11 @@ describe('[Auth] Reducer', () => {
expect
(
fromAuth
.
getUserProfile
(
state
)).
toBeNull
();
});
it
(
'
should get userRoles
'
,
()
=>
{
const
action
=
{}
as
authActions
.
Actions
;
const
state
=
fromAuth
.
reducer
(
undefined
,
action
);
expect
(
fromAuth
.
getUserRoles
(
state
).
length
).
toEqual
(
0
);
});
});
src/app/auth/auth.reducer.ts
View file @
9d5fa6a3
...
...
@@ -37,20 +37,16 @@ export const initialState: State = {
export
function
reducer
(
state
:
State
=
initialState
,
action
:
actions
.
Actions
):
State
{
switch
(
action
.
type
)
{
case
actions
.
LOGIN_COMPLETE
:
const
userProfile
=
action
.
payload
as
any
;
return
{
...
state
,
isAuthenticated
:
true
,
userProfile
userProfile
:
action
.
payload
};
case
actions
.
LOAD_USER_ROLES
:
const
userRoles
=
action
.
payload
;
return
{
...
state
,
userRoles
userRoles
:
action
.
payload
};
case
actions
.
LOGOUT
:
...
...
src/app/auth/auth.selector.spec.ts
View file @
9d5fa6a3
...
...
@@ -7,8 +7,18 @@ describe('[Auth] Selector', () => {
expect
(
authSelector
.
isAuthenticated
(
state
)).
toBeFalsy
();
});
it
(
'
should get isAuthenticated
'
,
()
=>
{
const
state
=
{
auth
:
{
...
fromAuth
.
initialState
}};
expect
(
authSelector
.
isAuthenticated
(
state
)).
toBeFalsy
();
});
it
(
'
should get userProfile
'
,
()
=>
{
const
state
=
{
auth
:
{
...
fromAuth
.
initialState
}};
expect
(
authSelector
.
getUserProfile
(
state
)).
toBeNull
();
});
it
(
'
should get userRoles
'
,
()
=>
{
const
state
=
{
auth
:
{
...
fromAuth
.
initialState
}};
expect
(
authSelector
.
getUserRoles
(
state
).
length
).
toEqual
(
0
);
});
});
\ No newline at end of file
src/app/auth/auth.service.ts
View file @
9d5fa6a3
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import
{
Injectable
}
from
'
@angular/core
'
;
import
{
of
}
from
'
rxjs
'
;
import
{
Observable
,
of
}
from
'
rxjs
'
;
import
{
OidcSecurityService
}
from
'
angular-auth-oidc-client
'
;
import
{
environment
}
from
'
src/environments/environment
'
;
import
{
environment
}
from
'
../../environments/environment
'
;
@
Injectable
({
providedIn
:
'
root
'
})
/**
* @class
* @classdesc Authentication service.
*/
export
class
AuthService
{
constructor
(
private
oidcSecurityService
:
OidcSecurityService
)
{
}
get
isLoggedIn
()
{
return
this
.
oidcSecurityService
.
isAuthenticated$
;
}
/**
* Gets user data.
*
* @return Observable<any>
*/
get
token
()
{
if
(
environment
.
ssoName
===
'
auth0
'
)
{
return
this
.
oidcSecurityService
.
getIdToken
();
...
...
@@ -20,19 +35,37 @@ export class AuthService {
}
}
get
userData
()
{
/**
* Gets user data.
*
* @return Observable<any>
*/
get
userData
():
Observable
<
any
>
{
return
this
.
oidcSecurityService
.
userData$
;
}
checkAuth
()
{
/**
* Checks authentication.
*
* @return Observable<boolean>
*/
checkAuth
():
Observable
<
boolean
>
{
return
this
.
oidcSecurityService
.
checkAuthIncludingServer
();
}
doLogin
()
{
/**
* Logs in user.
*
* @return Observable<void>
*/
doLogin
():
Observable
<
void
>
{
return
of
(
this
.
oidcSecurityService
.
authorize
());
}
signOut
()
{
/**
* Logs out user.
*/
signOut
():
void
{
this
.
oidcSecurityService
.
logoffAndRevokeTokens
();
}
}
src/app/auth/config-auth.ts
View file @
9d5fa6a3
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import
{
OidcConfigService
,
LogLevel
}
from
'
angular-auth-oidc-client
'
;
import
{
environment
}
from
'
../../environments/environment
'
;
export
function
configureAuth
(
oidcConfigService
:
OidcConfigService
)
{
return
()
=>
oidcConfigService
.
withConfig
({
stsServer
:
environment
.
ssoAuthUrl
,
redirectUrl
:
window
.
location
.
origin
,
postLogoutRedirectUri
:
window
.
location
.
origin
,
clientId
:
environment
.
ssoClientId
,
scope
:
'
openid profile email offline_access
'
,
responseType
:
'
code
'
,
silentRenew
:
true
,
silentRenewUrl
:
`
${
window
.
location
.
origin
}
/silent-renew.html`
,
renewTimeBeforeTokenExpiresInSeconds
:
10
,
logLevel
:
LogLevel
.
None
,
});
/**
* Adds user token to the request.
*
* @param {OidcConfigService} oidcConfigService - The single sign on service.
*
* @return any
*/
export
function
configureAuth
(
oidcConfigService
:
OidcConfigService
):
any
{
return
()
=>
oidcConfigService
.
withConfig
({
stsServer
:
environment
.
ssoAuthUrl
,
redirectUrl
:
window
.
location
.
origin
,
postLogoutRedirectUri
:
window
.
location
.
origin
,
clientId
:
environment
.
ssoClientId
,
scope
:
'
openid profile email offline_access
'
,
responseType
:
'
code
'
,
silentRenew
:
true
,
silentRenewUrl
:
`
${
window
.
location
.
origin
}
/silent-renew.html`
,
renewTimeBeforeTokenExpiresInSeconds
:
10
,
logLevel
:
LogLevel
.
None
,
});
}
src/app/auth/token-interceptor.ts
View file @
9d5fa6a3
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import
{
Injectable
}
from
'
@angular/core
'
;
import
{
HttpRequest
,
HttpHandler
,
HttpEvent
,
HttpInterceptor
}
from
'
@angular/common/http
'
;
...
...
@@ -16,9 +25,16 @@ import { environment } from '../../environments/environment';
export
class
TokenInterceptor
implements
HttpInterceptor
{
constructor
(
private
authService
:
AuthService
)
{
}
/**
* Adds user token to the request.
*
* @param {HttpRequest<any>} request - The outgoing request.
* @param {HttpHandler} next - The next interceptor.
*
* @return Observable<HttpEvent<any>>
*/
intercept
(
request
:
HttpRequest
<
any
>
,
next
:
HttpHandler
):
Observable
<
HttpEvent
<
any
>>
{
let
requestToForward
=
request
;
const
token
=
this
.
authService
.
token
;
const
isApiUrl
=
request
.
url
.
startsWith
(
environment
.
apiUrl
);
if
(
token
!==
''
&&
isApiUrl
)
{
...
...
@@ -28,7 +44,6 @@ export class TokenInterceptor implements HttpInterceptor {
}
});
}
return
next
.
handle
(
requestToForward
);
}
}
Write
Preview
Markdown
is supported
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