KEYCLOAK-6494: Address load time of new acct mgt console (#5100)

* Optimize loading. min bundles, stop double-loading, rxjs-system instead of
plain rxjs, clean up 404's

* Create module loading hierarchy.  Allows for lazy loading.

* Upgrade NG, remove jquery, load keycloak.js only from auth/js

* Delay systemjs loading.  Load home page instead of account.

* KEYCLOAK-6496: Cleanup and polish code after optimizations.

* Fix message bundle to be back the way it was.

* Remove unused png's. Remove comments in index.ftl. Remove javaMessages.
This commit is contained in:
Stan Silvert authored and GitHub committed 2018-03-27 12:42:13 -04:00
1 parent ffeb0420bf
commit 80feb67fc2
37 files changed
+1278 -362

No files matched your search

@@ -40,6 +40,7 @@ import org.keycloak.services.managers.Auth;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.util.ResolveRelative;
import org.keycloak.services.validation.Validation;
import org.keycloak.theme.beans.MessageFormatterMethod;
/**
* Created by st on 29/03/17.
@@ -101,7 +102,8 @@ public class AccountConsole {
if (auth != null) {
Locale locale = session.getContext().resolveLocale(auth.getUser());
map.put("locale", locale.toLanguageTag());
map.put("msg", messagesToJsonString(theme.getMessages(locale)));
Properties messages = theme.getMessages(locale);
map.put("msg", messagesToJsonString(messages));
}
} catch (Exception e) {
logger.warn("Failed to load messages", e);
@@ -8,7 +8,7 @@
var baseUrl = '${baseUrl}';
var realm = '${realm}';
var resourceUrl = '${resourceUrl}';
<#if referrer??>
var referrer = '${referrer}';
var referrer_uri = '${referrer_uri}';
@@ -17,6 +17,9 @@
<#if msg??>
var locale = '${locale}';
var l18n_msg = JSON.parse('${msg?no_esc}');
<#else>
var locale = 'en';
var l18n_msg = {};
</#if>
</script>
@@ -29,16 +32,6 @@
<link rel="icon" href="${resourceUrl}/app/assets/img/favicon.ico" type="image/x-icon"/>
<#if properties.styles?has_content>
<#list properties.styles?split(' ') as style>
<link href="${resourceUrl}/${style}" rel="stylesheet"/>
</#list>
</#if>
<link rel="stylesheet" href="${resourceUrl}/styles.css">
<!--<script src="${authUrl}/js/${resourceVersion}/keycloak.js" type="text/javascript"></script>-->
<!-- PatternFly -->
<!-- iPad retina icon -->
<link rel="apple-touch-icon-precomposed" sizes="152x152"
@@ -65,56 +58,150 @@
media="screen, print">
<link href="${resourceUrl}/node_modules/patternfly/dist/css/patternfly-additions.min.css" rel="stylesheet"
media="screen, print">
<script src="${resourceUrl}/node_modules/jquery/dist/jquery.min.js"></script>
<script src="${resourceUrl}/node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="${resourceUrl}/node_modules/jquery-match-height/dist/jquery.matchHeight-min.js"></script>
<script src="${resourceUrl}/node_modules/patternfly/dist/js/patternfly.min.js"></script>
<!-- Polyfill(s) for older browsers -->
<script src="${resourceUrl}/node_modules/core-js/client/shim.min.js"></script>
<script src="${resourceUrl}/node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="${resourceUrl}/node_modules/patternfly/dist/js/patternfly.min.js"></script>
<script src="${authUrl}/js/keycloak.js"></script>
<script>
var keycloak = Keycloak('${authUrl}/realms/${realm}/account/keycloak.json');
keycloak.init({onLoad: 'check-sso'}).success(function(authenticated) {
var loadjs = function (url,loadListener) {
const script = document.createElement("script");
script.src = resourceUrl + url;
if (loadListener) script.addEventListener("load", loadListener);
document.head.appendChild(script);
};
loadjs("/node_modules/core-js/client/shim.min.js", function(){
loadjs("/node_modules/zone.js/dist/zone.min.js");
loadjs("/node_modules/systemjs/dist/system.src.js", function() {
loadjs("/systemjs.config.js");
System.import('${resourceUrl}/main.js').catch(function (err) {
console.error(err);
});
if (!keycloak.authenticated) document.getElementById("signInButton").style.visibility='visible';
});
});
}).error(function() {
alert('failed to initialize keycloak');
});
</script>
<!-- TODO: We should save these css and js into variables and then load in
main.ts for better performance. These might be loaded twice.
-->
<#if properties.styles?has_content>
<#list properties.styles?split(' ') as style>
<link href="${resourceUrl}/${style}" rel="stylesheet"/>
</#list>
<a href="../../../../../../../../keycloak-quickstarts/app-profile-jee-html5/src/main/webapp/index.html"></a>
</#if>
<#if properties.scripts?has_content>
<#list properties.scripts?split(' ') as script>
<script type="text/javascript" src="${resourceUrl}/${script}"></script>
<script type="text/javascript" src="${resourceUrl}/${script}"></script>
</#list>
</#if>
</head>
<body>
<script src="${resourceUrl}/node_modules/zone.js/dist/zone.js"></script>
<script src="${resourceUrl}/node_modules/systemjs/dist/system.src.js"></script>
<!-- Top Navigation -->
<nav class="navbar navbar-pf-alt">
<script src="${resourceUrl}/systemjs.config.js"></script>
<script>
System.import('${resourceUrl}/main.js').catch(function (err) {
console.error(err);
});
</script>
</head>
<div class="navbar-header">
<a href="http://www.keycloak.org" class="navbar-brand">
<img class="navbar-brand-icon" type="image/svg+xml" src="${resourceUrl}/app/assets/img/keycloak-logo-min.png" alt="" width="auto" height="30px"/>
</a>
</div>
<nav class="collapse navbar-collapse">
<ul class="nav navbar-nav">
</ul>
<!-- This sign in button is only displayed in the rare case where we go directly to this page and we aren't logged in.
Note javascript code above that changes its visibility for that case. Also, because we are not logged in
we are unable to localize the button's message. Not sure what to do about that yet.
-->
<ul class="nav navbar-nav navbar-right navbar-iconic">
<li><button id="signInButton" style="visibility:hidden" onclick="keycloak.login();" class="btn btn-primary btn-lg" type="button">Sign In</button></li>
</ul>
</nav>
</nav>
<!--Top Nav -->
<!-- Home Page --->
<div class="cards-pf" id="welcomeScreen">
<div><h1 class="text-center">Welcome to Keycloak Account Management</h1></div>
<div class="container-fluid container-cards-pf">
<div class="row row-cards-pf">
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-3">
<div class="card-pf card-pf-view card-pf-view-select card-pf-view-single-select">
<div class="card-pf-body">
<div class="card-pf-top-element">
<span class="fa pficon-user card-pf-icon-circle"></span>
</div>
<h2 class="card-pf-title text-center">
Personal Info
</h2>
<h3 class="card-pf-info text-center">
<a href="${baseUrl}/#/account">Account</a>
</h3>
</div>
</div>
</div>
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-3">
<div class="card-pf card-pf-view card-pf-view-select card-pf-view-single-select">
<div class="card-pf-body">
<div class="card-pf-top-element">
<span class="fa fa-shield card-pf-icon-circle"></span>
</div>
<h2 class="card-pf-title text-center">
Account Security
</h2>
<h3 class="card-pf-info text-center">
More stuff goes here
</h3>
</div>
</div>
</div>
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-3">
<div class="card-pf card-pf-view card-pf-view-select card-pf-view-single-select">
<div class="card-pf-body">
<div class="card-pf-top-element">
<span class="fa fa-th card-pf-icon-circle"></span>
</div>
<h2 class="card-pf-title text-center">
Applications
</h2>
<h3 class="card-pf-info text-center">
More stuff goes here
</h3>
</div>
</div>
</div>
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-3">
<div class="card-pf card-pf-view card-pf-view-select card-pf-view-single-select">
<div class="card-pf-body">
<div class="card-pf-top-element">
<span class="fa pficon-repository card-pf-icon-circle"></span>
</div>
<h2 class="card-pf-title text-center">
My Resources
</h2>
<h3 class="card-pf-info text-center">
More stuff goes here
</h3>
</div>
</div>
</div>
<app-root>
<style>
.kc-background {
background: url('${resourceUrl}/img/keycloak-bg.png') top left no-repeat;
background-size: cover;
}
.logo-centered {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.kc-logo-text {
background-image: url("${resourceUrl}/img/keycloak-logo-text.png");
background-repeat: no-repeat;
width: 250px;
height: 38px;
}
</style>
<body class="cards-pf kc-background">
<div class='logo-centered kc-logo-text'/>
</body>
</app-root>
</div>
</div>
</div>
<app-root></app-root>
</body>
</html>
@@ -0,0 +1,44 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import {KeycloakGuard} from './keycloak-service/keycloak.guard';
import { HomePageComponent } from './content/home-page/home-page.component';
declare const resourceUrl: string;
export const routes: Routes = [
{path: '', canActivateChild:[KeycloakGuard], children: [
{ path: 'account', loadChildren: resourceUrl + '/app/content/account-page/account.module.js#AccountModule' },
{ path: 'password', loadChildren: resourceUrl + '/app/content/password-page/password.module.js#PasswordModule' },
{ path: 'authenticator', loadChildren: resourceUrl + '/app/content/authenticator-page/authenticator.module.js#AuthenticatorModule' },
{ path: 'sessions', loadChildren: resourceUrl + '/app/content/sessions-page/sessions.module.js#SessionsModule' },
{ path: 'applications', loadChildren: resourceUrl + '/app/content/applications-page/applications.module.js#ApplicationsModule' },
{ path: ':**', loadChildren: resourceUrl + '/app/content/page-not-found/page-not-found.module.js#PageNotFoundModule' },
]
}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
declarations: [HomePageComponent]
})
export class AppRoutingModule {}
@@ -1,9 +1,11 @@
<app-top-nav></app-top-nav>
<app-side-nav></app-side-nav>
<div *ngIf="kcService.authenticated()">
<app-top-nav [showSideNav]="showSideNav"></app-top-nav>
</div>
<app-side-nav *ngIf="showSideNav"></app-side-nav>
<div class="container-fluid container-pf-alt-nav-pf-vertical-alt {{this.contentWidthClass}}"> <!-- collapsed-nav hidden-nav -->
<router-outlet></router-outlet>
</div>
@@ -15,10 +15,12 @@
* limitations under the License.
*/
import {Component, HostListener} from '@angular/core';
import {Router, NavigationEnd} from '@angular/router';
import 'rxjs/add/observable/of'; // needed for ngx-translate
import {TranslateService} from '@ngx-translate/core';
import {ResponsivenessService, ContentWidthClass, MenuClickListener} from "./responsiveness-service/responsiveness.service";
import {KeycloakService} from "./keycloak-service/keycloak.service";
declare const locale: string;
@@ -30,8 +32,12 @@ declare const locale: string;
export class AppComponent implements MenuClickListener {
private contentWidthClass: ContentWidthClass = this.respSvc.calcSideContentWidthClass();
private showSideNav: boolean = false;
constructor(translate: TranslateService, private respSvc: ResponsivenessService) {
constructor(translate: TranslateService,
private respSvc: ResponsivenessService,
private router: Router,
private kcService: KeycloakService) {
// this language will be used as a fallback when a translation isn't found in the current language
translate.setDefaultLang('en');
@@ -39,6 +45,19 @@ export class AppComponent implements MenuClickListener {
translate.use(locale);
this.respSvc.addMenuClickListener(this);
// show side nav if we are past the welcome screen
this.router.events.subscribe(value => {
if (value instanceof NavigationEnd) {
const navEnd = value as NavigationEnd;
console.log(navEnd.url);
if (navEnd.url !== '/') {
this.showSideNav = true;
var welcomeScreen = document.getElementById('welcomeScreen')
if (welcomeScreen) welcomeScreen.remove();
}
}
});
}
public menuClicked() : void {
@@ -26,6 +26,7 @@ import { TranslateModule } from '@ngx-translate/core';
import { KeycloakService } from './keycloak-service/keycloak.service';
import { KEYCLOAK_HTTP_PROVIDER } from './keycloak-service/keycloak.http';
import {KeycloakGuard} from './keycloak-service/keycloak.guard';
import {ResponsivenessService} from './responsiveness-service/responsiveness.service'
@@ -38,55 +39,15 @@ import { TopNavComponent } from './top-nav/top-nav.component';
import { NotificationComponent } from './top-nav/notification.component';
import { ToastNotifier } from './top-nav/toast.notifier';
import { SideNavComponent } from './side-nav/side-nav.component';
import { AccountPageComponent } from './content/account-page/account-page.component';
import { PasswordPageComponent } from './content/password-page/password-page.component';
import { PageNotFoundComponent } from './content/page-not-found/page-not-found.component';
import { AuthenticatorPageComponent } from './content/authenticator-page/authenticator-page.component';
import { SessionsPageComponent } from './content/sessions-page/sessions-page.component';
import { LargeSessionCardComponent } from './content/sessions-page/large-session-card.component';
import { SmallSessionCardComponent } from './content/sessions-page/small-session-card.component';
import { ApplicationsPageComponent } from './content/applications-page/applications-page.component';
import { LargeAppCardComponent } from './content/applications-page/large-app-card.component';
import { SmallAppCardComponent } from './content/applications-page/small-app-card.component';
import { RowAppCardComponent } from './content/applications-page/row-app-card.component';
import { ToolbarComponent } from './content/widgets/toolbar.component';
import {OrderbyPipe} from './pipes/orderby.pipe';
import {FilterbyPipe} from './pipes/filterby.pipe';
const routes: Routes = [
{ path: 'account', component: AccountPageComponent },
{ path: 'password', component: PasswordPageComponent },
{ path: 'authenticator', component: AuthenticatorPageComponent },
{ path: 'sessions', component: SessionsPageComponent },
{ path: 'applications', component: ApplicationsPageComponent },
{ path: '', redirectTo: '/account', pathMatch: 'full' },
{ path: '**', component: PageNotFoundComponent}
];
/* Routing Module */
import { AppRoutingModule } from './app-routing.module';
const decs = [
AppComponent,
TopNavComponent,
NotificationComponent,
SideNavComponent,
AccountPageComponent,
PasswordPageComponent,
PageNotFoundComponent,
AuthenticatorPageComponent,
SessionsPageComponent,
LargeSessionCardComponent,
SmallSessionCardComponent,
ApplicationsPageComponent,
LargeAppCardComponent,
SmallAppCardComponent,
RowAppCardComponent,
ToolbarComponent,
OrderbyPipe,
FilterbyPipe
];
export const ORIGINAL_INCOMING_URL: Location = window.location;
@@ -100,10 +61,11 @@ export const ORIGINAL_INCOMING_URL: Location = window.location;
TranslateModule.forRoot({
loader: {provide: TranslateLoader, useClass: DeclaredVarTranslateLoader}
}),
RouterModule.forRoot(routes)
AppRoutingModule,
],
providers: [
KeycloakService,
KeycloakGuard,
KEYCLOAK_HTTP_PROVIDER,
ResponsivenessService,
AccountServiceClient,
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

@@ -0,0 +1,31 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AccountPageComponent } from './account-page.component';
const routes: Routes = [
{ path: '**', component: AccountPageComponent },
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AccountRoutingModule {}
@@ -0,0 +1,34 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { TranslateModule } from '@ngx-translate/core';
import { AccountPageComponent } from './account-page.component';
import { AccountRoutingModule } from './account-routing.module';
@NgModule({
imports: [ CommonModule, FormsModule, TranslateModule, AccountRoutingModule ],
declarations: [ AccountPageComponent ],
providers: [ ]
})
export class AccountModule {}
@@ -0,0 +1,31 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ApplicationsPageComponent } from './applications-page.component';
const routes: Routes = [
{ path: '**', component: ApplicationsPageComponent },
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class ApplicationsRoutingModule {}
@@ -0,0 +1,44 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import {WidgetsModule} from '../widgets/widgets.module';
import { ApplicationsPageComponent } from './applications-page.component';
import { ApplicationsRoutingModule } from './applications-routing.module';
import { LargeAppCardComponent } from './large-app-card.component';
import { SmallAppCardComponent } from './small-app-card.component';
import { RowAppCardComponent } from './row-app-card.component';
@NgModule({
imports: [ CommonModule,
TranslateModule,
ApplicationsRoutingModule,
WidgetsModule ],
declarations: [ ApplicationsPageComponent,
LargeAppCardComponent,
SmallAppCardComponent,
RowAppCardComponent ],
providers: [ ]
})
export class ApplicationsModule {}
@@ -0,0 +1,31 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AuthenticatorPageComponent } from './authenticator-page.component';
const routes: Routes = [
{ path: '**', component: AuthenticatorPageComponent },
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AuthenticatorRoutingModule {}
@@ -0,0 +1,33 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { AuthenticatorPageComponent } from './authenticator-page.component';
import { AuthenticatorRoutingModule } from './authenticator-routing.module';
@NgModule({
imports: [ CommonModule, TranslateModule, AuthenticatorRoutingModule ],
declarations: [ AuthenticatorPageComponent ],
providers: [ ]
})
export class AuthenticatorModule {}
@@ -0,0 +1,68 @@
<div class="cards-pf">
<div><h1 class="text-center">Welcome to Keycloak Account Management</h1></div>
<div class="container-fluid container-cards-pf">
<div class="row row-cards-pf">
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-3">
<div class="card-pf card-pf-view card-pf-view-select card-pf-view-single-select">
<div class="card-pf-body">
<div class="card-pf-top-element">
<span class="fa pficon-user card-pf-icon-circle"></span>
</div>
<h2 class="card-pf-title text-center">
Personal Info
</h2>
<h3 class="card-pf-info text-center">
More stuff goes here
</h3>
</div>
</div>
</div>
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-3">
<div class="card-pf card-pf-view card-pf-view-select card-pf-view-single-select">
<div class="card-pf-body">
<div class="card-pf-top-element">
<span class="fa fa-shield card-pf-icon-circle"></span>
</div>
<h2 class="card-pf-title text-center">
Account Security
</h2>
<h3 class="card-pf-info text-center">
More stuff goes here
</h3>
</div>
</div>
</div>
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-3">
<div class="card-pf card-pf-view card-pf-view-select card-pf-view-single-select">
<div class="card-pf-body">
<div class="card-pf-top-element">
<span class="fa fa-th card-pf-icon-circle"></span>
</div>
<h2 class="card-pf-title text-center">
Applications
</h2>
<h3 class="card-pf-info text-center">
More stuff goes here
</h3>
</div>
</div>
</div>
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-3">
<div class="card-pf card-pf-view card-pf-view-select card-pf-view-single-select">
<div class="card-pf-body">
<div class="card-pf-top-element">
<span class="fa pficon-repository card-pf-icon-circle"></span>
</div>
<h2 class="card-pf-title text-center">
My Resources
</h2>
<h3 class="card-pf-info text-center">
More stuff goes here
</h3>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -0,0 +1,28 @@
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Component} from '@angular/core';
@Component({
selector: 'home-page',
templateUrl: './home-page.component.html',
styleUrls: ['./home-page.component.css']
})
export class HomePageComponent {
constructor() {}
}
@@ -0,0 +1,31 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { PageNotFoundComponent } from './page-not-found.component';
const routes: Routes = [
{ path: '**', component: PageNotFoundComponent}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class PageNotFoundRoutingModule {}
@@ -0,0 +1,34 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { TranslateModule } from '@ngx-translate/core';
import { PageNotFoundComponent } from './page-not-found.component';
import { PageNotFoundRoutingModule } from './page-not-found-routing.module';
@NgModule({
imports: [ CommonModule, FormsModule, TranslateModule, PageNotFoundRoutingModule ],
declarations: [ PageNotFoundComponent ],
providers: [ ]
})
export class PageNotFoundModule {}
@@ -0,0 +1,31 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { PasswordPageComponent } from './password-page.component';
const routes: Routes = [
{ path: '**', component: PasswordPageComponent },
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class PasswordRoutingModule {}
@@ -0,0 +1,34 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { TranslateModule } from '@ngx-translate/core';
import { PasswordPageComponent } from './password-page.component';
import { PasswordRoutingModule } from './password-routing.module';
@NgModule({
imports: [ CommonModule, FormsModule, TranslateModule, PasswordRoutingModule ],
declarations: [ PasswordPageComponent ],
providers: [ ]
})
export class PasswordModule {}
@@ -18,7 +18,7 @@ import {Component, OnInit} from '@angular/core';
import {Response} from '@angular/http';
import {AccountServiceClient} from '../../account-service/account.service';
import {TranslateUtil} from '../../ngx-translate/translate.util';
import {TranslateUtil} from '../../ngx-translate/translate.util';
import {View} from '../widgets/toolbar.component';
import {PropertyLabel} from '../widgets/property.label';
@@ -0,0 +1,31 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { SessionsPageComponent } from './sessions-page.component';
const routes: Routes = [
{ path: '**', component: SessionsPageComponent },
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class SessionsRoutingModule {}
@@ -0,0 +1,43 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import {WidgetsModule} from '../widgets/widgets.module';
import { SessionsRoutingModule } from './sessions-routing.module';
import { SessionsPageComponent } from './sessions-page.component';
import { LargeSessionCardComponent } from './large-session-card.component';
import { SmallSessionCardComponent } from './small-session-card.component';
@NgModule({
imports: [ CommonModule,
TranslateModule,
SessionsRoutingModule,
WidgetsModule ],
declarations: [ SessionsPageComponent,
LargeSessionCardComponent,
SmallSessionCardComponent ],
providers: [ ]
})
export class SessionsModule {}
@@ -0,0 +1,36 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { ToolbarComponent } from './toolbar.component';
import {OrderbyPipe} from './orderby.pipe';
import {FilterbyPipe} from './filterby.pipe';
@NgModule({
imports: [ CommonModule, FormsModule ],
declarations: [ ToolbarComponent, OrderbyPipe, FilterbyPipe ],
exports: [ ToolbarComponent,
OrderbyPipe,
FilterbyPipe ],
providers: [ ]
})
export class WidgetsModule {}
@@ -0,0 +1,36 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Injectable} from '@angular/core';
import {CanActivateChild, ActivatedRouteSnapshot, RouterStateSnapshot} from '@angular/router';
import {KeycloakService} from './keycloak.service';
@Injectable()
export class KeycloakGuard implements CanActivateChild {
constructor(private keycloakService: KeycloakService) {}
canActivateChild(route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): boolean {
if (this.keycloakService.authenticated()) {
return true;
}
this.keycloakService.login();
return false;
}
}
@@ -40,6 +40,8 @@
}
}
var useNonce = true;
kc.init = function (initOptions) {
kc.authenticated = false;
@@ -50,7 +52,7 @@
} else if (initOptions && initOptions.adapter === 'default') {
adapter = loadAdapter();
} else {
if (window.Cordova) {
if (window.Cordova || window.cordova) {
adapter = loadAdapter('cordova');
} else {
adapter = loadAdapter();
@@ -58,6 +60,10 @@
}
if (initOptions) {
if (typeof initOptions.useNonce !== 'undefined') {
useNonce = initOptions.useNonce;
}
if (typeof initOptions.checkLoginIframe !== 'undefined') {
loginIframe.enable = initOptions.checkLoginIframe;
}
@@ -159,10 +165,15 @@
var callback = parseCallback(window.location.href);
if (callback) {
setupCheckLoginIframe();
window.history.replaceState({}, null, callback.newUrl);
processCallback(callback, initPromise);
return;
}
if (callback && callback.valid) {
return setupCheckLoginIframe().success(function() {
processCallback(callback, initPromise);
}).error(function (e) {
initPromise.setError();
});
} else if (initOptions) {
if (initOptions.token && initOptions.refreshToken) {
setToken(initOptions.token, initOptions.refreshToken, initOptions.idToken);
@@ -221,7 +232,7 @@
var callbackState = {
state: state,
nonce: nonce,
redirectUri: encodeURIComponent(redirectUri),
redirectUri: encodeURIComponent(redirectUri)
}
if (options && options.prompt) {
@@ -230,22 +241,25 @@
callbackStorage.add(callbackState);
var action = 'auth';
var baseUrl;
if (options && options.action == 'register') {
action = 'registrations';
baseUrl = kc.endpoints.register();
} else {
baseUrl = kc.endpoints.authorize();
}
var scope = (options && options.scope) ? "openid " + options.scope : "openid";
var url = getRealmUrl()
+ '/protocol/openid-connect/' + action
var url = baseUrl
+ '?client_id=' + encodeURIComponent(kc.clientId)
+ '&redirect_uri=' + encodeURIComponent(redirectUri)
+ '&state=' + encodeURIComponent(state)
+ '&nonce=' + encodeURIComponent(nonce)
+ '&response_mode=' + encodeURIComponent(kc.responseMode)
+ '&response_type=' + encodeURIComponent(kc.responseType)
+ '&scope=' + encodeURIComponent(scope);
if (useNonce) {
url = url + '&nonce=' + encodeURIComponent(nonce);
}
if (options && options.prompt) {
url += '&prompt=' + encodeURIComponent(options.prompt);
@@ -275,8 +289,7 @@
}
kc.createLogoutUrl = function(options) {
var url = getRealmUrl()
+ '/protocol/openid-connect/logout'
var url = kc.endpoints.logout()
+ '?redirect_uri=' + encodeURIComponent(adapter.redirectUri(options, false));
return url;
@@ -295,11 +308,14 @@
}
kc.createAccountUrl = function(options) {
var url = getRealmUrl()
var realm = getRealmUrl();
var url = undefined;
if (typeof realm !== 'undefined') {
url = realm
+ '/account'
+ '?referrer=' + encodeURIComponent(kc.clientId)
+ '&referrer_uri=' + encodeURIComponent(adapter.redirectUri(options));
}
return url;
}
@@ -347,7 +363,7 @@
}
kc.loadUserInfo = function() {
var url = getRealmUrl() + '/protocol/openid-connect/userinfo';
var url = kc.endpoints.userinfo();
var req = new XMLHttpRequest();
req.open('GET', url, true);
req.setRequestHeader('Accept', 'application/json');
@@ -412,7 +428,7 @@
promise.setSuccess(false);
} else {
var params = 'grant_type=refresh_token&' + 'refresh_token=' + kc.refreshToken;
var url = getRealmUrl() + '/protocol/openid-connect/token';
var url = kc.endpoints.token();
refreshQueue.push(promise);
@@ -486,10 +502,14 @@
}
function getRealmUrl() {
if (kc.authServerUrl.charAt(kc.authServerUrl.length - 1) == '/') {
return kc.authServerUrl + 'realms/' + encodeURIComponent(kc.realm);
if (typeof kc.authServerUrl !== 'undefined') {
if (kc.authServerUrl.charAt(kc.authServerUrl.length - 1) == '/') {
return kc.authServerUrl + 'realms/' + encodeURIComponent(kc.realm);
} else {
return kc.authServerUrl + '/realms/' + encodeURIComponent(kc.realm);
}
} else {
return kc.authServerUrl + '/realms/' + encodeURIComponent(kc.realm);
return undefined;
}
}
@@ -523,7 +543,7 @@
if ((kc.flow != 'implicit') && code) {
var params = 'code=' + code + '&grant_type=authorization_code';
var url = getRealmUrl() + '/protocol/openid-connect/token';
var url = kc.endpoints.token();
var req = new XMLHttpRequest();
req.open('POST', url, true);
@@ -560,9 +580,9 @@
setToken(accessToken, refreshToken, idToken, timeLocal);
if ((kc.tokenParsed && kc.tokenParsed.nonce != oauth.storedNonce) ||
if (useNonce && ((kc.tokenParsed && kc.tokenParsed.nonce != oauth.storedNonce) ||
(kc.refreshTokenParsed && kc.refreshTokenParsed.nonce != oauth.storedNonce) ||
(kc.idTokenParsed && kc.idTokenParsed.nonce != oauth.storedNonce)) {
(kc.idTokenParsed && kc.idTokenParsed.nonce != oauth.storedNonce))) {
console.info('[KEYCLOAK] Invalid nonce, clearing token');
kc.clearToken();
@@ -587,6 +607,65 @@
configUrl = config;
}
function setupOidcEndoints(oidcConfiguration) {
if (! oidcConfiguration) {
kc.endpoints = {
authorize: function() {
return getRealmUrl() + '/protocol/openid-connect/auth';
},
token: function() {
return getRealmUrl() + '/protocol/openid-connect/token';
},
logout: function() {
return getRealmUrl() + '/protocol/openid-connect/logout';
},
checkSessionIframe: function() {
var src = getRealmUrl() + '/protocol/openid-connect/login-status-iframe.html';
if (kc.iframeVersion) {
src = src + '?version=' + kc.iframeVersion;
}
return src;
},
register: function() {
return getRealmUrl() + '/protocol/openid-connect/registrations';
},
userinfo: function() {
return getRealmUrl() + '/protocol/openid-connect/userinfo';
}
};
} else {
kc.endpoints = {
authorize: function() {
return oidcConfiguration.authorization_endpoint;
},
token: function() {
return oidcConfiguration.token_endpoint;
},
logout: function() {
if (!oidcConfiguration.end_session_endpoint) {
throw "Not supported by the OIDC server";
}
return oidcConfiguration.end_session_endpoint;
},
checkSessionIframe: function() {
if (!oidcConfiguration.check_session_iframe) {
throw "Not supported by the OIDC server";
}
return oidcConfiguration.check_session_iframe;
},
register: function() {
throw 'Redirection to "Register user" page not supported in standard OIDC mode';
},
userinfo: function() {
if (!oidcConfiguration.userinfo_endpoint) {
throw "Not supported by the OIDC server";
}
return oidcConfiguration.userinfo_endpoint;
}
}
}
}
if (configUrl) {
var req = new XMLHttpRequest();
req.open('GET', configUrl, true);
@@ -601,7 +680,7 @@
kc.realm = config['realm'];
kc.clientId = config['resource'];
kc.clientSecret = (config['credentials'] || {})['secret'];
setupOidcEndoints(null);
promise.setSuccess();
} else {
promise.setError();
@@ -611,30 +690,62 @@
req.send();
} else {
if (!config['url']) {
var scripts = document.getElementsByTagName('script');
for (var i = 0; i < scripts.length; i++) {
if (scripts[i].src.match(/.*keycloak\.js/)) {
config.url = scripts[i].src.substr(0, scripts[i].src.indexOf('/js/keycloak.js'));
break;
}
}
}
if (!config.realm) {
throw 'realm missing';
}
if (!config.clientId) {
throw 'clientId missing';
}
kc.authServerUrl = config.url;
kc.realm = config.realm;
kc.clientId = config.clientId;
kc.clientSecret = (config.credentials || {}).secret;
promise.setSuccess();
var oidcProvider = config['oidcProvider'];
if (!oidcProvider) {
if (!config['url']) {
var scripts = document.getElementsByTagName('script');
for (var i = 0; i < scripts.length; i++) {
if (scripts[i].src.match(/.*keycloak\.js/)) {
config.url = scripts[i].src.substr(0, scripts[i].src.indexOf('/js/keycloak.js'));
break;
}
}
}
if (!config.realm) {
throw 'realm missing';
}
kc.authServerUrl = config.url;
kc.realm = config.realm;
setupOidcEndoints(null);
promise.setSuccess();
} else {
if (typeof oidcProvider === 'string') {
var oidcProviderConfigUrl;
if (oidcProvider.charAt(oidcProvider.length - 1) == '/') {
oidcProviderConfigUrl = oidcProvider + '.well-known/openid-configuration';
} else {
oidcProviderConfigUrl = oidcProvider + '/.well-known/openid-configuration';
}
var req = new XMLHttpRequest();
req.open('GET', oidcProviderConfigUrl, true);
req.setRequestHeader('Accept', 'application/json');
req.onreadystatechange = function () {
if (req.readyState == 4) {
if (req.status == 200 || fileLoaded(req)) {
var oidcProviderConfig = JSON.parse(req.responseText);
setupOidcEndoints(oidcProviderConfig);
promise.setSuccess();
} else {
promise.setError();
}
}
};
req.send();
} else {
setupOidcEndoints(oidcProvider);
promise.setSuccess();
}
}
}
return promise.promise;
@@ -753,23 +864,137 @@
}
function parseCallback(url) {
var oauth = new CallbackParser(url, kc.responseMode).parseUri();
var oauth = parseCallbackUrl(url);
if (!oauth) {
return;
}
var oauthState = callbackStorage.get(oauth.state);
if (oauthState && (oauth.code || oauth.error || oauth.access_token || oauth.id_token)) {
if (oauthState) {
oauth.valid = true;
oauth.redirectUri = oauthState.redirectUri;
oauth.storedNonce = oauthState.nonce;
oauth.prompt = oauthState.prompt;
}
if (oauth.fragment) {
oauth.newUrl += '#' + oauth.fragment;
return oauth;
}
function parseCallbackUrl(url) {
var supportedParams;
switch (kc.flow) {
case 'standard':
supportedParams = ['code', 'state', 'session_state'];
break;
case 'implicit':
supportedParams = ['access_token', 'id_token', 'state', 'session_state'];
break;
case 'hybrid':
supportedParams = ['access_token', 'id_token', 'code', 'state', 'session_state'];
break;
}
supportedParams.push('error');
supportedParams.push('error_description');
supportedParams.push('error_uri');
var queryIndex = url.indexOf('?');
var fragmentIndex = url.indexOf('#');
var newUrl;
var parsed;
if (kc.responseMode === 'query' && queryIndex !== -1) {
newUrl = url.substring(0, queryIndex);
parsed = parseCallbackParams(url.substring(queryIndex + 1, fragmentIndex !== -1 ? fragmentIndex : url.length), supportedParams);
if (parsed.paramsString !== '') {
newUrl += '?' + parsed.paramsString;
}
if (fragmentIndex !== -1) {
newUrl += url.substring(fragmentIndex);
}
} else if (kc.responseMode === 'fragment' && fragmentIndex !== -1) {
newUrl = url.substring(0, fragmentIndex);
parsed = parseCallbackParams(url.substring(fragmentIndex + 1), supportedParams);
if (parsed.paramsString !== '') {
newUrl += '#' + parsed.paramsString;
}
}
return oauth;
if (parsed && parsed.oauthParams) {
if (kc.flow === 'standard' || kc.flow === 'hybrid') {
if ((parsed.oauthParams.code || parsed.oauthParams.error) && parsed.oauthParams.state) {
parsed.oauthParams.newUrl = newUrl;
return parsed.oauthParams;
}
} else if (kc.flow === 'implicit') {
if ((parsed.oauthParams.access_token || parsed.oauthParams.error) && parsed.oauthParams.state) {
parsed.oauthParams.newUrl = newUrl;
return parsed.oauthParams;
}
}
}
}
function parseCallbackParams(paramsString, supportedParams) {
var p = paramsString.split('&');
var result = {
paramsString: '',
oauthParams: {}
}
for (var i = 0; i < p.length; i++) {
var t = p[i].split('=');
if (supportedParams.indexOf(t[0]) !== -1) {
result.oauthParams[t[0]] = t[1];
} else {
if (result.paramsString !== '') {
result.paramsString += '&';
}
result.paramsString += p[i];
}
}
return result;
}
function createPromise() {
if (typeof Promise === "function") {
return createNativePromise();
} else {
return createLegacyPromise();
}
}
function createNativePromise() {
// Need to create a native Promise which also preserves the
// interface of the custom promise type previously used by the API
var p = {
setSuccess: function(result) {
p.success = true;
p.resolve(result);
},
setError: function(result) {
p.success = false;
p.reject(result);
}
};
p.promise = new Promise(function(resolve, reject) {
p.resolve = resolve;
p.reject = reject;
});
p.promise.success = function(callback) {
p.promise.then(callback);
return p.promise;
}
p.promise.error = function(callback) {
p.promise.catch(callback);
return p.promise;
}
return p;
}
function createLegacyPromise() {
var p = {
setSuccess: function(result) {
p.success = true;
@@ -826,23 +1051,20 @@
loginIframe.iframe = iframe;
iframe.onload = function() {
var realmUrl = getRealmUrl();
if (realmUrl.charAt(0) === '/') {
var authUrl = kc.endpoints.authorize();
if (authUrl.charAt(0) === '/') {
loginIframe.iframeOrigin = getOrigin();
} else {
loginIframe.iframeOrigin = realmUrl.substring(0, realmUrl.indexOf('/', 8));
loginIframe.iframeOrigin = authUrl.substring(0, authUrl.indexOf('/', 8));
}
promise.setSuccess();
setTimeout(check, loginIframe.interval * 1000);
}
var src = getRealmUrl() + '/protocol/openid-connect/login-status-iframe.html';
if (kc.iframeVersion) {
src = src + '?version=' + kc.iframeVersion;
}
var src = kc.endpoints.checkSessionIframe();
iframe.setAttribute('src', src );
iframe.setAttribute('title', 'keycloak-session-iframe' );
iframe.style.display = 'none';
document.body.appendChild(iframe);
@@ -920,7 +1142,12 @@
},
accountManagement : function() {
window.location.href = kc.createAccountUrl();
var accountUrl = kc.createAccountUrl();
if (typeof accountUrl !== 'undefined') {
window.location.href = accountUrl;
} else {
throw "Not supported by the OIDC server";
}
return createPromise().promise;
},
@@ -934,12 +1161,7 @@
} else if (kc.redirectUri) {
return kc.redirectUri;
} else {
var redirectUri = location.href;
if (location.hash && encodeHash) {
redirectUri = redirectUri.substring(0, location.href.indexOf('#'));
redirectUri += (redirectUri.indexOf('?') == -1 ? '?' : '&') + 'redirect_fragment=' + encodeURIComponent(location.hash.substring(1));
}
return redirectUri;
return location.href;
}
}
};
@@ -947,7 +1169,14 @@
if (type == 'cordova') {
loginIframe.enable = false;
var cordovaOpenWindowWrapper = function(loginUrl, target, options) {
if (window.cordova && window.cordova.InAppBrowser) {
// Use inappbrowser for IOS and Android if available
return window.cordova.InAppBrowser.open(loginUrl, target, options);
} else {
return window.open(loginUrl, target, options);
}
};
return {
login: function(options) {
var promise = createPromise();
@@ -958,8 +1187,7 @@
}
var loginUrl = kc.createLoginUrl(options);
var ref = window.open(loginUrl, '_blank', o);
var ref = cordovaOpenWindowWrapper(loginUrl, '_blank', o);
var completed = false;
ref.addEventListener('loadstart', function(event) {
@@ -992,7 +1220,7 @@
var promise = createPromise();
var logoutUrl = kc.createLogoutUrl(options);
var ref = window.open(logoutUrl, '_blank', 'location=no,hidden=yes');
var ref = cordovaOpenWindowWrapper(logoutUrl, '_blank', 'location=no,hidden=yes');
var error;
@@ -1025,7 +1253,7 @@
register : function() {
var registerUrl = kc.createRegisterUrl();
var ref = window.open(registerUrl, '_blank', 'location=no');
var ref = cordovaOpenWindowWrapper(registerUrl, '_blank', 'location=no');
ref.addEventListener('loadstart', function(event) {
if (event.url.indexOf('http://localhost') == 0) {
ref.close();
@@ -1035,12 +1263,16 @@
accountManagement : function() {
var accountUrl = kc.createAccountUrl();
var ref = window.open(accountUrl, '_blank', 'location=no');
ref.addEventListener('loadstart', function(event) {
if (event.url.indexOf('http://localhost') == 0) {
ref.close();
}
});
if (typeof accountUrl !== 'undefined') {
var ref = cordovaOpenWindowWrapper(accountUrl, '_blank', 'location=no');
ref.addEventListener('loadstart', function(event) {
if (event.url.indexOf('http://localhost') == 0) {
ref.close();
}
});
} else {
throw "Not supported by the OIDC server";
}
},
redirectUri: function(options) {
@@ -1170,99 +1402,6 @@
return new CookieStorage();
}
var CallbackParser = function(uriToParse, responseMode) {
if (!(this instanceof CallbackParser)) {
return new CallbackParser(uriToParse, responseMode);
}
var parser = this;
var initialParse = function() {
var baseUri = null;
var queryString = null;
var fragmentString = null;
var questionMarkIndex = uriToParse.indexOf("?");
var fragmentIndex = uriToParse.indexOf("#", questionMarkIndex + 1);
if (questionMarkIndex == -1 && fragmentIndex == -1) {
baseUri = uriToParse;
} else if (questionMarkIndex != -1) {
baseUri = uriToParse.substring(0, questionMarkIndex);
queryString = uriToParse.substring(questionMarkIndex + 1);
if (fragmentIndex != -1) {
fragmentIndex = queryString.indexOf("#");
fragmentString = queryString.substring(fragmentIndex + 1);
queryString = queryString.substring(0, fragmentIndex);
}
} else {
baseUri = uriToParse.substring(0, fragmentIndex);
fragmentString = uriToParse.substring(fragmentIndex + 1);
}
return { baseUri: baseUri, queryString: queryString, fragmentString: fragmentString };
}
var parseParams = function(paramString) {
var result = {};
var params = paramString.split('&');
for (var i = 0; i < params.length; i++) {
var p = params[i].split('=');
var paramName = decodeURIComponent(p[0]);
var paramValue = decodeURIComponent(p[1]);
result[paramName] = paramValue;
}
return result;
}
var handleQueryParam = function(paramName, paramValue, oauth) {
var supportedOAuthParams = [ 'code', 'state', 'error', 'error_description' ];
for (var i = 0 ; i< supportedOAuthParams.length ; i++) {
if (paramName === supportedOAuthParams[i]) {
oauth[paramName] = paramValue;
return true;
}
}
return false;
}
parser.parseUri = function() {
var parsedUri = initialParse();
var queryParams = {};
if (parsedUri.queryString) {
queryParams = parseParams(parsedUri.queryString);
}
var oauth = { newUrl: parsedUri.baseUri };
for (var param in queryParams) {
switch (param) {
case 'redirect_fragment':
oauth.fragment = queryParams[param];
break;
default:
if (responseMode != 'query' || !handleQueryParam(param, queryParams[param], oauth)) {
oauth.newUrl += (oauth.newUrl.indexOf('?') == -1 ? '?' : '&') + param + '=' + encodeURIComponent(queryParams[param]);
}
break;
}
}
if (responseMode === 'fragment') {
var fragmentParams = {};
if (parsedUri.fragmentString) {
fragmentParams = parseParams(parsedUri.fragmentString);
}
for (var param in fragmentParams) {
oauth[param] = fragmentParams[param];
}
}
return oauth;
}
}
}
if ( typeof module === "object" && module && typeof module.exports === "object" ) {
@@ -15,6 +15,7 @@
* limitations under the License.
*/
import {Injectable} from '@angular/core';
import {KeycloakLoginOptions} from './keycloak.d';
// If using a local keycloak.js, uncomment this import. With keycloak.js fetched
// from the server, you get a compile-time warning on use of the Keycloak()
@@ -23,7 +24,7 @@ import {Injectable} from '@angular/core';
//
import * as Keycloak from './keycloak';
type KeycloakClient = Keycloak.KeycloakInstance;
export type KeycloakClient = Keycloak.KeycloakInstance;
type InitOptions = Keycloak.KeycloakInitOptions;
@Injectable()
@@ -52,17 +53,21 @@ export class KeycloakService {
});
});
}
static setKeycloakAuth(kc:KeycloakClient) {
this.keycloakAuth = kc;
}
authenticated(): boolean {
return KeycloakService.keycloakAuth.authenticated;
}
login() {
KeycloakService.keycloakAuth.login();
login(options?: KeycloakLoginOptions) {
KeycloakService.keycloakAuth.login(options);
}
logout() {
KeycloakService.keycloakAuth.logout();
logout(redirectUri?: string) {
KeycloakService.keycloakAuth.logout({redirectUri: redirectUri});
}
account() {
@@ -1,17 +1,17 @@
<!-- Top Nav -->
<nav class="navbar navbar-pf-alt">
<notification></notification>
<div class="navbar-header">
<button (click)="menuClicked()" type="button" class="navbar-toggle">
<button *ngIf="keycloakService.authenticated() && showSideNav" (click)="menuClicked()" type="button" class="navbar-toggle">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a href="http://www.keycloak.org" class="navbar-brand">
<img class="navbar-brand-icon" type="image/svg+xml" src="{{resourceUrl}}/app/assets/img/keycloak-logo.png" alt="" width="auto" height="30px"/>
<img class="navbar-brand-icon" type="image/svg+xml" src="{{resourceUrl}}/app/assets/img/keycloak-logo-min.png" alt="" width="auto" height="30px"/>
</a>
</div>
<nav class="collapse navbar-collapse">
@@ -21,17 +21,7 @@
<li *ngIf="referrer.exists()">
<a class="nav-item-iconic" href="{{referrer.getUri()}}"><span class="pficon-arrow"></span> {{'backTo' | translate:referrer.getName()}}</a>
</li>
<li class="dropdown">
<a class="dropdown-toggle nav-item-iconic" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
<span title="Help" class="fa pficon-help"></span>
<span class="caret"></span>
</a>
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
<li><a href="#">Help</a></li>
<li><a href="#">About</a></li>
</ul>
</li>
<li class="dropdown">
<li class="dropdown" (click)="logout()">
<a class="dropdown-toggle nav-item-iconic" id="dropdownMenu2" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
<span title="Username" class="fa pficon-user"></span>
<span class="caret"></span>
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Component, OnInit} from '@angular/core';
import {Component, OnInit, Input} from '@angular/core';
import {TranslateUtil} from '../ngx-translate/translate.util';
import {KeycloakService} from '../keycloak-service/keycloak.service';
import {ResponsivenessService} from "../responsiveness-service/responsiveness.service";
@@ -30,6 +30,7 @@ declare const referrer_uri: string;
styleUrls: ['./top-nav.component.css']
})
export class TopNavComponent implements OnInit {
@Input() showSideNav: String;
public resourceUrl: string = resourceUrl;
@@ -21,7 +21,8 @@ import { platformBrowser } from '@angular/platform-browser';
import { AppModule } from './app/app.module';
//import { environment } from './environments/environment';
import { KeycloakService } from './app/keycloak-service/keycloak.service';
import { KeycloakService, KeycloakClient } from './app/keycloak-service/keycloak.service';
//if (environment.production) {
// enableProdMode();
@@ -30,17 +31,17 @@ import { KeycloakService } from './app/keycloak-service/keycloak.service';
declare const authUrl: string;
declare const resourceUrl: string;
declare const realm: string;
declare const keycloak: KeycloakClient;
const noLogin: boolean = false; // convenient for development
if (noLogin) {
platformBrowserDynamic().bootstrapModule(AppModule);
} else {
KeycloakService.init(authUrl + '/realms/' + realm + '/account/keycloak.json',
{onLoad: 'login-required'})
.then(() => {
platformBrowserDynamic().bootstrapModule(AppModule);
})
.catch((e: any) => {
console.log('Error in bootstrap: ' + JSON.stringify(e));
});
KeycloakService.setKeycloakAuth(keycloak);
loadCss('/styles.css');
platformBrowserDynamic().bootstrapModule(AppModule);
function loadCss(url:string) {
const link = document.createElement("link");
link.href = resourceUrl + url;
link.rel = "stylesheet";
link.media = "screen, print";
document.head.appendChild(link);
}
@@ -24,18 +24,22 @@
"author": "Stan Silvert",
"license": "Apache 2.0",
"dependencies": {
"@angular/common": "~4.0.0",
"@angular/compiler": "~4.0.0",
"@angular/core": "~4.0.0",
"@angular/forms": "~4.0.0",
"@angular/http": "~4.0.0",
"@angular/platform-browser": "~4.0.0",
"@angular/platform-browser-dynamic": "~4.0.0",
"@angular/router": "~4.0.0",
"@ngx-translate/core": "^7.1.0",
"@angular/animations": "'5.0.0'",
"@angular/common": "'5.0.0'",
"@angular/compiler": "'5.0.0'",
"@angular/compiler-cli": "'5.0.0'",
"@angular/core": "'5.0.0'",
"@angular/forms": "'5.0.0'",
"@angular/http": "'5.0.0'",
"@angular/platform-browser": "'5.0.0'",
"@angular/platform-browser-dynamic": "'5.0.0'",
"@angular/platform-server": "'5.0.0'",
"@angular/router": "'5.0.0'",
"@ngx-translate/core": "^9.1.1",
"core-js": "^2.4.1",
"patternfly": "^3.23.2",
"rxjs": "^5.4.2",
"rxjs": "5.5.2",
"rxjs-system-bundle": "^5.5.6",
"systemjs": "^0.20.17",
"zone.js": "^0.8.4"
},
@@ -55,7 +59,7 @@
"protractor": "~4.0.14",
"rimraf": "^2.5.4",
"tslint": "^3.15.1",
"typescript": "^2.4.2"
"typescript": "2.4.2"
},
"repository": {}
}
@@ -1,5 +0,0 @@
h1 {
color: #369;
font-family: Arial, Helvetica, sans-serif;
font-size: 250%;
}
@@ -14,20 +14,32 @@
'app': resourceUrl + '/app',
// angular bundles
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'@angular/http': 'npm:@angular/http/bundles/http.umd.js',
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
'@angular/core': 'npm:@angular/core/bundles/core.umd.min.js',
'@angular/common': 'npm:@angular/common/bundles/common.umd.min.js',
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.min.js',
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.min.js',
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.min.js',
'@angular/http': 'npm:@angular/http/bundles/http.umd.min.js',
'@angular/router': 'npm:@angular/router/bundles/router.umd.min.js',
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.min.js',
// other libraries
'@ngx-translate/core': 'npm:@ngx-translate/core/bundles/core.umd.js',
'rxjs': 'npm:rxjs',
'@ngx-translate/core': 'npm:@ngx-translate/core/bundles/core.umd.min.js',
'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js'
},
bundles: {
"npm:rxjs-system-bundle/Rx.system.min.js": [
"rxjs",
"rxjs/*",
"rxjs/operator/*",
"rxjs/observable/*",
"rxjs/scheduler/*",
"rxjs/symbol/*",
"rxjs/add/operator/*",
"rxjs/add/observable/*",
"rxjs/util/*"
]
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
app: {
@@ -42,7 +54,7 @@
defaultExtension: 'js'
},
rxjs: {
defaultExtension: 'js'
defaultExtension: false
}
}
});
@@ -2,41 +2,80 @@
# yarn lockfile v1
"@angular/common@~4.0.0":
version "4.0.3"
resolved "https://registry.yarnpkg.com/@angular/common/-/common-4.0.3.tgz#17472895eb425f2812b3a79162b5b494d2506a5b"
"@angular/animations@'5.0.0'":
version "5.2.7"
resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-5.2.7.tgz#a99fe128f3809e3f8082441a6e676992b9352db7"
dependencies:
tslib "^1.7.1"
"@angular/compiler@~4.0.0":
version "4.0.3"
resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-4.0.3.tgz#8b3cad338ac539328e10a6a4bfaa057094f7bc89"
"@angular/common@'5.0.0'":
version "5.2.7"
resolved "https://registry.yarnpkg.com/@angular/common/-/common-5.2.7.tgz#11bb9f00afe91af8d772ecdff83c2e179f9a67a0"
dependencies:
tslib "^1.7.1"
"@angular/core@~4.0.0":
version "4.0.3"
resolved "https://registry.yarnpkg.com/@angular/core/-/core-4.0.3.tgz#61be21db6aa5778e33159ffd38cbbebaf16120d9"
"@angular/compiler-cli@'5.0.0'":
version "5.2.7"
resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-5.2.7.tgz#3bad9d5e4c25ebc51e1bb67b71b26a97d6d3bbc5"
dependencies:
chokidar "^1.4.2"
minimist "^1.2.0"
reflect-metadata "^0.1.2"
tsickle "^0.27.2"
"@angular/forms@~4.0.0":
version "4.0.3"
resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-4.0.3.tgz#fb8e6e0aede782bf58730a31d1179b323271816e"
"@angular/compiler@'5.0.0'":
version "5.2.7"
resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-5.2.7.tgz#31dcc7ac18cc23cb115e76c541b982737ff93ec9"
dependencies:
tslib "^1.7.1"
"@angular/http@~4.0.0":
version "4.0.3"
resolved "https://registry.yarnpkg.com/@angular/http/-/http-4.0.3.tgz#efbb701a215ec7704c021676484b85ed47392f4b"
"@angular/core@'5.0.0'":
version "5.2.7"
resolved "https://registry.yarnpkg.com/@angular/core/-/core-5.2.7.tgz#e5607fc39d90f9fe4fbaaeeeb6cdb9371966bfe3"
dependencies:
tslib "^1.7.1"
"@angular/platform-browser-dynamic@~4.0.0":
version "4.0.3"
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-4.0.3.tgz#5fa3b98f725999b631d7d7174e6e43dcbf6aa9ac"
"@angular/forms@'5.0.0'":
version "5.2.7"
resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-5.2.7.tgz#a0c41fdb6b8ba2c32653cc3058d5fa47db91fbf8"
dependencies:
tslib "^1.7.1"
"@angular/platform-browser@~4.0.0":
version "4.0.3"
resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-4.0.3.tgz#170b18d5af4ee02b248aa6a1f1e0584ac841681e"
"@angular/http@'5.0.0'":
version "5.2.7"
resolved "https://registry.yarnpkg.com/@angular/http/-/http-5.2.7.tgz#a163f6958f12d2665419123861b0d613c1c82afb"
dependencies:
tslib "^1.7.1"
"@angular/router@~4.0.0":
version "4.0.3"
resolved "https://registry.yarnpkg.com/@angular/router/-/router-4.0.3.tgz#24184e9b1266c4ad017b2be81573464b1e4c5dfa"
"@angular/platform-browser-dynamic@'5.0.0'":
version "5.2.7"
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-5.2.7.tgz#91f903c1f73de2fba1004bc31ed1c3117c7f0406"
dependencies:
tslib "^1.7.1"
"@ngx-translate/core@^7.1.0":
version "7.1.0"
resolved "https://registry.yarnpkg.com/@ngx-translate/core/-/core-7.1.0.tgz#5087a65c8ff312e4244ca0646ed45cde83b170cd"
"@angular/platform-browser@'5.0.0'":
version "5.2.7"
resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-5.2.7.tgz#cfd86040c25380aabf0322ef47aecc61f23cc532"
dependencies:
tslib "^1.7.1"
"@angular/platform-server@'5.0.0'":
version "5.2.7"
resolved "https://registry.yarnpkg.com/@angular/platform-server/-/platform-server-5.2.7.tgz#e2eb290952dbe6ab04b4edae115a0d02b8b2dc4c"
dependencies:
domino "^1.0.29"
tslib "^1.7.1"
xhr2 "^0.1.4"
"@angular/router@'5.0.0'":
version "5.2.7"
resolved "https://registry.yarnpkg.com/@angular/router/-/router-5.2.7.tgz#691c375fe32f01bea56d169469ad8f254b136af9"
dependencies:
tslib "^1.7.1"
"@ngx-translate/core@^9.1.1":
version "9.1.1"
resolved "https://registry.yarnpkg.com/@ngx-translate/core/-/core-9.1.1.tgz#ae103928836b8a9e069fd2e2e76fa2198cc7e628"
"@types/jasmine@2.5.36", "@types/jasmine@^2.5.36":
version "2.5.36"
@@ -471,7 +510,7 @@ chalk@^1.1.1, chalk@^1.1.3:
strip-ansi "^3.0.0"
supports-color "^2.0.0"
chokidar@1.7.0, chokidar@^1.4.1:
chokidar@1.7.0, chokidar@^1.4.1, chokidar@^1.4.2:
version "1.7.0"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468"
dependencies:
@@ -769,6 +808,10 @@ dom-serialize@^2.2.0:
extend "^3.0.0"
void-elements "^2.0.0"
domino@^1.0.29:
version "1.0.30"
resolved "https://registry.yarnpkg.com/domino/-/domino-1.0.30.tgz#54a4154ecae968616680f8feba3cedff355c71f4"
drmonty-datatables-colvis@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/drmonty-datatables-colvis/-/drmonty-datatables-colvis-1.1.2.tgz#96ab9edfb48643cc2edda3f87b88933cdee8127c"
@@ -2196,6 +2239,10 @@ readdirp@^2.0.0:
readable-stream "^2.0.2"
set-immediate-shim "^1.0.1"
reflect-metadata@^0.1.2:
version "0.1.12"
resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.12.tgz#311bf0c6b63cd782f228a81abe146a2bfa9c56f2"
regex-cache@^0.4.2:
version "0.4.3"
resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145"
@@ -2310,9 +2357,13 @@ rx@4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782"
rxjs@^5.4.2:
version "5.4.2"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.4.2.tgz#2a3236fcbf03df57bae06fd6972fd99e5c08fcf7"
rxjs-system-bundle@^5.5.6:
version "5.5.6"
resolved "https://registry.yarnpkg.com/rxjs-system-bundle/-/rxjs-system-bundle-5.5.6.tgz#3af9c02a36938f750fe33751cca2397fcf3d04b8"
rxjs@5.5.2:
version "5.5.2"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.2.tgz#28d403f0071121967f18ad665563255d54236ac3"
dependencies:
symbol-observable "^1.0.1"
@@ -2497,6 +2548,12 @@ socket.io@1.7.3:
socket.io-client "1.7.3"
socket.io-parser "2.3.1"
source-map-support@^0.5.0:
version "0.5.3"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.3.tgz#2b3d5fff298cfa4d1afd7d4352d569e9a0158e76"
dependencies:
source-map "^0.6.0"
source-map-support@~0.4.0:
version "0.4.15"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.15.tgz#03202df65c06d2bd8c7ec2362a193056fef8d3b1"
@@ -2507,6 +2564,10 @@ source-map@^0.5.3, source-map@^0.5.6:
version "0.5.6"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
source-map@^0.6.0:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
spawn-command@^0.0.2-1:
version "0.0.2"
resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2.tgz#9544e1a43ca045f8531aac1a48cb29bdae62338e"
@@ -2614,8 +2675,8 @@ supports-color@^3.2.3:
has-flag "^1.0.0"
symbol-observable@^1.0.1:
version "1.0.4"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d"
version "1.2.0"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
systemjs@^0.20.17:
version "0.20.17"
@@ -2673,6 +2734,19 @@ tree-kill@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.1.0.tgz#c963dcf03722892ec59cba569e940b71954d1729"
tsickle@^0.27.2:
version "0.27.2"
resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.27.2.tgz#f33d46d046f73dd5c155a37922e422816e878736"
dependencies:
minimist "^1.2.0"
mkdirp "^0.5.1"
source-map "^0.6.0"
source-map-support "^0.5.0"
tslib@^1.7.1:
version "1.9.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8"
tslint@^3.15.1:
version "3.15.1"
resolved "https://registry.yarnpkg.com/tslint/-/tslint-3.15.1.tgz#da165ca93d8fdc2c086b51165ee1bacb48c98ea5"
@@ -2706,7 +2780,7 @@ type-is@~1.6.14:
media-typer "0.3.0"
mime-types "~2.1.15"
typescript@^2.4.2:
typescript@2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.4.2.tgz#f8395f85d459276067c988aa41837a8f82870844"
@@ -2868,6 +2942,10 @@ wtf-8@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/wtf-8/-/wtf-8-1.0.0.tgz#392d8ba2d0f1c34d1ee2d630f15d0efb68e1048a"
xhr2@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/xhr2/-/xhr2-0.1.4.tgz#7f87658847716db5026323812f818cadab387a5f"
xml2js@0.4.4:
version "0.4.4"
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.4.tgz#3111010003008ae19240eba17497b57c729c555d"
@@ -1,3 +1,2 @@
parent=base
import=login/keycloak
deprecatedMode=false