mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-08 10:34:15 +00:00
Improvement for authentication module
This commit is contained in:
@@ -12,7 +12,9 @@ import { Store } from '@ngrx/store';
|
|||||||
export class HomePageComponent implements OnInit {
|
export class HomePageComponent implements OnInit {
|
||||||
public isAuthenticated: Observable<boolean>;
|
public isAuthenticated: Observable<boolean>;
|
||||||
|
|
||||||
constructor(private store: Store<AppState>) {}
|
constructor(private store: Store<AppState>) {
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
// set loading
|
// set loading
|
||||||
this.isAuthenticated = this.store.select(isAuthenticated);
|
this.isAuthenticated = this.store.select(isAuthenticated);
|
||||||
|
@@ -1,14 +1,18 @@
|
|||||||
|
|
||||||
import { AuthType } from './auth-type';
|
import { AuthType } from './auth-type';
|
||||||
import { AuthStatus } from './models/auth-status.model';
|
|
||||||
import { GenericConstructor } from '../shared/generic-constructor';
|
import { GenericConstructor } from '../shared/generic-constructor';
|
||||||
import { DSpaceObject } from '../shared/dspace-object.model';
|
import { NormalizedAuthStatus } from './models/normalized-auth-status.model';
|
||||||
|
import { NormalizedDSpaceObject } from '../cache/models/normalized-dspace-object.model';
|
||||||
|
import { NormalizedEpersonModel } from '../eperson/models/NormalizedEperson.model';
|
||||||
|
|
||||||
export class AuthObjectFactory {
|
export class AuthObjectFactory {
|
||||||
public static getConstructor(type): GenericConstructor<DSpaceObject> {
|
public static getConstructor(type): GenericConstructor<NormalizedDSpaceObject> {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
case AuthType.Eperson: {
|
||||||
|
return NormalizedEpersonModel
|
||||||
|
}
|
||||||
|
|
||||||
case AuthType.Status: {
|
case AuthType.Status: {
|
||||||
return AuthStatus
|
return NormalizedAuthStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
|
@@ -6,7 +6,7 @@ import { GLOBAL_CONFIG } from '../../../config';
|
|||||||
import { GlobalConfig } from '../../../config/global-config.interface';
|
import { GlobalConfig } from '../../../config/global-config.interface';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { isNotEmpty } from '../../shared/empty.util';
|
import { isNotEmpty } from '../../shared/empty.util';
|
||||||
import { AuthPostRequest, PostRequest, RestRequest } from '../data/request.models';
|
import { AuthGetRequest, AuthPostRequest, PostRequest, RestRequest } from '../data/request.models';
|
||||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
||||||
import { AuthSuccessResponse, ErrorResponse, RestResponse } from '../cache/response-cache.models';
|
import { AuthSuccessResponse, ErrorResponse, RestResponse } from '../cache/response-cache.models';
|
||||||
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
|
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
|
||||||
@@ -28,16 +28,16 @@ export class AuthRequestService extends HALEndpointService {
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected submitRequest(request: RestRequest): Observable<any> {
|
protected fetchRequest(request: RestRequest): Observable<any> {
|
||||||
const [successResponse, errorResponse] = this.responseCache.get(request.href)
|
const [successResponse, errorResponse] = this.responseCache.get(request.href)
|
||||||
.map((entry: ResponseCacheEntry) => entry.response)
|
.map((entry: ResponseCacheEntry) => entry.response)
|
||||||
.partition((response: RestResponse) => response.isSuccessful);
|
.partition((response: RestResponse) => response.isSuccessful);
|
||||||
return Observable.merge(
|
return Observable.merge(
|
||||||
errorResponse.flatMap((response: ErrorResponse) =>
|
errorResponse.flatMap((response: ErrorResponse) =>
|
||||||
Observable.throw(new Error(`Couldn't send data to server`))),
|
Observable.throw(new Error(response.errorMessage))),
|
||||||
successResponse
|
successResponse
|
||||||
.filter((response: AuthSuccessResponse) => isNotEmpty(response))
|
.filter((response: AuthSuccessResponse) => isNotEmpty(response))
|
||||||
.map((response: AuthSuccessResponse) => response.authResponse)
|
.map((response: AuthSuccessResponse) => response.response)
|
||||||
.distinctUntilChanged());
|
.distinctUntilChanged());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,8 +51,19 @@ export class AuthRequestService extends HALEndpointService {
|
|||||||
.map((endpointURL) => this.getEndpointByMethod(endpointURL, method))
|
.map((endpointURL) => this.getEndpointByMethod(endpointURL, method))
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.map((endpointURL: string) => new AuthPostRequest(this.requestService.generateRequestId(), endpointURL, body, options))
|
.map((endpointURL: string) => new AuthPostRequest(this.requestService.generateRequestId(), endpointURL, body, options))
|
||||||
.do((request: PostRequest) => this.requestService.configure(request))
|
.do((request: PostRequest) => this.requestService.configure(request, true))
|
||||||
.flatMap((request: PostRequest) => this.submitRequest(request))
|
.flatMap((request: PostRequest) => this.fetchRequest(request))
|
||||||
|
.distinctUntilChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getRequest(method: string, options?: HttpOptions): Observable<any> {
|
||||||
|
return this.getEndpoint()
|
||||||
|
.filter((href: string) => isNotEmpty(href))
|
||||||
|
.map((endpointURL) => this.getEndpointByMethod(endpointURL, method))
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.map((endpointURL: string) => new AuthGetRequest(this.requestService.generateRequestId(), endpointURL, options))
|
||||||
|
.do((request: PostRequest) => this.requestService.configure(request, true))
|
||||||
|
.flatMap((request: PostRequest) => this.fetchRequest(request))
|
||||||
.distinctUntilChanged();
|
.distinctUntilChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,8 @@ import { Inject, Injectable } from '@angular/core';
|
|||||||
import { AuthObjectFactory } from './auth-object-factory';
|
import { AuthObjectFactory } from './auth-object-factory';
|
||||||
import { BaseResponseParsingService } from '../data/base-response-parsing.service';
|
import { BaseResponseParsingService } from '../data/base-response-parsing.service';
|
||||||
import {
|
import {
|
||||||
|
AuthErrorResponse,
|
||||||
|
AuthStatusResponse,
|
||||||
AuthSuccessResponse, ConfigSuccessResponse, ErrorResponse,
|
AuthSuccessResponse, ConfigSuccessResponse, ErrorResponse,
|
||||||
RestResponse
|
RestResponse
|
||||||
} from '../cache/response-cache.models';
|
} from '../cache/response-cache.models';
|
||||||
@@ -11,10 +13,15 @@ import { ConfigObject } from '../shared/config/config.model';
|
|||||||
import { ConfigType } from '../shared/config/config-type';
|
import { ConfigType } from '../shared/config/config-type';
|
||||||
import { GLOBAL_CONFIG } from '../../../config';
|
import { GLOBAL_CONFIG } from '../../../config';
|
||||||
import { GlobalConfig } from '../../../config/global-config.interface';
|
import { GlobalConfig } from '../../../config/global-config.interface';
|
||||||
import { isNotEmpty } from '../../shared/empty.util';
|
import { isEmpty, isNotEmpty } from '../../shared/empty.util';
|
||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { ResponseParsingService } from '../data/parsing.service';
|
import { ResponseParsingService } from '../data/parsing.service';
|
||||||
import { RestRequest } from '../data/request.models';
|
import { RestRequest } from '../data/request.models';
|
||||||
|
import { AuthType } from './auth-type';
|
||||||
|
import { NormalizedObject } from '../cache/models/normalized-object.model';
|
||||||
|
import { AuthTokenInfo } from './models/auth-token-info.model';
|
||||||
|
import { NormalizedAuthStatus } from './models/normalized-auth-status.model';
|
||||||
|
import { AuthStatus } from './models/auth-status.model';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthResponseParsingService extends BaseResponseParsingService implements ResponseParsingService {
|
export class AuthResponseParsingService extends BaseResponseParsingService implements ResponseParsingService {
|
||||||
@@ -29,19 +36,14 @@ export class AuthResponseParsingService extends BaseResponseParsingService imple
|
|||||||
}
|
}
|
||||||
|
|
||||||
parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse {
|
parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse {
|
||||||
/*if (isNotEmpty(data.payload) && isNotEmpty(data.payload._links) && data.statusCode === '200') {
|
if (isNotEmpty(data.payload) && isNotEmpty(data.payload._links) && data.statusCode === '200') {
|
||||||
const configDefinition = this.process<ConfigObject,ConfigType>(data.payload, request.href);
|
const response = this.process<AuthStatus,AuthType>(data.payload, request.href);
|
||||||
return new ConfigSuccessResponse(configDefinition[Object.keys(configDefinition)[0]], data.statusCode, this.processPageInfo(data.payload.page));
|
return new AuthStatusResponse(response[Object.keys(response)[0]][0], data.statusCode);
|
||||||
|
} else if (isEmpty(data.payload) && isNotEmpty(data.headers.get('authorization')) && data.statusCode === '200') {
|
||||||
|
return new AuthSuccessResponse(new AuthTokenInfo(data.headers.get('authorization')), data.statusCode);
|
||||||
} else {
|
} else {
|
||||||
return new ErrorResponse(
|
return new AuthStatusResponse(data.payload as AuthStatus, data.statusCode);
|
||||||
Object.assign(
|
}
|
||||||
new Error('Unexpected response from config endpoint'),
|
|
||||||
{statusText: data.statusCode}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}*/
|
|
||||||
console.log(data);
|
|
||||||
return new AuthSuccessResponse(data.payload, data.statusCode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
34
src/app/core/auth/auth-storage.service.ts
Normal file
34
src/app/core/auth/auth-storage.service.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { Inject, Injectable, PLATFORM_ID } from '@angular/core';
|
||||||
|
import { isPlatformBrowser } from '@angular/common';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The auth service.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class AuthStorageService {
|
||||||
|
|
||||||
|
constructor(@Inject(PLATFORM_ID) private platformId: string) {}
|
||||||
|
|
||||||
|
public get(key: string): any {
|
||||||
|
let item = null;
|
||||||
|
if (isPlatformBrowser(this.platformId)) {
|
||||||
|
item = JSON.parse(localStorage.getItem(key));
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public store(key: string, item: any) {
|
||||||
|
if (isPlatformBrowser(this.platformId)) {
|
||||||
|
localStorage.setItem(key, JSON.stringify(item));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public remove(key: string) {
|
||||||
|
if (isPlatformBrowser(this.platformId)) {
|
||||||
|
localStorage.removeItem(key);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,3 +1,4 @@
|
|||||||
export enum AuthType {
|
export enum AuthType {
|
||||||
|
Eperson = 'eperson',
|
||||||
Status = 'status'
|
Status = 'status'
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,7 @@ import { type } from '../../shared/ngrx/type';
|
|||||||
|
|
||||||
// import models
|
// import models
|
||||||
import { Eperson } from '../eperson/models/eperson.model';
|
import { Eperson } from '../eperson/models/eperson.model';
|
||||||
|
import { AuthTokenInfo } from './models/auth-token-info.model';
|
||||||
|
|
||||||
export const AuthActionTypes = {
|
export const AuthActionTypes = {
|
||||||
AUTHENTICATE: type('dspace/auth/AUTHENTICATE'),
|
AUTHENTICATE: type('dspace/auth/AUTHENTICATE'),
|
||||||
@@ -49,9 +50,9 @@ export class AuthenticateAction implements Action {
|
|||||||
*/
|
*/
|
||||||
export class AuthenticatedAction implements Action {
|
export class AuthenticatedAction implements Action {
|
||||||
public type: string = AuthActionTypes.AUTHENTICATED;
|
public type: string = AuthActionTypes.AUTHENTICATED;
|
||||||
payload: string;
|
payload: AuthTokenInfo;
|
||||||
|
|
||||||
constructor(token: string) {
|
constructor(token: AuthTokenInfo) {
|
||||||
this.payload = token;
|
this.payload = token;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,10 +109,10 @@ export class AuthenticationErrorAction implements Action {
|
|||||||
*/
|
*/
|
||||||
export class AuthenticationSuccessAction implements Action {
|
export class AuthenticationSuccessAction implements Action {
|
||||||
public type: string = AuthActionTypes.AUTHENTICATE_SUCCESS;
|
public type: string = AuthActionTypes.AUTHENTICATE_SUCCESS;
|
||||||
payload: Eperson;
|
payload: AuthTokenInfo;
|
||||||
|
|
||||||
constructor(user: Eperson) {
|
constructor(token: AuthTokenInfo) {
|
||||||
this.payload = user;
|
this.payload = token;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -23,23 +23,7 @@ import {
|
|||||||
RegistrationSuccessAction
|
RegistrationSuccessAction
|
||||||
} from './auth.actions';
|
} from './auth.actions';
|
||||||
import { Eperson } from '../eperson/models/eperson.model';
|
import { Eperson } from '../eperson/models/eperson.model';
|
||||||
|
import { AuthStatus } from './models/auth-status.model';
|
||||||
/**
|
|
||||||
* Effects offer a way to isolate and easily test side-effects within your
|
|
||||||
* application.
|
|
||||||
* The `toPayload` helper function returns just
|
|
||||||
* the payload of the currently dispatched action, useful in
|
|
||||||
* instances where the current state is not necessary.
|
|
||||||
*
|
|
||||||
* Documentation on `toPayload` can be found here:
|
|
||||||
* https://github.com/ngrx/effects/blob/master/docs/api.md#topayload
|
|
||||||
*
|
|
||||||
* If you are unfamiliar with the operators being used in these examples, please
|
|
||||||
* check out the sources below:
|
|
||||||
*
|
|
||||||
* Official Docs: http://reactivex.io/rxjs/manual/overview.html#categories-of-operators
|
|
||||||
* RxJS 5 Operators By Example: https://gist.github.com/btroncone/d6cf141d6f2c00dc6b35
|
|
||||||
*/
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthEffects {
|
export class AuthEffects {
|
||||||
@@ -51,18 +35,29 @@ export class AuthEffects {
|
|||||||
@Effect()
|
@Effect()
|
||||||
public authenticate: Observable<Action> = this.actions$
|
public authenticate: Observable<Action> = this.actions$
|
||||||
.ofType(AuthActionTypes.AUTHENTICATE)
|
.ofType(AuthActionTypes.AUTHENTICATE)
|
||||||
.debounceTime(500)
|
|
||||||
.switchMap((action: AuthenticateAction) => {
|
.switchMap((action: AuthenticateAction) => {
|
||||||
return this.authService.authenticate(action.payload.email, action.payload.password)
|
return this.authService.authenticate(action.payload.email, action.payload.password)
|
||||||
.map((user: Eperson) => new AuthenticationSuccessAction(user))
|
.map((response: AuthStatus) => new AuthenticationSuccessAction(response.token))
|
||||||
.catch((error) => Observable.of(new AuthenticationErrorAction(error)));
|
.catch((error) => Observable.of(new AuthenticationErrorAction(error)));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// It means "reacts to this action but don't send another"
|
||||||
|
@Effect()
|
||||||
|
public authenticateSuccess: Observable<Action> = this.actions$
|
||||||
|
.ofType(AuthActionTypes.AUTHENTICATE_SUCCESS)
|
||||||
|
.do((action: AuthenticationSuccessAction) => this.authService.storeToken(action.payload))
|
||||||
|
.map((action: AuthenticationSuccessAction) => new AuthenticatedAction(action.payload))
|
||||||
|
|
||||||
|
@Effect({dispatch: false})
|
||||||
|
public logOutSuccess: Observable<Action> = this.actions$
|
||||||
|
.ofType(AuthActionTypes.LOG_OUT_SUCCESS)
|
||||||
|
.do((action: LogOutSuccessAction) => this.authService.removeToken());
|
||||||
|
|
||||||
@Effect()
|
@Effect()
|
||||||
public authenticated: Observable<Action> = this.actions$
|
public authenticated: Observable<Action> = this.actions$
|
||||||
.ofType(AuthActionTypes.AUTHENTICATED)
|
.ofType(AuthActionTypes.AUTHENTICATED)
|
||||||
.switchMap((action: AuthenticatedAction) => {
|
.switchMap((action: AuthenticatedAction) => {
|
||||||
return this.authService.authenticatedUser()
|
return this.authService.authenticatedUser(action.payload)
|
||||||
.map((user: Eperson) => new AuthenticatedSuccessAction((user !== null), user))
|
.map((user: Eperson) => new AuthenticatedSuccessAction((user !== null), user))
|
||||||
.catch((error) => Observable.of(new AuthenticatedErrorAction(error)));
|
.catch((error) => Observable.of(new AuthenticatedErrorAction(error)));
|
||||||
});
|
});
|
||||||
@@ -70,7 +65,7 @@ export class AuthEffects {
|
|||||||
@Effect()
|
@Effect()
|
||||||
public createUser: Observable<Action> = this.actions$
|
public createUser: Observable<Action> = this.actions$
|
||||||
.ofType(AuthActionTypes.REGISTRATION)
|
.ofType(AuthActionTypes.REGISTRATION)
|
||||||
.debounceTime(500)
|
.debounceTime(500) // to remove when functionality is implemented
|
||||||
.switchMap((action: RegistrationAction) => {
|
.switchMap((action: RegistrationAction) => {
|
||||||
return this.authService.create(action.payload)
|
return this.authService.create(action.payload)
|
||||||
.map((user: Eperson) => new RegistrationSuccessAction(user))
|
.map((user: Eperson) => new RegistrationSuccessAction(user))
|
||||||
|
97
src/app/core/auth/auth.interceptor.ts
Normal file
97
src/app/core/auth/auth.interceptor.ts
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
import { Injectable, Injector } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import {
|
||||||
|
HttpClient, HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse,
|
||||||
|
HttpErrorResponse
|
||||||
|
} from '@angular/common/http';
|
||||||
|
import { Observable } from 'rxjs/Rx';
|
||||||
|
import 'rxjs/add/observable/throw'
|
||||||
|
import 'rxjs/add/operator/catch';
|
||||||
|
|
||||||
|
import { AuthService } from './auth.service';
|
||||||
|
import { AuthStatus } from './models/auth-status.model';
|
||||||
|
import { AuthType } from './auth-type';
|
||||||
|
import { ResourceType } from '../shared/resource-type';
|
||||||
|
import { AuthTokenInfo } from './models/auth-token-info.model';
|
||||||
|
import { isNotEmpty } from '../../shared/empty.util';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AuthInterceptor implements HttpInterceptor {
|
||||||
|
constructor(private inj: Injector, private router: Router) { }
|
||||||
|
|
||||||
|
private isUnauthorized(status: number): boolean {
|
||||||
|
return status === 401 || status === 403;
|
||||||
|
}
|
||||||
|
|
||||||
|
private isLoginResponse(url: string): boolean {
|
||||||
|
return url.endsWith('/authn/login');
|
||||||
|
}
|
||||||
|
|
||||||
|
private isLogoutResponse(url: string): boolean {
|
||||||
|
return url.endsWith('/authn/logout');
|
||||||
|
}
|
||||||
|
|
||||||
|
private makeAuthStatusObject(authenticated:boolean, accessToken?: string, error?: string): AuthStatus {
|
||||||
|
const authStatus = new AuthStatus();
|
||||||
|
authStatus.id = null;
|
||||||
|
authStatus.okay = true;
|
||||||
|
if (authenticated) {
|
||||||
|
authStatus.authenticated = true;
|
||||||
|
authStatus.token = new AuthTokenInfo(accessToken);
|
||||||
|
} else {
|
||||||
|
authStatus.authenticated = false;
|
||||||
|
authStatus.error = JSON.parse(error);
|
||||||
|
}
|
||||||
|
return authStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||||
|
|
||||||
|
const authService = this.inj.get(AuthService);
|
||||||
|
|
||||||
|
// Get the auth header from the service.
|
||||||
|
const Authorization = authService.getAuthHeader();
|
||||||
|
|
||||||
|
let authReq;
|
||||||
|
if (isNotEmpty(Authorization)) {
|
||||||
|
// Clone the request to add the new header.
|
||||||
|
authReq = req.clone({headers: req.headers.set('authorization', Authorization)});
|
||||||
|
} else {
|
||||||
|
authReq = req.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass on the cloned request instead of the original request.
|
||||||
|
return next.handle(authReq)
|
||||||
|
.map((response) => {
|
||||||
|
if (response instanceof HttpResponse && response.status === 200 && (this.isLoginResponse(response.url) || this.isLogoutResponse(response.url))) {
|
||||||
|
let authRes: HttpResponse<any>;
|
||||||
|
if (this.isLoginResponse(response.url)) {
|
||||||
|
const token = response.headers.get('authorization');
|
||||||
|
authRes = response.clone({body: this.makeAuthStatusObject(true, token)});
|
||||||
|
} else {
|
||||||
|
authRes = response.clone({body: this.makeAuthStatusObject(false)});
|
||||||
|
}
|
||||||
|
return authRes;
|
||||||
|
} else {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error, caught) => {
|
||||||
|
// Intercept an unauthorized error response
|
||||||
|
if (error instanceof HttpErrorResponse && this.isUnauthorized(error.status)) {
|
||||||
|
// Create a new HttpResponse and return it, so it can be handle properly by AuthService.
|
||||||
|
const authResponse = new HttpResponse({
|
||||||
|
body: this.makeAuthStatusObject(false, null, error.error),
|
||||||
|
headers: error.headers,
|
||||||
|
status: error.status,
|
||||||
|
statusText: error.statusText,
|
||||||
|
url: error.url
|
||||||
|
});
|
||||||
|
return Observable.of(authResponse);
|
||||||
|
} else {
|
||||||
|
// Return error response as is.
|
||||||
|
return Observable.throw(error);
|
||||||
|
}
|
||||||
|
}) as any;
|
||||||
|
}
|
||||||
|
}
|
@@ -6,6 +6,7 @@ import {
|
|||||||
|
|
||||||
// import models
|
// import models
|
||||||
import { Eperson } from '../eperson/models/eperson.model';
|
import { Eperson } from '../eperson/models/eperson.model';
|
||||||
|
import { AuthTokenInfo } from './models/auth-token-info.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The auth state.
|
* The auth state.
|
||||||
@@ -25,6 +26,9 @@ export interface AuthState {
|
|||||||
// true when loading
|
// true when loading
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
|
|
||||||
|
// access token
|
||||||
|
token?: AuthTokenInfo;
|
||||||
|
|
||||||
// the authenticated user
|
// the authenticated user
|
||||||
user?: Eperson;
|
user?: Eperson;
|
||||||
}
|
}
|
||||||
@@ -62,8 +66,10 @@ export function authReducer(state: any = initialState, action: AuthActions): Aut
|
|||||||
|
|
||||||
case AuthActionTypes.AUTHENTICATED_SUCCESS:
|
case AuthActionTypes.AUTHENTICATED_SUCCESS:
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
authenticated: (action as AuthenticatedSuccessAction).payload.authenticated,
|
authenticated: true,
|
||||||
loaded: true,
|
loaded: true,
|
||||||
|
error: undefined,
|
||||||
|
loading: false,
|
||||||
user: (action as AuthenticatedSuccessAction).payload.user
|
user: (action as AuthenticatedSuccessAction).payload.user
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -76,26 +82,26 @@ export function authReducer(state: any = initialState, action: AuthActions): Aut
|
|||||||
});
|
});
|
||||||
|
|
||||||
case AuthActionTypes.AUTHENTICATE_SUCCESS:
|
case AuthActionTypes.AUTHENTICATE_SUCCESS:
|
||||||
case AuthActionTypes.REGISTRATION_SUCCESS:
|
const token: AuthTokenInfo = (action as AuthenticationSuccessAction).payload;
|
||||||
const user: Eperson = (action as AuthenticationSuccessAction).payload;
|
|
||||||
|
|
||||||
// verify user is not null
|
// verify token is not null
|
||||||
if (user === null) {
|
if (token === null) {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
authenticated: true,
|
token: token
|
||||||
error: undefined,
|
|
||||||
loading: false,
|
|
||||||
user: user
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
case AuthActionTypes.REGISTRATION_SUCCESS:
|
||||||
|
return state;
|
||||||
|
|
||||||
case AuthActionTypes.RESET_ERROR:
|
case AuthActionTypes.RESET_ERROR:
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
authenticated: null,
|
authenticated: null,
|
||||||
|
error: undefined,
|
||||||
loaded: false,
|
loaded: false,
|
||||||
loading: false
|
loading: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
case AuthActionTypes.LOG_OUT_ERROR:
|
case AuthActionTypes.LOG_OUT_ERROR:
|
||||||
@@ -109,7 +115,10 @@ export function authReducer(state: any = initialState, action: AuthActions): Aut
|
|||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
authenticated: false,
|
authenticated: false,
|
||||||
error: undefined,
|
error: undefined,
|
||||||
user: undefined
|
loaded: false,
|
||||||
|
loading: false,
|
||||||
|
user: undefined,
|
||||||
|
token: undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
case AuthActionTypes.REGISTRATION:
|
case AuthActionTypes.REGISTRATION:
|
||||||
|
@@ -6,6 +6,10 @@ import { Eperson } from '../eperson/models/eperson.model';
|
|||||||
import { AuthRequestService } from './auth-request.service';
|
import { AuthRequestService } from './auth-request.service';
|
||||||
import { HttpHeaders } from '@angular/common/http';
|
import { HttpHeaders } from '@angular/common/http';
|
||||||
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
|
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
|
||||||
|
import { AuthStatus } from './models/auth-status.model';
|
||||||
|
import { AuthTokenInfo } from './models/auth-token-info.model';
|
||||||
|
import { isNotEmpty, isNotNull } from '../../shared/empty.util';
|
||||||
|
import { AuthStorageService } from './auth-storage.service';
|
||||||
|
|
||||||
export const MOCK_USER = new Eperson();
|
export const MOCK_USER = new Eperson();
|
||||||
MOCK_USER.id = '92a59227-ccf7-46da-9776-86c3fc64147f';
|
MOCK_USER.id = '92a59227-ccf7-46da-9776-86c3fc64147f';
|
||||||
@@ -30,21 +34,28 @@ MOCK_USER.metadata = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
export const TOKENITEM = 'ds-token';
|
export const TOKENITEM = 'dsAuthInfo';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The user service.
|
* The auth service.
|
||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthService {
|
export class AuthService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if authenticated
|
* True if authenticated
|
||||||
* @type
|
* @type boolean
|
||||||
*/
|
*/
|
||||||
private _authenticated = false;
|
private _authenticated = false;
|
||||||
|
|
||||||
constructor(private authRequestService: AuthRequestService) {}
|
/**
|
||||||
|
* The url to redirect after login
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
private _redirectUrl: string;
|
||||||
|
|
||||||
|
constructor(private authRequestService: AuthRequestService, private storage: AuthStorageService) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Authenticate the user
|
* Authenticate the user
|
||||||
@@ -53,32 +64,28 @@ export class AuthService {
|
|||||||
* @param {string} password The user's password
|
* @param {string} password The user's password
|
||||||
* @returns {Observable<User>} The authenticated user observable.
|
* @returns {Observable<User>} The authenticated user observable.
|
||||||
*/
|
*/
|
||||||
public authenticate(user: string, password: string): Observable<Eperson> {
|
public authenticate(user: string, password: string): Observable<AuthStatus> {
|
||||||
// Normally you would do an HTTP request to determine to
|
// Normally you would do an HTTP request to determine to
|
||||||
// attempt authenticating the user using the supplied credentials.
|
// attempt authenticating the user using the supplied credentials.
|
||||||
// const body = `user=${user}&password=${password}`;
|
// const body = `user=${user}&password=${password}`;
|
||||||
// const body = encodeURI('password=test&user=vera.aloe@mailinator.com');
|
// const body = encodeURI('password=test&user=vera.aloe@mailinator.com');
|
||||||
// const body = [{user}, {password}];
|
// const body = [{user}, {password}];
|
||||||
const formData: FormData = new FormData();
|
// const body = encodeURI('password=' + password.toString() + '&user=' + user.toString());
|
||||||
formData.append('user', user);
|
const body = encodeURI(`password=${password}&user=${user}`);
|
||||||
formData.append('password', password);
|
|
||||||
const body = 'password=' + password.toString() + '&user=' + user.toString();
|
|
||||||
const options: HttpOptions = Object.create({});
|
const options: HttpOptions = Object.create({});
|
||||||
let headers = new HttpHeaders();
|
let headers = new HttpHeaders();
|
||||||
headers = headers.append('Content-Type', 'application/x-www-form-urlencoded');
|
headers = headers.append('Content-Type', 'application/x-www-form-urlencoded');
|
||||||
headers = headers.append('Accept', 'application/json');
|
|
||||||
options.headers = headers;
|
options.headers = headers;
|
||||||
options.responseType = 'text';
|
options.responseType = 'text';
|
||||||
this.authRequestService.postToEndpoint('login', body, options)
|
return this.authRequestService.postToEndpoint('login', body, options)
|
||||||
.subscribe((r) => {
|
.map((status: AuthStatus) => {
|
||||||
console.log(r);
|
if (status.authenticated) {
|
||||||
})
|
return status;
|
||||||
if (user === 'test' && password === 'password') {
|
} else {
|
||||||
this._authenticated = true;
|
throw(new Error('Invalid email or password'));
|
||||||
return Observable.of(MOCK_USER);
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return Observable.throw(new Error('Invalid email or password'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -93,11 +100,25 @@ export class AuthService {
|
|||||||
* Returns the authenticated user
|
* Returns the authenticated user
|
||||||
* @returns {User}
|
* @returns {User}
|
||||||
*/
|
*/
|
||||||
public authenticatedUser(): Observable<Eperson> {
|
public authenticatedUser(token: AuthTokenInfo): Observable<Eperson> {
|
||||||
// Normally you would do an HTTP request to determine if
|
// Normally you would do an HTTP request to determine if
|
||||||
// the user has an existing auth session on the server
|
// the user has an existing auth session on the server
|
||||||
// but, let's just return the mock user for this example.
|
// but, let's just return the mock user for this example.
|
||||||
return Observable.of(MOCK_USER);
|
const options: HttpOptions = Object.create({});
|
||||||
|
let headers = new HttpHeaders();
|
||||||
|
headers = headers.append('Accept', 'application/json');
|
||||||
|
headers = headers.append('Authorization', `Bearer ${token.accessToken}`);
|
||||||
|
options.headers = headers;
|
||||||
|
return this.authRequestService.getRequest('status', options)
|
||||||
|
.map((status: AuthStatus) => {
|
||||||
|
if (status.authenticated) {
|
||||||
|
this._authenticated = true;
|
||||||
|
return status.eperson[0];
|
||||||
|
} else {
|
||||||
|
this._authenticated = false;
|
||||||
|
throw(new Error('Not authenticated'));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -119,7 +140,47 @@ export class AuthService {
|
|||||||
public signout(): Observable<boolean> {
|
public signout(): Observable<boolean> {
|
||||||
// Normally you would do an HTTP request sign end the session
|
// Normally you would do an HTTP request sign end the session
|
||||||
// but, let's just return an observable of true.
|
// but, let's just return an observable of true.
|
||||||
|
let headers = new HttpHeaders();
|
||||||
|
headers = headers.append('Content-Type', 'application/x-www-form-urlencoded');
|
||||||
|
const options: HttpOptions = Object.create({headers, responseType: 'text'});
|
||||||
|
return this.authRequestService.getRequest('logout', options)
|
||||||
|
.map((status: AuthStatus) => {
|
||||||
|
if (!status.authenticated) {
|
||||||
this._authenticated = false;
|
this._authenticated = false;
|
||||||
return Observable.of(true);
|
return true;
|
||||||
|
} else {
|
||||||
|
throw(new Error('Invalid email or password'));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public getAuthHeader(): string {
|
||||||
|
// Retrieve authentication token info
|
||||||
|
const token = this.storage.get(TOKENITEM);
|
||||||
|
return (isNotNull(token) && this._authenticated) ? `Bearer ${token.accessToken}` : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public getToken(): AuthTokenInfo {
|
||||||
|
// Retrieve authentication token info
|
||||||
|
return this.storage.get(TOKENITEM);
|
||||||
|
}
|
||||||
|
|
||||||
|
public storeToken(token: AuthTokenInfo) {
|
||||||
|
// Save authentication token info
|
||||||
|
return this.storage.store(TOKENITEM, JSON.stringify(token));
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeToken() {
|
||||||
|
// Remove authentication token info
|
||||||
|
return this.storage.remove(TOKENITEM);
|
||||||
|
}
|
||||||
|
|
||||||
|
get redirectUrl(): string {
|
||||||
|
return this._redirectUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
set redirectUrl(value: string) {
|
||||||
|
this._redirectUrl = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,6 +7,7 @@ import { Store } from '@ngrx/store';
|
|||||||
// reducers
|
// reducers
|
||||||
import { CoreState } from '../core.reducers';
|
import { CoreState } from '../core.reducers';
|
||||||
import { isAuthenticated } from './selectors';
|
import { isAuthenticated } from './selectors';
|
||||||
|
import { AuthService } from './auth.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prevent unauthorized activating and loading of routes
|
* Prevent unauthorized activating and loading of routes
|
||||||
@@ -18,37 +19,44 @@ export class AuthenticatedGuard implements CanActivate, CanLoad {
|
|||||||
/**
|
/**
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
constructor(private router: Router, private store: Store<CoreState>) {}
|
constructor(private authService: AuthService, private router: Router, private store: Store<CoreState>) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True when user is authenticated
|
* True when user is authenticated
|
||||||
* @method canActivate
|
* @method canActivate
|
||||||
*/
|
*/
|
||||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
|
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
|
||||||
// get observable
|
const url = state.url;
|
||||||
const observable = this.store.select(isAuthenticated);
|
|
||||||
|
|
||||||
// redirect to sign in page if user is not authenticated
|
return this.handleAuth(url);
|
||||||
observable.subscribe((authenticated) => {
|
|
||||||
if (!authenticated) {
|
|
||||||
this.router.navigate(['/login']);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
return observable;
|
/**
|
||||||
|
* True when user is authenticated
|
||||||
|
* @method canActivateChild
|
||||||
|
*/
|
||||||
|
canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
|
||||||
|
return this.canActivate(route, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True when user is authenticated
|
* True when user is authenticated
|
||||||
* @method canLoad
|
* @method canLoad
|
||||||
*/
|
*/
|
||||||
canLoad(route: Route): Observable<boolean> | Promise<boolean> | boolean {
|
canLoad(route: Route): Observable<boolean> {
|
||||||
|
const url = `/${route.path}`;
|
||||||
|
|
||||||
|
return this.handleAuth(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleAuth(url: string): Observable<boolean> {
|
||||||
// get observable
|
// get observable
|
||||||
const observable = this.store.select(isAuthenticated);
|
const observable = this.store.select(isAuthenticated);
|
||||||
|
|
||||||
// redirect to sign in page if user is not authenticated
|
// redirect to sign in page if user is not authenticated
|
||||||
observable.subscribe((authenticated) => {
|
observable.subscribe((authenticated) => {
|
||||||
if (!authenticated) {
|
if (!authenticated) {
|
||||||
|
this.authService.redirectUrl = url;
|
||||||
this.router.navigate(['/login']);
|
this.router.navigate(['/login']);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
7
src/app/core/auth/models/auth-error.model.ts
Normal file
7
src/app/core/auth/models/auth-error.model.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export interface AuthError {
|
||||||
|
error: string,
|
||||||
|
message: string,
|
||||||
|
path: string,
|
||||||
|
status: number
|
||||||
|
timestamp: number
|
||||||
|
}
|
@@ -1,5 +0,0 @@
|
|||||||
export interface AuthInfo {
|
|
||||||
access_token?: string,
|
|
||||||
expires?: number,
|
|
||||||
expires_in?: number
|
|
||||||
}
|
|
@@ -1,4 +1,7 @@
|
|||||||
|
import { AuthError } from './auth-error.model';
|
||||||
|
import { AuthTokenInfo } from './auth-token-info.model';
|
||||||
import { DSpaceObject } from '../../shared/dspace-object.model';
|
import { DSpaceObject } from '../../shared/dspace-object.model';
|
||||||
|
import { Eperson } from '../../eperson/models/eperson.model';
|
||||||
|
|
||||||
export class AuthStatus extends DSpaceObject {
|
export class AuthStatus extends DSpaceObject {
|
||||||
|
|
||||||
@@ -6,4 +9,9 @@ export class AuthStatus extends DSpaceObject {
|
|||||||
|
|
||||||
authenticated: boolean;
|
authenticated: boolean;
|
||||||
|
|
||||||
|
error?: AuthError;
|
||||||
|
|
||||||
|
eperson: Eperson[];
|
||||||
|
|
||||||
|
token?: AuthTokenInfo
|
||||||
}
|
}
|
||||||
|
11
src/app/core/auth/models/auth-token-info.model.ts
Normal file
11
src/app/core/auth/models/auth-token-info.model.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
export class AuthTokenInfo {
|
||||||
|
public accessToken: string;
|
||||||
|
public expires?: number;
|
||||||
|
|
||||||
|
constructor(token: string, expiresIn?: number) {
|
||||||
|
this.accessToken = token.replace('Bearer ', '');
|
||||||
|
if (expiresIn) {
|
||||||
|
this.expires = expiresIn * 1000 + Date.now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
src/app/core/auth/models/normalized-auth-status.model.ts
Normal file
26
src/app/core/auth/models/normalized-auth-status.model.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { AuthStatus } from './auth-status.model';
|
||||||
|
import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize';
|
||||||
|
import { mapsTo } from '../../cache/builders/build-decorators';
|
||||||
|
import { NormalizedDSpaceObject } from '../../cache/models/normalized-dspace-object.model';
|
||||||
|
import { Eperson } from '../../eperson/models/eperson.model';
|
||||||
|
|
||||||
|
@mapsTo(AuthStatus)
|
||||||
|
@inheritSerialization(NormalizedDSpaceObject)
|
||||||
|
export class NormalizedAuthStatus extends NormalizedDSpaceObject {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if REST API is up and running, should never return false
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
okay: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if the token is valid, false if there was no token or the token wasn't valid
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
authenticated: boolean;
|
||||||
|
|
||||||
|
@autoserializeAs(Eperson)
|
||||||
|
eperson: Eperson[];
|
||||||
|
|
||||||
|
}
|
32
src/app/core/cache/response-cache.models.ts
vendored
32
src/app/core/cache/response-cache.models.ts
vendored
@@ -2,12 +2,16 @@ import { RequestError } from '../data/request.models';
|
|||||||
import { PageInfo } from '../shared/page-info.model';
|
import { PageInfo } from '../shared/page-info.model';
|
||||||
import { BrowseDefinition } from '../shared/browse-definition.model';
|
import { BrowseDefinition } from '../shared/browse-definition.model';
|
||||||
import { ConfigObject } from '../shared/config/config.model';
|
import { ConfigObject } from '../shared/config/config.model';
|
||||||
|
import { AuthTokenInfo } from '../auth/models/auth-token-info.model';
|
||||||
|
import { NormalizedAuthStatus } from '../auth/models/normalized-auth-status.model';
|
||||||
|
import { AuthStatus } from '../auth/models/auth-status.model';
|
||||||
|
|
||||||
/* tslint:disable:max-classes-per-file */
|
/* tslint:disable:max-classes-per-file */
|
||||||
export class RestResponse {
|
export class RestResponse {
|
||||||
|
public toCache = true;
|
||||||
constructor(
|
constructor(
|
||||||
public isSuccessful: boolean,
|
public isSuccessful: boolean,
|
||||||
public statusCode: string
|
public statusCode: string,
|
||||||
) { }
|
) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,11 +67,31 @@ export class ConfigSuccessResponse extends RestResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AuthSuccessResponse extends RestResponse {
|
export class AuthStatusResponse extends RestResponse {
|
||||||
|
public toCache = false;
|
||||||
constructor(
|
constructor(
|
||||||
public authResponse: any,
|
public response: AuthStatus,
|
||||||
|
public statusCode: string
|
||||||
|
) {
|
||||||
|
super(true, statusCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AuthSuccessResponse extends RestResponse {
|
||||||
|
public toCache = false;
|
||||||
|
constructor(
|
||||||
|
public response: AuthTokenInfo,
|
||||||
|
public statusCode: string
|
||||||
|
) {
|
||||||
|
super(true, statusCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AuthErrorResponse extends RestResponse {
|
||||||
|
public toCache = false;
|
||||||
|
constructor(
|
||||||
|
public response: any,
|
||||||
public statusCode: string,
|
public statusCode: string,
|
||||||
public pageInfo?: PageInfo
|
|
||||||
) {
|
) {
|
||||||
super(true, statusCode);
|
super(true, statusCode);
|
||||||
}
|
}
|
||||||
|
@@ -41,6 +41,10 @@ import { UUIDService } from './shared/uuid.service';
|
|||||||
import { AuthService } from './auth/auth.service';
|
import { AuthService } from './auth/auth.service';
|
||||||
import { AuthenticatedGuard } from './auth/authenticated.guard';
|
import { AuthenticatedGuard } from './auth/authenticated.guard';
|
||||||
import { AuthRequestService } from './auth/auth-request.service';
|
import { AuthRequestService } from './auth/auth-request.service';
|
||||||
|
import { AuthResponseParsingService } from './auth/auth-response-parsing.service';
|
||||||
|
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||||
|
import { AuthInterceptor } from './auth/auth.interceptor';
|
||||||
|
import { AuthStorageService } from './auth/auth-storage.service';
|
||||||
|
|
||||||
const IMPORTS = [
|
const IMPORTS = [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@@ -60,7 +64,9 @@ const PROVIDERS = [
|
|||||||
ApiService,
|
ApiService,
|
||||||
AuthenticatedGuard,
|
AuthenticatedGuard,
|
||||||
AuthRequestService,
|
AuthRequestService,
|
||||||
|
AuthResponseParsingService,
|
||||||
AuthService,
|
AuthService,
|
||||||
|
AuthStorageService,
|
||||||
CommunityDataService,
|
CommunityDataService,
|
||||||
CollectionDataService,
|
CollectionDataService,
|
||||||
DSOResponseParsingService,
|
DSOResponseParsingService,
|
||||||
@@ -83,7 +89,13 @@ const PROVIDERS = [
|
|||||||
SubmissionFormsConfigService,
|
SubmissionFormsConfigService,
|
||||||
SubmissionSectionsConfigService,
|
SubmissionSectionsConfigService,
|
||||||
UUIDService,
|
UUIDService,
|
||||||
{ provide: NativeWindowService, useFactory: NativeWindowFactory }
|
{ provide: NativeWindowService, useFactory: NativeWindowFactory },
|
||||||
|
// register TokenInterceptor as HttpInterceptor
|
||||||
|
{
|
||||||
|
provide: HTTP_INTERCEPTORS,
|
||||||
|
useClass: AuthInterceptor,
|
||||||
|
multi: true
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@@ -193,8 +193,8 @@ export class AuthPostRequest extends PostRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class AuthGetRequest extends GetRequest {
|
export class AuthGetRequest extends GetRequest {
|
||||||
constructor(uuid: string, href: string) {
|
constructor(uuid: string, href: string, public options?: HttpOptions) {
|
||||||
super(uuid, href);
|
super(uuid, href, null, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
getResponseParser(): GenericConstructor<ResponseParsingService> {
|
getResponseParser(): GenericConstructor<ResponseParsingService> {
|
||||||
|
@@ -17,6 +17,7 @@ import { RequestConfigureAction, RequestExecuteAction } from './request.actions'
|
|||||||
import { GetRequest, RestRequest, RestRequestMethod } from './request.models';
|
import { GetRequest, RestRequest, RestRequestMethod } from './request.models';
|
||||||
|
|
||||||
import { RequestEntry, RequestState } from './request.reducer';
|
import { RequestEntry, RequestState } from './request.reducer';
|
||||||
|
import { ResponseCacheRemoveAction } from '../cache/response-cache.actions';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RequestService {
|
export class RequestService {
|
||||||
@@ -66,9 +67,9 @@ export class RequestService {
|
|||||||
.flatMap((uuid: string) => this.getByUUID(uuid));
|
.flatMap((uuid: string) => this.getByUUID(uuid));
|
||||||
}
|
}
|
||||||
|
|
||||||
configure<T extends CacheableObject>(request: RestRequest): void {
|
configure<T extends CacheableObject>(request: RestRequest, overrideRequest: boolean = false): void {
|
||||||
if (request.method !== RestRequestMethod.Get || !this.isCachedOrPending(request)) {
|
if (request.method !== RestRequestMethod.Get || !this.isCachedOrPending(request) || overrideRequest) {
|
||||||
this.dispatchRequest(request);
|
this.dispatchRequest(request, overrideRequest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,10 +102,10 @@ export class RequestService {
|
|||||||
return isCached || isPending;
|
return isCached || isPending;
|
||||||
}
|
}
|
||||||
|
|
||||||
private dispatchRequest(request: RestRequest) {
|
private dispatchRequest(request: RestRequest, overrideRequest: boolean) {
|
||||||
this.store.dispatch(new RequestConfigureAction(request));
|
this.store.dispatch(new RequestConfigureAction(request));
|
||||||
this.store.dispatch(new RequestExecuteAction(request.uuid));
|
this.store.dispatch(new RequestExecuteAction(request.uuid));
|
||||||
if (request.method === RestRequestMethod.Get) {
|
if (request.method === RestRequestMethod.Get && !overrideRequest) {
|
||||||
this.trackRequestsOnTheirWayToTheStore(request);
|
this.trackRequestsOnTheirWayToTheStore(request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
import { HttpHeaders } from '@angular/common/http';
|
||||||
|
|
||||||
export interface DSpaceRESTV2Response {
|
export interface DSpaceRESTV2Response {
|
||||||
payload: {
|
payload: {
|
||||||
[name: string]: any;
|
[name: string]: any;
|
||||||
@@ -5,5 +7,6 @@ export interface DSpaceRESTV2Response {
|
|||||||
_links?: any;
|
_links?: any;
|
||||||
page?: any;
|
page?: any;
|
||||||
},
|
},
|
||||||
|
headers: HttpHeaders,
|
||||||
statusCode: string
|
statusCode: string
|
||||||
}
|
}
|
||||||
|
@@ -61,21 +61,16 @@ export class DSpaceRESTv2Service {
|
|||||||
requestOptions.body = body;
|
requestOptions.body = body;
|
||||||
requestOptions.observe = 'response';
|
requestOptions.observe = 'response';
|
||||||
if (options && options.headers) {
|
if (options && options.headers) {
|
||||||
let headers = new HttpHeaders();
|
requestOptions.headers = Object.assign(new HttpHeaders(), options.headers);
|
||||||
headers = headers.append('Accept', 'application/json');
|
|
||||||
headers = headers.append('Content-Type', 'application/x-www-form-urlencoded');
|
|
||||||
// requestOptions.headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded');
|
|
||||||
requestOptions.headers = headers;
|
|
||||||
/* const keys = options.headers.getAll('');
|
|
||||||
keys.forEach((key) => {
|
|
||||||
requestOptions.headers.append(key, options.headers.get(key));
|
|
||||||
})*/
|
|
||||||
}
|
}
|
||||||
if (options && options.responseType) {
|
if (options && options.responseType) {
|
||||||
// requestOptions.responseType = options.responseType;
|
requestOptions.responseType = options.responseType;
|
||||||
}
|
}
|
||||||
return this.http.request(method, url, requestOptions)
|
return this.http.request(method, url, requestOptions)
|
||||||
.map((res) => ({ payload: res.body, statusCode: res.statusText }))
|
.map((res) => {
|
||||||
|
console.log(res);
|
||||||
|
return ({ payload: res.body, headers: res.headers, statusCode: res.statusText })
|
||||||
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log('Error: ', err);
|
console.log('Error: ', err);
|
||||||
return Observable.throw(err);
|
return Observable.throw(err);
|
||||||
|
Reference in New Issue
Block a user