diff options
Diffstat (limited to 'install')
-rw-r--r-- | install/ui/doc/categories.json | 3 | ||||
-rw-r--r-- | install/ui/src/freeipa/app.js | 1 | ||||
-rw-r--r-- | install/ui/src/freeipa/plugins/sync_otp.js | 103 | ||||
-rw-r--r-- | install/ui/src/freeipa/widgets/SyncOTPScreen.js | 238 | ||||
-rw-r--r-- | install/ui/test/data/ipa_init.json | 6 |
5 files changed, 350 insertions, 1 deletions
diff --git a/install/ui/doc/categories.json b/install/ui/doc/categories.json index 8bc0d8b1a..b55fb81a0 100644 --- a/install/ui/doc/categories.json +++ b/install/ui/doc/categories.json @@ -245,7 +245,8 @@ "radiusproxy", "user", "plugins.load", - "plugins.login" + "plugins.login", + "plugins.sync_otp" ] } ] diff --git a/install/ui/src/freeipa/app.js b/install/ui/src/freeipa/app.js index 1915ccbe6..d87ce6611 100644 --- a/install/ui/src/freeipa/app.js +++ b/install/ui/src/freeipa/app.js @@ -23,6 +23,7 @@ define([ './app_container', './plugins/load_page', './plugins/login', + './plugins/sync_otp', // entities './aci', './automember', diff --git a/install/ui/src/freeipa/plugins/sync_otp.js b/install/ui/src/freeipa/plugins/sync_otp.js new file mode 100644 index 000000000..810503dc7 --- /dev/null +++ b/install/ui/src/freeipa/plugins/sync_otp.js @@ -0,0 +1,103 @@ +/* Authors: + * Petr Vobornik <pvoborni@redhat.com> + * + * Copyright (C) 2013 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/on', + '../facets/Facet', + '../auth', + '../navigation', + '../phases', + '../reg', + '../widget', + '../widgets/SyncOTPScreen' + ], + function(declare, lang, on, Facet, auth, navigation, phases, reg, widget, SyncOTPScreen) { + + /** + * Sync OTP Facet plugin + * + * Creates and registers a facet with sync otp page. + * + * @class plugins.sync_otp + * @singleton + */ + var sync_otp = {}; + + sync_otp.facet_spec = { + name: 'sync-otp', + 'class': 'login-pf-body', + preferred_container: 'simple', + requires_auth: false, + widgets: [ + { + $type: 'activity', + name: 'activity', + text: 'Synchronizing', + visible: false + }, + { + $type: 'sync_otp_screen', + name: 'sync_screen' + } + ] + }; + + sync_otp.SyncOTPFacet = declare([Facet], { + + init: function() { + this.inherited(arguments); + var sync_screen = this.get_widget('sync_screen'); + var self = this; + on(sync_screen, 'sync-success', function(args) { + self.emit('sync-success', args); + }); + + on(sync_screen, 'sync-cancel', function(args) { + self.emit('sync-cancel', args); + }); + + on(this, 'show', function(args) { + sync_screen.refresh(); + }); + }, + + set_user: function(user) { + var sync_screen = this.get_widget('sync_screen'); + sync_screen.set('user', user); + } + }); + + phases.on('registration', function() { + + var fa = reg.facet; + var w = reg.widget; + + w.register('sync_otp_screen', SyncOTPScreen); + + fa.register({ + type: 'sync-otp', + factory: sync_otp.SyncOTPFacet, + spec: sync_otp.facet_spec + }); + }); + + return sync_otp; +});
\ No newline at end of file diff --git a/install/ui/src/freeipa/widgets/SyncOTPScreen.js b/install/ui/src/freeipa/widgets/SyncOTPScreen.js new file mode 100644 index 000000000..13c47ae88 --- /dev/null +++ b/install/ui/src/freeipa/widgets/SyncOTPScreen.js @@ -0,0 +1,238 @@ +/* Authors: + * Petr Vobornik <pvoborni@redhat.com> + * + * Copyright (C) 2013 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/dom-construct', + 'dojo/dom-style', + 'dojo/query', + 'dojo/on', + '../ipa', + '../auth', + '../reg', + '../FieldBinder', + '../text', + '../util', + './LoginScreenBase' + ], + function(declare, lang, Deferred, construct, dom_style, query, on, + IPA, auth, reg, FieldBinder, text, util, LoginScreenBase) { + + + /** + * Widget with OTP sync form. + * + * @class widgets.SyncOTPScreen + */ + var SyncOTPScreen = declare([LoginScreenBase], { + + sync_fail: "Token synchronization failed", + + invalid_credentials: "The username, password or token codes are not correct", + + sync_success: "Token was synchronized", + + allow_cancel: true, + + user: null, + + //nodes: + cancel_btn_node: null, + sync_btn_node: null, + + render_buttons: function(container) { + this.cancel_btn_node = IPA.button({ + name: 'cancel', + label: 'Cancel', + 'class': 'btn-default btn-lg', + click: lang.hitch(this, this.on_cancel) + })[0]; + if (this.allow_cancel) { + construct.place(this.cancel_btn_node, container); + } + this.sync_btn_node = IPA.button({ + label: text.get('@i18n:password.sync_otp_token', "Sync OTP Token"), + 'class': 'btn-primary btn-lg', + click: lang.hitch(this, this.on_confirm) + })[0]; + construct.place(this.sync_btn_node, container); + }, + + refresh: function() { + this.reset(); + this.get_widget('validation').remove('sync'); + if (this.user) { + this.get_field('user').set_value([this.user]); + this.get_widget('password').focus_input(); + } else { + this.get_widget('user').focus_input(); + } + if (this.buttons_node) { + this.buttons_node.innerHTML = ""; + if (this.allow_cancel) { + construct.place(this.cancel_btn_node, this.buttons_node); + } + construct.create('span', { innerHTML: ' '}, this.buttons_node); + construct.place(this.sync_btn_node, this.buttons_node); + } + }, + + on_cancel: function() { + this.emit('sync-cancel', { source: this }); + }, + + on_confirm: function() { + this.sync(); + }, + + sync: function() { + + var val_summary = this.get_widget('validation'); + val_summary.remove('sync'); + + if (!this.validate()) return; + + var user = this.get_field('user').get_value()[0]; + var password_f = this.get_field('password'); + var password = password_f.get_value()[0]; + var otp1 = this.get_field('first_code').get_value()[0]; + var otp2 = this.get_field('second_code').get_value()[0]; + var token = this.get_field('token').get_value()[0]; + + var p = this.sync_core(user, password, otp1, otp2, token); + p.then(lang.hitch(this, function(result) { + var msg = this.sync_fail; + var evt = 'sync-fail'; + var type = 'error'; + this.refresh(); + if (result === 'ok') { + evt = 'sync-success'; + msg = this.sync_success; + val_summary.add_success('sync', msg); + } else if (result === 'invalid-credentials') { + msg = this.invalid_credentials; + val_summary.add_error('sync', msg); + } else { + val_summary.add_error('sync', msg); + } + this.emit(evt, { source: this, message: msg, status: result }); + })); + }, + + sync_core: function(user, password, otp1, otp2, token) { + + var d = new Deferred(); + var data = { + user: user, + password: password, + first_code: otp1, + second_code: otp2 + }; + if (token) data.token = token; + + var handler = function(data, text_status, xhr) { + var result = xhr.getResponseHeader("X-IPA-TokenSync-Result"); + result = result || 'error'; + IPA.hide_activity_icon(); + d.resolve(result); + }; + + var request = { + url: '/ipa/session/sync_token', + data: data, + contentType: 'application/x-www-form-urlencoded', + processData: true, + dataType: 'html', + type: 'POST', + success: handler, + error: handler + }; + + IPA.display_activity_icon(); + $.ajax(request); + return d.promise; + }, + + setUser: function(value) { + this.user = value; + this.get_field('user').set_value([value]); + }, + + constructor: function(spec) { + spec = spec || {}; + + this.sync_fail = text.get(spec.sync_fail || '@i18n:password.otp_sync_fail', + this.sync_fail); + + this.sync_success = text.get(spec.sync_success || '@i18n:password.otp_sync_success', + this.sync_success); + + this.invalid_credentials = text.get(spec.invalid_credentials || '@i18n:password.otp_sync_invalid', + this.invalid_credentials); + + this.field_specs = SyncOTPScreen.field_specs; + } + }); + + SyncOTPScreen.field_specs = [ + { + $type: 'text', + name: 'user', + label: text.get('@i18n:login.username', "Username"), + show_errors: false, + undo: false, + required: true + }, + { + $type: 'password', + name: 'password', + label: text.get('@i18n:login.password', "Password"), + show_errors: false, + undo: false, + required: true + }, + { + $type: 'password', + name: 'first_code', + label: text.get('@i18n:password.first_otp', "First OTP"), + show_errors: false, + undo: false, + required: true + }, + { + $type: 'password', + name: 'second_code', + label: text.get('@i18n:password.second_otp', "Second OTP"), + show_errors: false, + undo: false, + required: true + }, + { + $type: 'text', + name: 'token', + label: text.get('@i18n:password.token_id', "Token ID"), + show_errors: false, + undo: false + } + ]; + + return SyncOTPScreen; +});
\ No newline at end of file diff --git a/install/ui/test/data/ipa_init.json b/install/ui/test/data/ipa_init.json index 0c32395ee..3da2b4f4a 100644 --- a/install/ui/test/data/ipa_init.json +++ b/install/ui/test/data/ipa_init.json @@ -506,11 +506,15 @@ "current_password": "Current Password", "current_password_required": "Current password is required", "expires_in": "Your password expires in ${days} days.", + "first_otp": "First OTP", "invalid_password": "The password or username you entered is incorrect.", "new_password": "New Password", "new_password_required": "New password is required", "otp": "OTP", "otp_long": "One-Time-Password", + "otp_sync_fail": "Token synchronization failed", + "otp_sync_invalid": "The username, password or token codes are not correct", + "otp_sync_success": "Token was synchronized", "password": "Password", "password_and_otp": "Password or Password+One-Time-Password", "password_change_complete": "Password change complete", @@ -518,6 +522,8 @@ "reset_failure": "Password reset was not successful.", "reset_password": "Reset Password", "reset_password_sentence": "Reset your password.", + "second_otp": "Second OTP", + "token_id": "Token ID", "verify_password": "Verify Password" }, "search": { |