Browse Source

RX Everything!

pull/65/merge
Sebastian Stehle 9 years ago
parent
commit
d87093ee90
  1. 2
      src/Squidex.Domain.Users/UserExtensions.cs
  2. 4
      src/Squidex.Domain.Users/UserManagerExtensions.cs
  3. 2
      src/Squidex/Controllers/UI/Extensions.cs
  4. 4
      src/Squidex/app/features/content/pages/content/content-page.component.ts
  5. 9
      src/Squidex/app/features/dashboard/pages/dashboard-page.component.ts
  6. 6
      src/Squidex/app/framework/angular/can-deactivate.guard.spec.ts
  7. 2
      src/Squidex/app/framework/angular/can-deactivate.guard.ts
  8. 13
      src/Squidex/app/shared/guards/app-must-exist.guard.spec.ts
  9. 14
      src/Squidex/app/shared/guards/app-must-exist.guard.ts
  10. 13
      src/Squidex/app/shared/guards/must-be-authenticated.guard.spec.ts
  11. 16
      src/Squidex/app/shared/guards/must-be-authenticated.guard.ts
  12. 13
      src/Squidex/app/shared/guards/must-be-not-authenticated.guard.spec.ts
  13. 16
      src/Squidex/app/shared/guards/must-be-not-authenticated.guard.ts
  14. 6
      src/Squidex/app/shared/guards/resolve-app-languages.guard.spec.ts
  15. 16
      src/Squidex/app/shared/guards/resolve-app-languages.guard.ts
  16. 6
      src/Squidex/app/shared/guards/resolve-content.guard.spec.ts
  17. 16
      src/Squidex/app/shared/guards/resolve-content.guard.ts
  18. 8
      src/Squidex/app/shared/guards/resolve-published-schema.guard.spec.ts
  19. 18
      src/Squidex/app/shared/guards/resolve-published-schema.guard.ts
  20. 6
      src/Squidex/app/shared/guards/resolve-schema.guard.spec.ts
  21. 16
      src/Squidex/app/shared/guards/resolve-schema.guard.ts
  22. 6
      src/Squidex/app/shared/guards/resolve-user.guard.spec.ts
  23. 16
      src/Squidex/app/shared/guards/resolve-user.guard.ts
  24. 16
      src/Squidex/app/shared/interceptors/auth.interceptor.spec.ts
  25. 38
      src/Squidex/app/shared/interceptors/auth.interceptor.ts
  26. 49
      src/Squidex/app/shared/services/apps-store.service.spec.ts
  27. 18
      src/Squidex/app/shared/services/apps-store.service.ts
  28. 82
      src/Squidex/app/shared/services/auth.service.ts
  29. 14
      src/Squidex/app/shell/pages/internal/profile-menu.component.ts

2
src/Squidex.Domain.Users/UserExtensions.cs

@ -18,7 +18,7 @@ namespace Squidex.Domain.Users
{
public static class UserExtensions
{
public static void UpdateDisplayName(this IUser user, string displayName)
public static void SetDisplayName(this IUser user, string displayName)
{
user.SetClaim(SquidexClaimTypes.SquidexDisplayName, displayName);
}

4
src/Squidex.Domain.Users/UserManagerExtensions.cs

@ -56,7 +56,7 @@ namespace Squidex.Domain.Users
try
{
user.UpdateDisplayName(displayName);
user.SetDisplayName(displayName);
user.SetPictureUrlFromGravatar(email);
await DoChecked(() => userManager.CreateAsync(user), "Cannot create user.");
@ -92,7 +92,7 @@ namespace Squidex.Domain.Users
if (!string.IsNullOrWhiteSpace(displayName))
{
user.UpdateDisplayName(displayName);
user.SetDisplayName(displayName);
}
await DoChecked(() => userManager.UpdateAsync(user), "Cannot update user.");

2
src/Squidex/Controllers/UI/Extensions.cs

@ -19,7 +19,7 @@ namespace Squidex.Controllers.UI
public static Task<IdentityResult> UpdateAsync(this UserManager<IUser> userManager, IUser user, string email, string displayName)
{
user.UpdateEmail(email);
user.UpdateDisplayName(displayName);
user.SetDisplayName(displayName);
return userManager.UpdateAsync(user);
}

4
src/Squidex/app/features/content/pages/content/content-page.component.ts

@ -91,9 +91,9 @@ export class ContentPageComponent extends AppComponentBase implements CanCompone
});
}
public canDeactivate(): Observable<boolean> | Promise<boolean> | boolean {
public canDeactivate(): Observable<boolean> {
if (!this.contentForm.dirty) {
return true;
return Observable.of(true);
} else {
this.cancelDialog.show();

9
src/Squidex/app/features/dashboard/pages/dashboard-page.component.ts

@ -154,14 +154,7 @@ export class DashboardPageComponent extends AppComponentBase implements OnInit,
};
});
this.authenticationSubscription =
this.authService.isAuthenticated.subscribe(() => {
const user = this.authService.user;
if (user) {
this.profileDisplayName = user.displayName;
}
});
this.profileDisplayName = this.authService.user.displayName;
}
public showForum() {

6
src/Squidex/app/framework/angular/can-deactivate.guard.spec.ts

@ -5,6 +5,8 @@
* Copyright (c) Sebastian Stehle. All rights reserved
*/
import { Observable } from 'rxjs';
import { CanDeactivateGuard } from './can-deactivate.guard';
describe('CanDeactivateGuard', () => {
@ -15,13 +17,13 @@ describe('CanDeactivateGuard', () => {
canDeactivate: () => {
called = true;
return true;
return Observable.of(true);
}
};
const result = new CanDeactivateGuard().canDeactivate(component);
expect(result).toBeTruthy();
expect(result).toBeDefined();
expect(called).toBeTruthy();
});
});

2
src/Squidex/app/framework/angular/can-deactivate.guard.ts

@ -10,7 +10,7 @@ import { CanDeactivate } from '@angular/router';
import { Observable } from 'rxjs/Observable';
export interface CanComponentDeactivate {
canDeactivate(): Observable<boolean> | Promise<boolean> | boolean;
canDeactivate(): Observable<boolean>;
}
@Injectable()

13
src/Squidex/app/shared/guards/app-must-exist.guard.spec.ts

@ -6,6 +6,7 @@
*/
import { IMock, Mock } from 'typemoq';
import { Observable } from 'rxjs';
import { AppsStoreService } from 'shared';
@ -21,14 +22,14 @@ describe('AppMustExistGuard', () => {
it('should navigate to 404 page if app is not found', (done) => {
appsStore.setup(x => x.selectApp('my-app'))
.returns(() => Promise.resolve(false));
.returns(() => Observable.of(false));
const router = new RouterMockup();
const route = <any> { params: { appName: 'my-app' } };
const guard = new AppMustExistGuard(appsStore.object, <any>router);
guard.canActivate(route, <any>{})
.then(result => {
.subscribe(result => {
expect(result).toBeFalsy();
expect(router.lastNavigation).toEqual(['/404']);
@ -38,14 +39,14 @@ describe('AppMustExistGuard', () => {
it('should navigate to 404 page if app loading fails', (done) => {
appsStore.setup(x => x.selectApp('my-app'))
.returns(() => Promise.reject<boolean>('error'));
.returns(() => Observable.throw('error'));
const router = new RouterMockup();
const route = <any> { params: { appName: 'my-app' } };
const guard = new AppMustExistGuard(appsStore.object, <any>router);
guard.canActivate(route, <any>{})
.then(result => {
.subscribe(result => {
expect(result).toBeFalsy();
expect(router.lastNavigation).toEqual(['/404']);
@ -55,14 +56,14 @@ describe('AppMustExistGuard', () => {
it('should return true if app is found', (done) => {
appsStore.setup(x => x.selectApp('my-app'))
.returns(() => Promise.resolve(true));
.returns(() => Observable.of(true));
const router = new RouterMockup();
const route = <any> { params: { appName: 'my-app' } };
const guard = new AppMustExistGuard(appsStore.object, <any>router);
guard.canActivate(route, <any>{})
.then(result => {
.subscribe(result => {
expect(result).toBeTruthy();
expect(router.lastNavigation).toBeUndefined();

14
src/Squidex/app/shared/guards/app-must-exist.guard.ts

@ -7,6 +7,7 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { AppsStoreService } from './../services/apps-store.service';
@ -18,21 +19,20 @@ export class AppMustExistGuard implements CanActivate {
) {
}
public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
const appName = route.params['appName'];
const result =
this.appsStore.selectApp(appName)
.then(hasApp => {
if (!hasApp) {
.do(dto => {
if (!dto) {
this.router.navigate(['/404']);
}
return hasApp;
}, () => {
})
.catch(error => {
this.router.navigate(['/404']);
return false;
return Observable.of(false);
});
return result;

13
src/Squidex/app/shared/guards/must-be-authenticated.guard.spec.ts

@ -6,6 +6,7 @@
*/
import { IMock, Mock } from 'typemoq';
import { Observable } from 'rxjs';
import { AuthService } from 'shared';
@ -20,14 +21,14 @@ describe('MustBeAuthenticatedGuard', () => {
});
it('should navigate to default page if not authenticated', (done) => {
authService.setup(x => x.checkLogin())
.returns(() => Promise.resolve(false));
authService.setup(x => x.userChanges)
.returns(() => Observable.of(null));
const router = new RouterMockup();
const guard = new MustBeAuthenticatedGuard(authService.object, <any>router);
guard.canActivate(<any>{}, <any>{})
.then(result => {
.subscribe(result => {
expect(result).toBeFalsy();
expect(router.lastNavigation).toEqual(['']);
@ -36,14 +37,14 @@ describe('MustBeAuthenticatedGuard', () => {
});
it('should return true if authenticated', (done) => {
authService.setup(x => x.checkLogin())
.returns(() => Promise.resolve(true));
authService.setup(x => x.userChanges)
.returns(() => Observable.of(<any>{}));
const router = new RouterMockup();
const guard = new MustBeAuthenticatedGuard(authService.object, <any>router);
guard.canActivate(<any>{}, <any>{})
.then(result => {
.subscribe(result => {
expect(result).toBeTruthy();
expect(router.lastNavigation).toBeUndefined();

16
src/Squidex/app/shared/guards/must-be-authenticated.guard.ts

@ -7,6 +7,7 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthService } from './../services/auth.service';
@ -18,12 +19,13 @@ export class MustBeAuthenticatedGuard implements CanActivate {
) {
}
public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
return this.authService.checkLogin().then(isAuthenticated => {
if (!isAuthenticated) {
this.router.navigate(['']);
}
return isAuthenticated;
});
public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.authService.userChanges.first()
.do(user => {
if (!user) {
this.router.navigate(['']);
}
})
.map(user => !!user);
}
}

13
src/Squidex/app/shared/guards/must-be-not-authenticated.guard.spec.ts

@ -6,6 +6,7 @@
*/
import { IMock, Mock } from 'typemoq';
import { Observable } from 'rxjs';
import { AuthService } from 'shared';
@ -20,14 +21,14 @@ describe('MustBeNotAuthenticatedGuard', () => {
});
it('should navigate to app page if authenticated', (done) => {
authService.setup(x => x.checkLogin())
.returns(() => Promise.resolve(true));
authService.setup(x => x.userChanges)
.returns(() => Observable.of(<any>{}));
const router = new RouterMockup();
const guard = new MustBeNotAuthenticatedGuard(authService.object, <any>router);
guard.canActivate(<any>{}, <any>{})
.then(result => {
.subscribe(result => {
expect(result).toBeFalsy();
expect(router.lastNavigation).toEqual(['app']);
@ -36,14 +37,14 @@ describe('MustBeNotAuthenticatedGuard', () => {
});
it('should return true if not authenticated', (done) => {
authService.setup(x => x.checkLogin())
.returns(() => Promise.resolve(false));
authService.setup(x => x.userChanges)
.returns(() => Observable.of(null));
const router = new RouterMockup();
const guard = new MustBeNotAuthenticatedGuard(authService.object, <any>router);
guard.canActivate(<any>{}, <any>{})
.then(result => {
.subscribe(result => {
expect(result).toBeTruthy();
expect(router.lastNavigation).toBeUndefined();

16
src/Squidex/app/shared/guards/must-be-not-authenticated.guard.ts

@ -7,6 +7,7 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthService } from './../services/auth.service';
@ -18,12 +19,13 @@ export class MustBeNotAuthenticatedGuard implements CanActivate {
) {
}
public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
return this.authService.checkLogin().then(isAuthenticated => {
if (isAuthenticated) {
this.router.navigate(['app']);
}
return !isAuthenticated;
});
public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.authService.userChanges.first()
.do(user => {
if (user) {
this.router.navigate(['app']);
}
})
.map(user => !user);
}
}

6
src/Squidex/app/shared/guards/resolve-app-languages.guard.spec.ts

@ -43,7 +43,7 @@ describe('ResolveAppLanguagesGuard', () => {
const guard = new ResolveAppLanguagesGuard(appLanguagesService.object, <any>router);
guard.resolve(<any>route, <any>{})
.then(result => {
.subscribe(result => {
expect(result).toBeFalsy();
expect(router.lastNavigation).toEqual(['/404']);
@ -59,7 +59,7 @@ describe('ResolveAppLanguagesGuard', () => {
const guard = new ResolveAppLanguagesGuard(appLanguagesService.object, <any>router);
guard.resolve(<any>route, <any>{})
.then(result => {
.subscribe(result => {
expect(result).toBeFalsy();
expect(router.lastNavigation).toEqual(['/404']);
@ -77,7 +77,7 @@ describe('ResolveAppLanguagesGuard', () => {
const guard = new ResolveAppLanguagesGuard(appLanguagesService.object, <any>router);
guard.resolve(<any>route, <any>{})
.then(result => {
.subscribe(result => {
expect(result).toBe(languages);
done();

16
src/Squidex/app/shared/guards/resolve-app-languages.guard.ts

@ -7,6 +7,7 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { allParams } from 'framework';
@ -20,7 +21,7 @@ export class ResolveAppLanguagesGuard implements Resolve<AppLanguageDto[]> {
) {
}
public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<AppLanguageDto[]> {
public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<AppLanguageDto[]> {
const params = allParams(route);
const appName = params['appName'];
@ -30,19 +31,16 @@ export class ResolveAppLanguagesGuard implements Resolve<AppLanguageDto[]> {
}
const result =
this.appLanguagesService.getLanguages(appName).toPromise()
.then(dto => {
this.appLanguagesService.getLanguages(appName)
.do(dto => {
if (!dto) {
this.router.navigate(['/404']);
return null;
}
return dto;
}, error => {
})
.catch(error => {
this.router.navigate(['/404']);
return null;
return Observable.of(null);
});
return result;

6
src/Squidex/app/shared/guards/resolve-content.guard.spec.ts

@ -62,7 +62,7 @@ describe('ResolveContentGuard', () => {
const guard = new ResolveContentGuard(appsStore.object, <any>router);
guard.resolve(<any>route, <any>{})
.then(result => {
.subscribe(result => {
expect(result).toBeFalsy();
expect(router.lastNavigation).toEqual(['/404']);
@ -78,7 +78,7 @@ describe('ResolveContentGuard', () => {
const guard = new ResolveContentGuard(appsStore.object, <any>router);
guard.resolve(<any>route, <any>{})
.then(result => {
.subscribe(result => {
expect(result).toBeFalsy();
expect(router.lastNavigation).toEqual(['/404']);
@ -96,7 +96,7 @@ describe('ResolveContentGuard', () => {
const guard = new ResolveContentGuard(appsStore.object, <any>router);
guard.resolve(<any>route, <any>{})
.then(result => {
.subscribe(result => {
expect(result).toBe(content);
done();

16
src/Squidex/app/shared/guards/resolve-content.guard.ts

@ -7,6 +7,7 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { allParams } from 'framework';
@ -20,7 +21,7 @@ export class ResolveContentGuard implements Resolve<ContentDto> {
) {
}
public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<ContentDto> {
public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<ContentDto> {
const params = allParams(route);
const appName = params['appName'];
@ -42,19 +43,16 @@ export class ResolveContentGuard implements Resolve<ContentDto> {
}
const result =
this.contentsService.getContent(appName, schemaName, contentId).toPromise()
.then(dto => {
this.contentsService.getContent(appName, schemaName, contentId)
.do(dto => {
if (!dto) {
this.router.navigate(['/404']);
return null;
}
return dto;
}, error => {
})
.catch(error => {
this.router.navigate(['/404']);
return null;
return Observable.of(null);
});
return result;

8
src/Squidex/app/shared/guards/resolve-published-schema.guard.spec.ts

@ -51,7 +51,7 @@ describe('ResolvePublishedSchemaGuard', () => {
const guard = new ResolvePublishedSchemaGuard(schemasService.object, <any>router);
guard.resolve(<any>route, <any>{})
.then(result => {
.subscribe(result => {
expect(result).toBeFalsy();
expect(router.lastNavigation).toEqual(['/404']);
@ -67,7 +67,7 @@ describe('ResolvePublishedSchemaGuard', () => {
const guard = new ResolvePublishedSchemaGuard(schemasService.object, <any>router);
guard.resolve(<any>route, <any>{})
.then(result => {
.subscribe(result => {
expect(result).toBeFalsy();
expect(router.lastNavigation).toEqual(['/404']);
@ -85,7 +85,7 @@ describe('ResolvePublishedSchemaGuard', () => {
const guard = new ResolvePublishedSchemaGuard(schemasService.object, <any>router);
guard.resolve(<any>route, <any>{})
.then(result => {
.subscribe(result => {
expect(result).toBeFalsy();
expect(router.lastNavigation).toEqual(['/404']);
@ -103,7 +103,7 @@ describe('ResolvePublishedSchemaGuard', () => {
const guard = new ResolvePublishedSchemaGuard(schemasService.object, <any>router);
guard.resolve(<any>route, <any>{})
.then(result => {
.subscribe(result => {
expect(result).toBe(schema);
done();

18
src/Squidex/app/shared/guards/resolve-published-schema.guard.ts

@ -7,6 +7,7 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { allParams } from 'framework';
@ -20,7 +21,7 @@ export class ResolvePublishedSchemaGuard implements Resolve<SchemaDetailsDto> {
) {
}
public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<SchemaDetailsDto> {
public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<SchemaDetailsDto> {
const params = allParams(route);
const appName = params['appName'];
@ -36,19 +37,16 @@ export class ResolvePublishedSchemaGuard implements Resolve<SchemaDetailsDto> {
}
const result =
this.schemasService.getSchema(appName, schemaName).toPromise()
.then(dto => {
if (!dto || !dto.isPublished) {
this.schemasService.getSchema(appName, schemaName).map(dto => dto && dto.isPublished ? dto : null)
.do(dto => {
if (!dto) {
this.router.navigate(['/404']);
return null;
}
return dto;
}, error => {
})
.catch(error => {
this.router.navigate(['/404']);
return null;
return Observable.of(error);
});
return result;

6
src/Squidex/app/shared/guards/resolve-schema.guard.spec.ts

@ -51,7 +51,7 @@ describe('ResolveSchemaGuard', () => {
const guard = new ResolveSchemaGuard(schemasService.object, <any>router);
guard.resolve(<any>route, <any>{})
.then(result => {
.subscribe(result => {
expect(result).toBeFalsy();
expect(router.lastNavigation).toEqual(['/404']);
@ -67,7 +67,7 @@ describe('ResolveSchemaGuard', () => {
const guard = new ResolveSchemaGuard(schemasService.object, <any>router);
guard.resolve(<any>route, <any>{})
.then(result => {
.subscribe(result => {
expect(result).toBeFalsy();
expect(router.lastNavigation).toEqual(['/404']);
@ -85,7 +85,7 @@ describe('ResolveSchemaGuard', () => {
const guard = new ResolveSchemaGuard(schemasService.object, <any>router);
guard.resolve(<any>route, <any>{})
.then(result => {
.subscribe(result => {
expect(result).toBe(schema);
done();

16
src/Squidex/app/shared/guards/resolve-schema.guard.ts

@ -7,6 +7,7 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { allParams } from 'framework';
@ -20,7 +21,7 @@ export class ResolveSchemaGuard implements Resolve<SchemaDetailsDto> {
) {
}
public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<SchemaDetailsDto> {
public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<SchemaDetailsDto> {
const params = allParams(route);
const appName = params['appName'];
@ -36,19 +37,16 @@ export class ResolveSchemaGuard implements Resolve<SchemaDetailsDto> {
}
const result =
this.schemasService.getSchema(appName, schemaName).toPromise()
.then(dto => {
this.schemasService.getSchema(appName, schemaName)
.do(dto => {
if (!dto) {
this.router.navigate(['/404']);
return null;
}
return dto;
}, error => {
})
.catch(error => {
this.router.navigate(['/404']);
return null;
return Observable.of(error);
});
return result;

6
src/Squidex/app/shared/guards/resolve-user.guard.spec.ts

@ -43,7 +43,7 @@ describe('ResolveUserGuard', () => {
const guard = new ResolveUserGuard(usersService.object, <any>router);
guard.resolve(<any>route, <any>{})
.then(result => {
.subscribe(result => {
expect(result).toBeFalsy();
expect(router.lastNavigation).toEqual(['/404']);
@ -59,7 +59,7 @@ describe('ResolveUserGuard', () => {
const guard = new ResolveUserGuard(usersService.object, <any>router);
guard.resolve(<any>route, <any>{})
.then(result => {
.subscribe(result => {
expect(result).toBeFalsy();
expect(router.lastNavigation).toEqual(['/404']);
@ -77,7 +77,7 @@ describe('ResolveUserGuard', () => {
const guard = new ResolveUserGuard(usersService.object, <any>router);
guard.resolve(<any>route, <any>{})
.then(result => {
.subscribe(result => {
expect(result).toBe(user);
done();

16
src/Squidex/app/shared/guards/resolve-user.guard.ts

@ -7,6 +7,7 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { allParams } from 'framework';
@ -20,7 +21,7 @@ export class ResolveUserGuard implements Resolve<UserDto> {
) {
}
public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<UserDto> {
public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<UserDto> {
const params = allParams(route);
const userId = params['userId'];
@ -30,19 +31,16 @@ export class ResolveUserGuard implements Resolve<UserDto> {
}
const result =
this.userManagementService.getUser(userId).toPromise()
.then(dto => {
this.userManagementService.getUser(userId)
.do(dto => {
if (!dto) {
this.router.navigate(['/404']);
return null;
}
return dto;
}, error => {
})
.catch(error => {
this.router.navigate(['/404']);
return null;
return Observable.of(null);
});
return result;

16
src/Squidex/app/shared/interceptors/auth.interceptor.spec.ts

@ -8,6 +8,7 @@
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { HttpClient, HttpHeaders, HTTP_INTERCEPTORS } from '@angular/common/http';
import { inject, TestBed } from '@angular/core/testing';
import { Observable } from 'rxjs';
import { IMock, Mock, Times } from 'typemoq';
import {
@ -16,12 +17,11 @@ import {
AuthInterceptor
} from './../';
describe('AppClientsService', () => {
describe('AuthInterceptor', () => {
let authService: IMock<AuthService> = null;
beforeEach(() => {
authService = Mock.ofType(AuthService);
authService.setup(x => x.user).returns(() => { return <any>{ authToken: 'letmein' }; });
TestBed.configureTestingModule({
imports: [
@ -46,6 +46,8 @@ describe('AppClientsService', () => {
it('should append headers to request',
inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => {
authService.setup(x => x.userChanges).returns(() => { return Observable.of(<any>{ authToken: 'letmein' }); });
http.get('http://service/p/apps').subscribe();
const req = httpMock.expectOne('http://service/p/apps');
@ -59,6 +61,8 @@ describe('AppClientsService', () => {
it('should not append headers for no auth headers',
inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => {
authService.setup(x => x.userChanges).returns(() => { return Observable.of(<any>{ authToken: 'letmein' }); });
http.get('http://service/p/apps', { headers: new HttpHeaders().set('NoAuth', '') }).subscribe();
const req = httpMock.expectOne('http://service/p/apps');
@ -72,6 +76,8 @@ describe('AppClientsService', () => {
it('should not append headers for other requests',
inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => {
authService.setup(x => x.userChanges).returns(() => { return Observable.of(<any>{ authToken: 'letmein' }); });
http.get('http://cloud/p/apps').subscribe();
const req = httpMock.expectOne('http://cloud/p/apps');
@ -85,7 +91,7 @@ describe('AppClientsService', () => {
it(`should logout for 404 status code when user is expired.`,
inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => {
authService.setup(x => x.user).returns(() => { return <any>{ authToken: 'letmein', isExpired: true }; });
authService.setup(x => x.userChanges).returns(() => { return Observable.of(<any>{ authToken: 'letmein', isExpired: true }); });
http.get('http://service/p/apps').subscribe(
_ => { /* NOOP */ },
@ -102,6 +108,8 @@ describe('AppClientsService', () => {
it(`should logout for ${statusCode} status code`,
inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => {
authService.setup(x => x.userChanges).returns(() => { return Observable.of(<any>{ authToken: 'letmein' }); });
http.get('http://service/p/apps').subscribe(
_ => { /* NOOP */ },
_ => { /* NOOP */ });
@ -118,6 +126,8 @@ describe('AppClientsService', () => {
it(`should not logout for ${statusCode} status code`,
inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => {
authService.setup(x => x.userChanges).returns(() => { return Observable.of(<any>{ authToken: 'letmein' }); });
http.get('http://service/p/apps').subscribe(
_ => { /* NOOP */ },
_ => { /* NOOP */ });

38
src/Squidex/app/shared/interceptors/auth.interceptor.ts

@ -24,26 +24,30 @@ export class AuthInterceptor implements HttpInterceptor {
public intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.url.indexOf(this.baseUrl) === 0 && !req.headers.has('NoAuth')) {
const authReq = req.clone({
headers: req.headers
.set('Authorization', this.authService.user ? this.authService.user.authToken : '')
.set('Accept-Language', '*')
.set('Pragma', 'no-cache')
});
return this.authService.userChanges.first().switchMap(user => {
const token = user ? user.authToken : '';
const authReq = req.clone({
headers: req.headers
.set('Authorization', token)
.set('Accept-Language', '*')
.set('Pragma', 'no-cache')
});
return next.handle(authReq)
.catch((error: HttpErrorResponse) => {
if (error.status === 404 && (!this.authService.user || this.authService.user.isExpired)) {
this.authService.logoutRedirect();
return next.handle(authReq)
.catch((error: HttpErrorResponse) => {
if (error.status === 404 && (!user || user.isExpired)) {
this.authService.logoutRedirect();
return Observable.empty<Response>();
} else if (error.status === 401 || error.status === 403) {
this.authService.logoutRedirect();
return Observable.empty<Response>();
} else if (error.status === 401 || error.status === 403) {
this.authService.logoutRedirect();
return Observable.empty<Response>();
}
return Observable.throw(error);
});
return Observable.empty<Response>();
}
return Observable.throw(error);
});
});
} else {
return next.handle(req);
}

49
src/Squidex/app/shared/services/apps-store.service.spec.ts

@ -12,7 +12,6 @@ import {
AppDto,
AppsService,
AppsStoreService,
AuthService,
CreateAppDto,
DateTime
} from './../';
@ -24,23 +23,17 @@ describe('AppsStoreService', () => {
const newApp = new AppDto('id', 'new-name', 'Owner', now, now);
let appsService: IMock<AppsService>;
let authService: IMock<AuthService>;
beforeEach(() => {
appsService = Mock.ofType(AppsService);
authService = Mock.ofType(AuthService);
});
it('should load when authenticated once', () => {
authService.setup(x => x.isAuthenticated)
.returns(() => Observable.of(true))
.verifiable(Times.once());
it('should load automatically', () => {
appsService.setup(x => x.getApps())
.returns(() => Observable.of(oldApps))
.verifiable(Times.once());
const store = new AppsStoreService(authService.object, appsService.object);
const store = new AppsStoreService(appsService.object);
let result1: AppDto[] | null = null;
let result2: AppDto[] | null = null;
@ -60,10 +53,6 @@ describe('AppsStoreService', () => {
});
it('should add app to cache when created', () => {
authService.setup(x => x.isAuthenticated)
.returns(() => Observable.of(true))
.verifiable(Times.once());
appsService.setup(x => x.getApps())
.returns(() => Observable.of(oldApps))
.verifiable(Times.once());
@ -72,7 +61,7 @@ describe('AppsStoreService', () => {
.returns(() => Observable.of(newApp))
.verifiable(Times.once());
const store = new AppsStoreService(authService.object, appsService.object);
const store = new AppsStoreService(appsService.object);
let result1: AppDto[] | null = null;
let result2: AppDto[] | null = null;
@ -93,42 +82,14 @@ describe('AppsStoreService', () => {
appsService.verifyAll();
});
it('should not add app to cache when cache is null', () => {
authService.setup(x => x.isAuthenticated)
.returns(() => Observable.of(false))
.verifiable(Times.once());
appsService.setup(x => x.postApp(It.isAny()))
.returns(() => Observable.of(newApp))
.verifiable(Times.once());
const store = new AppsStoreService(authService.object, appsService.object);
let result: AppDto[] | null = null;
store.createApp(new CreateAppDto('new-name'), now).subscribe(x => { /* Do Nothing */ });
store.apps.subscribe(x => {
result = x;
}).unsubscribe();
expect(result).toBeNull();
appsService.verifyAll();
});
it('should select app', (done) => {
authService.setup(x => x.isAuthenticated)
.returns(() => Observable.of(true))
.verifiable(Times.once());
appsService.setup(x => x.getApps())
.returns(() => Observable.of(oldApps))
.verifiable(Times.once());
const store = new AppsStoreService(authService.object, appsService.object);
const store = new AppsStoreService(appsService.object);
store.selectApp('old-name').then((isSelected) => {
store.selectApp('old-name').subscribe(isSelected => {
expect(isSelected).toBeTruthy();
appsService.verifyAll();

18
src/Squidex/app/shared/services/apps-store.service.ts

@ -16,8 +16,6 @@ import {
CreateAppDto
} from './apps.service';
import { AuthService } from './auth.service';
@Injectable()
export class AppsStoreService {
private readonly apps$ = new ReplaySubject<AppDto[]>();
@ -32,21 +30,21 @@ export class AppsStoreService {
}
constructor(
private readonly authService: AuthService,
private readonly appsService: AppsService
) {
if (!appsService) {
return;
}
this.authService.isAuthenticated.filter(t => !!t).first()
.switchMap(() => this.appsService.getApps())
this.appsService.getApps()
.subscribe(apps => {
this.apps$.next(apps);
}, error => {
this.apps$.next([]);
});
}
public selectApp(name: string | null): Promise<boolean> {
public selectApp(name: string | null): Observable<boolean> {
return Observable.create((observer: Observer<boolean>) => {
this.apps$.subscribe(apps => {
const app = apps.find(x => x.name === name) || null;
@ -58,7 +56,7 @@ export class AppsStoreService {
}, error => {
observer.error(error);
});
}).toPromise();
});
}
public createApp(dto: CreateAppDto, now?: DateTime): Observable<AppDto> {
@ -69,10 +67,8 @@ export class AppsStoreService {
return new AppDto(created.id, dto.name, 'Owner', now, now);
})
.do(app => {
this.apps$.defaultIfEmpty().first().subscribe(apps => {
if (apps) {
this.apps$.next(apps.concat([app]));
}
this.apps$.first().subscribe(apps => {
this.apps$.next(apps.concat([app]));
});
});
}

82
src/Squidex/app/shared/services/auth.service.ts

@ -6,7 +6,7 @@
*/
import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { Observable, ReplaySubject } from 'rxjs';
import {
Log,
@ -54,22 +54,15 @@ export class Profile {
@Injectable()
export class AuthService {
private readonly userManager: UserManager;
private readonly isAuthenticatedChanged$ = new Subject<boolean>();
private loginCompleted: boolean | null = false;
private loginCache: Promise<boolean> | null = null;
private currentUser: Profile | null = null;
private readonly isAuthenticatedChangedPublished$ =
this.isAuthenticatedChanged$
.distinctUntilChanged()
.publishReplay(1);
private readonly user$ = new ReplaySubject<Profile | null>();
private currentUser: Profile = null;
public get user(): Profile | null {
return this.currentUser;
}
public get isAuthenticated(): Observable<boolean> {
return this.isAuthenticatedChangedPublished$;
public get userChanges(): Observable<Profile | null> {
return this.user$;
}
constructor(apiUrl: ApiUrlConfig) {
@ -92,27 +85,18 @@ export class AuthService {
});
this.userManager.events.addUserLoaded(user => {
this.onAuthenticated(user);
this.user$.next(new Profile(user));
});
this.userManager.events.addUserUnloaded(() => {
this.onDeauthenticated();
this.user$.next(null);
});
this.checkLogin();
this.isAuthenticatedChangedPublished$.connect();
}
this.user$.subscribe(user => {
this.currentUser = user;
});
public checkLogin(): Promise<boolean> {
if (this.loginCompleted) {
return Promise.resolve(this.currentUser !== null);
} else {
if (!this.loginCache) {
this.loginCache = this.checkState(this.userManager.signinSilent());
}
return this.loginCache;
}
this.userManager.signinSilent();
}
public logoutRedirect(): Observable<any> {
@ -123,6 +107,10 @@ export class AuthService {
return Observable.fromPromise(this.userManager.signoutRedirectCallback());
}
public loginPopup(): Observable<any> {
return Observable.fromPromise(this.userManager.signinPopup());
}
public loginRedirect(): Observable<any> {
return Observable.fromPromise(this.userManager.signinRedirect());
}
@ -130,44 +118,4 @@ export class AuthService {
public loginRedirectComplete(): Observable<any> {
return Observable.fromPromise(this.userManager.signinRedirectCallback());
}
public loginPopup(): Observable<boolean> {
const promise = this.checkState(this.userManager.signinPopup());
return Observable.fromPromise(promise);
}
private onAuthenticated(user: User) {
this.currentUser = new Profile(user);
this.isAuthenticatedChanged$.next(true);
}
private onDeauthenticated() {
this.currentUser = null;
this.isAuthenticatedChanged$.next(false);
}
private checkState(promise: Promise<User>): Promise<boolean> {
const resultPromise =
promise
.then(user => {
this.loginCache = null;
this.loginCompleted = null;
this.onAuthenticated(user);
return !!this.currentUser;
}).catch((err) => {
this.loginCache = null;
this.loginCompleted = null;
this.onDeauthenticated();
return false;
});
return resultPromise;
}
}

14
src/Squidex/app/shell/pages/internal/profile-menu.component.ts

@ -47,16 +47,12 @@ export class ProfileMenuComponent implements OnInit, OnDestroy {
public ngOnInit() {
this.authenticationSubscription =
this.authService.isAuthenticated.first()
.subscribe(() => {
const user = this.authService.user;
this.authService.userChanges.filter(user => !!user)
.subscribe(user => {
this.profileId = user.id;
this.profileDisplayName = user.displayName;
if (user) {
this.profileId = user.id;
this.profileDisplayName = user.displayName;
this.isAdmin = user.isAdmin;
}
this.isAdmin = user.isAdmin;
});
}

Loading…
Cancel
Save