aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author杨宇千 <crupest@outlook.com>2019-03-14 16:51:46 +0800
committerGitHub <noreply@github.com>2019-03-14 16:51:46 +0800
commitc65db42c9ce3532509bbd0b4abfc500692cfe4d3 (patch)
tree9e6a91af019e14fa3fb40e03bf84507a98a33d4c
parent4535d1fd30eb02b3fe60718235a0725e3b30049e (diff)
parent7b929db4fcfa34acc82885b75a573e6e4bd40465 (diff)
downloadtimeline-c65db42c9ce3532509bbd0b4abfc500692cfe4d3.tar.gz
timeline-c65db42c9ce3532509bbd0b4abfc500692cfe4d3.tar.bz2
timeline-c65db42c9ce3532509bbd0b4abfc500692cfe4d3.zip
Merge pull request #13 from crupest/auth-guard
Add auth guard.
-rw-r--r--Timeline/ClientApp/src/app/app.component.html6
-rw-r--r--Timeline/ClientApp/src/app/app.component.ts9
-rw-r--r--Timeline/ClientApp/src/app/app.module.ts2
-rw-r--r--Timeline/ClientApp/src/app/user/auth.guard.spec.ts61
-rw-r--r--Timeline/ClientApp/src/app/user/auth.guard.ts79
-rw-r--r--Timeline/ClientApp/src/app/user/redirect.component.ts15
-rw-r--r--Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.css5
-rw-r--r--Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.html5
-rw-r--r--Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.spec.ts59
-rw-r--r--Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.ts23
-rw-r--r--Timeline/ClientApp/src/app/user/user-login-success/user-login-success.component.spec.ts4
-rw-r--r--Timeline/ClientApp/src/app/user/user-login-success/user-login-success.component.ts2
-rw-r--r--Timeline/ClientApp/src/app/user/user-login/user-login.component.spec.ts19
-rw-r--r--Timeline/ClientApp/src/app/user/user-login/user-login.component.ts12
-rw-r--r--Timeline/ClientApp/src/app/user/user.module.ts13
-rw-r--r--Timeline/ClientApp/src/app/user/user.service.ts30
-rw-r--r--Timeline/ClientApp/src/styles.css4
17 files changed, 230 insertions, 118 deletions
diff --git a/Timeline/ClientApp/src/app/app.component.html b/Timeline/ClientApp/src/app/app.component.html
index a5df80ac..92c88625 100644
--- a/Timeline/ClientApp/src/app/app.component.html
+++ b/Timeline/ClientApp/src/app/app.component.html
@@ -1,12 +1,12 @@
<body>
<mat-toolbar color="primary" class="mat-elevation-z4">
<a mat-button routerLink="/">
- <img width="30" height="30" src="assets/icon.svg"> Timeline</a>
+ <img width="30" height="30" src="assets/icon.svg">Timeline</a>
<a mat-button routerLink="/todo">TodoList</a>
<span class="fill-remaining-space"></span>
- <button mat-icon-button (click)="openUserDialog()">
+ <a mat-icon-button [routerLink]="[{outlets: {user: ['login']}}]">
<mat-icon>account_circle</mat-icon>
- </button>
+ </a>
</mat-toolbar>
<div>
diff --git a/Timeline/ClientApp/src/app/app.component.ts b/Timeline/ClientApp/src/app/app.component.ts
index ee02f833..33f33048 100644
--- a/Timeline/ClientApp/src/app/app.component.ts
+++ b/Timeline/ClientApp/src/app/app.component.ts
@@ -1,4 +1,5 @@
import { Component } from '@angular/core';
+
import { UserService } from './user/user.service';
@Component({
@@ -7,10 +8,6 @@ import { UserService } from './user/user.service';
styleUrls: ['./app.component.css']
})
export class AppComponent {
-
- constructor(private userService: UserService) { }
-
- openUserDialog() {
- this.userService.openUserDialog();
- }
+ // never remove userService because we need it explicit constructing.
+ constructor(userService: UserService) { }
}
diff --git a/Timeline/ClientApp/src/app/app.module.ts b/Timeline/ClientApp/src/app/app.module.ts
index 85c4c43d..b75e10e2 100644
--- a/Timeline/ClientApp/src/app/app.module.ts
+++ b/Timeline/ClientApp/src/app/app.module.ts
@@ -9,6 +9,7 @@ import { AppComponent } from './app.component';
import { TodoModule } from './todo/todo.module';
import { HomeModule } from './home/home.module';
import { UserModule } from './user/user.module';
+import { UserService } from './user/user.service';
@NgModule({
@@ -22,6 +23,7 @@ import { UserModule } from './user/user.module';
{ path: '', redirectTo: '/home', pathMatch: 'full' },
])
],
+ providers: [UserService],
bootstrap: [AppComponent]
})
export class AppModule { }
diff --git a/Timeline/ClientApp/src/app/user/auth.guard.spec.ts b/Timeline/ClientApp/src/app/user/auth.guard.spec.ts
new file mode 100644
index 00000000..42e35bf7
--- /dev/null
+++ b/Timeline/ClientApp/src/app/user/auth.guard.spec.ts
@@ -0,0 +1,61 @@
+import { AuthGuard, AuthStrategy } from './auth.guard';
+import { UserInfo } from './entities';
+
+describe('AuthGuard', () => {
+ class ConfiurableAuthGuard extends AuthGuard {
+ constructor(mockInternalUserService: any) {
+ super(mockInternalUserService);
+ }
+
+ authStrategy: AuthStrategy = 'all';
+ onAuthFailed: () => void = () => { };
+ }
+
+ let mockUserService: { currentUserInfo: UserInfo | null };
+ let guard: ConfiurableAuthGuard;
+ let onAuthFialedSpy: jasmine.Spy;
+
+ const mockRoles = ['role1', 'role2'];
+
+ interface ActivateResultMap {
+ nologin: boolean;
+ loginWithNoRole: boolean;
+ loginWithMockRoles: boolean;
+ }
+
+
+ function createTest(authStrategy: AuthStrategy, result: ActivateResultMap): () => void {
+ return () => {
+ guard.authStrategy = authStrategy;
+
+ mockUserService.currentUserInfo = null;
+ expect(guard.canActivate(<any>null, <any>null)).toBe(result.nologin);
+
+ mockUserService.currentUserInfo = { username: 'user', roles: [] };
+ expect(guard.canActivate(<any>null, <any>null)).toBe(result.loginWithNoRole);
+
+ mockUserService.currentUserInfo = { username: 'user', roles: mockRoles };
+ expect(guard.canActivate(<any>null, <any>null)).toBe(result.loginWithMockRoles);
+ };
+ }
+
+ beforeEach(() => {
+ mockUserService = { currentUserInfo: null };
+ guard = new ConfiurableAuthGuard(mockUserService);
+ onAuthFialedSpy = spyOn(guard, 'onAuthFailed');
+ });
+
+
+ it('all should work', createTest('all', { nologin: true, loginWithNoRole: true, loginWithMockRoles: true }));
+ it('require login should work', createTest('requirelogin', { nologin: false, loginWithNoRole: true, loginWithMockRoles: true }));
+ it('require no login should work', createTest('requirenologin', { nologin: true, loginWithNoRole: false, loginWithMockRoles: false }));
+ it('good roles should work', createTest(mockRoles, { nologin: false, loginWithNoRole: false, loginWithMockRoles: true }));
+ it('bad roles should work', createTest(['role3'], { nologin: false, loginWithNoRole: false, loginWithMockRoles: false }));
+
+ it('auth failed callback should be called', () => {
+ guard.authStrategy = 'requirelogin';
+ mockUserService.currentUserInfo = null;
+ guard.canActivate(<any>null, <any>null);
+ expect(onAuthFialedSpy).toHaveBeenCalled();
+ });
+});
diff --git a/Timeline/ClientApp/src/app/user/auth.guard.ts b/Timeline/ClientApp/src/app/user/auth.guard.ts
new file mode 100644
index 00000000..561a0c53
--- /dev/null
+++ b/Timeline/ClientApp/src/app/user/auth.guard.ts
@@ -0,0 +1,79 @@
+import { Injectable } from '@angular/core';
+import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from '@angular/router';
+import { Observable } from 'rxjs';
+
+import { InternalUserService } from './internal-user-service/internal-user.service';
+
+export type AuthStrategy = 'all' | 'requirelogin' | 'requirenologin' | string[];
+
+export abstract class AuthGuard implements CanActivate {
+
+ constructor(protected internalUserService: InternalUserService) { }
+
+ onAuthFailed() { }
+
+ abstract get authStrategy(): AuthStrategy;
+
+ canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot):
+ Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
+
+ const { authStrategy } = this;
+
+ if (authStrategy === 'all') {
+ return true;
+ }
+
+ const { currentUserInfo } = this.internalUserService;
+
+ if (currentUserInfo === null) {
+ if (authStrategy === 'requirenologin') {
+ return true;
+ }
+ } else {
+ if (authStrategy === 'requirelogin') {
+ return true;
+ } else if (authStrategy instanceof Array) {
+ const { roles } = currentUserInfo;
+ if (authStrategy.every(value => roles.includes(value))) {
+ return true;
+ }
+ }
+ }
+
+ // reach here means auth fails
+ this.onAuthFailed();
+ return false;
+ }
+}
+
+@Injectable({
+ providedIn: 'root'
+})
+export class RequireLoginGuard extends AuthGuard {
+ readonly authStrategy: AuthStrategy = 'requirelogin';
+
+ // never remove this constructor or you will get an injection error.
+ constructor(internalUserService: InternalUserService) {
+ super(internalUserService);
+ }
+
+ onAuthFailed() {
+ this.internalUserService.userRouteNavigate(['login']);
+ }
+}
+
+@Injectable({
+ providedIn: 'root'
+})
+export class RequireNoLoginGuard extends AuthGuard {
+ readonly authStrategy: AuthStrategy = 'requirenologin';
+
+ // never remove this constructor or you will get an injection error.
+ constructor(internalUserService: InternalUserService) {
+ super(internalUserService);
+ }
+
+ onAuthFailed() {
+ this.internalUserService.userRouteNavigate(['success']);
+ }
+}
diff --git a/Timeline/ClientApp/src/app/user/redirect.component.ts b/Timeline/ClientApp/src/app/user/redirect.component.ts
new file mode 100644
index 00000000..438b38b9
--- /dev/null
+++ b/Timeline/ClientApp/src/app/user/redirect.component.ts
@@ -0,0 +1,15 @@
+import { Component, OnInit } from '@angular/core';
+import { InternalUserService } from './internal-user-service/internal-user.service';
+
+@Component({
+ selector: 'app-redirect',
+ template: ''
+})
+export class RedirectComponent implements OnInit {
+
+ constructor(private userService: InternalUserService) { }
+
+ ngOnInit() {
+ this.userService.userRouteNavigate(['login']);
+ }
+}
diff --git a/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.css b/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.css
index a443e3c0..e69de29b 100644
--- a/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.css
+++ b/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.css
@@ -1,5 +0,0 @@
-.container {
- display: flex;
- justify-content: center;
- align-content: center;
-}
diff --git a/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.html b/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.html
index 58dff0e4..e8dbb003 100644
--- a/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.html
+++ b/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.html
@@ -1,4 +1 @@
-<div class="container">
- <mat-progress-spinner *ngIf="isLoading" mode="indeterminate" diameter="50"></mat-progress-spinner>
- <router-outlet name="user"></router-outlet>
-</div>
+<router-outlet name="user"></router-outlet>
diff --git a/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.spec.ts b/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.spec.ts
index c56e1ed1..47860eee 100644
--- a/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.spec.ts
+++ b/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.spec.ts
@@ -1,20 +1,10 @@
import { Component } from '@angular/core';
-import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
-import { By } from '@angular/platform-browser';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { Router, Event } from '@angular/router';
-import { of, Observable } from 'rxjs';
-import { delay } from 'rxjs/operators';
-import { UserDialogComponent } from './user-dialog.component';
-import { createMockInternalUserService } from '../internal-user-service/internal-user.service.mock';
-import { InternalUserService, UserLoginState } from '../internal-user-service/internal-user.service';
+import { Observable } from 'rxjs';
-@Component({
- /* tslint:disable-next-line:component-selector*/
- selector: 'mat-progress-spinner',
- template: ''
-})
-class MatProgressSpinnerStubComponent { }
+import { UserDialogComponent } from './user-dialog.component';
@Component({
/* tslint:disable-next-line:component-selector*/
@@ -27,16 +17,12 @@ class RouterOutletStubComponent { }
describe('UserDialogComponent', () => {
let component: UserDialogComponent;
let fixture: ComponentFixture<UserDialogComponent>;
- let mockInternalUserService: jasmine.SpyObj<InternalUserService>;
beforeEach(async(() => {
- mockInternalUserService = createMockInternalUserService();
-
TestBed.configureTestingModule({
- declarations: [UserDialogComponent, MatProgressSpinnerStubComponent, RouterOutletStubComponent],
- providers: [{ provide: InternalUserService, useValue: mockInternalUserService },
- { // for the workaround
+ declarations: [UserDialogComponent, RouterOutletStubComponent],
+ providers: [{ // for the workaround
provide: Router, useValue: {
events: new Observable<Event>()
}
@@ -48,41 +34,10 @@ describe('UserDialogComponent', () => {
beforeEach(() => {
fixture = TestBed.createComponent(UserDialogComponent);
component = fixture.componentInstance;
- });
-
- it('progress spinner should work well', fakeAsync(() => {
- mockInternalUserService.refreshAndGetUserState.and.returnValue(of(<UserLoginState>'nologin').pipe(delay(10)));
fixture.detectChanges();
- expect(fixture.debugElement.query(By.css('mat-progress-spinner'))).toBeTruthy();
- tick(10);
- fixture.detectChanges();
- expect(fixture.debugElement.query(By.css('mat-progress-spinner'))).toBeFalsy();
- }));
-
- it('nologin should work well', () => {
- mockInternalUserService.refreshAndGetUserState.and.returnValue(of(<UserLoginState>'nologin'));
-
- fixture.detectChanges();
-
- expect(mockInternalUserService.refreshAndGetUserState).toHaveBeenCalled();
- expect(mockInternalUserService.userRouteNavigate).toHaveBeenCalledWith(['login', { reason: 'nologin' }]);
});
- it('invalid login should work well', () => {
- mockInternalUserService.refreshAndGetUserState.and.returnValue(of(<UserLoginState>'invalidlogin'));
-
- fixture.detectChanges();
-
- expect(mockInternalUserService.refreshAndGetUserState).toHaveBeenCalled();
- expect(mockInternalUserService.userRouteNavigate).toHaveBeenCalledWith(['login', { reason: 'invalidlogin' }]);
- });
-
- it('success should work well', () => {
- mockInternalUserService.refreshAndGetUserState.and.returnValue(of(<UserLoginState>'success'));
-
- fixture.detectChanges();
-
- expect(mockInternalUserService.refreshAndGetUserState).toHaveBeenCalled();
- expect(mockInternalUserService.userRouteNavigate).toHaveBeenCalledWith(['success', { reason: 'already' }]);
+ it('should create', () => {
+ expect(component).toBeTruthy();
});
});
diff --git a/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.ts b/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.ts
index cf5f3643..2887f0a6 100644
--- a/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.ts
+++ b/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.ts
@@ -1,5 +1,4 @@
-import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
-import { InternalUserService } from '../internal-user-service/internal-user.service';
+import { Component, OnInit, ViewChild } from '@angular/core';
import { RouterOutlet, Router, ActivationStart } from '@angular/router';
@Component({
@@ -7,14 +6,12 @@ import { RouterOutlet, Router, ActivationStart } from '@angular/router';
templateUrl: './user-dialog.component.html',
styleUrls: ['./user-dialog.component.css']
})
-export class UserDialogComponent implements OnInit, OnDestroy {
+export class UserDialogComponent implements OnInit {
- constructor(private userService: InternalUserService, private router: Router) { }
+ constructor(private router: Router) { }
@ViewChild(RouterOutlet) outlet!: RouterOutlet;
- isLoading = true;
-
ngOnInit() {
// this is a workaround for a bug. see https://github.com/angular/angular/issues/20694
const subscription = this.router.events.subscribe(e => {
@@ -23,19 +20,5 @@ export class UserDialogComponent implements OnInit, OnDestroy {
subscription.unsubscribe();
}
});
-
-
- this.userService.refreshAndGetUserState().subscribe(result => {
- this.isLoading = false;
- if (result === 'success') {
- this.userService.userRouteNavigate(['success', { reason: 'already' }]);
- } else {
- this.userService.userRouteNavigate(['login', { reason: result }]);
- }
- });
- }
-
- ngOnDestroy() {
- this.userService.userRouteNavigate(null);
}
}
diff --git a/Timeline/ClientApp/src/app/user/user-login-success/user-login-success.component.spec.ts b/Timeline/ClientApp/src/app/user/user-login-success/user-login-success.component.spec.ts
index 1efbb5c7..ff253add 100644
--- a/Timeline/ClientApp/src/app/user/user-login-success/user-login-success.component.spec.ts
+++ b/Timeline/ClientApp/src/app/user/user-login-success/user-login-success.component.spec.ts
@@ -60,10 +60,8 @@ describe('UserLoginSuccessComponent', () => {
});
it('login success message should display well', () => {
- mockActivatedRoute.pushSnapshotWithParamMap({ reason: 'login' });
-
+ mockActivatedRoute.pushSnapshotWithParamMap({ fromlogin: 'true' });
fixture.detectChanges();
-
expect((fixture.debugElement.query(By.css('p.login-success-message')))).toBeTruthy();
});
});
diff --git a/Timeline/ClientApp/src/app/user/user-login-success/user-login-success.component.ts b/Timeline/ClientApp/src/app/user/user-login-success/user-login-success.component.ts
index 22f6a41f..2ae584d6 100644
--- a/Timeline/ClientApp/src/app/user/user-login-success/user-login-success.component.ts
+++ b/Timeline/ClientApp/src/app/user/user-login-success/user-login-success.component.ts
@@ -20,6 +20,6 @@ export class UserLoginSuccessComponent implements OnInit {
ngOnInit() {
this.userInfo = throwIfNullOrUndefined(this.userService.currentUserInfo, () => 'Route error. No login now!');
- this.displayLoginSuccessMessage = this.route.snapshot.paramMap.get('reason') === 'login';
+ this.displayLoginSuccessMessage = this.route.snapshot.paramMap.get('fromlogin') === 'true';
}
}
diff --git a/Timeline/ClientApp/src/app/user/user-login/user-login.component.spec.ts b/Timeline/ClientApp/src/app/user/user-login/user-login.component.spec.ts
index 9c9ee1dc..693d5b6e 100644
--- a/Timeline/ClientApp/src/app/user/user-login/user-login.component.spec.ts
+++ b/Timeline/ClientApp/src/app/user/user-login/user-login.component.spec.ts
@@ -7,7 +7,6 @@ import { ActivatedRoute } from '@angular/router';
import { of, throwError } from 'rxjs';
import { createMockInternalUserService } from '../internal-user-service/internal-user.service.mock';
-import { MockActivatedRoute } from '../../test-utilities/activated-route.mock';
import { UserLoginComponent } from './user-login.component';
import { InternalUserService } from '../internal-user-service/internal-user.service';
import { UserInfo } from '../entities';
@@ -16,17 +15,17 @@ describe('UserLoginComponent', () => {
let component: UserLoginComponent;
let fixture: ComponentFixture<UserLoginComponent>;
let mockInternalUserService: jasmine.SpyObj<InternalUserService>;
- let mockActivatedRoute: MockActivatedRoute;
beforeEach(async(() => {
mockInternalUserService = createMockInternalUserService();
- mockActivatedRoute = new MockActivatedRoute();
+
+ // mock property
+ (<any>mockInternalUserService).currentUserInfo = null;
TestBed.configureTestingModule({
declarations: [UserLoginComponent],
providers: [
- { provide: InternalUserService, useValue: mockInternalUserService },
- { provide: ActivatedRoute, useValue: mockActivatedRoute }
+ { provide: InternalUserService, useValue: mockInternalUserService }
],
imports: [ReactiveFormsModule],
schemas: [NO_ERRORS_SCHEMA]
@@ -77,22 +76,22 @@ describe('UserLoginComponent', () => {
component.onLoginButtonClick();
expect(mockInternalUserService.tryLogin).toHaveBeenCalledWith(mockValue);
- expect(mockInternalUserService.userRouteNavigate).toHaveBeenCalledWith(['success', { reason: 'login' }]);
+ expect(mockInternalUserService.userRouteNavigate).toHaveBeenCalledWith(['success', { fromlogin: 'true' }]);
});
describe('message display', () => {
it('nologin reason should display', () => {
- mockActivatedRoute.pushSnapshotWithParamMap({ reason: 'nologin' });
fixture.detectChanges();
- expect(component.message).toBe('nologin');
+ component.message = 'nologin';
+ fixture.detectChanges();
expect((fixture.debugElement.query(By.css('p.mat-body')).nativeElement as
HTMLParagraphElement).textContent).toBe('You haven\'t login.');
});
it('invalid login reason should display', () => {
- mockActivatedRoute.pushSnapshotWithParamMap({ reason: 'invalidlogin' });
fixture.detectChanges();
- expect(component.message).toBe('invalidlogin');
+ component.message = 'invalidlogin';
+ fixture.detectChanges();
expect((fixture.debugElement.query(By.css('p.mat-body')).nativeElement as
HTMLParagraphElement).textContent).toBe('Your login is no longer valid.');
});
diff --git a/Timeline/ClientApp/src/app/user/user-login/user-login.component.ts b/Timeline/ClientApp/src/app/user/user-login/user-login.component.ts
index 3505d50b..836202de 100644
--- a/Timeline/ClientApp/src/app/user/user-login/user-login.component.ts
+++ b/Timeline/ClientApp/src/app/user/user-login/user-login.component.ts
@@ -1,11 +1,10 @@
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
-import { ActivatedRoute } from '@angular/router';
import { InternalUserService } from '../internal-user-service/internal-user.service';
-export type LoginMessage = 'nologin' | 'invalidlogin' | string | null | undefined;
+export type LoginMessage = 'nologin' | 'invalidlogin' | string | null | undefined;
@Component({
selector: 'app-user-login',
@@ -14,7 +13,7 @@ export type LoginMessage = 'nologin' | 'invalidlogin' | string | null | undefine
})
export class UserLoginComponent implements OnInit {
- constructor(private route: ActivatedRoute, private userService: InternalUserService) { }
+ constructor(private userService: InternalUserService) { }
message: LoginMessage;
@@ -24,12 +23,15 @@ export class UserLoginComponent implements OnInit {
});
ngOnInit() {
- this.message = this.route.snapshot.paramMap.get('reason');
+ if (this.userService.currentUserInfo) {
+ throw new Error('Route error! Already login!');
+ }
+ this.message = 'nologin';
}
onLoginButtonClick() {
this.userService.tryLogin(this.form.value).subscribe(_ => {
- this.userService.userRouteNavigate(['success', { reason: 'login' }]);
+ this.userService.userRouteNavigate(['success', { fromlogin: 'true' }]);
}, (error: Error) => this.message = error.message);
}
}
diff --git a/Timeline/ClientApp/src/app/user/user.module.ts b/Timeline/ClientApp/src/app/user/user.module.ts
index c399c9e0..8f3b9a9c 100644
--- a/Timeline/ClientApp/src/app/user/user.module.ts
+++ b/Timeline/ClientApp/src/app/user/user.module.ts
@@ -2,24 +2,27 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { RouterModule } from '@angular/router';
import {
MatFormFieldModule, MatProgressSpinnerModule,
MatDialogModule, MatInputModule, MatButtonModule
} from '@angular/material';
+import { RequireNoLoginGuard, RequireLoginGuard } from './auth.guard';
import { UserDialogComponent } from './user-dialog/user-dialog.component';
import { UserLoginComponent } from './user-login/user-login.component';
import { UserLoginSuccessComponent } from './user-login-success/user-login-success.component';
+import { RedirectComponent } from './redirect.component';
import { UtilityModule } from '../utilities/utility.module';
-import { RouterModule } from '@angular/router';
-import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
@NgModule({
- declarations: [UserDialogComponent, UserLoginComponent, UserLoginSuccessComponent],
+ declarations: [UserDialogComponent, UserLoginComponent, UserLoginSuccessComponent, RedirectComponent],
imports: [
RouterModule.forChild([
- { path: 'login', component: UserLoginComponent, outlet: 'user' },
- { path: 'success', component: UserLoginSuccessComponent, outlet: 'user' }
+ { path: 'login', canActivate: [RequireNoLoginGuard], component: UserLoginComponent, outlet: 'user' },
+ { path: 'success', canActivate: [RequireLoginGuard], component: UserLoginSuccessComponent, outlet: 'user' },
+ { path: '**', component: RedirectComponent, outlet: 'user' }
]),
CommonModule, HttpClientModule, ReactiveFormsModule, BrowserAnimationsModule,
MatFormFieldModule, MatProgressSpinnerModule, MatDialogModule, MatInputModule, MatButtonModule,
diff --git a/Timeline/ClientApp/src/app/user/user.service.ts b/Timeline/ClientApp/src/app/user/user.service.ts
index e876706c..e7d50dd2 100644
--- a/Timeline/ClientApp/src/app/user/user.service.ts
+++ b/Timeline/ClientApp/src/app/user/user.service.ts
@@ -1,5 +1,6 @@
import { Injectable } from '@angular/core';
-import { MatDialog } from '@angular/material';
+import { MatDialog, MatDialogRef } from '@angular/material';
+import { Router, ActivationStart } from '@angular/router';
import { Observable } from 'rxjs';
@@ -15,7 +16,18 @@ import { UserDialogComponent } from './user-dialog/user-dialog.component';
providedIn: 'root'
})
export class UserService {
- constructor(private dialog: MatDialog, private internalService: InternalUserService) { }
+
+ private dialogRef: MatDialogRef<UserDialogComponent> | null = null;
+
+ constructor(router: Router, private dialog: MatDialog, private internalService: InternalUserService) {
+ router.events.subscribe(event => {
+ if (event instanceof ActivationStart && event.snapshot.outlet === 'user') {
+ if (!this.dialogRef) {
+ setTimeout(() => this.openUserDialog(), 0);
+ }
+ }
+ });
+ }
get currentUserInfo(): UserInfo | null {
return this.internalService.currentUserInfo;
@@ -25,9 +37,19 @@ export class UserService {
return this.internalService.userInfo$;
}
- openUserDialog() {
- this.dialog.open(UserDialogComponent, {
+ private openUserDialog() {
+ if (this.dialogRef) {
+ return;
+ }
+
+ this.dialogRef = this.dialog.open(UserDialogComponent, {
width: '300px'
});
+
+ const subscription = this.dialogRef.afterClosed().subscribe(_ => {
+ this.internalService.userRouteNavigate(null);
+ this.dialogRef = null;
+ subscription.unsubscribe();
+ });
}
}
diff --git a/Timeline/ClientApp/src/styles.css b/Timeline/ClientApp/src/styles.css
index fad44c53..f60c9204 100644
--- a/Timeline/ClientApp/src/styles.css
+++ b/Timeline/ClientApp/src/styles.css
@@ -1,6 +1,10 @@
/* You can add global styles to this file, and also import other style files */
@import "~@angular/material/prebuilt-themes/indigo-pink.css";
+html {
+ overflow: unset!important; /* why cdk-global-scrollblock add overflow-y: scroll ??????????? */
+}
+
body {
margin: 0;
}