diff options
Diffstat (limited to 'install/ui/src/freeipa')
-rw-r--r-- | install/ui/src/freeipa/Application_controller.js | 58 | ||||
-rw-r--r-- | install/ui/src/freeipa/auth.js | 252 | ||||
-rw-r--r-- | install/ui/src/freeipa/ipa.js | 28 | ||||
-rw-r--r-- | install/ui/src/freeipa/rpc.js | 31 |
4 files changed, 315 insertions, 54 deletions
diff --git a/install/ui/src/freeipa/Application_controller.js b/install/ui/src/freeipa/Application_controller.js index 135e9be87..c166e36ee 100644 --- a/install/ui/src/freeipa/Application_controller.js +++ b/install/ui/src/freeipa/Application_controller.js @@ -102,13 +102,13 @@ define([ on(this.app_widget, 'logout-click', lang.hitch(this, this.on_logout)); on(this.app_widget, 'password-reset-click', lang.hitch(this, this.on_password_reset)); on(this.app_widget, 'about-click', lang.hitch(this, this.on_about)); - on(this.menu, 'selected', lang.hitch(this, this.on_menu_select)); on(this.router, 'facet-show', lang.hitch(this, this.on_facet_show)); on(this.router, 'facet-change', lang.hitch(this, this.on_facet_change)); on(this.router, 'facet-change-canceled', lang.hitch(this, this.on_facet_canceled)); on(this.router, 'error', lang.hitch(this, this.on_router_error)); topic.subscribe('phase-error', lang.hitch(this, this.on_phase_error)); + topic.subscribe('authenticate', lang.hitch(this, this.on_authenticate)); this.app_widget.render(); this.app_widget.hide(); @@ -263,6 +263,8 @@ define([ var new_facet = event.facet; var current_facet = this.current_facet; + if (current_facet === new_facet) return; + if (current_facet && !current_facet.can_leave()) { var permit_clb = lang.hitch(this, function() { // Some facet's might not call reset before this call but after @@ -417,29 +419,45 @@ define([ }, /** - * Watches menu changes and adjusts facet space when there is - * a need for larger menu space. - * - * Show extended menu space when: - * * there is 3+ levels of menu - * - * Don't show when: - * * all items of levels 3+ are hidden + * Starts authentication process in authentication UI + * @returns {undefined} */ - on_menu_select: function(select_state) { + on_authenticate: function() { - var visible_levels = 0; - var levels = select_state.new_selection.length; - for (var i=0; i< levels; i++) { - var item = select_state.new_selection[i]; - if(!item.hidden) visible_levels++; - } + var self = this; + if (this.auth_ui === 'dialog') { + var dummy_command = { + execute: function() { + topic.publish('auth-successful'); + } + }; - var three_levels = visible_levels >= 3; + var dialog = IPA.unauthorized_dialog({ + close_on_escape: false, + error_thrown: { name: '', message: ''}, + command: dummy_command + }); - dom_class.toggle(this.app_widget.content_node, - 'nav-space-3', - three_levels); + dialog.open(); + } else { + var facet = this.current_facet; + + // we don't want the load facet to be displayed after successful auth + if (facet && facet.name === 'load') { + facet = null; + } + var login_facet = reg.facet.get('login'); + + on.once(login_facet, "logged_in", function() { + + if (facet) { + self.show_facet(facet); + } + topic.publish('auth-successful'); + }); + + this.show_facet(login_facet); + } } }); diff --git a/install/ui/src/freeipa/auth.js b/install/ui/src/freeipa/auth.js new file mode 100644 index 000000000..5e160a7a4 --- /dev/null +++ b/install/ui/src/freeipa/auth.js @@ -0,0 +1,252 @@ +/* Authors: + * Petr Vobornik <pvoborni@redhat.com> + * + * Copyright (C) 2014 Red Hat + * see file 'COPYING' for use and warranty information + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +define([ + 'dojo/_base/declare', + 'dojo/_base/lang', + 'dojo/Deferred', + 'dojo/Evented', + 'dojo/Stateful', + 'dojo/topic', + 'dojo/when' + ], + function(declare, lang, Deferred, Evented, Stateful, topic, when) { + +/** + * Authentication module + * @class auth + * @singleton + */ +var auth = { + /** + * Current authentication state + * @property {auth.Auth} + */ + current: null +}; + +/** + * Authentication interface and state. + * + * Can be used for checking whether user is authenticated, by what method or + * what methods can be used for authentication. Actual authentication is + * done by separate object - authentication provider. + * + * Communication with authentication providers is done through global messages + * (`dojo/topic`). + * + * Some component can initiate the authentication process by calling: + * + * var auth_promise = auth.current.authenticate(); + * + * `auth_promise` is a promise which is resolve on auth success and rejected + * on auth failure. + * + * Logout works in similar fashion: + * + * var logout_promise = auth.current.logout(); + * + * The communication with authentication providers works as follows: + * + * 1. `auth.current.authenticate();` publishes `authenticate` topic + * 2. provider starts the authentication process + * 3. if it finishes with a success provider publishes `auth-successful`, if not + * it publishes `auth-failed` + * 4. the promise is resolved or rejected + * + * Logout works in similar fashion, only the topic names are `log-out`, + * `logout-successful` and `logout-failed`. + * + * New `authenticate` or `log-out` topics are not published if there is + * already authentication or logout in progress. The promises from subsequent + * `authenticate()` or `logout()` calls are resolved as expected. + * + * `login`, `principal`, `whoami`, `fullname` properties are supposed to be + * set by authentication providers. + * + * @class + */ +auth.Auth = declare([Stateful, Evented], { + /** + * Raw User information + * + * @property {Object} + */ + whoami: {}, + + /** + * User is authenticated + * + * Use `set_authenticated(state, method)` for setting it. + * + * @property {boolean} + * @readonly + */ + authenticated: false, + + /** + * Method used for authentication + * @property {string} + */ + authenticated_by: "", + + /** + * Enabled auth methods + * @property {string[]} + */ + auth_methods: ['kerberos', 'password'], + + /** + * Authenticated user's Kerberos principal + * @property {string} + */ + principal: "", + + /** + * Authenticated user's login + * @property {string} + */ + login: "", + + /** + * Authenticated user's fullname + * @property {string} + */ + fullname: "", + + /** + * Authentication is in progress + * @property {boolean} + */ + authenticating: false, + + /** + * Logging out is in progress + * @property {boolean} + */ + logging_out: false, + + /** + * Indicates whether user was previously authenticated + * @property {boolean} + */ + expired: false, + + /** + * Update authenticated state + * @param {boolean} state User is authenticated + * @param {string} method used for authentication + */ + set_authenticated: function(state, method) { + + if (this.authenticated && !state) { + this.set('expired', true); + } + + this.set('authenticated', state); + this.set('authenticated_by', method); + + if (this.authenticated) { + this.set('expired', false); + } + }, + + /** + * Initiate authentication process (if not already initiated) + * + * Returns promise which is fulfilled when user is authenticated. It's + * rejected when authentication is canceled. + * @returns {Promise} + */ + authenticate: function() { + var authenticated = new Deferred(); + var ok_handler = topic.subscribe('auth-successful', function(info) { + authenticated.resolve(true); + ok_handler.remove(); + fail_handler.remove(); + }); + var fail_handler = topic.subscribe('auth-failed', function(info) { + authenticated.reject(); + ok_handler.remove(); + fail_handler.remove(); + }); + if (!this.authenticating) { + topic.publish('authenticate', this); + } + return authenticated.promise; + }, + + /** + * Initiate logout process (if not already initiated) + * + * Returns promise which is fulfilled when user is logged-out. It's + * rejected when logout failed. + * @returns {Promise} + */ + logout: function() { + var loggedout = new Deferred(); + var ok_handler = topic.subscribe('logout-successful', function(info) { + loggedout.resolve(true); + ok_handler.remove(); + fail_handler.remove(); + }); + var fail_handler = topic.subscribe('logout-failed', function(info) { + loggedout.reject(); + ok_handler.remove(); + fail_handler.remove(); + }); + if (!this.logging_out) { + topic.publish('log-out', this); + } + return loggedout.promise; + }, + + /** + * Initializes instance + * + * @private + */ + postscript: function() { + var self = this; + var auth_true = function() { + self.set('authenticating', true); + }; + var auth_false = function() { + self.set('authenticating', false); + }; + var out_true = function() { + self.set('logging_out', true); + }; + var out_false = function() { + self.set('logging_out', false); + }; + + topic.subscribe('auth-successful', auth_false); + topic.subscribe('auth-failed', auth_false); + topic.subscribe('authenticate', auth_true); + topic.subscribe('logout-successful', out_true); + topic.subscribe('logout-failed', out_true); + topic.subscribe('log-out', out_false); + } +}); + +auth.current = new auth.Auth(); +return auth; +});
\ No newline at end of file diff --git a/install/ui/src/freeipa/ipa.js b/install/ui/src/freeipa/ipa.js index d6ae67d9c..a44d60b24 100644 --- a/install/ui/src/freeipa/ipa.js +++ b/install/ui/src/freeipa/ipa.js @@ -28,6 +28,7 @@ define([ './jquery', './json2', './_base/i18n', + './auth', './datetime', './metadata', './builder', @@ -35,7 +36,7 @@ define([ './rpc', './text', 'exports' - ], function(keys, topic, $, JSON, i18n, datetime, metadata_provider, + ], function(keys, topic, $, JSON, i18n, auth, datetime, metadata_provider, builder, reg, rpc, text, exports) { /** @@ -107,9 +108,6 @@ var IPA = function () { * - metadata * - user information * - server configuration - * @property {boolean} logged_kerberos - User authenticated by - * Kerberos negotiation - * @property {boolean} logged_password - User authenticated by password */ that.ui = {}; @@ -362,7 +360,7 @@ IPA.object = function(s) { /** * Make request on Kerberos authentication url to initialize Kerberos negotiation. * - * Set result to IPA.ui.logged_kerberos. + * Set result to auth module. * * @member IPA */ @@ -371,12 +369,11 @@ IPA.get_credentials = function() { function error_handler(xhr, text_status, error_thrown) { status = xhr.status; - IPA.ui.logged_kerberos = false; } function success_handler(data, text_status, xhr) { status = xhr.status; - IPA.ui.logged_kerberos = true; + auth.current.set_authenticated(true, 'kerberos'); } var request = { @@ -397,7 +394,7 @@ IPA.get_credentials = function() { * Logout * * - terminate the session. - * - redirect to logout landing page on success + * - reloads UI * * @member IPA */ @@ -412,21 +409,22 @@ IPA.logout = function() { dialog.open(); } - function redirect () { - window.location = 'logout.html'; + function reload () { + var l = window.location; + l.assign(l.href.split('#')[0]); } function success_handler(data, text_status, xhr) { if (data && data.error) { show_error(data.error.message); } else { - redirect(); + reload(); } } function error_handler(xhr, text_status, error_thrown) { if (xhr.status === 401) { - redirect(); + reload(); } else { show_error(text_status); } @@ -461,7 +459,7 @@ IPA.login_password = function(username, password) { function success_handler(data, text_status, xhr) { result = 'success'; - IPA.ui.logged_password = true; + auth.current.set_authenticated(true, 'password'); } function error_handler(xhr, text_status, error_thrown) { @@ -475,8 +473,6 @@ IPA.login_password = function(username, password) { result = reason; } } - - IPA.ui.logged_password = false; } var data = { @@ -825,7 +821,7 @@ IPA.error_dialog = function(spec) { IPA.confirm_mixin().apply(that); /** @property {XMLHttpRequest} xhr Command's xhr */ - that.xhr = spec.xhr || {}; + that.xhr = spec.xhr || null; /** @property {string} text_status Command's text status */ that.text_status = spec.text_status || ''; /** @property {{name:string,message:string}} error_thrown Command's error */ diff --git a/install/ui/src/freeipa/rpc.js b/install/ui/src/freeipa/rpc.js index bd5184650..6bb0ea228 100644 --- a/install/ui/src/freeipa/rpc.js +++ b/install/ui/src/freeipa/rpc.js @@ -23,12 +23,13 @@ */ define([ - 'dojo/_base/lang', + 'dojo/_base/lang', + './auth', './ipa', './text', 'exports' ], - function(lang, IPA, text, rpc /*exports*/) { + function(lang, auth, IPA, text, rpc /*exports*/) { /** * Call an IPA command over JSON-RPC. @@ -206,19 +207,12 @@ rpc.command = function(spec) { dialog.open(); } - function auth_dialog_open(xhr, text_status, error_thrown) { + function error_handler_auth(xhr, text_status, error_thrown) { - var ajax = this; - - var dialog = IPA.unauthorized_dialog({ - xhr: xhr, - text_status: text_status, - error_thrown: error_thrown, - close_on_escape: false, - command: that + auth.current.set_authenticated(false, ''); + auth.current.authenticate().then(function() { + that.execute(); }); - - dialog.open(); } /* @@ -259,7 +253,7 @@ rpc.command = function(spec) { IPA.hide_activity_icon(); if (xhr.status === 401) { - auth_dialog_open(xhr, text_status, error_thrown); + error_handler_auth(xhr, text_status, error_thrown); return; } else if (!error_thrown) { error_thrown = { @@ -281,13 +275,14 @@ rpc.command = function(spec) { error_thrown.message = error_msg; } - // global specical cases error handlers section + // global special cases error handlers section // With trusts, user from trusted domain can use his ticket but he - // doesn't have rights for LDAP modify. It will throw internal errror. + // doesn't have rights for LDAP modify. It will throw internal error. // We should offer form base login. - if (xhr.status === 500 && IPA.ui.logged_kerberos && !IPA.ui.initialized) { - auth_dialog_open(xhr, text_status, error_thrown); + if (xhr.status === 500 && auth.authenticated_by === 'kerberos' && + !IPA.ui.initialized) { + error_handler_auth(xhr, text_status, error_thrown); return; } |