keycloak-uncached
Changes
integration/js/src/main/resources/keycloak.js 1315(+689 -626)
Details
diff --git a/examples/demo-template/customer-app-js/src/main/webapp/customers/view.html b/examples/demo-template/customer-app-js/src/main/webapp/customers/view.html
index 445876f..ae7dcc5 100755
--- a/examples/demo-template/customer-app-js/src/main/webapp/customers/view.html
+++ b/examples/demo-template/customer-app-js/src/main/webapp/customers/view.html
@@ -73,11 +73,32 @@ User <b id="subject"></b> made this request.
};
var reloadData = function () {
- keycloak.updateToken(10).success(loadData).error(loadFailure);
+ keycloak.checkLoginIframe(
+ function() {
+ keycloak.updateToken(10).success(loadData).error(loadFailure);
+ },
+ function() {
+ document.getElementById('customers').innerHTML = '<b>Failed to load data. User is logged out.</b>';
+ //window.location.reload();
+ }
+ );
}
- keycloak.onAuthSuccess = loadData;
- keycloak.init('login-required');
+ //
+ // NOTE!!!: keycloak.setupCheckLoginIframe and checkLoginIframe are an optional way
+ // of discovering if you are logged out. This approach may not always be feasible, but it is a lightweight
+ // approach that requires no network call to determine if the user is logged in. The downside is that it
+ // requires a callback.
+ //
+ // Alternatively, on a single log out, the access token will eventually time out, then refresh token will fail
+ // as the session will be logged out on the auth server.
+ //
+ //
+
+ keycloak.init('login-required').success(function() {
+ // Use an iframe to detect if the user has done an SSO logout in a different window.
+ keycloak.setupCheckLoginIframe(document, window);
+ });
</script>
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/index.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/index.html
index 742fd5e..2384e1b 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/index.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/index.html
@@ -1,100 +1,100 @@
-<!doctype html>
-<html lang="en">
-
-<head>
- <meta charset="utf-8">
- <title>Keycloak Admin Console</title>
-
- <link rel="icon" href="img/favicon.ico">
-
- <link rel="stylesheet" href="css/styles.css">
-
- <script src="lib/jquery/jquery-1.10.2.js" type="text/javascript"></script>
- <script src="lib/select2-3.4.1/select2.js" type="text/javascript"></script>
-
- <script src="lib/angular/angular.js"></script>
- <script src="lib/angular/angular-resource.js"></script>
- <script src="lib/angular/angular-route.js"></script>
- <script src="lib/angular/ui-bootstrap-tpls-0.4.0.js"></script>
-
- <script src="lib/jquery/jquery.idletimer.js" type="text/javascript"></script>
- <script src="lib/jquery/jquery.idletimeout.js" type="text/javascript"></script>
- <script src="lib/angular/select2.js" type="text/javascript"></script>
- <script src="lib/fileupload/angular-file-upload.min.js"></script>
-
- <script src="js/keycloak.js" type="text/javascript"></script>
- <script src="js/app.js" type="text/javascript"></script>
- <script src="js/controllers/realm.js" type="text/javascript"></script>
- <script src="js/controllers/applications.js" type="text/javascript"></script>
- <script src="js/controllers/oauth-clients.js" type="text/javascript"></script>
- <script src="js/controllers/users.js" type="text/javascript"></script>
- <script src="js/loaders.js" type="text/javascript"></script>
- <script src="js/services.js" type="text/javascript"></script>
-
- <style>
- [ng\:cloak], [ng-cloak], .ng-cloak {
- display: none !important;
- }
- </style>
-</head>
-
-<body class="admin-console" data-ng-controller="GlobalCtrl" data-ng-cloak>
-<div id="idletimeout">
- You will be logged off in <strong><span></span> seconds</strong> due to inactivity.
- <a id="idletimeout-resume" href="#">Click here to continue using this web page</a>.
-</div>
-
-<div class="feedback-aligner" data-ng-show="notification" data-ng-click="notification = null">
- <div class="alert alert-{{notification.type}}">
- <span class="pficon pficon-ok" ng-show="notification.type == 'success'"></span>
- <span class="pficon pficon-info" ng-show="notification.type == 'info'"></span>
- <span class="pficon-layered" ng-show="notification.type == 'danger'">
- <span class="pficon pficon-error-octagon"></span>
- <span class="pficon pficon-error-exclamation"></span>
- </span>
- <span class="pficon-layered" ng-show="notification.type == 'warning'">
- <span class="pficon pficon-warning-triangle"></span>
- <span class="pficon pficon-warning-exclamation"></span>
- </span>
- <strong>{{notification.header}}</strong> {{notification.message}}
- </div>
-</div>
-
-<header class="navbar navbar-default navbar-pf navbar-main header">
- <div data-ng-include data-src="'partials/menu.html'"></div>
-</header>
-
-<div class="container">
- <div data-ng-view id="view"></div>
- <div id="loading" class="loading-backdrop">
- <div class="loading">
- <span>Loading...</span>
- </div>
- </div>
-</div>
-
-<script type="text/javascript">
- $.idleTimeout('#idletimeout', '#idletimeout a', {
- idleAfter: 300,
- pollingInterval: 60,
-// keepAliveURL: authUrl + '/admin/keepalive', would need to change this path
- serverResponseEquals: '',
- failedRequests: 1,
- onTimeout: function(){
- $(this).slideUp();
- logout();
- },
- onIdle: function(){
- $(this).slideDown(); // show the warning bar
- },
- onCountdown: function( counter ){
- $(this).find("span").html( counter ); // update the counter
- },
- onResume: function(){
- $(this).slideUp(); // hide the warning bar
- }
- });
-</script>
-
-</body>
-</html>
+<!doctype html>
+<html lang="en">
+
+<head>
+ <meta charset="utf-8">
+ <title>Keycloak Admin Console</title>
+
+ <link rel="icon" href="img/favicon.ico">
+
+ <link rel="stylesheet" href="css/styles.css">
+
+ <script src="lib/jquery/jquery-1.10.2.js" type="text/javascript"></script>
+ <script src="lib/select2-3.4.1/select2.js" type="text/javascript"></script>
+
+ <script src="lib/angular/angular.js"></script>
+ <script src="lib/angular/angular-resource.js"></script>
+ <script src="lib/angular/angular-route.js"></script>
+ <script src="lib/angular/ui-bootstrap-tpls-0.4.0.js"></script>
+
+ <script src="lib/jquery/jquery.idletimer.js" type="text/javascript"></script>
+ <script src="lib/jquery/jquery.idletimeout.js" type="text/javascript"></script>
+ <script src="lib/angular/select2.js" type="text/javascript"></script>
+ <script src="lib/fileupload/angular-file-upload.min.js"></script>
+
+ <script src="js/keycloak.js" type="text/javascript"></script>
+ <script src="js/app.js" type="text/javascript"></script>
+ <script src="js/controllers/realm.js" type="text/javascript"></script>
+ <script src="js/controllers/applications.js" type="text/javascript"></script>
+ <script src="js/controllers/oauth-clients.js" type="text/javascript"></script>
+ <script src="js/controllers/users.js" type="text/javascript"></script>
+ <script src="js/loaders.js" type="text/javascript"></script>
+ <script src="js/services.js" type="text/javascript"></script>
+
+ <style>
+ [ng\:cloak], [ng-cloak], .ng-cloak {
+ display: none !important;
+ }
+ </style>
+</head>
+
+<body class="admin-console" data-ng-controller="GlobalCtrl" data-ng-cloak>
+<div id="idletimeout">
+ You will be logged off in <strong><span></span> seconds</strong> due to inactivity.
+ <a id="idletimeout-resume" href="#">Click here to continue using this web page</a>.
+</div>
+
+<div class="feedback-aligner" data-ng-show="notification" data-ng-click="notification = null">
+ <div class="alert alert-{{notification.type}}">
+ <span class="pficon pficon-ok" ng-show="notification.type == 'success'"></span>
+ <span class="pficon pficon-info" ng-show="notification.type == 'info'"></span>
+ <span class="pficon-layered" ng-show="notification.type == 'danger'">
+ <span class="pficon pficon-error-octagon"></span>
+ <span class="pficon pficon-error-exclamation"></span>
+ </span>
+ <span class="pficon-layered" ng-show="notification.type == 'warning'">
+ <span class="pficon pficon-warning-triangle"></span>
+ <span class="pficon pficon-warning-exclamation"></span>
+ </span>
+ <strong>{{notification.header}}</strong> {{notification.message}}
+ </div>
+</div>
+
+<header class="navbar navbar-default navbar-pf navbar-main header">
+ <div data-ng-include data-src="'partials/menu.html'"></div>
+</header>
+
+<div class="container">
+ <div data-ng-view id="view"></div>
+ <div id="loading" class="loading-backdrop">
+ <div class="loading">
+ <span>Loading...</span>
+ </div>
+ </div>
+</div>
+
+<script type="text/javascript">
+ $.idleTimeout('#idletimeout', '#idletimeout a', {
+ idleAfter: 300,
+ pollingInterval: 60,
+// keepAliveURL: authUrl + '/admin/keepalive', would need to change this path
+ serverResponseEquals: '',
+ failedRequests: 1,
+ onTimeout: function(){
+ $(this).slideUp();
+ logout();
+ },
+ onIdle: function(){
+ $(this).slideDown(); // show the warning bar
+ },
+ onCountdown: function( counter ){
+ $(this).find("span").html( counter ); // update the counter
+ },
+ onResume: function(){
+ $(this).slideUp(); // hide the warning bar
+ }
+ });
+</script>
+
+</body>
+</html>
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
index 65db064..b63ba29 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
@@ -1,1153 +1,1153 @@
-'use strict';
-
-var indexUrl = window.location.href;
-var consoleBaseUrl = window.location.href;
-consoleBaseUrl = consoleBaseUrl.substring(0, consoleBaseUrl.indexOf("/console"));
-consoleBaseUrl = consoleBaseUrl + "/console";
-var configUrl = consoleBaseUrl + "/config";
-var logoutUrl = consoleBaseUrl + "/logout";
-var auth = {};
-var logout = function(){
- console.log('*** LOGOUT');
- auth.loggedIn = false;
- auth.authz = null;
- auth.user = null;
- window.location = logoutUrl;
-};
-
-
-var authUrl = window.location.href;
-authUrl = authUrl.substring(0, authUrl.indexOf('/admin/'));
-
-
-var module = angular.module('keycloak', [ 'keycloak.services', 'keycloak.loaders', 'ui.bootstrap', 'ui.select2', 'angularFileUpload' ]);
-var resourceRequests = 0;
-var loadingTimer = -1;
-
-angular.element(document).ready(function ($http) {
- var keycloakAuth = new Keycloak(configUrl);
- var auth = {};
- auth.loggedIn = false;
-
- keycloakAuth.init('login-required').success(function () {
- auth.loggedIn = true;
- auth.authz = keycloakAuth;
- module.factory('Auth', function() {
- return auth;
- });
- angular.bootstrap(document, ["keycloak"]);
- }).error(function () {
- window.location.reload();
- });
-
-});
-
-module.factory('authInterceptor', function($q, Auth) {
- return {
- request: function (config) {
- var deferred = $q.defer();
- if (Auth.authz.token) {
- Auth.authz.updateToken(5).success(function() {
- config.headers = config.headers || {};
- config.headers.Authorization = 'Bearer ' + Auth.authz.token;
-
- deferred.resolve(config);
- }).error(function() {
- deferred.reject('Failed to refresh token');
- });
- }
- return deferred.promise;
- }
- };
-});
-
-
-
-
-module.config([ '$routeProvider', function($routeProvider) {
-
- $routeProvider
- /*
- .when('/create/realm', {
- templateUrl : 'partials/realm-detail.html',
- resolve : {
- realm : function(RealmLoader) {
- return {};
- }
- },
- controller : 'RealmDetailCtrl'
- })
- */
-
- .when('/create/realm', {
- templateUrl : 'partials/realm-create.html',
- resolve : {
-
- },
- controller : 'RealmCreateCtrl'
- })
- .when('/realms/:realm', {
- templateUrl : 'partials/realm-detail.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- serverInfo : function(ServerInfoLoader) {
- return ServerInfoLoader();
- }
- },
- controller : 'RealmDetailCtrl'
- })
- .when('/realms', {
- templateUrl : 'partials/realm-list.html',
- controller : 'RealmListCtrl'
- })
- .when('/realms/:realm/token-settings', {
- templateUrl : 'partials/realm-tokens.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- }
- },
- controller : 'RealmTokenDetailCtrl'
- })
- .when('/realms/:realm/keys-settings', {
- templateUrl : 'partials/realm-keys.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- }
- },
- controller : 'RealmKeysDetailCtrl'
- })
- .when('/realms/:realm/social-settings', {
- templateUrl : 'partials/realm-social.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- serverInfo : function(ServerInfoLoader) {
- return ServerInfoLoader();
- }
- },
- controller : 'RealmSocialCtrl'
- })
- .when('/realms/:realm/default-roles', {
- templateUrl : 'partials/realm-default-roles.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- applications : function(ApplicationListLoader) {
- return ApplicationListLoader();
- },
- roles : function(RoleListLoader) {
- return RoleListLoader();
- }
- },
- controller : 'RealmDefaultRolesCtrl'
- })
- .when('/realms/:realm/required-credentials', {
- templateUrl : 'partials/realm-credentials.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- }
- },
- controller : 'RealmRequiredCredentialsCtrl'
- })
- .when('/realms/:realm/smtp-settings', {
- templateUrl : 'partials/realm-smtp.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- }
- },
- controller : 'RealmSMTPSettingsCtrl'
- })
- .when('/realms/:realm/ldap-settings', {
- templateUrl : 'partials/realm-ldap.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- }
- },
- controller : 'RealmLdapSettingsCtrl'
- })
- .when('/realms/:realm/audit', {
- templateUrl : 'partials/realm-audit.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- }
- },
- controller : 'RealmAuditEventsCtrl'
- })
- .when('/realms/:realm/audit-settings', {
- templateUrl : 'partials/realm-audit-config.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- serverInfo : function(ServerInfoLoader) {
- return ServerInfoLoader();
- },
- auditConfig : function(RealmAuditLoader) {
- return RealmAuditLoader();
- }
- },
- controller : 'RealmAuditCtrl'
- })
- .when('/realms/:realm/auth-settings', {
- templateUrl : 'partials/realm-auth-list.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- }
- },
- controller : 'RealmAuthSettingsCtrl'
- })
- .when('/realms/:realm/auth-settings/create', {
- templateUrl : 'partials/realm-auth-detail.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- serverInfo : function(ServerInfoLoader) {
- return ServerInfoLoader();
- }
- },
- controller : 'RealmAuthSettingsDetailCtrl'
- })
- .when('/realms/:realm/auth-settings/:index', {
- templateUrl : 'partials/realm-auth-detail.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- serverInfo : function(ServerInfoLoader) {
- return ServerInfoLoader();
- }
- },
- controller : 'RealmAuthSettingsDetailCtrl'
- })
- .when('/create/user/:realm', {
- templateUrl : 'partials/user-detail.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- user : function() {
- return {};
- }
- },
- controller : 'UserDetailCtrl'
- })
- .when('/realms/:realm/users/:user', {
- templateUrl : 'partials/user-detail.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- user : function(UserLoader) {
- return UserLoader();
- }
- },
- controller : 'UserDetailCtrl'
- })
- .when('/realms/:realm/users/:user/user-credentials', {
- templateUrl : 'partials/user-credentials.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- user : function(UserLoader) {
- return UserLoader();
- }
- },
- controller : 'UserCredentialsCtrl'
- })
- .when('/realms/:realm/users/:user/role-mappings', {
- templateUrl : 'partials/role-mappings.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- user : function(UserLoader) {
- return UserLoader();
- },
- applications : function(ApplicationListLoader) {
- return ApplicationListLoader();
- },
- roles : function(RoleListLoader) {
- return RoleListLoader();
- }
- },
- controller : 'UserRoleMappingCtrl'
- })
- .when('/realms/:realm/users/:user/sessions', {
- templateUrl : 'partials/user-sessions.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- user : function(UserLoader) {
- return UserLoader();
- },
- stats : function(UserSessionStatsLoader) {
- return UserSessionStatsLoader();
- }
- },
- controller : 'UserSessionsCtrl'
- })
- .when('/realms/:realm/users/:user/social-links', {
- templateUrl : 'partials/user-social-links.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- user : function(UserLoader) {
- return UserLoader();
- },
- socialLinks : function(UserSocialLinksLoader) {
- return UserSocialLinksLoader();
- }
- },
- controller : 'UserSocialCtrl'
- })
- .when('/realms/:realm/users', {
- templateUrl : 'partials/user-list.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- }
- },
- controller : 'UserListCtrl'
- })
-
- .when('/create/role/:realm', {
- templateUrl : 'partials/role-detail.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- role : function() {
- return {};
- },
- roles : function(RoleListLoader) {
- return RoleListLoader();
- },
- applications : function(ApplicationListLoader) {
- return ApplicationListLoader();
- }
- },
- controller : 'RoleDetailCtrl'
- })
- .when('/realms/:realm/roles/:role', {
- templateUrl : 'partials/role-detail.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- role : function(RoleLoader) {
- return RoleLoader();
- },
- roles : function(RoleListLoader) {
- return RoleListLoader();
- },
- applications : function(ApplicationListLoader) {
- return ApplicationListLoader();
- }
- },
- controller : 'RoleDetailCtrl'
- })
- .when('/realms/:realm/roles', {
- templateUrl : 'partials/role-list.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- roles : function(RoleListLoader) {
- return RoleListLoader();
- }
- },
- controller : 'RoleListCtrl'
- })
-
- .when('/create/role/:realm/applications/:application', {
- templateUrl : 'partials/application-role-detail.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- application : function(ApplicationLoader) {
- return ApplicationLoader();
- },
- role : function() {
- return {};
- },
- roles : function(RoleListLoader) {
- return RoleListLoader();
- },
- applications : function(ApplicationListLoader) {
- return ApplicationListLoader();
- }
- },
- controller : 'ApplicationRoleDetailCtrl'
- })
- .when('/realms/:realm/applications/:application/roles/:role', {
- templateUrl : 'partials/application-role-detail.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- application : function(ApplicationLoader) {
- return ApplicationLoader();
- },
- role : function(ApplicationRoleLoader) {
- return ApplicationRoleLoader();
- },
- roles : function(RoleListLoader) {
- return RoleListLoader();
- },
- applications : function(ApplicationListLoader) {
- return ApplicationListLoader();
- }
- },
- controller : 'ApplicationRoleDetailCtrl'
- })
- .when('/realms/:realm/applications/:application/claims', {
- templateUrl : 'partials/application-claims.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- application : function(ApplicationLoader) {
- return ApplicationLoader();
- },
- claims : function(ApplicationClaimsLoader) {
- return ApplicationClaimsLoader();
- }
- },
- controller : 'ApplicationClaimsCtrl'
- })
- .when('/realms/:realm/applications/:application/sessions', {
- templateUrl : 'partials/application-sessions.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- application : function(ApplicationLoader) {
- return ApplicationLoader();
- },
- stats : function(ApplicationSessionStatsLoader) {
- return ApplicationSessionStatsLoader();
- }
- },
- controller : 'ApplicationSessionsCtrl'
- })
- .when('/realms/:realm/applications/:application/credentials', {
- templateUrl : 'partials/application-credentials.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- application : function(ApplicationLoader) {
- return ApplicationLoader();
- }
- },
- controller : 'ApplicationCredentialsCtrl'
- })
- .when('/realms/:realm/applications/:application/roles', {
- templateUrl : 'partials/application-role-list.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- application : function(ApplicationLoader) {
- return ApplicationLoader();
- },
- roles : function(ApplicationRoleListLoader) {
- return ApplicationRoleListLoader();
- }
- },
- controller : 'ApplicationRoleListCtrl'
- })
- .when('/realms/:realm/applications/:application/revocation', {
- templateUrl : 'partials/application-revocation.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- application : function(ApplicationLoader) {
- return ApplicationLoader();
- }
- },
- controller : 'ApplicationRevocationCtrl'
- })
- .when('/realms/:realm/applications/:application/scope-mappings', {
- templateUrl : 'partials/application-scope-mappings.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- application : function(ApplicationLoader) {
- return ApplicationLoader();
- },
- applications : function(ApplicationListLoader) {
- return ApplicationListLoader();
- },
- roles : function(RoleListLoader) {
- return RoleListLoader();
- }
- },
- controller : 'ApplicationScopeMappingCtrl'
- })
- .when('/realms/:realm/applications/:application/installation', {
- templateUrl : 'partials/application-installation.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- application : function(ApplicationLoader) {
- return ApplicationLoader();
- }
- },
- controller : 'ApplicationInstallationCtrl'
- })
- .when('/create/application/:realm', {
- templateUrl : 'partials/application-detail.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- applications : function(ApplicationListLoader) {
- return ApplicationListLoader();
- },
- application : function() {
- return {};
- }
- },
- controller : 'ApplicationDetailCtrl'
- })
- .when('/realms/:realm/applications/:application', {
- templateUrl : 'partials/application-detail.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- applications : function(ApplicationListLoader) {
- return ApplicationListLoader();
- },
- application : function(ApplicationLoader) {
- return ApplicationLoader();
- }
- },
- controller : 'ApplicationDetailCtrl'
- })
- .when('/realms/:realm/applications', {
- templateUrl : 'partials/application-list.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- applications : function(ApplicationListLoader) {
- return ApplicationListLoader();
- }
- },
- controller : 'ApplicationListCtrl'
- })
-
- // OAUTH Client
-
- .when('/realms/:realm/oauth-clients/:oauth/claims', {
- templateUrl : 'partials/oauth-client-claims.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- oauth : function(OAuthClientLoader) {
- return OAuthClientLoader();
- },
- claims : function(OAuthClientClaimsLoader) {
- return OAuthClientClaimsLoader();
- }
- },
- controller : 'OAuthClientClaimsCtrl'
- })
- .when('/realms/:realm/oauth-clients/:oauth/revocation', {
- templateUrl : 'partials/oauth-client-revocation.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- oauth : function(OAuthClientLoader) {
- return OAuthClientLoader();
- }
- },
- controller : 'OAuthClientRevocationCtrl'
- })
- .when('/realms/:realm/oauth-clients/:oauth/credentials', {
- templateUrl : 'partials/oauth-client-credentials.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- oauth : function(OAuthClientLoader) {
- return OAuthClientLoader();
- }
- },
- controller : 'OAuthClientCredentialsCtrl'
- })
- .when('/realms/:realm/oauth-clients/:oauth/scope-mappings', {
- templateUrl : 'partials/oauth-client-scope-mappings.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- oauth : function(OAuthClientLoader) {
- return OAuthClientLoader();
- },
- applications : function(ApplicationListLoader) {
- return ApplicationListLoader();
- },
- roles : function(RoleListLoader) {
- return RoleListLoader();
- }
- },
- controller : 'OAuthClientScopeMappingCtrl'
- })
- .when('/realms/:realm/oauth-clients/:oauth/installation', {
- templateUrl : 'partials/oauth-client-installation.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- oauth : function(OAuthClientLoader) {
- return OAuthClientLoader();
- },
- installation : function(OAuthClientInstallationLoader) {
- return OAuthClientInstallationLoader();
- }
- },
- controller : 'OAuthClientInstallationCtrl'
- })
- .when('/create/oauth-client/:realm', {
- templateUrl : 'partials/oauth-client-detail.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- oauth : function() {
- return {};
- }
- },
- controller : 'OAuthClientDetailCtrl'
- })
- .when('/realms/:realm/oauth-clients/:oauth', {
- templateUrl : 'partials/oauth-client-detail.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- oauth : function(OAuthClientLoader) {
- return OAuthClientLoader();
- }
- },
- controller : 'OAuthClientDetailCtrl'
- })
- .when('/realms/:realm/oauth-clients', {
- templateUrl : 'partials/oauth-client-list.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- oauthClients : function(OAuthClientListLoader) {
- return OAuthClientListLoader();
- }
- },
- controller : 'OAuthClientListCtrl'
- })
-
- .when('/', {
- templateUrl : 'partials/home.html',
- controller : 'HomeCtrl'
- })
- .when('/mocks/:realm', {
- templateUrl : 'partials/realm-detail_mock.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- serverInfo : function(ServerInfoLoader) {
- return ServerInfoLoader();
- }
- },
- controller : 'RealmDetailCtrl'
- })
- .when('/realms/:realm/sessions/revocation', {
- templateUrl : 'partials/session-revocation.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- }
- },
- controller : 'RealmRevocationCtrl'
- })
- .when('/realms/:realm/sessions/brute-force', {
- templateUrl : 'partials/session-brute-force.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- }
- },
- controller : 'RealmBruteForceCtrl'
- })
- .when('/realms/:realm/sessions/realm', {
- templateUrl : 'partials/session-realm.html',
- resolve : {
- realm : function(RealmLoader) {
- return RealmLoader();
- },
- stats : function(RealmSessionStatsLoader) {
- return RealmSessionStatsLoader();
- }
- },
- controller : 'RealmSessionStatsCtrl'
- })
- .when('/logout', {
- templateUrl : 'partials/home.html',
- controller : 'LogoutCtrl'
- })
- .otherwise({
- templateUrl : 'partials/notfound.html'
- });
-} ]);
-
-module.config(function($httpProvider) {
- $httpProvider.responseInterceptors.push('errorInterceptor');
-
- var spinnerFunction = function(data, headersGetter) {
- if (resourceRequests == 0) {
- loadingTimer = window.setTimeout(function() {
- $('#loading').show();
- loadingTimer = -1;
- }, 500);
- }
- resourceRequests++;
- return data;
- };
- $httpProvider.defaults.transformRequest.push(spinnerFunction);
-
- $httpProvider.responseInterceptors.push('spinnerInterceptor');
- $httpProvider.interceptors.push('authInterceptor');
-
-});
-
-module.factory('errorInterceptor', function($q, $window, $rootScope, $location,Notifications) {
- return function(promise) {
- return promise.then(function(response) {
- return response;
- }, function(response) {
- if (response.status == 401) {
- console.log('session timeout?');
- logout();
- } else if (response.status == 403) {
- Notifications.error("Forbidden");
- } else if (response.status == 404) {
- Notifications.error("Not found");
- } else if (response.status) {
- if (response.data && response.data.errorMessage) {
- Notifications.error(response.data.errorMessage);
- } else {
- Notifications.error("An unexpected server error has occurred");
- }
- }
- return $q.reject(response);
- });
- };
-});
-
-module.factory('spinnerInterceptor', function($q, $window, $rootScope, $location) {
- return function(promise) {
- return promise.then(function(response) {
- resourceRequests--;
- if (resourceRequests == 0) {
- if(loadingTimer != -1) {
- window.clearTimeout(loadingTimer);
- loadingTimer = -1;
- }
- $('#loading').hide();
- }
- return response;
- }, function(response) {
- resourceRequests--;
- if (resourceRequests == 0) {
- if(loadingTimer != -1) {
- window.clearTimeout(loadingTimer);
- loadingTimer = -1;
- }
- $('#loading').hide();
- }
-
- return $q.reject(response);
- });
- };
-});
-
-// collapsable form fieldsets
-module.directive('collapsable', function() {
- return function(scope, element, attrs) {
- element.click(function() {
- $(this).toggleClass('collapsed');
- $(this).find('.toggle-icons').toggleClass('kc-icon-collapse').toggleClass('kc-icon-expand');
- $(this).find('.toggle-icons').text($(this).text() == "Icon: expand" ? "Icon: collapse" : "Icon: expand");
- $(this).parent().find('.form-group').toggleClass('hidden');
- });
- }
-});
-
-// collapsable form fieldsets
-module.directive('uncollapsed', function() {
- return function(scope, element, attrs) {
- element.prepend('<span class="kc-icon-collapse toggle-icons">Icon: collapse</span>');
- element.click(function() {
- $(this).toggleClass('collapsed');
- $(this).find('.toggle-icons').toggleClass('kc-icon-collapse').toggleClass('kc-icon-expand');
- $(this).find('.toggle-icons').text($(this).text() == "Icon: expand" ? "Icon: collapse" : "Icon: expand");
- $(this).parent().find('.form-group').toggleClass('hidden');
- });
- }
-});
-
-// collapsable form fieldsets
-module.directive('collapsed', function() {
- return function(scope, element, attrs) {
- element.prepend('<span class="kc-icon-expand toggle-icons">Icon: expand</span>');
- element.parent().find('.form-group').toggleClass('hidden');
- element.click(function() {
- $(this).toggleClass('collapsed');
- $(this).find('.toggle-icons').toggleClass('kc-icon-collapse').toggleClass('kc-icon-expand');
- $(this).find('.toggle-icons').text($(this).text() == "Icon: expand" ? "Icon: collapse" : "Icon: expand");
- $(this).parent().find('.form-group').toggleClass('hidden');
- });
- }
-});
-
-/**
- * Directive for presenting an ON-OFF switch for checkbox.
- * Usage: <input ng-model="mmm" name="nnn" id="iii" onoffswitch [on-text="ooo" off-text="fff"] />
- */
-module.directive('onoffswitch', function() {
- return {
- restrict: "EA",
- replace: true,
- scope: {
- name: '@',
- id: '@',
- ngModel: '=',
- ngDisabled: '=',
- kcOnText: '@onText',
- kcOffText: '@offText'
- },
- // TODO - The same code acts differently when put into the templateURL. Find why and move the code there.
- //templateUrl: "templates/kc-switch.html",
- template: "<span><div class='onoffswitch' tabindex='0'><input type='checkbox' ng-model='ngModel' ng-disabled='ngDisabled' class='onoffswitch-checkbox' name='{{name}}' id='{{id}}'><label for='{{id}}' class='onoffswitch-label'><span class='onoffswitch-inner'><span class='onoffswitch-active'>{{kcOnText}}</span><span class='onoffswitch-inactive'>{{kcOffText}}</span></span><span class='onoffswitch-switch'></span></label></div></span>",
- compile: function(element, attrs) {
- /*
- We don't want to propagate basic attributes to the root element of directive. Id should be passed to the
- input element only to achieve proper label binding (and validity).
- */
- element.removeAttr('name');
- element.removeAttr('id');
-
- if (!attrs.onText) { attrs.onText = "ON"; }
- if (!attrs.offText) { attrs.offText = "OFF"; }
-
- element.bind('keydown', function(e){
- var code = e.keyCode || e.which;
- if (code === 32 || code === 13) {
- e.stopImmediatePropagation();
- e.preventDefault();
- $(e.target).find('input').click();
- }
- });
- }
- }
-});
-
-module.directive('kcInput', function() {
- var d = {
- scope : true,
- replace : false,
- link : function(scope, element, attrs) {
- var form = element.children('form');
- var label = element.children('label');
- var input = element.children('input');
-
- var id = form.attr('name') + '.' + input.attr('name');
-
- element.attr('class', 'control-group');
-
- label.attr('class', 'control-label');
- label.attr('for', id);
-
- input.wrap('<div class="controls"/>');
- input.attr('id', id);
-
- if (!input.attr('placeHolder')) {
- input.attr('placeHolder', label.text());
- }
-
- if (input.attr('required')) {
- label.append(' <span class="required">*</span>');
- }
- }
- };
- return d;
-});
-
-module.directive('kcEnter', function() {
- return function(scope, element, attrs) {
- element.bind("keydown keypress", function(event) {
- if (event.which === 13) {
- scope.$apply(function() {
- scope.$eval(attrs.kcEnter);
- });
-
- event.preventDefault();
- }
- });
- };
-});
-
-module.directive('kcSave', function ($compile, Notifications) {
- return {
- restrict: 'A',
- link: function ($scope, elem, attr, ctrl) {
- elem.addClass("btn btn-primary btn-lg");
- elem.attr("type","submit");
- elem.bind('click', function() {
- $scope.$apply(function() {
- var form = elem.closest('form');
- if (form && form.attr('name')) {
- var ngValid = form.find('.ng-valid');
- if ($scope[form.attr('name')].$valid) {
- //ngValid.removeClass('error');
- ngValid.parent().removeClass('has-error');
- $scope['save']();
- } else {
- Notifications.error("Missing or invalid field(s). Please verify the fields in red.")
- //ngValid.removeClass('error');
- ngValid.parent().removeClass('has-error');
-
- var ngInvalid = form.find('.ng-invalid');
- //ngInvalid.addClass('error');
- ngInvalid.parent().addClass('has-error');
- }
- }
- });
- })
- }
- }
-});
-
-module.directive('kcReset', function ($compile, Notifications) {
- return {
- restrict: 'A',
- link: function ($scope, elem, attr, ctrl) {
- elem.addClass("btn btn-default btn-lg");
- elem.attr("type","submit");
- elem.bind('click', function() {
- $scope.$apply(function() {
- var form = elem.closest('form');
- if (form && form.attr('name')) {
- form.find('.ng-valid').removeClass('error');
- form.find('.ng-invalid').removeClass('error');
- $scope['reset']();
- }
- })
- })
- }
- }
-});
-
-module.directive('kcCancel', function ($compile, Notifications) {
- return {
- restrict: 'A',
- link: function ($scope, elem, attr, ctrl) {
- elem.addClass("btn btn-default btn-lg");
- elem.attr("type","submit");
- }
- }
-});
-
-module.directive('kcDelete', function ($compile, Notifications) {
- return {
- restrict: 'A',
- link: function ($scope, elem, attr, ctrl) {
- elem.addClass("btn btn-danger btn-lg");
- elem.attr("type","submit");
- }
- }
-});
-
-
-module.directive('kcDropdown', function ($compile, Notifications) {
- return {
- scope: {
- kcOptions: '=',
- kcModel: '=',
- id: "=",
- kcPlaceholder: '@'
- },
- restrict: 'EA',
- replace: true,
- templateUrl: 'templates/kc-select.html',
- link: function(scope, element, attr) {
- scope.updateModel = function(item) {
- scope.kcModel = item;
- };
- }
- }
-});
-
-module.directive('kcReadOnly', function() {
- var disabled = {};
-
- var d = {
- replace : false,
- link : function(scope, element, attrs) {
- var disable = function(i, e) {
- if (!e.disabled) {
- disabled[e.tagName + i] = true;
- e.disabled = true;
- }
- }
-
- var enable = function(i, e) {
- if (disabled[e.tagName + i]) {
- e.disabled = false;
- delete disabled[i];
- }
- }
-
- scope.$watch(attrs.kcReadOnly, function(readOnly) {
- if (readOnly) {
- console.debug('readonly');
- element.find('input').each(disable);
- element.find('button').each(disable);
- element.find('select').each(disable);
- element.find('textarea').each(disable);
- } else {
- element.find('input').each(enable);
- element.find('input').each(enable);
- element.find('button').each(enable);
- element.find('select').each(enable);
- element.find('textarea').each(enable);
- }
- });
- }
- };
- return d;
-});
-
-module.directive('kcNavigation', function ($compile, Notifications) {
- return {
- scope: true,
- restrict: 'E',
- replace: true,
- templateUrl: 'templates/kc-navigation.html',
-
- compile: function(element, attrs){
- if (!attrs.kcSocial) {
- attrs.kcSocial = false;
- }
- }
- }
-});
-
-/*
-* Used to select the element (invoke $(elem).select()) on specified action list.
-* Usages kc-select-action="click mouseover"
-* When used in the textarea element, this will select/highlight the textarea content on specified action (i.e. click).
-*/
-module.directive('kcSelectAction', function ($compile, Notifications) {
- return {
- restrict: 'A',
- compile: function (elem, attrs) {
-
- var events = attrs.kcSelectAction.split(" ");
-
- for(var i=0; i < events.length; i++){
-
- elem.bind(events[i], function(){
- elem.select();
- });
- }
- }
- }
-});
-
-module.filter('remove', function() {
- return function(input, remove, attribute) {
- if (!input || !remove) {
- return input;
- }
-
- var out = [];
- for ( var i = 0; i < input.length; i++) {
- var e = input[i];
-
- if (Array.isArray(remove)) {
- for (var j = 0; j < remove.length; j++) {
- if (attribute) {
- if (remove[j][attribute] == e[attribute]) {
- e = null;
- break;
- }
- } else {
- if (remove[j] == e) {
- e = null;
- break;
- }
- }
- }
- } else {
- if (attribute) {
- if (remove[attribute] == e[attribute]) {
- e = null;
- }
- } else {
- if (remove == e) {
- e = null;
- }
- }
- }
-
- if (e != null) {
- out.push(e);
- }
- }
-
- return out;
- };
-});
-
-module.filter('capitalize', function() {
- return function(input) {
- if (!input) {
- return;
- }
- var result = input.substring(0, 1).toUpperCase();
- var s = input.substring(1);
- for (var i=0; i<s.length ; i++) {
- var c = s[i];
- if (c.match(/[A-Z]/)) {
- result = result.concat(" ")
- };
- result = result.concat(c);
- };
- return result;
- };
+'use strict';
+
+var indexUrl = window.location.href;
+var consoleBaseUrl = window.location.href;
+consoleBaseUrl = consoleBaseUrl.substring(0, consoleBaseUrl.indexOf("/console"));
+consoleBaseUrl = consoleBaseUrl + "/console";
+var configUrl = consoleBaseUrl + "/config";
+var logoutUrl = consoleBaseUrl + "/logout";
+var auth = {};
+var logout = function(){
+ console.log('*** LOGOUT');
+ auth.loggedIn = false;
+ auth.authz = null;
+ auth.user = null;
+ window.location = logoutUrl;
+};
+
+
+var authUrl = window.location.href;
+authUrl = authUrl.substring(0, authUrl.indexOf('/admin/'));
+
+
+var module = angular.module('keycloak', [ 'keycloak.services', 'keycloak.loaders', 'ui.bootstrap', 'ui.select2', 'angularFileUpload' ]);
+var resourceRequests = 0;
+var loadingTimer = -1;
+
+angular.element(document).ready(function ($http) {
+ var keycloakAuth = new Keycloak(configUrl);
+ var auth = {};
+ auth.loggedIn = false;
+
+ keycloakAuth.init('login-required').success(function () {
+ auth.loggedIn = true;
+ auth.authz = keycloakAuth;
+ module.factory('Auth', function() {
+ return auth;
+ });
+ angular.bootstrap(document, ["keycloak"]);
+ }).error(function () {
+ window.location.reload();
+ });
+
+});
+
+module.factory('authInterceptor', function($q, Auth) {
+ return {
+ request: function (config) {
+ var deferred = $q.defer();
+ if (Auth.authz.token) {
+ Auth.authz.updateToken(5).success(function() {
+ config.headers = config.headers || {};
+ config.headers.Authorization = 'Bearer ' + Auth.authz.token;
+
+ deferred.resolve(config);
+ }).error(function() {
+ deferred.reject('Failed to refresh token');
+ });
+ }
+ return deferred.promise;
+ }
+ };
+});
+
+
+
+
+module.config([ '$routeProvider', function($routeProvider) {
+
+ $routeProvider
+ /*
+ .when('/create/realm', {
+ templateUrl : 'partials/realm-detail.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return {};
+ }
+ },
+ controller : 'RealmDetailCtrl'
+ })
+ */
+
+ .when('/create/realm', {
+ templateUrl : 'partials/realm-create.html',
+ resolve : {
+
+ },
+ controller : 'RealmCreateCtrl'
+ })
+ .when('/realms/:realm', {
+ templateUrl : 'partials/realm-detail.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ serverInfo : function(ServerInfoLoader) {
+ return ServerInfoLoader();
+ }
+ },
+ controller : 'RealmDetailCtrl'
+ })
+ .when('/realms', {
+ templateUrl : 'partials/realm-list.html',
+ controller : 'RealmListCtrl'
+ })
+ .when('/realms/:realm/token-settings', {
+ templateUrl : 'partials/realm-tokens.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ }
+ },
+ controller : 'RealmTokenDetailCtrl'
+ })
+ .when('/realms/:realm/keys-settings', {
+ templateUrl : 'partials/realm-keys.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ }
+ },
+ controller : 'RealmKeysDetailCtrl'
+ })
+ .when('/realms/:realm/social-settings', {
+ templateUrl : 'partials/realm-social.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ serverInfo : function(ServerInfoLoader) {
+ return ServerInfoLoader();
+ }
+ },
+ controller : 'RealmSocialCtrl'
+ })
+ .when('/realms/:realm/default-roles', {
+ templateUrl : 'partials/realm-default-roles.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ applications : function(ApplicationListLoader) {
+ return ApplicationListLoader();
+ },
+ roles : function(RoleListLoader) {
+ return RoleListLoader();
+ }
+ },
+ controller : 'RealmDefaultRolesCtrl'
+ })
+ .when('/realms/:realm/required-credentials', {
+ templateUrl : 'partials/realm-credentials.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ }
+ },
+ controller : 'RealmRequiredCredentialsCtrl'
+ })
+ .when('/realms/:realm/smtp-settings', {
+ templateUrl : 'partials/realm-smtp.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ }
+ },
+ controller : 'RealmSMTPSettingsCtrl'
+ })
+ .when('/realms/:realm/ldap-settings', {
+ templateUrl : 'partials/realm-ldap.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ }
+ },
+ controller : 'RealmLdapSettingsCtrl'
+ })
+ .when('/realms/:realm/audit', {
+ templateUrl : 'partials/realm-audit.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ }
+ },
+ controller : 'RealmAuditEventsCtrl'
+ })
+ .when('/realms/:realm/audit-settings', {
+ templateUrl : 'partials/realm-audit-config.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ serverInfo : function(ServerInfoLoader) {
+ return ServerInfoLoader();
+ },
+ auditConfig : function(RealmAuditLoader) {
+ return RealmAuditLoader();
+ }
+ },
+ controller : 'RealmAuditCtrl'
+ })
+ .when('/realms/:realm/auth-settings', {
+ templateUrl : 'partials/realm-auth-list.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ }
+ },
+ controller : 'RealmAuthSettingsCtrl'
+ })
+ .when('/realms/:realm/auth-settings/create', {
+ templateUrl : 'partials/realm-auth-detail.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ serverInfo : function(ServerInfoLoader) {
+ return ServerInfoLoader();
+ }
+ },
+ controller : 'RealmAuthSettingsDetailCtrl'
+ })
+ .when('/realms/:realm/auth-settings/:index', {
+ templateUrl : 'partials/realm-auth-detail.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ serverInfo : function(ServerInfoLoader) {
+ return ServerInfoLoader();
+ }
+ },
+ controller : 'RealmAuthSettingsDetailCtrl'
+ })
+ .when('/create/user/:realm', {
+ templateUrl : 'partials/user-detail.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ user : function() {
+ return {};
+ }
+ },
+ controller : 'UserDetailCtrl'
+ })
+ .when('/realms/:realm/users/:user', {
+ templateUrl : 'partials/user-detail.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ user : function(UserLoader) {
+ return UserLoader();
+ }
+ },
+ controller : 'UserDetailCtrl'
+ })
+ .when('/realms/:realm/users/:user/user-credentials', {
+ templateUrl : 'partials/user-credentials.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ user : function(UserLoader) {
+ return UserLoader();
+ }
+ },
+ controller : 'UserCredentialsCtrl'
+ })
+ .when('/realms/:realm/users/:user/role-mappings', {
+ templateUrl : 'partials/role-mappings.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ user : function(UserLoader) {
+ return UserLoader();
+ },
+ applications : function(ApplicationListLoader) {
+ return ApplicationListLoader();
+ },
+ roles : function(RoleListLoader) {
+ return RoleListLoader();
+ }
+ },
+ controller : 'UserRoleMappingCtrl'
+ })
+ .when('/realms/:realm/users/:user/sessions', {
+ templateUrl : 'partials/user-sessions.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ user : function(UserLoader) {
+ return UserLoader();
+ },
+ stats : function(UserSessionStatsLoader) {
+ return UserSessionStatsLoader();
+ }
+ },
+ controller : 'UserSessionsCtrl'
+ })
+ .when('/realms/:realm/users/:user/social-links', {
+ templateUrl : 'partials/user-social-links.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ user : function(UserLoader) {
+ return UserLoader();
+ },
+ socialLinks : function(UserSocialLinksLoader) {
+ return UserSocialLinksLoader();
+ }
+ },
+ controller : 'UserSocialCtrl'
+ })
+ .when('/realms/:realm/users', {
+ templateUrl : 'partials/user-list.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ }
+ },
+ controller : 'UserListCtrl'
+ })
+
+ .when('/create/role/:realm', {
+ templateUrl : 'partials/role-detail.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ role : function() {
+ return {};
+ },
+ roles : function(RoleListLoader) {
+ return RoleListLoader();
+ },
+ applications : function(ApplicationListLoader) {
+ return ApplicationListLoader();
+ }
+ },
+ controller : 'RoleDetailCtrl'
+ })
+ .when('/realms/:realm/roles/:role', {
+ templateUrl : 'partials/role-detail.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ role : function(RoleLoader) {
+ return RoleLoader();
+ },
+ roles : function(RoleListLoader) {
+ return RoleListLoader();
+ },
+ applications : function(ApplicationListLoader) {
+ return ApplicationListLoader();
+ }
+ },
+ controller : 'RoleDetailCtrl'
+ })
+ .when('/realms/:realm/roles', {
+ templateUrl : 'partials/role-list.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ roles : function(RoleListLoader) {
+ return RoleListLoader();
+ }
+ },
+ controller : 'RoleListCtrl'
+ })
+
+ .when('/create/role/:realm/applications/:application', {
+ templateUrl : 'partials/application-role-detail.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ application : function(ApplicationLoader) {
+ return ApplicationLoader();
+ },
+ role : function() {
+ return {};
+ },
+ roles : function(RoleListLoader) {
+ return RoleListLoader();
+ },
+ applications : function(ApplicationListLoader) {
+ return ApplicationListLoader();
+ }
+ },
+ controller : 'ApplicationRoleDetailCtrl'
+ })
+ .when('/realms/:realm/applications/:application/roles/:role', {
+ templateUrl : 'partials/application-role-detail.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ application : function(ApplicationLoader) {
+ return ApplicationLoader();
+ },
+ role : function(ApplicationRoleLoader) {
+ return ApplicationRoleLoader();
+ },
+ roles : function(RoleListLoader) {
+ return RoleListLoader();
+ },
+ applications : function(ApplicationListLoader) {
+ return ApplicationListLoader();
+ }
+ },
+ controller : 'ApplicationRoleDetailCtrl'
+ })
+ .when('/realms/:realm/applications/:application/claims', {
+ templateUrl : 'partials/application-claims.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ application : function(ApplicationLoader) {
+ return ApplicationLoader();
+ },
+ claims : function(ApplicationClaimsLoader) {
+ return ApplicationClaimsLoader();
+ }
+ },
+ controller : 'ApplicationClaimsCtrl'
+ })
+ .when('/realms/:realm/applications/:application/sessions', {
+ templateUrl : 'partials/application-sessions.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ application : function(ApplicationLoader) {
+ return ApplicationLoader();
+ },
+ stats : function(ApplicationSessionStatsLoader) {
+ return ApplicationSessionStatsLoader();
+ }
+ },
+ controller : 'ApplicationSessionsCtrl'
+ })
+ .when('/realms/:realm/applications/:application/credentials', {
+ templateUrl : 'partials/application-credentials.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ application : function(ApplicationLoader) {
+ return ApplicationLoader();
+ }
+ },
+ controller : 'ApplicationCredentialsCtrl'
+ })
+ .when('/realms/:realm/applications/:application/roles', {
+ templateUrl : 'partials/application-role-list.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ application : function(ApplicationLoader) {
+ return ApplicationLoader();
+ },
+ roles : function(ApplicationRoleListLoader) {
+ return ApplicationRoleListLoader();
+ }
+ },
+ controller : 'ApplicationRoleListCtrl'
+ })
+ .when('/realms/:realm/applications/:application/revocation', {
+ templateUrl : 'partials/application-revocation.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ application : function(ApplicationLoader) {
+ return ApplicationLoader();
+ }
+ },
+ controller : 'ApplicationRevocationCtrl'
+ })
+ .when('/realms/:realm/applications/:application/scope-mappings', {
+ templateUrl : 'partials/application-scope-mappings.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ application : function(ApplicationLoader) {
+ return ApplicationLoader();
+ },
+ applications : function(ApplicationListLoader) {
+ return ApplicationListLoader();
+ },
+ roles : function(RoleListLoader) {
+ return RoleListLoader();
+ }
+ },
+ controller : 'ApplicationScopeMappingCtrl'
+ })
+ .when('/realms/:realm/applications/:application/installation', {
+ templateUrl : 'partials/application-installation.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ application : function(ApplicationLoader) {
+ return ApplicationLoader();
+ }
+ },
+ controller : 'ApplicationInstallationCtrl'
+ })
+ .when('/create/application/:realm', {
+ templateUrl : 'partials/application-detail.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ applications : function(ApplicationListLoader) {
+ return ApplicationListLoader();
+ },
+ application : function() {
+ return {};
+ }
+ },
+ controller : 'ApplicationDetailCtrl'
+ })
+ .when('/realms/:realm/applications/:application', {
+ templateUrl : 'partials/application-detail.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ applications : function(ApplicationListLoader) {
+ return ApplicationListLoader();
+ },
+ application : function(ApplicationLoader) {
+ return ApplicationLoader();
+ }
+ },
+ controller : 'ApplicationDetailCtrl'
+ })
+ .when('/realms/:realm/applications', {
+ templateUrl : 'partials/application-list.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ applications : function(ApplicationListLoader) {
+ return ApplicationListLoader();
+ }
+ },
+ controller : 'ApplicationListCtrl'
+ })
+
+ // OAUTH Client
+
+ .when('/realms/:realm/oauth-clients/:oauth/claims', {
+ templateUrl : 'partials/oauth-client-claims.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ oauth : function(OAuthClientLoader) {
+ return OAuthClientLoader();
+ },
+ claims : function(OAuthClientClaimsLoader) {
+ return OAuthClientClaimsLoader();
+ }
+ },
+ controller : 'OAuthClientClaimsCtrl'
+ })
+ .when('/realms/:realm/oauth-clients/:oauth/revocation', {
+ templateUrl : 'partials/oauth-client-revocation.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ oauth : function(OAuthClientLoader) {
+ return OAuthClientLoader();
+ }
+ },
+ controller : 'OAuthClientRevocationCtrl'
+ })
+ .when('/realms/:realm/oauth-clients/:oauth/credentials', {
+ templateUrl : 'partials/oauth-client-credentials.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ oauth : function(OAuthClientLoader) {
+ return OAuthClientLoader();
+ }
+ },
+ controller : 'OAuthClientCredentialsCtrl'
+ })
+ .when('/realms/:realm/oauth-clients/:oauth/scope-mappings', {
+ templateUrl : 'partials/oauth-client-scope-mappings.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ oauth : function(OAuthClientLoader) {
+ return OAuthClientLoader();
+ },
+ applications : function(ApplicationListLoader) {
+ return ApplicationListLoader();
+ },
+ roles : function(RoleListLoader) {
+ return RoleListLoader();
+ }
+ },
+ controller : 'OAuthClientScopeMappingCtrl'
+ })
+ .when('/realms/:realm/oauth-clients/:oauth/installation', {
+ templateUrl : 'partials/oauth-client-installation.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ oauth : function(OAuthClientLoader) {
+ return OAuthClientLoader();
+ },
+ installation : function(OAuthClientInstallationLoader) {
+ return OAuthClientInstallationLoader();
+ }
+ },
+ controller : 'OAuthClientInstallationCtrl'
+ })
+ .when('/create/oauth-client/:realm', {
+ templateUrl : 'partials/oauth-client-detail.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ oauth : function() {
+ return {};
+ }
+ },
+ controller : 'OAuthClientDetailCtrl'
+ })
+ .when('/realms/:realm/oauth-clients/:oauth', {
+ templateUrl : 'partials/oauth-client-detail.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ oauth : function(OAuthClientLoader) {
+ return OAuthClientLoader();
+ }
+ },
+ controller : 'OAuthClientDetailCtrl'
+ })
+ .when('/realms/:realm/oauth-clients', {
+ templateUrl : 'partials/oauth-client-list.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ oauthClients : function(OAuthClientListLoader) {
+ return OAuthClientListLoader();
+ }
+ },
+ controller : 'OAuthClientListCtrl'
+ })
+
+ .when('/', {
+ templateUrl : 'partials/home.html',
+ controller : 'HomeCtrl'
+ })
+ .when('/mocks/:realm', {
+ templateUrl : 'partials/realm-detail_mock.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ serverInfo : function(ServerInfoLoader) {
+ return ServerInfoLoader();
+ }
+ },
+ controller : 'RealmDetailCtrl'
+ })
+ .when('/realms/:realm/sessions/revocation', {
+ templateUrl : 'partials/session-revocation.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ }
+ },
+ controller : 'RealmRevocationCtrl'
+ })
+ .when('/realms/:realm/sessions/brute-force', {
+ templateUrl : 'partials/session-brute-force.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ }
+ },
+ controller : 'RealmBruteForceCtrl'
+ })
+ .when('/realms/:realm/sessions/realm', {
+ templateUrl : 'partials/session-realm.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ stats : function(RealmSessionStatsLoader) {
+ return RealmSessionStatsLoader();
+ }
+ },
+ controller : 'RealmSessionStatsCtrl'
+ })
+ .when('/logout', {
+ templateUrl : 'partials/home.html',
+ controller : 'LogoutCtrl'
+ })
+ .otherwise({
+ templateUrl : 'partials/notfound.html'
+ });
+} ]);
+
+module.config(function($httpProvider) {
+ $httpProvider.responseInterceptors.push('errorInterceptor');
+
+ var spinnerFunction = function(data, headersGetter) {
+ if (resourceRequests == 0) {
+ loadingTimer = window.setTimeout(function() {
+ $('#loading').show();
+ loadingTimer = -1;
+ }, 500);
+ }
+ resourceRequests++;
+ return data;
+ };
+ $httpProvider.defaults.transformRequest.push(spinnerFunction);
+
+ $httpProvider.responseInterceptors.push('spinnerInterceptor');
+ $httpProvider.interceptors.push('authInterceptor');
+
+});
+
+module.factory('errorInterceptor', function($q, $window, $rootScope, $location,Notifications) {
+ return function(promise) {
+ return promise.then(function(response) {
+ return response;
+ }, function(response) {
+ if (response.status == 401) {
+ console.log('session timeout?');
+ logout();
+ } else if (response.status == 403) {
+ Notifications.error("Forbidden");
+ } else if (response.status == 404) {
+ Notifications.error("Not found");
+ } else if (response.status) {
+ if (response.data && response.data.errorMessage) {
+ Notifications.error(response.data.errorMessage);
+ } else {
+ Notifications.error("An unexpected server error has occurred");
+ }
+ }
+ return $q.reject(response);
+ });
+ };
+});
+
+module.factory('spinnerInterceptor', function($q, $window, $rootScope, $location) {
+ return function(promise) {
+ return promise.then(function(response) {
+ resourceRequests--;
+ if (resourceRequests == 0) {
+ if(loadingTimer != -1) {
+ window.clearTimeout(loadingTimer);
+ loadingTimer = -1;
+ }
+ $('#loading').hide();
+ }
+ return response;
+ }, function(response) {
+ resourceRequests--;
+ if (resourceRequests == 0) {
+ if(loadingTimer != -1) {
+ window.clearTimeout(loadingTimer);
+ loadingTimer = -1;
+ }
+ $('#loading').hide();
+ }
+
+ return $q.reject(response);
+ });
+ };
+});
+
+// collapsable form fieldsets
+module.directive('collapsable', function() {
+ return function(scope, element, attrs) {
+ element.click(function() {
+ $(this).toggleClass('collapsed');
+ $(this).find('.toggle-icons').toggleClass('kc-icon-collapse').toggleClass('kc-icon-expand');
+ $(this).find('.toggle-icons').text($(this).text() == "Icon: expand" ? "Icon: collapse" : "Icon: expand");
+ $(this).parent().find('.form-group').toggleClass('hidden');
+ });
+ }
+});
+
+// collapsable form fieldsets
+module.directive('uncollapsed', function() {
+ return function(scope, element, attrs) {
+ element.prepend('<span class="kc-icon-collapse toggle-icons">Icon: collapse</span>');
+ element.click(function() {
+ $(this).toggleClass('collapsed');
+ $(this).find('.toggle-icons').toggleClass('kc-icon-collapse').toggleClass('kc-icon-expand');
+ $(this).find('.toggle-icons').text($(this).text() == "Icon: expand" ? "Icon: collapse" : "Icon: expand");
+ $(this).parent().find('.form-group').toggleClass('hidden');
+ });
+ }
+});
+
+// collapsable form fieldsets
+module.directive('collapsed', function() {
+ return function(scope, element, attrs) {
+ element.prepend('<span class="kc-icon-expand toggle-icons">Icon: expand</span>');
+ element.parent().find('.form-group').toggleClass('hidden');
+ element.click(function() {
+ $(this).toggleClass('collapsed');
+ $(this).find('.toggle-icons').toggleClass('kc-icon-collapse').toggleClass('kc-icon-expand');
+ $(this).find('.toggle-icons').text($(this).text() == "Icon: expand" ? "Icon: collapse" : "Icon: expand");
+ $(this).parent().find('.form-group').toggleClass('hidden');
+ });
+ }
+});
+
+/**
+ * Directive for presenting an ON-OFF switch for checkbox.
+ * Usage: <input ng-model="mmm" name="nnn" id="iii" onoffswitch [on-text="ooo" off-text="fff"] />
+ */
+module.directive('onoffswitch', function() {
+ return {
+ restrict: "EA",
+ replace: true,
+ scope: {
+ name: '@',
+ id: '@',
+ ngModel: '=',
+ ngDisabled: '=',
+ kcOnText: '@onText',
+ kcOffText: '@offText'
+ },
+ // TODO - The same code acts differently when put into the templateURL. Find why and move the code there.
+ //templateUrl: "templates/kc-switch.html",
+ template: "<span><div class='onoffswitch' tabindex='0'><input type='checkbox' ng-model='ngModel' ng-disabled='ngDisabled' class='onoffswitch-checkbox' name='{{name}}' id='{{id}}'><label for='{{id}}' class='onoffswitch-label'><span class='onoffswitch-inner'><span class='onoffswitch-active'>{{kcOnText}}</span><span class='onoffswitch-inactive'>{{kcOffText}}</span></span><span class='onoffswitch-switch'></span></label></div></span>",
+ compile: function(element, attrs) {
+ /*
+ We don't want to propagate basic attributes to the root element of directive. Id should be passed to the
+ input element only to achieve proper label binding (and validity).
+ */
+ element.removeAttr('name');
+ element.removeAttr('id');
+
+ if (!attrs.onText) { attrs.onText = "ON"; }
+ if (!attrs.offText) { attrs.offText = "OFF"; }
+
+ element.bind('keydown', function(e){
+ var code = e.keyCode || e.which;
+ if (code === 32 || code === 13) {
+ e.stopImmediatePropagation();
+ e.preventDefault();
+ $(e.target).find('input').click();
+ }
+ });
+ }
+ }
+});
+
+module.directive('kcInput', function() {
+ var d = {
+ scope : true,
+ replace : false,
+ link : function(scope, element, attrs) {
+ var form = element.children('form');
+ var label = element.children('label');
+ var input = element.children('input');
+
+ var id = form.attr('name') + '.' + input.attr('name');
+
+ element.attr('class', 'control-group');
+
+ label.attr('class', 'control-label');
+ label.attr('for', id);
+
+ input.wrap('<div class="controls"/>');
+ input.attr('id', id);
+
+ if (!input.attr('placeHolder')) {
+ input.attr('placeHolder', label.text());
+ }
+
+ if (input.attr('required')) {
+ label.append(' <span class="required">*</span>');
+ }
+ }
+ };
+ return d;
+});
+
+module.directive('kcEnter', function() {
+ return function(scope, element, attrs) {
+ element.bind("keydown keypress", function(event) {
+ if (event.which === 13) {
+ scope.$apply(function() {
+ scope.$eval(attrs.kcEnter);
+ });
+
+ event.preventDefault();
+ }
+ });
+ };
+});
+
+module.directive('kcSave', function ($compile, Notifications) {
+ return {
+ restrict: 'A',
+ link: function ($scope, elem, attr, ctrl) {
+ elem.addClass("btn btn-primary btn-lg");
+ elem.attr("type","submit");
+ elem.bind('click', function() {
+ $scope.$apply(function() {
+ var form = elem.closest('form');
+ if (form && form.attr('name')) {
+ var ngValid = form.find('.ng-valid');
+ if ($scope[form.attr('name')].$valid) {
+ //ngValid.removeClass('error');
+ ngValid.parent().removeClass('has-error');
+ $scope['save']();
+ } else {
+ Notifications.error("Missing or invalid field(s). Please verify the fields in red.")
+ //ngValid.removeClass('error');
+ ngValid.parent().removeClass('has-error');
+
+ var ngInvalid = form.find('.ng-invalid');
+ //ngInvalid.addClass('error');
+ ngInvalid.parent().addClass('has-error');
+ }
+ }
+ });
+ })
+ }
+ }
+});
+
+module.directive('kcReset', function ($compile, Notifications) {
+ return {
+ restrict: 'A',
+ link: function ($scope, elem, attr, ctrl) {
+ elem.addClass("btn btn-default btn-lg");
+ elem.attr("type","submit");
+ elem.bind('click', function() {
+ $scope.$apply(function() {
+ var form = elem.closest('form');
+ if (form && form.attr('name')) {
+ form.find('.ng-valid').removeClass('error');
+ form.find('.ng-invalid').removeClass('error');
+ $scope['reset']();
+ }
+ })
+ })
+ }
+ }
+});
+
+module.directive('kcCancel', function ($compile, Notifications) {
+ return {
+ restrict: 'A',
+ link: function ($scope, elem, attr, ctrl) {
+ elem.addClass("btn btn-default btn-lg");
+ elem.attr("type","submit");
+ }
+ }
+});
+
+module.directive('kcDelete', function ($compile, Notifications) {
+ return {
+ restrict: 'A',
+ link: function ($scope, elem, attr, ctrl) {
+ elem.addClass("btn btn-danger btn-lg");
+ elem.attr("type","submit");
+ }
+ }
+});
+
+
+module.directive('kcDropdown', function ($compile, Notifications) {
+ return {
+ scope: {
+ kcOptions: '=',
+ kcModel: '=',
+ id: "=",
+ kcPlaceholder: '@'
+ },
+ restrict: 'EA',
+ replace: true,
+ templateUrl: 'templates/kc-select.html',
+ link: function(scope, element, attr) {
+ scope.updateModel = function(item) {
+ scope.kcModel = item;
+ };
+ }
+ }
+});
+
+module.directive('kcReadOnly', function() {
+ var disabled = {};
+
+ var d = {
+ replace : false,
+ link : function(scope, element, attrs) {
+ var disable = function(i, e) {
+ if (!e.disabled) {
+ disabled[e.tagName + i] = true;
+ e.disabled = true;
+ }
+ }
+
+ var enable = function(i, e) {
+ if (disabled[e.tagName + i]) {
+ e.disabled = false;
+ delete disabled[i];
+ }
+ }
+
+ scope.$watch(attrs.kcReadOnly, function(readOnly) {
+ if (readOnly) {
+ console.debug('readonly');
+ element.find('input').each(disable);
+ element.find('button').each(disable);
+ element.find('select').each(disable);
+ element.find('textarea').each(disable);
+ } else {
+ element.find('input').each(enable);
+ element.find('input').each(enable);
+ element.find('button').each(enable);
+ element.find('select').each(enable);
+ element.find('textarea').each(enable);
+ }
+ });
+ }
+ };
+ return d;
+});
+
+module.directive('kcNavigation', function ($compile, Notifications) {
+ return {
+ scope: true,
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'templates/kc-navigation.html',
+
+ compile: function(element, attrs){
+ if (!attrs.kcSocial) {
+ attrs.kcSocial = false;
+ }
+ }
+ }
+});
+
+/*
+* Used to select the element (invoke $(elem).select()) on specified action list.
+* Usages kc-select-action="click mouseover"
+* When used in the textarea element, this will select/highlight the textarea content on specified action (i.e. click).
+*/
+module.directive('kcSelectAction', function ($compile, Notifications) {
+ return {
+ restrict: 'A',
+ compile: function (elem, attrs) {
+
+ var events = attrs.kcSelectAction.split(" ");
+
+ for(var i=0; i < events.length; i++){
+
+ elem.bind(events[i], function(){
+ elem.select();
+ });
+ }
+ }
+ }
+});
+
+module.filter('remove', function() {
+ return function(input, remove, attribute) {
+ if (!input || !remove) {
+ return input;
+ }
+
+ var out = [];
+ for ( var i = 0; i < input.length; i++) {
+ var e = input[i];
+
+ if (Array.isArray(remove)) {
+ for (var j = 0; j < remove.length; j++) {
+ if (attribute) {
+ if (remove[j][attribute] == e[attribute]) {
+ e = null;
+ break;
+ }
+ } else {
+ if (remove[j] == e) {
+ e = null;
+ break;
+ }
+ }
+ }
+ } else {
+ if (attribute) {
+ if (remove[attribute] == e[attribute]) {
+ e = null;
+ }
+ } else {
+ if (remove == e) {
+ e = null;
+ }
+ }
+ }
+
+ if (e != null) {
+ out.push(e);
+ }
+ }
+
+ return out;
+ };
+});
+
+module.filter('capitalize', function() {
+ return function(input) {
+ if (!input) {
+ return;
+ }
+ var result = input.substring(0, 1).toUpperCase();
+ var s = input.substring(1);
+ for (var i=0; i<s.length ; i++) {
+ var c = s[i];
+ if (c.match(/[A-Z]/)) {
+ result = result.concat(" ")
+ };
+ result = result.concat(c);
+ };
+ return result;
+ };
});
\ No newline at end of file
integration/js/src/main/resources/keycloak.js 1315(+689 -626)
diff --git a/integration/js/src/main/resources/keycloak.js b/integration/js/src/main/resources/keycloak.js
index c142663..c204f83 100755
--- a/integration/js/src/main/resources/keycloak.js
+++ b/integration/js/src/main/resources/keycloak.js
@@ -1,626 +1,689 @@
-var Keycloak = function (config) {
- if (!(this instanceof Keycloak)) {
- return new Keycloak(config);
- }
-
- var kc = this;
- var adapter;
-
- kc.init = function (init) {
- kc.authenticated = false;
-
- if (window.Cordova) {
- adapter = loadAdapter('cordova');
- } else {
- adapter = loadAdapter();
- }
-
- var promise = createPromise();
-
- var initPromise = createPromise();
- initPromise.promise.success(function() {
- kc.onReady && kc.onReady(kc.authenticated);
- promise.setSuccess(kc.authenticated);
- }).error(function() {
- promise.setError();
- });
-
- var configPromise = loadConfig(config);
-
- function processInit() {
- var callback = parseCallback(window.location.href);
- if (callback) {
- window.history.replaceState({}, null, location.protocol + '//' + location.host + location.pathname + (callback.fragment ? '#' + callback.fragment : ''));
- processCallback(callback, initPromise);
- return;
- } else if (init) {
- if (init.code || init.error) {
- processCallback(init, initPromise);
- return;
- } else if (init.token || init.refreshToken) {
- setToken(init.token, init.refreshToken);
- initPromise.setSuccess();
- } else if (init == 'login-required') {
- var p = kc.login();
- if (p) {
- p.success(function() {
- initPromise.setSuccess();
- }).error(function() {
- initPromise.setError();
- });
- };
- } else if (init == 'check-sso') {
- var p = kc.login({ prompt: 'none' });
- if (p) {
- p.success(function() {
- initPromise.setSuccess();
- }).error(function() {
- initPromise.setSuccess();
- });
- };
- } else {
- throw 'invalid init: ' + init;
- }
- } else {
- initPromise.setSuccess();
- }
- }
-
- configPromise.success(processInit);
- configPromise.error(function() {
- promise.setError();
- });
-
- return promise.promise;
- }
-
- kc.login = function (options) {
- return adapter.login(options);
- }
-
- kc.createLoginUrl = function(options) {
- var state = createUUID();
-
- var redirectUri = adapter.redirectUri(options);
- if (options && options.prompt) {
- if (redirectUri.indexOf('?') == -1) {
- redirectUri += '?prompt=' + options.prompt;
- } else {
- redirectUri += '&prompt=' + options.prompt;
- }
- }
-
- sessionStorage.oauthState = state;
-
- var url = getRealmUrl()
- + '/tokens/login'
- + '?client_id=' + encodeURIComponent(kc.clientId)
- + '&redirect_uri=' + encodeURIComponent(redirectUri)
- + '&state=' + encodeURIComponent(state)
- + '&response_type=code';
-
- if (options && options.prompt) {
- url += '&prompt=' + options.prompt;
- }
-
- return url;
- }
-
- kc.logout = function(options) {
- return adapter.logout(options);
- }
-
- kc.createLogoutUrl = function(options) {
- var url = getRealmUrl()
- + '/tokens/logout'
- + '?redirect_uri=' + encodeURIComponent(adapter.redirectUri(options));
-
- return url;
- }
-
- kc.createAccountUrl = function(options) {
- var url = getRealmUrl()
- + '/account'
- + '?referrer=' + kc.clientId
- + '&referrer_uri=' + encodeURIComponent(adapter.redirectUri(options));
-
- return url;
- }
-
- kc.accountManagement = function() {
- return adapter.accountManagement();
- }
-
- kc.hasRealmRole = function (role) {
- var access = kc.realmAccess;
- return access && access.roles.indexOf(role) >= 0 || false;
- }
-
- kc.hasResourceRole = function(role, resource) {
- if (!kc.resourceAccess) {
- return false;
- }
-
- var access = kc.resourceAccess[resource || kc.clientId];
- return access && access.roles.indexOf(role) >= 0 || false;
- }
-
- kc.loadUserProfile = function() {
- var url = getRealmUrl() + '/account';
- var req = new XMLHttpRequest();
- req.open('GET', url, true);
- req.setRequestHeader('Accept', 'application/json');
- req.setRequestHeader('Authorization', 'bearer ' + kc.token);
-
- var promise = createPromise();
-
- req.onreadystatechange = function () {
- if (req.readyState == 4) {
- if (req.status == 200) {
- kc.profile = JSON.parse(req.responseText);
- promise.setSuccess(kc.profile);
- } else {
- promise.setError();
- }
- }
- }
-
- req.send();
-
- return promise.promise;
- }
-
- kc.isTokenExpired = function(minValidity) {
- if (!kc.tokenParsed || !kc.refreshToken) {
- throw 'Not authenticated';
- }
-
- var expiresIn = kc.tokenParsed['exp'] - (new Date().getTime() / 1000);
- if (minValidity) {
- expiresIn -= minValidity;
- }
-
- return expiresIn < 0;
- }
-
- kc.updateToken = function(minValidity) {
- if (!kc.tokenParsed || !kc.refreshToken) {
- throw 'Not authenticated';
- }
-
- var promise = createPromise();
-
- if (minValidity) {
- if (!kc.isTokenExpired(minValidity)) {
- promise.setSuccess(false);
- return promise.promise;
- }
- }
-
- var params = 'grant_type=refresh_token&' + 'refresh_token=' + kc.refreshToken;
- var url = getRealmUrl() + '/tokens/refresh';
-
- var req = new XMLHttpRequest();
- req.open('POST', url, true);
- req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
-
- if (kc.clientId && kc.clientSecret) {
- req.setRequestHeader('Authorization', 'Basic ' + btoa(kc.clientId + ':' + kc.clientSecret));
- } else {
- params += '&client_id=' + encodeURIComponent(kc.clientId);
- }
-
- req.onreadystatechange = function() {
- if (req.readyState == 4) {
- if (req.status == 200) {
- var tokenResponse = JSON.parse(req.responseText);
- setToken(tokenResponse['access_token'], tokenResponse['refresh_token']);
- kc.onAuthRefreshSuccess && kc.onAuthRefreshSuccess();
- promise.setSuccess(true);
- } else {
- kc.onAuthRefreshError && kc.onAuthRefreshError();
- promise.setError();
- }
- }
- };
-
- req.send(params);
-
- return promise.promise;
- }
-
- function getRealmUrl() {
- return kc.authServerUrl + '/realms/' + encodeURIComponent(kc.realm);
- }
-
- function processCallback(oauth, promise) {
- var code = oauth.code;
- var error = oauth.error;
- var prompt = oauth.prompt;
-
- if (code) {
- var params = 'code=' + code;
- var url = getRealmUrl() + '/tokens/access/codes';
-
- var req = new XMLHttpRequest();
- req.open('POST', url, true);
- req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
-
- if (kc.clientId && kc.clientSecret) {
- req.setRequestHeader('Authorization', 'Basic ' + btoa(kc.clientId + ':' + kc.clientSecret));
- } else {
- params += '&client_id=' + encodeURIComponent(kc.clientId);
- }
-
- req.withCredentials = true;
-
- req.onreadystatechange = function() {
- if (req.readyState == 4) {
- if (req.status == 200) {
- var tokenResponse = JSON.parse(req.responseText);
- setToken(tokenResponse['access_token'], tokenResponse['refresh_token']);
- kc.onAuthSuccess && kc.onAuthSuccess();
- promise && promise.setSuccess();
- } else {
- kc.onAuthError && kc.onAuthError();
- promise && promise.setError();
- }
- }
- };
-
- req.send(params);
- } else if (error) {
- if (prompt != 'none') {
- kc.onAuthError && kc.onAuthError();
- promise && promise.setError();
- } else {
- promise && promise.setSuccess();
- }
- }
- }
-
- function loadConfig(url) {
- var promise = createPromise();
- var configUrl;
-
- if (!config) {
- configUrl = 'keycloak.json';
- } else if (typeof config === 'string') {
- configUrl = config;
- }
-
- if (configUrl) {
- var req = new XMLHttpRequest();
- req.open('GET', configUrl, true);
- req.setRequestHeader('Accept', 'application/json');
-
- req.onreadystatechange = function () {
- if (req.readyState == 4) {
- if (req.status == 200) {
- var config = JSON.parse(req.responseText);
-
- kc.authServerUrl = config['auth-server-url'];
- kc.realm = config['realm'];
- kc.clientId = config['resource'];
-
- promise.setSuccess();
- } else {
- promise.setError();
- }
- }
- };
-
- 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;
-
- promise.setSuccess();
- }
-
- return promise.promise;
- }
-
- function clearToken() {
- setToken(null, null);
- kc.onAuthLogout && kc.onAuthLogout();
- }
-
- function setToken(token, refreshToken) {
- if (token) {
- kc.token = token;
- kc.tokenParsed = JSON.parse(decodeURIComponent(escape(window.atob( token.split('.')[1] ))));
- kc.authenticated = true;
- kc.subject = kc.tokenParsed.sub;
- kc.realmAccess = kc.tokenParsed.realm_access;
- kc.resourceAccess = kc.tokenParsed.resource_access;
-
- for (var i = 0; i < idTokenProperties.length; i++) {
- var n = idTokenProperties[i];
- if (kc.tokenParsed[n]) {
- if (!kc.idToken) {
- kc.idToken = {};
- }
- kc.idToken[n] = kc.tokenParsed[n];
- }
- }
- } else {
- delete kc.token;
- delete kc.tokenParsed;
- delete kc.subject;
- delete kc.realmAccess;
- delete kc.resourceAccess;
- delete kc.idToken;
-
- kc.authenticated = false;
- }
-
- if (refreshToken) {
- kc.refreshToken = refreshToken;
- kc.refreshTokenParsed = JSON.parse(atob(refreshToken.split('.')[1]));
- } else {
- delete kc.refreshToken;
- delete kc.refreshTokenParsed;
- }
- }
-
- function createUUID() {
- var s = [];
- var hexDigits = '0123456789abcdef';
- for (var i = 0; i < 36; i++) {
- s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
- }
- s[14] = '4';
- s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
- s[8] = s[13] = s[18] = s[23] = '-';
- var uuid = s.join('');
- return uuid;
- }
-
- function parseCallback(url) {
- if (url.indexOf('?') != -1) {
- var oauth = {};
-
- var params = url.split('?')[1].split('&');
- for (var i = 0; i < params.length; i++) {
- var p = params[i].split('=');
- switch (decodeURIComponent(p[0])) {
- case 'code':
- oauth.code = p[1];
- break;
- case 'error':
- oauth.error = p[1];
- break;
- case 'state':
- oauth.state = decodeURIComponent(p[1]);
- break;
- case 'redirect_fragment':
- oauth.fragment = decodeURIComponent(p[1]);
- break;
- case 'prompt':
- oauth.prompt = p[1];
- break;
- }
- }
-
- if ((oauth.code || oauth.error) && oauth.state && oauth.state == sessionStorage.oauthState) {
- delete sessionStorage.oauthState;
- return oauth;
- }
- }
- }
-
- function createPromise() {
- var p = {
- setSuccess: function(result) {
- p.success = true;
- p.result = result;
- if (p.successCallback) {
- p.successCallback(result);
- }
- },
-
- setError: function(result) {
- p.error = true;
- p.result = result;
- if (p.errorCallback) {
- p.errorCallback(result);
- }
- },
-
- promise: {
- success: function(callback) {
- if (p.success) {
- callback(p.result);
- } else if (!p.error) {
- p.successCallback = callback;
- }
- return p.promise;
- },
- error: function(callback) {
- if (p.error) {
- callback(p.result);
- } else if (!p.success) {
- p.errorCallback = callback;
- }
- return p.promise;
- }
- }
- }
- return p;
- }
-
- function loadAdapter(type) {
- if (!type || type == 'default') {
- return {
- login: function(options) {
- window.location.href = kc.createLoginUrl(options);
- },
-
- logout: function(options) {
- window.location.href = kc.createLogoutUrl(options);
- },
-
- accountManagement : function() {
- window.location.href = kc.createAccountUrl();
- },
-
- redirectUri: function(options) {
- if (options && options.redirectUri) {
- return options.redirectUri;
- } else if (kc.redirectUri) {
- return kc.redirectUri;
- } else {
- var url = (location.protocol + '//' + location.hostname + (location.port && (':' + location.port)) + location.pathname);
- if (location.hash) {
- url += '?redirect_fragment=' + encodeURIComponent(location.hash.substring(1));
- }
- return url;
- }
- }
- };
- }
-
- if (type == 'cordova') {
- console.debug('Enabling Cordova support');
-
- return {
- login: function(options) {
- var promise = createPromise();
-
- var o = 'location=no';
- if (options && options.prompt == 'none') {
- o += ',hidden=yes';
- }
-
- var loginUrl = kc.createLoginUrl(options);
- var ref = window.open(loginUrl, '_blank', o);
-
- var callback;
- var error;
-
- ref.addEventListener('loadstart', function(event) {
- if (event.url.indexOf('http://localhost') == 0) {
- callback = parseCallback(event.url);
- ref.close();
- }
- });
-
- ref.addEventListener('loaderror', function(event) {
- if (event.url.indexOf('http://localhost') != 0) {
- error = true;
- ref.close();
- }
- });
-
- ref.addEventListener('exit', function(event) {
- if (error || !callback) {
- promise.setError();
- } else {
- processCallback(callback, promise);
- }
- });
-
- return promise.promise;
- },
-
- logout: function(options) {
- var promise = createPromise();
-
- var logoutUrl = kc.createLogoutUrl(options);
- var ref = window.open(logoutUrl, '_blank', 'location=no,hidden=yes');
-
- var error;
-
- ref.addEventListener('loadstart', function(event) {
- if (event.url.indexOf('http://localhost') == 0) {
- ref.close();
- }
- });
-
- ref.addEventListener('loaderror', function(event) {
- if (event.url.indexOf('http://localhost') != 0) {
- error = true;
- ref.close();
- }
- });
-
- ref.addEventListener('exit', function(event) {
- if (error) {
- promise.setError();
- } else {
- clearToken();
- promise.setSuccess();
- }
- });
-
- return promise.promise;
- },
-
- 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();
- }
- });
- },
-
- redirectUri: function(options) {
- return 'http://localhost';
- }
- }
- }
-
- throw 'invalid adapter type: ' + type;
- }
-
- var idTokenProperties = [
- "name",
- "given_name",
- "family_name",
- "middle_name",
- "nickname",
- "preferred_username",
- "profile",
- "picture",
- "website",
- "email",
- "email_verified",
- "gender",
- "birthdate",
- "zoneinfo",
- "locale",
- "phone_number",
- "phone_number_verified",
- "address",
- "updated_at",
- "formatted",
- "street_address",
- "locality",
- "region",
- "postal_code",
- "country",
- "claims_locales"
- ]
-}
+var Keycloak = function (config) {
+ if (!(this instanceof Keycloak)) {
+ return new Keycloak(config);
+ }
+
+ var kc = this;
+ var adapter;
+
+ kc.callbackMap = new Object();
+
+
+
+ kc.init = function (init) {
+ kc.authenticated = false;
+
+ if (window.Cordova) {
+ adapter = loadAdapter('cordova');
+ } else {
+ adapter = loadAdapter();
+ }
+
+ var promise = createPromise();
+
+ var initPromise = createPromise();
+ initPromise.promise.success(function() {
+ kc.onReady && kc.onReady(kc.authenticated);
+ promise.setSuccess(kc.authenticated);
+ }).error(function() {
+ promise.setError();
+ });
+
+ var configPromise = loadConfig(config);
+
+ function processInit() {
+ var callback = parseCallback(window.location.href);
+ if (callback) {
+ window.history.replaceState({}, null, location.protocol + '//' + location.host + location.pathname + (callback.fragment ? '#' + callback.fragment : ''));
+ processCallback(callback, initPromise);
+ return;
+ } else if (init) {
+ if (init.code || init.error) {
+ processCallback(init, initPromise);
+ return;
+ } else if (init.token || init.refreshToken) {
+ setToken(init.token, init.refreshToken);
+ initPromise.setSuccess();
+ } else if (init == 'login-required') {
+ var p = kc.login();
+ if (p) {
+ p.success(function() {
+ initPromise.setSuccess();
+ }).error(function() {
+ initPromise.setError();
+ });
+ };
+ } else if (init == 'check-sso') {
+ var p = kc.login({ prompt: 'none' });
+ if (p) {
+ p.success(function() {
+ initPromise.setSuccess();
+ }).error(function() {
+ initPromise.setSuccess();
+ });
+ };
+ } else {
+ throw 'invalid init: ' + init;
+ }
+ } else {
+ initPromise.setSuccess();
+ }
+ }
+
+ configPromise.success(processInit);
+ configPromise.error(function() {
+ promise.setError();
+ });
+
+ return promise.promise;
+ }
+
+ kc.login = function (options) {
+ return adapter.login(options);
+ }
+
+ kc.setupCheckLoginIframe = function(doc, win) {
+ kc.iframe = doc.createElement('iframe');
+ var src = getRealmUrl() + "/login-status-iframe.html?client_id=" + encodeURIComponent(kc.clientId);
+ console.log('iframe src='+ src);
+ kc.iframe.setAttribute('src', src );
+ kc.iframe.style.display = "none";
+ doc.body.appendChild(kc.iframe);
+
+ var messageCallback = function(event) {
+ if (event.origin !== kc.iframe.contentWindow.location.origin) {
+ return;
+ }
+ var data = event.data;
+ var success = kc.callbackMap[data.successId];
+ var failure = kc.callbackMap[data.failureId];
+ delete kc.callbackMap[data.successId];
+ delete kc.callbackMap[data.failureId];
+ if (kc.sessionId != data.session) {
+ console.log("session doesn't match received session: " + event.data.session);
+ console.log("forcing loggedIn to be false");
+ failure();
+ }
+ if (data.loggedIn) {
+ success();
+ } else {
+ failure();
+ }
+ };
+ win.addEventListener("message", messageCallback, false);
+ }
+
+ kc.checkLoginIframe = function(success, failure) {
+ var msg = {};
+ if (!success) {
+ throw "You must define a success method";
+ }
+ if (!failure) {
+ throw "You must define a failure method";
+ }
+ msg.successId = createCallbackId();
+ msg.failureId = createCallbackId();
+ kc.callbackMap[msg.successId] = success;
+ kc.callbackMap[msg.failureId] = failure;
+ kc.iframe.contentWindow.postMessage(msg, kc.iframe.contentWindow.location.origin);
+ }
+
+ kc.createLoginUrl = function(options) {
+ var state = createUUID();
+
+ var redirectUri = adapter.redirectUri(options);
+ if (options && options.prompt) {
+ if (redirectUri.indexOf('?') == -1) {
+ redirectUri += '?prompt=' + options.prompt;
+ } else {
+ redirectUri += '&prompt=' + options.prompt;
+ }
+ }
+
+ sessionStorage.oauthState = state;
+
+ var url = getRealmUrl()
+ + '/tokens/login'
+ + '?client_id=' + encodeURIComponent(kc.clientId)
+ + '&redirect_uri=' + encodeURIComponent(redirectUri)
+ + '&state=' + encodeURIComponent(state)
+ + '&response_type=code';
+
+ if (options && options.prompt) {
+ url += '&prompt=' + options.prompt;
+ }
+
+ return url;
+ }
+
+ kc.logout = function(options) {
+ return adapter.logout(options);
+ }
+
+ kc.createLogoutUrl = function(options) {
+ var url = getRealmUrl()
+ + '/tokens/logout'
+ + '?redirect_uri=' + encodeURIComponent(adapter.redirectUri(options));
+
+ return url;
+ }
+
+ kc.createAccountUrl = function(options) {
+ var url = getRealmUrl()
+ + '/account'
+ + '?referrer=' + kc.clientId
+ + '&referrer_uri=' + encodeURIComponent(adapter.redirectUri(options));
+
+ return url;
+ }
+
+ kc.accountManagement = function() {
+ return adapter.accountManagement();
+ }
+
+ kc.hasRealmRole = function (role) {
+ var access = kc.realmAccess;
+ return access && access.roles.indexOf(role) >= 0 || false;
+ }
+
+ kc.hasResourceRole = function(role, resource) {
+ if (!kc.resourceAccess) {
+ return false;
+ }
+
+ var access = kc.resourceAccess[resource || kc.clientId];
+ return access && access.roles.indexOf(role) >= 0 || false;
+ }
+
+ kc.loadUserProfile = function() {
+ var url = getRealmUrl() + '/account';
+ var req = new XMLHttpRequest();
+ req.open('GET', url, true);
+ req.setRequestHeader('Accept', 'application/json');
+ req.setRequestHeader('Authorization', 'bearer ' + kc.token);
+
+ var promise = createPromise();
+
+ req.onreadystatechange = function () {
+ if (req.readyState == 4) {
+ if (req.status == 200) {
+ kc.profile = JSON.parse(req.responseText);
+ promise.setSuccess(kc.profile);
+ } else {
+ promise.setError();
+ }
+ }
+ }
+
+ req.send();
+
+ return promise.promise;
+ }
+
+ kc.isTokenExpired = function(minValidity) {
+ if (!kc.tokenParsed || !kc.refreshToken) {
+ throw 'Not authenticated';
+ }
+
+ var expiresIn = kc.tokenParsed['exp'] - (new Date().getTime() / 1000);
+ if (minValidity) {
+ expiresIn -= minValidity;
+ }
+
+ return expiresIn < 0;
+ }
+
+ kc.updateToken = function(minValidity) {
+ if (!kc.tokenParsed || !kc.refreshToken) {
+ throw 'Not authenticated';
+ }
+
+ var promise = createPromise();
+
+ if (minValidity) {
+ if (!kc.isTokenExpired(minValidity)) {
+ promise.setSuccess(false);
+ return promise.promise;
+ }
+ }
+
+ var params = 'grant_type=refresh_token&' + 'refresh_token=' + kc.refreshToken;
+ var url = getRealmUrl() + '/tokens/refresh';
+
+ var req = new XMLHttpRequest();
+ req.open('POST', url, true);
+ req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
+
+ if (kc.clientId && kc.clientSecret) {
+ req.setRequestHeader('Authorization', 'Basic ' + btoa(kc.clientId + ':' + kc.clientSecret));
+ } else {
+ params += '&client_id=' + encodeURIComponent(kc.clientId);
+ }
+
+ req.onreadystatechange = function() {
+ if (req.readyState == 4) {
+ if (req.status == 200) {
+ var tokenResponse = JSON.parse(req.responseText);
+ setToken(tokenResponse['access_token'], tokenResponse['refresh_token']);
+ kc.onAuthRefreshSuccess && kc.onAuthRefreshSuccess();
+ promise.setSuccess(true);
+ } else {
+ kc.onAuthRefreshError && kc.onAuthRefreshError();
+ promise.setError();
+ }
+ }
+ };
+
+ req.send(params);
+
+ return promise.promise;
+ }
+
+ function getRealmUrl() {
+ return kc.authServerUrl + '/realms/' + encodeURIComponent(kc.realm);
+ }
+
+ function processCallback(oauth, promise) {
+ var code = oauth.code;
+ var error = oauth.error;
+ var prompt = oauth.prompt;
+
+ if (code) {
+ var params = 'code=' + code;
+ var url = getRealmUrl() + '/tokens/access/codes';
+
+ var req = new XMLHttpRequest();
+ req.open('POST', url, true);
+ req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
+
+ if (kc.clientId && kc.clientSecret) {
+ req.setRequestHeader('Authorization', 'Basic ' + btoa(kc.clientId + ':' + kc.clientSecret));
+ } else {
+ params += '&client_id=' + encodeURIComponent(kc.clientId);
+ }
+
+ req.withCredentials = true;
+
+ req.onreadystatechange = function() {
+ if (req.readyState == 4) {
+ if (req.status == 200) {
+ var tokenResponse = JSON.parse(req.responseText);
+ setToken(tokenResponse['access_token'], tokenResponse['refresh_token']);
+ kc.onAuthSuccess && kc.onAuthSuccess();
+ promise && promise.setSuccess();
+ } else {
+ kc.onAuthError && kc.onAuthError();
+ promise && promise.setError();
+ }
+ }
+ };
+
+ req.send(params);
+ } else if (error) {
+ if (prompt != 'none') {
+ kc.onAuthError && kc.onAuthError();
+ promise && promise.setError();
+ } else {
+ promise && promise.setSuccess();
+ }
+ }
+ }
+
+ function loadConfig(url) {
+ var promise = createPromise();
+ var configUrl;
+
+ if (!config) {
+ configUrl = 'keycloak.json';
+ } else if (typeof config === 'string') {
+ configUrl = config;
+ }
+
+ if (configUrl) {
+ var req = new XMLHttpRequest();
+ req.open('GET', configUrl, true);
+ req.setRequestHeader('Accept', 'application/json');
+
+ req.onreadystatechange = function () {
+ if (req.readyState == 4) {
+ if (req.status == 200) {
+ var config = JSON.parse(req.responseText);
+
+ kc.authServerUrl = config['auth-server-url'];
+ kc.realm = config['realm'];
+ kc.clientId = config['resource'];
+
+ promise.setSuccess();
+ } else {
+ promise.setError();
+ }
+ }
+ };
+
+ 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;
+
+ promise.setSuccess();
+ }
+
+ return promise.promise;
+ }
+
+ function clearToken() {
+ setToken(null, null);
+ kc.onAuthLogout && kc.onAuthLogout();
+ }
+
+ function setToken(token, refreshToken) {
+ if (token) {
+ kc.token = token;
+ kc.tokenParsed = JSON.parse(decodeURIComponent(escape(window.atob( token.split('.')[1] ))));
+ var sessionId = kc.realm + '-' + kc.tokenParsed.sub;
+ if (kc.tokenParsed.session_state) {
+ sessionId = sessionId + '-' + kc.tokenParsed.session_state;
+ }
+ kc.sessionId = sessionId;
+ kc.authenticated = true;
+ kc.subject = kc.tokenParsed.sub;
+ kc.realmAccess = kc.tokenParsed.realm_access;
+ kc.resourceAccess = kc.tokenParsed.resource_access;
+
+ for (var i = 0; i < idTokenProperties.length; i++) {
+ var n = idTokenProperties[i];
+ if (kc.tokenParsed[n]) {
+ if (!kc.idToken) {
+ kc.idToken = {};
+ }
+ kc.idToken[n] = kc.tokenParsed[n];
+ }
+ }
+ } else {
+ delete kc.token;
+ delete kc.tokenParsed;
+ delete kc.subject;
+ delete kc.realmAccess;
+ delete kc.resourceAccess;
+ delete kc.idToken;
+
+ kc.authenticated = false;
+ }
+
+ if (refreshToken) {
+ kc.refreshToken = refreshToken;
+ kc.refreshTokenParsed = JSON.parse(atob(refreshToken.split('.')[1]));
+ } else {
+ delete kc.refreshToken;
+ delete kc.refreshTokenParsed;
+ }
+ }
+
+ function createUUID() {
+ var s = [];
+ var hexDigits = '0123456789abcdef';
+ for (var i = 0; i < 36; i++) {
+ s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
+ }
+ s[14] = '4';
+ s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
+ s[8] = s[13] = s[18] = s[23] = '-';
+ var uuid = s.join('');
+ return uuid;
+ }
+
+ kc.callback_id = 0;
+
+ function createCallbackId() {
+ var id = '<id: ' + (kc.callback_id++) + (Math.random()) + '>';
+ return id;
+
+ }
+
+ function parseCallback(url) {
+ if (url.indexOf('?') != -1) {
+ var oauth = {};
+
+ var params = url.split('?')[1].split('&');
+ for (var i = 0; i < params.length; i++) {
+ var p = params[i].split('=');
+ switch (decodeURIComponent(p[0])) {
+ case 'code':
+ oauth.code = p[1];
+ break;
+ case 'error':
+ oauth.error = p[1];
+ break;
+ case 'state':
+ oauth.state = decodeURIComponent(p[1]);
+ break;
+ case 'redirect_fragment':
+ oauth.fragment = decodeURIComponent(p[1]);
+ break;
+ case 'prompt':
+ oauth.prompt = p[1];
+ break;
+ }
+ }
+
+ if ((oauth.code || oauth.error) && oauth.state && oauth.state == sessionStorage.oauthState) {
+ delete sessionStorage.oauthState;
+ return oauth;
+ }
+ }
+ }
+
+ function createPromise() {
+ var p = {
+ setSuccess: function(result) {
+ p.success = true;
+ p.result = result;
+ if (p.successCallback) {
+ p.successCallback(result);
+ }
+ },
+
+ setError: function(result) {
+ p.error = true;
+ p.result = result;
+ if (p.errorCallback) {
+ p.errorCallback(result);
+ }
+ },
+
+ promise: {
+ success: function(callback) {
+ if (p.success) {
+ callback(p.result);
+ } else if (!p.error) {
+ p.successCallback = callback;
+ }
+ return p.promise;
+ },
+ error: function(callback) {
+ if (p.error) {
+ callback(p.result);
+ } else if (!p.success) {
+ p.errorCallback = callback;
+ }
+ return p.promise;
+ }
+ }
+ }
+ return p;
+ }
+
+ function loadAdapter(type) {
+ if (!type || type == 'default') {
+ return {
+ login: function(options) {
+ window.location.href = kc.createLoginUrl(options);
+ },
+
+ logout: function(options) {
+ window.location.href = kc.createLogoutUrl(options);
+ },
+
+ accountManagement : function() {
+ window.location.href = kc.createAccountUrl();
+ },
+
+ redirectUri: function(options) {
+ if (options && options.redirectUri) {
+ return options.redirectUri;
+ } else if (kc.redirectUri) {
+ return kc.redirectUri;
+ } else {
+ var url = (location.protocol + '//' + location.hostname + (location.port && (':' + location.port)) + location.pathname);
+ if (location.hash) {
+ url += '?redirect_fragment=' + encodeURIComponent(location.hash.substring(1));
+ }
+ return url;
+ }
+ }
+ };
+ }
+
+ if (type == 'cordova') {
+ console.debug('Enabling Cordova support');
+
+ return {
+ login: function(options) {
+ var promise = createPromise();
+
+ var o = 'location=no';
+ if (options && options.prompt == 'none') {
+ o += ',hidden=yes';
+ }
+
+ var loginUrl = kc.createLoginUrl(options);
+ var ref = window.open(loginUrl, '_blank', o);
+
+ var callback;
+ var error;
+
+ ref.addEventListener('loadstart', function(event) {
+ if (event.url.indexOf('http://localhost') == 0) {
+ callback = parseCallback(event.url);
+ ref.close();
+ }
+ });
+
+ ref.addEventListener('loaderror', function(event) {
+ if (event.url.indexOf('http://localhost') != 0) {
+ error = true;
+ ref.close();
+ }
+ });
+
+ ref.addEventListener('exit', function(event) {
+ if (error || !callback) {
+ promise.setError();
+ } else {
+ processCallback(callback, promise);
+ }
+ });
+
+ return promise.promise;
+ },
+
+ logout: function(options) {
+ var promise = createPromise();
+
+ var logoutUrl = kc.createLogoutUrl(options);
+ var ref = window.open(logoutUrl, '_blank', 'location=no,hidden=yes');
+
+ var error;
+
+ ref.addEventListener('loadstart', function(event) {
+ if (event.url.indexOf('http://localhost') == 0) {
+ ref.close();
+ }
+ });
+
+ ref.addEventListener('loaderror', function(event) {
+ if (event.url.indexOf('http://localhost') != 0) {
+ error = true;
+ ref.close();
+ }
+ });
+
+ ref.addEventListener('exit', function(event) {
+ if (error) {
+ promise.setError();
+ } else {
+ clearToken();
+ promise.setSuccess();
+ }
+ });
+
+ return promise.promise;
+ },
+
+ 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();
+ }
+ });
+ },
+
+ redirectUri: function(options) {
+ return 'http://localhost';
+ }
+ }
+ }
+
+ throw 'invalid adapter type: ' + type;
+ }
+
+ var idTokenProperties = [
+ "name",
+ "given_name",
+ "family_name",
+ "middle_name",
+ "nickname",
+ "preferred_username",
+ "profile",
+ "picture",
+ "website",
+ "email",
+ "email_verified",
+ "gender",
+ "birthdate",
+ "zoneinfo",
+ "locale",
+ "phone_number",
+ "phone_number_verified",
+ "address",
+ "updated_at",
+ "formatted",
+ "street_address",
+ "locality",
+ "region",
+ "postal_code",
+ "country",
+ "claims_locales"
+ ]
+}
diff --git a/integration/js/src/main/resources/login-status-iframe.html b/integration/js/src/main/resources/login-status-iframe.html
new file mode 100755
index 0000000..a8c6409
--- /dev/null
+++ b/integration/js/src/main/resources/login-status-iframe.html
@@ -0,0 +1,31 @@
+<script>
+ function getCookie(cname)
+ {
+ var name = cname + "=";
+ var ca = document.cookie.split(';');
+ for(var i=0; i<ca.length; i++)
+ {
+ var c = ca[i].trim();
+ if (c.indexOf(name)==0) return c.substring(name.length,c.length);
+ }
+ return null;
+ }
+ function receiveMessage(event)
+ {
+ if (event.origin !== "ORIGIN") {
+ console.log(event.origin + " does not match built origin");
+ return;
+
+ }
+ event.data.loggedIn = false;
+ var cookie = getCookie('KEYCLOAK_SESSION');
+ if (cookie) {
+ event.data.loggedIn = true;
+ event.data.session = cookie;
+ }
+
+ event.source.postMessage(event.data,
+ event.origin);
+ }
+ window.addEventListener("message", receiveMessage, false);
+</script>
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index c8df10c..ce5afb7 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -27,6 +27,7 @@ import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.NewCookie;
+import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.net.URI;
import java.util.HashSet;
@@ -41,7 +42,10 @@ import java.util.Set;
public class AuthenticationManager {
protected static Logger logger = Logger.getLogger(AuthenticationManager.class);
public static final String FORM_USERNAME = "username";
+ // used for auth login
public static final String KEYCLOAK_IDENTITY_COOKIE = "KEYCLOAK_IDENTITY";
+ // used solely to determine is user is logged in
+ public static final String KEYCLOAK_SESSION_COOKIE = "KEYCLOAK_SESSION";
public static final String KEYCLOAK_REMEMBER_ME = "KEYCLOAK_REMEMBER_ME";
protected ProviderSession providerSession;
@@ -72,18 +76,11 @@ public class AuthenticationManager {
return token;
}
- public NewCookie createLoginCookie(RealmModel realm, UserModel user, UserSessionModel session, UriInfo uriInfo, boolean rememberMe) {
+ public void createLoginCookie(Response.ResponseBuilder builder, RealmModel realm, UserModel user, UserSessionModel session, UriInfo uriInfo, boolean rememberMe) {
logger.info("createLoginCookie");
String cookieName = KEYCLOAK_IDENTITY_COOKIE;
String cookiePath = getIdentityCookiePath(realm, uriInfo);
- return createLoginCookie(realm, user, session, null, cookieName, cookiePath, rememberMe);
- }
-
- protected NewCookie createLoginCookie(RealmModel realm, UserModel user, UserSessionModel session, ClientModel client, String cookieName, String cookiePath, boolean rememberMe) {
AccessToken identityToken = createIdentityToken(realm, user, session);
- if (client != null) {
- identityToken.issuedFor(client.getClientId());
- }
String encoded = encodeToken(realm, identityToken);
boolean secureOnly = !realm.isSslNotRequired();
logger.debugv("creatingLoginCookie - name: {0} path: {1}", cookieName, cookiePath);
@@ -92,8 +89,14 @@ public class AuthenticationManager {
maxAge = realm.getCentralLoginLifespan();
logger.info("createLoginCookie maxAge: " + maxAge);
}
- NewCookie cookie = new NewCookie(cookieName, encoded, cookiePath, null, null, maxAge, secureOnly);// todo httponly , true);
- return cookie;
+ builder.cookie(new NewCookie(cookieName, encoded, cookiePath, null, null, maxAge, secureOnly));// todo httponly , true);
+
+ String sessionCookieValue = realm.getName() + "-" + user.getId();
+ if (session != null) {
+ sessionCookieValue += "-" + session.getId();
+ }
+ builder.cookie(new NewCookie(KEYCLOAK_SESSION_COOKIE, sessionCookieValue, cookiePath, null, null, maxAge, secureOnly));// todo httponly , true);
+
}
public NewCookie createRememberMeCookie(RealmModel realm, UriInfo uriInfo) {
@@ -116,6 +119,7 @@ public class AuthenticationManager {
String path = getIdentityCookiePath(realm, uriInfo);
String cookieName = KEYCLOAK_IDENTITY_COOKIE;
expireCookie(cookieName, path);
+ expireCookie(KEYCLOAK_SESSION_COOKIE, path);
}
public void expireRememberMeCookie(RealmModel realm, UriInfo uriInfo) {
logger.debug("Expiring remember me cookie");
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java b/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
index a5c0abe..d436318 100755
--- a/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
+++ b/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
@@ -93,7 +93,7 @@ public class OAuthFlows {
Response.ResponseBuilder location = Response.status(302).location(redirectUri.build());
Cookie remember = request.getHttpHeaders().getCookies().get(AuthenticationManager.KEYCLOAK_REMEMBER_ME);
rememberMe = rememberMe || remember != null;
- location.cookie(authManager.createLoginCookie(realm, accessCode.getUser(), session, uriInfo, rememberMe));
+ authManager.createLoginCookie(location, realm, accessCode.getUser(), session, uriInfo, rememberMe);
return location.build();
}
}
diff --git a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
index 0c1c8ae..a5878d2 100755
--- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
@@ -1,10 +1,13 @@
package org.keycloak.services.resources;
import org.jboss.logging.Logger;
+import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.NotFoundException;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.jboss.resteasy.spi.UnauthorizedException;
import org.keycloak.audit.Audit;
import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
@@ -16,13 +19,21 @@ import org.keycloak.services.managers.BruteForceProtector;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.managers.SocialRequestManager;
import org.keycloak.services.managers.TokenManager;
+import org.keycloak.util.StreamUtil;
+import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -74,6 +85,63 @@ public class RealmsResource {
public static UriBuilder accountUrl(UriBuilder base) {
return base.path(RealmsResource.class).path(RealmsResource.class, "getAccountService");
}
+
+ /**
+ *
+ *
+ * @param name
+ * @param client_id
+ * @return
+ */
+ @Path("{realm}/login-status-iframe.html")
+ @GET
+ @Produces(MediaType.TEXT_HTML)
+ @NoCache
+ public String getLoginStatusIframe(final @PathParam("realm") String name,
+ @QueryParam("client_id") String client_id) {
+ AuthenticationManager auth = new AuthenticationManager(providers);
+
+ //logger.info("getting login-status-iframe.html for client_id: " + client_id);
+ RealmManager realmManager = new RealmManager(session);
+ RealmModel realm = locateRealm(name, realmManager);
+ ClientModel client = realm.findClient(client_id);
+ if (client == null) {
+ throw new NotFoundException("could not find client: " + client_id);
+ }
+ AuthenticationManager.AuthResult result = auth.authenticateIdentityCookie(realm, uriInfo, headers);
+ if (result == null) {
+ throw new UnauthorizedException("not logged in, can't get page");
+ }
+
+ InputStream is = getClass().getClassLoader().getResourceAsStream("login-status-iframe.html");
+ if (is == null) throw new NotFoundException("Could not find login-status-iframe.html ");
+ Set<String> redirectUris = TokenService.resolveValidRedirects(uriInfo, client.getRedirectUris());
+ String origin = null;
+ for (String redirect : redirectUris) {
+
+ int index = redirect.indexOf("://");
+ if (index == -1) continue;
+ index = redirect.indexOf('/', index + 3);
+ if (index == -1) {
+ origin = redirect;
+ } else {
+ origin = redirect.substring(0, index);
+ }
+ break;
+
+ }
+ String file = null;
+ try {
+ file = StreamUtil.readString(is);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ file = file.replace("ORIGIN", origin);
+ //System.out.println(file);
+ return file;
+
+ }
+
@Path("{realm}/tokens")
public TokenService getTokenService(final @PathParam("realm") String name) {
RealmManager realmManager = new RealmManager(session);
diff --git a/services/src/main/java/org/keycloak/services/resources/TokenService.java b/services/src/main/java/org/keycloak/services/resources/TokenService.java
index 4054953..aeda045 100755
--- a/services/src/main/java/org/keycloak/services/resources/TokenService.java
+++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java
@@ -968,21 +968,8 @@ public class TokenService {
return redirectUri;
} else {
String r = redirectUri.indexOf('?') != -1 ? redirectUri.substring(0, redirectUri.indexOf('?')) : redirectUri;
+ Set<String> resolveValidRedirects = resolveValidRedirects(uriInfo, validRedirects);
- // If the valid redirect URI is relative (no scheme, host, port) then use the request's scheme, host, and port
- Set<String> resolveValidRedirects = new HashSet<String>();
- for (String validRedirect : validRedirects) {
- if (validRedirect.startsWith("/")) {
- URI baseUri = uriInfo.getBaseUri();
- String uri = baseUri.getScheme() + "://" + baseUri.getHost();
- if (baseUri.getPort() != -1) {
- uri += ":" + baseUri.getPort();
- }
- validRedirect = uri + validRedirect;
- logger.debugv("replacing relative valid redirect with: {0}", validRedirect);
- }
- resolveValidRedirects.add(validRedirect);
- }
boolean valid = matchesRedirects(resolveValidRedirects, r);
@@ -1005,6 +992,24 @@ public class TokenService {
}
}
+ public static Set<String> resolveValidRedirects(UriInfo uriInfo, Set<String> validRedirects) {
+ // If the valid redirect URI is relative (no scheme, host, port) then use the request's scheme, host, and port
+ Set<String> resolveValidRedirects = new HashSet<String>();
+ for (String validRedirect : validRedirects) {
+ if (validRedirect.startsWith("/")) {
+ URI baseUri = uriInfo.getBaseUri();
+ String uri = baseUri.getScheme() + "://" + baseUri.getHost();
+ if (baseUri.getPort() != -1) {
+ uri += ":" + baseUri.getPort();
+ }
+ validRedirect = uri + validRedirect;
+ logger.debugv("replacing relative valid redirect with: {0}", validRedirect);
+ }
+ resolveValidRedirects.add(validRedirect);
+ }
+ return resolveValidRedirects;
+ }
+
private boolean checkSsl() {
if (realm.isSslNotRequired()) {
return true;