Skip to content

Commit

Permalink
- Issue #39 - access app without login
Browse files Browse the repository at this point in the history
 - Issue #40 - query editor page has readonly modus without login
 - Issue #41 - import a query to ui by pasting or uploading
  • Loading branch information
thkoehler11 committed Sep 21, 2022
1 parent 29608cb commit 74f9f52
Show file tree
Hide file tree
Showing 42 changed files with 714 additions and 56 deletions.
3 changes: 2 additions & 1 deletion src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { RoleGuard } from './core/auth/guards/role.guard'
export const routes: Routes = [
{
path: 'home',
canLoad: [AuthGuard],
// canLoad: [AuthGuard],
canActivate: [RoleGuard],
data: {
navId: 'home',
},
Expand Down
8 changes: 6 additions & 2 deletions src/app/core/auth/guards/role.guard.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ActivatedRouteSnapshot, Route, RouterStateSnapshot } from '@angular/router'
import { ActivatedRouteSnapshot, Route, Router, RouterStateSnapshot } from '@angular/router'
import { OAuthService } from 'angular-oauth2-oidc'

import { RoleGuard } from './role.guard'
Expand All @@ -13,8 +13,12 @@ describe('RoleGuard', () => {
loadDiscoveryDocumentAndLogin: () => Promise.resolve(true),
} as unknown) as OAuthService

const router = {
navigate: ([]) => Promise.resolve(true),
} as Router

beforeEach(() => {
guard = new RoleGuard(authService)
guard = new RoleGuard(authService, router)
})

afterEach(() => {
Expand Down
8 changes: 7 additions & 1 deletion src/app/core/auth/guards/role.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,23 @@ import {
Route,
ActivatedRouteSnapshot,
RouterStateSnapshot,
Router,
} from '@angular/router'
import { OAuthService } from 'angular-oauth2-oidc'

@Injectable({
providedIn: 'root',
})
export class RoleGuard implements CanActivate, CanLoad {
constructor(private oauthService: OAuthService) {}
constructor(private oauthService: OAuthService, private router: Router) {}

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
const redirectUri = window.location.origin + state.url
const isLoggedIn =
this.oauthService.hasValidIdToken() && this.oauthService.hasValidAccessToken()
if (!isLoggedIn) {
this.router.navigate(['/querybuilder/editor'], { state: { preventReset: true } })
}
return this.isAllowed(route, redirectUri)
}

Expand Down
16 changes: 10 additions & 6 deletions src/app/core/auth/oauth-init.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ describe('OAuth Init Service', () => {

const authService = ({
configure: () => {},
loadDiscoveryDocumentAndLogin: () => Promise.resolve(true),
loadDiscoveryDocument: jest.fn().mockImplementation(),
tryLoginCodeFlow: jest.fn().mockImplementation(),
silentRefresh: jest.fn().mockImplementation(),
setupAutomaticSilentRefresh: () => {},
} as unknown) as OAuthService

Expand Down Expand Up @@ -47,20 +49,22 @@ describe('OAuth Init Service', () => {

it('Calls init on OAuth Server with correct config and options', async () => {
jest.spyOn(authService, 'configure')
jest.spyOn(authService, 'loadDiscoveryDocumentAndLogin')
jest.spyOn(authService, 'loadDiscoveryDocument').mockResolvedValue(undefined)
jest.spyOn(authService, 'tryLoginCodeFlow').mockResolvedValue(undefined)
jest.spyOn(authService, 'silentRefresh').mockResolvedValue(undefined)
jest.spyOn(authService, 'setupAutomaticSilentRefresh')

await initService.initOAuth()

expect(authService.configure).toHaveBeenCalledWith(authConfig)
expect(authService.loadDiscoveryDocumentAndLogin).toHaveBeenCalled()
expect(authService.loadDiscoveryDocument).toHaveBeenCalled()
expect(authService.setupAutomaticSilentRefresh).toHaveBeenCalled()
})
})

describe('When OAuth Server gets initialized with no success', () => {
it('fails', async () => {
jest.spyOn(authService, 'loadDiscoveryDocumentAndLogin').mockImplementation(() => {
jest.spyOn(authService, 'loadDiscoveryDocument').mockImplementation(() => {
return Promise.reject()
})

Expand All @@ -76,9 +80,9 @@ describe('OAuth Init Service', () => {
})

it('fails', async () => {
jest.spyOn(authService, 'loadDiscoveryDocumentAndLogin').mockImplementation(() => {
jest.spyOn(authService, 'loadDiscoveryDocument').mockImplementation(() => {
jest.advanceTimersByTime(25_000)
return Promise.resolve(true)
return Promise.reject()
})

initService.initOAuth().catch((error) => {
Expand Down
14 changes: 10 additions & 4 deletions src/app/core/auth/oauth-init.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,18 @@ export class OAuthInitService {
})

this.oauthService.configure(this.AUTH_CONFIG)
this.oauthService.setupAutomaticSilentRefresh()

const init = this.oauthService
.loadDiscoveryDocumentAndLogin()
.then(() => {
this.oauthService.setupAutomaticSilentRefresh()
.loadDiscoveryDocument()
.then(async () => {
await this.oauthService.tryLoginCodeFlow().catch(async () => {
await this.oauthService.silentRefresh().catch(() => {
return
})
})
})
.catch(() => {
.catch((error) => {
return reject(this.ERROR_UNREACHABLE)
})

Expand Down
13 changes: 12 additions & 1 deletion src/app/core/constants/navigation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import INavItem from '../../layout/models/nav-item.interface'
import { inject, InjectionToken } from '@angular/core'
import { FeatureService } from '../../service/feature.service'
import { AppConfigService } from '../../config/app-config.service'
import { FeatureProviderService } from '../../modules/querybuilder/service/feature-provider.service'

export const mainNavItems: INavItem[] = [
{
Expand Down Expand Up @@ -27,10 +31,17 @@ export const mainNavItems: INavItem[] = [
},
]

export const secondaryNavItems: INavItem[] = [
export const secondaryNavItemsLoggedIn: INavItem[] = [
{
routeTo: '#logout',
icon: 'sign-out-alt',
translationKey: 'NAVIGATION.SIGNOUT',
},
]
export const secondaryNavItemsLoggedOut: INavItem[] = [
{
routeTo: '#login',
icon: 'sign-in-alt',
translationKey: 'NAVIGATION.SIGNIN',
},
]
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { ActivatedRouteSnapshot, ActivationEnd, ActivationStart, Router } from '
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'
import { FeatureProviderService } from '../../../modules/querybuilder/service/feature-provider.service'
import { IAppConfig } from '../../../config/app-config.model'
import { HttpClientTestingModule } from '@angular/common/http/testing'

describe('AppLayoutComponent', () => {
let component: AppLayoutComponent
Expand All @@ -32,6 +33,7 @@ describe('AppLayoutComponent', () => {
const authService = {
logOut: () => {},
loadUserProfile: () => Promise.resolve({}),
hasValidAccessToken: () => true,
} as OAuthService

@Component({ selector: 'num-footer', template: '' })
Expand Down Expand Up @@ -81,6 +83,7 @@ describe('AppLayoutComponent', () => {
StubComponent,
],
imports: [
HttpClientTestingModule,
BrowserAnimationsModule,
MaterialModule,
FontAwesomeModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { OAuthService } from 'angular-oauth2-oidc'
import { DirectivesModule } from 'src/app/shared/directives/directives.module'
import { RoleGuard } from '../../../core/auth/guards/role.guard'
import { FeatureService } from '../../../service/feature.service'
import { HttpClientTestingModule } from '@angular/common/http/testing'

describe('SideMenuComponent', () => {
let component: SideMenuComponent
Expand All @@ -17,6 +18,7 @@ describe('SideMenuComponent', () => {
const authService = {
logOut: () => {},
loadUserProfile: () => Promise.resolve({}),
hasValidAccessToken: () => true,
} as OAuthService

beforeEach(async () => {
Expand All @@ -37,6 +39,7 @@ describe('SideMenuComponent', () => {
RouterTestingModule.withRoutes([]),
TranslateModule.forRoot(),
DirectivesModule,
HttpClientTestingModule,
],
providers: [
{
Expand Down
28 changes: 25 additions & 3 deletions src/app/layout/components/side-menu/side-menu.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
import { routes } from '../../../app-routing.module'
import INavItem from '../../models/nav-item.interface'
import { OAuthService } from 'angular-oauth2-oidc'
import { mainNavItems, secondaryNavItems } from '../../../core/constants/navigation'
import {
mainNavItems,
secondaryNavItemsLoggedIn,
secondaryNavItemsLoggedOut,
} from '../../../core/constants/navigation'
import { FeatureService } from '../../../service/feature.service'
import { OAuthInitService } from '../../../core/auth/oauth-init.service'

@Component({
selector: 'num-side-menu',
Expand All @@ -13,14 +18,16 @@ import { FeatureService } from '../../../service/feature.service'
export class SideMenuComponent implements OnInit {
routes = routes
mainNavItems = mainNavItems
secondaryNavItems = secondaryNavItems
secondaryNavItems: INavItem[]
isLoggedIn = false

@Input() isSideMenuExpanded = true
@Output() toggleSideMenu = new EventEmitter()

constructor(private oauthService: OAuthService, public featureService: FeatureService) {}

ngOnInit(): void {
this.handleUserInfo()
this.mainNavItems?.forEach((item) => {
let roles = item.roles ? item.roles : []
if (roles[0] === 'main') {
Expand All @@ -37,14 +44,18 @@ export class SideMenuComponent implements OnInit {
}
}
item.roles = roles
console.log('Sidemenu')
console.log(roles)
})
}

menuItemClicked($event: Event, item?: INavItem): void {
if (item?.routeTo === '#logout') {
this.oauthService.logOut()
}

if (item?.routeTo === '#login') {
this.oauthService.initCodeFlow()
}
const target = $event.currentTarget as HTMLElement
target.blur()
this.toggleSideMenu.emit({ item })
Expand All @@ -57,4 +68,15 @@ export class SideMenuComponent implements OnInit {
}
return showIt
}
handleUserInfo(): void {
const isLoggedIn = this.oauthService.hasValidAccessToken()
console.log('isLoggedIn:' + isLoggedIn)
if (isLoggedIn) {
this.isLoggedIn = true
this.secondaryNavItems = secondaryNavItemsLoggedIn
} else {
this.isLoggedIn = false
this.secondaryNavItems = secondaryNavItemsLoggedOut
}
}
}
2 changes: 2 additions & 0 deletions src/app/layout/font-awesome-icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { faEnvelope } from '@fortawesome/free-solid-svg-icons'
import { faCubes } from '@fortawesome/free-solid-svg-icons'
import { faBell } from '@fortawesome/free-solid-svg-icons'
import { faCog } from '@fortawesome/free-solid-svg-icons'
import { faSignInAlt } from '@fortawesome/free-solid-svg-icons'
import { faSignOutAlt } from '@fortawesome/free-solid-svg-icons'
import { faDna } from '@fortawesome/free-solid-svg-icons'
import { faPlus } from '@fortawesome/free-solid-svg-icons'
Expand Down Expand Up @@ -47,6 +48,7 @@ export const FONT_AWESOME_ICONS = [
faCubes,
faBell,
faCog,
faSignInAlt,
faSignOutAlt,
faDna,
faPlus,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export class DashboardComponent implements OnInit {
stylesheet: string

ngOnInit(): void {
console.log('dashboard')
this.init()
this.stylesheet = this.featureService.getStylesheet()
}
Expand All @@ -28,9 +29,11 @@ export class DashboardComponent implements OnInit {

async init(): Promise<void> {
const isLoggedIn = this.oauthService.hasValidAccessToken()
console.log(isLoggedIn)
if (isLoggedIn) {
const profile = await this.oauthService.loadUserProfile()
const roles = profile.groups
console.log(roles)
this.authTest = 'Hello ' + profile.name
if (roles) {
this.authTest = this.authTest + ', Roles: ' + roles.join(', ')
Expand Down
Loading

0 comments on commit 74f9f52

Please sign in to comment.