From efc9e66f4ddff7c0aaae5d460ab41f6026fbd32d Mon Sep 17 00:00:00 2001 From: Petr Vobornik Date: Wed, 13 Nov 2013 16:02:48 +0100 Subject: webui: login screen widget Reimplementation of unauthorized dialog into separate widget. It uses RCUE design. New features compared to unauthorized dialog: - reflects auth methods from `auth` module - validation summary - differentiates Kerberos auth failure with session expiration - Caps Lock warning - form based method doesn't allow password only submission https://fedorahosted.org/freeipa/ticket/4017 https://fedorahosted.org/freeipa/ticket/3903 Reviewed-By: Adam Misnyovszki --- install/ui/images/Makefile.am | 3 + install/ui/images/login-screen-background.jpg | Bin 0 -> 69168 bytes install/ui/images/login-screen-logo.png | Bin 0 -> 5802 bytes install/ui/images/product-name.png | Bin 0 -> 13622 bytes install/ui/less/login.less | 167 ++++++++ install/ui/less/rcue.less | 1 + install/ui/src/freeipa/dialog.js | 4 +- install/ui/src/freeipa/widgets/LoginScreen.js | 549 ++++++++++++++++++++++++++ 8 files changed, 722 insertions(+), 2 deletions(-) create mode 100644 install/ui/images/login-screen-background.jpg create mode 100644 install/ui/images/login-screen-logo.png create mode 100644 install/ui/images/product-name.png create mode 100644 install/ui/less/login.less create mode 100644 install/ui/src/freeipa/widgets/LoginScreen.js (limited to 'install/ui') diff --git a/install/ui/images/Makefile.am b/install/ui/images/Makefile.am index 0a92fdf9a..e63af6912 100644 --- a/install/ui/images/Makefile.am +++ b/install/ui/images/Makefile.am @@ -17,12 +17,15 @@ app_DATA = \ ie-icon.png \ ipa-banner.png \ ipa-logo.png \ + login-screen-background.jpg \ + login-screen-logo.png \ mainnav-tab-off.png \ mainnav-tab-on.png \ modal-background.png \ nav-arrow.png \ outer-background.png \ panel-background.png \ + product-name.png \ remove-icon.png \ reset-icon.png \ search-background.png \ diff --git a/install/ui/images/login-screen-background.jpg b/install/ui/images/login-screen-background.jpg new file mode 100644 index 000000000..b2ffc1ac5 Binary files /dev/null and b/install/ui/images/login-screen-background.jpg differ diff --git a/install/ui/images/login-screen-logo.png b/install/ui/images/login-screen-logo.png new file mode 100644 index 000000000..38f948efd Binary files /dev/null and b/install/ui/images/login-screen-logo.png differ diff --git a/install/ui/images/product-name.png b/install/ui/images/product-name.png new file mode 100644 index 000000000..79551f3ac Binary files /dev/null and b/install/ui/images/product-name.png differ diff --git a/install/ui/less/login.less b/install/ui/less/login.less new file mode 100644 index 000000000..de9d651b0 --- /dev/null +++ b/install/ui/less/login.less @@ -0,0 +1,167 @@ +/** + * Authors: + * UXD team + * Petr Vobornik + * + * 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 . + */ + +.facet[name=login] { + height: 100%; +} + +.rcue-login-screen { + width: 100%; + + height: 100%; + background-color: #1D2226; + background-image: url("../images/login-screen-background.jpg"); + background-position: top left; + background-size: auto; + background-repeat: no-repeat; + + img.logo { + // position: absolute; + // top: 50px; + // right: 64px; + display: none; + } + + .login-form { + position: absolute; + bottom: 15%; + left: 0; + right: 0; + border-top: 1px rgba(255, 255, 255, 0.05) solid; + border-bottom: 1px rgba(255, 255, 255, 0.05) solid; + background-color: rgba(0, 0, 0, 0.3); + color: #fff; + + aside { + padding: 30px; + p { + margin-bottom: 8px; + } + } + + fieldset { + float: left; + margin: 30px 30px 30px 0; + padding: 10px; + padding-right: 34px; + width: 426px; + border-right: 1px rgba(255, 255, 255, 0.15) solid; + + legend { + position: absolute; + top: -100px; + padding-left: 100px; + width: auto; + text-transform: uppercase; + letter-spacing: 0.5px; + font-weight: 200; + font-size: 22px; + border-bottom: none; + + strong { + margin-right: 4px; + font-weight: 700; + } + } + + input[type="text"], + input[type="password"] { + width: 282px; + color: black; + } + + .buttons { + float: right; + } + + label, button { + font-size: 13px; + } + + small { + line-height: 22px; + margin-left: 87px; + margin-top: 10px; + display: block; + float: left; + } + } + } +} + +@media (min-width: 1280px) { + .rcue-login-screen { + background-size: 100% auto; + } +} + + +.login_small() { + .rcue-login-screen .login-form { + top: 100px; + bottom: inherit; + } +} + +@media (max-width: 480px) { + + .login_small; + + .rcue-login-screen .login-form { + fieldset { + float: none; + width: auto; + .buttons { + float: none; + } + } + aside { + padding: 10px; + } + } +} + +@media (max-height: 700px) { + .login_small; +} + +@media (max-height: 480px) { + .login_small; + .rcue-login-screen .login-form legend { + position: relative; + } +} + +@media (max-width: 740px) { + .rcue-login-screen .login-form { + fieldset { + border-right: none; + legend { + padding-left: 0; + width: 100%; + } + } + aside { + clear: both; + } + } +} diff --git a/install/ui/less/rcue.less b/install/ui/less/rcue.less index 60e41d95f..2d41aa283 100644 --- a/install/ui/less/rcue.less +++ b/install/ui/less/rcue.less @@ -11,3 +11,4 @@ @import "forms-override"; @import "widgets"; @import "plugins/otp"; +@import "login.less"; diff --git a/install/ui/src/freeipa/dialog.js b/install/ui/src/freeipa/dialog.js index 39c48efd3..4c6c37f88 100644 --- a/install/ui/src/freeipa/dialog.js +++ b/install/ui/src/freeipa/dialog.js @@ -1291,7 +1291,7 @@ IPA.confirm_mixin = function() { var self = this; this._on_key_up_listener = function(e) { self.on_key_up(e); }; this._on_key_down_listener = function(e) { self._on_key_down(e); }; - var dialog_container = this.dom_node; + var dialog_container = $(this.dom_node); dialog_container.bind('keyup', this._on_key_up_listener); dialog_container.bind('keydown', this._on_key_down_listener); }, @@ -1300,7 +1300,7 @@ IPA.confirm_mixin = function() { * Removal of registered event handlers */ remove_listeners: function() { - var dialog_container = this.dom_node; + var dialog_container = $(this.dom_node); dialog_container.unbind('keyup', this._on_key_up_listener); dialog_container.unbind('keydown', this._on_key_down_listener); }, diff --git a/install/ui/src/freeipa/widgets/LoginScreen.js b/install/ui/src/freeipa/widgets/LoginScreen.js new file mode 100644 index 000000000..9d149af4c --- /dev/null +++ b/install/ui/src/freeipa/widgets/LoginScreen.js @@ -0,0 +1,549 @@ +/* Authors: + * Petr Vobornik + * + * 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 . +*/ + +define(['dojo/_base/declare', + 'dojo/_base/lang', + 'dojo/dom-construct', + 'dojo/dom-style', + 'dojo/query', + 'dojo/on', + 'dojo/Evented', + 'dojo/Stateful', + '../ipa', + '../auth', + '../reg', + '../FieldBinder', + '../FormMixin', + '../text', + '../util', + './ContainerMixin' + ], + function(declare, lang, construct, dom_style, query, on, + Evented, Stateful, IPA, auth, reg, FieldBinder, FormMixin, text, + util, ContainerMixin) { + + var ConfirmMixin = declare(null, IPA.confirm_mixin().mixin); + + /** + * Widget with login form. + * + * Supported operations: + * + * - login with password, kerberos + * - password change + * + * @class widgets.LoginScreen + */ + var LoginScreen = declare([Stateful, Evented, FormMixin, ContainerMixin, ConfirmMixin], { + + id: 'rcue-login-screen', + + 'class': 'rcue-login-screen', + + logo_src: 'images/login-screen-logo.png', + + product_name_src: 'images/product-name.png', + + expired_msg: "Your session has expired. Please re-login.", + + form_auth_msg: "To login with username and password, enter them in the fields below, then click Login.", + + kerberos_msg: " To login with Kerberos, please make sure you" + + " have valid tickets (obtainable via kinit) and " + + "configured" + + " the browser correctly, then click Login. ", + + form_auth_failed: "The password or username you entered is incorrect. ", + + krb_auth_failed: "Authentication with Kerberos failed", + + password_expired: "Your password has expired. Please enter a new password.", + + denied: "Sorry you are not allowed to access this service.", + + caps_warning_msg: "Warning: CAPS LOCK key is on", + + /** + * Details builder + * @property {IPA.details_builder} + * @protected + */ + details_builder: null, + + /** + * Aside text + * @property {string} + */ + aside: "", + + //nodes: + dom_node: null, + container_node: null, + content_node: null, + aside_node: null, + login_btn_node: null, + reset_btn_node: null, + buttons_node: null, + + /** + * View this form is in. + * + * Possible views: ['login', 'reset'] + * @property {string} + */ + view: 'login', + + /** + * Indicates that CAPS LOCK warning is on. Null indicates that we don't + * know the state. + * @property {boolean|null} + */ + caps_warning: null, + + + _asideSetter: function(text) { + this.aside = text; + if (this.aside_node) { + this.aside_node.innerHTML = this.aside; + } + }, + + _viewSetter: function(view) { + this.view = view; + this.refresh(); + }, + + render: function() { + + this.dom_node = construct.create('div', { + id: this.id, + 'class': this['class'] + }); + + if (this.container_node) { + construct.place(this.dom_node, this.container_node); + } + + this.render_content(); + this.register_listeners(); + + return this.dom_node; + }, + + render_content: function() { + + construct.empty(this.dom_node); + + construct.create('img', { + 'class': 'logo', + src: this.logo_src + }, this.dom_node); + + this.render_form(this.dom_node); + }, + + render_form: function(container) { + + var form_cont = construct.create('div', { + 'class': 'login-form' + }, container); + + var fieldset = construct.create('fieldset', {}, form_cont); + + var legend = construct.create('legend', {}, fieldset); + construct.create('img', { + src: this.product_name_src + }, legend); + + var layout = IPA.fluid_layout({}); + var form = layout.create(this.get_widgets()); + construct.place(form[0], fieldset); + this.register_caps_check(); + + this.buttons_node = construct.create('div', { + 'class': 'buttons' + }, fieldset); + + this.login_btn_node = IPA.button({ + label: text.get('@i18n:login.login', "Login"), + 'class': 'btn-primary', + click: lang.hitch(this, this.on_confirm) + })[0]; + construct.place(this.login_btn_node, this.buttons_node); + + this.reset_btn_node = IPA.button({ + label: text.get('@i18n:buttons.reset_password_and_login', "Reset Password and Login"), + 'class': 'btn-primary', + click: lang.hitch(this, this.on_confirm) + })[0]; + + this.render_aside(form_cont); + }, + + render_aside: function(container) { + + this.aside_node = construct.create('aside', { + innerHTML: this.aside + }, container); + }, + + create_fields: function() { + + var validation_summary = { + $type: 'validation_summary', + name: 'validation', + visible: false + }; + + var val_w = this.add_widget(validation_summary); + var fields = LoginScreen.field_specs; + for (var i=0, l=fields.length; i-1; + f.set_enabled(enable); + w.set_visible(enable); + } + }, + + set_login_aside_text: function() { + var aside = ""; + if (auth.current.expired) { + aside += "

"+this.expired_msg;+"

"; + } + if (this.password_enabled()) { + aside += "

"+this.form_auth_msg;+"

"; + } + if (this.kerberos_enabled()) { + aside += "

"+this.kerberos_msg;+"

"; + } + this.set('aside', aside); + }, + + set_reset_aside_text: function() { + this.set('aside', ''); + }, + + kerberos_enabled: function() { + return auth.current.auth_methods.indexOf('kerberos') > -1; + }, + + password_enabled: function() { + return auth.current.auth_methods.indexOf('password') > -1; + }, + + postscript: function(args) { + this.create_fields(); + }, + + constructor: function(spec) { + spec = spec || {}; + declare.safeMixin(this, spec); + + this.expired_msg = text.get(spec.expired_msg || '@i18n:ajax.401.message', + this.expired_msg); + + this.form_auth_msg = text.get(spec.form_auth_msg || '@i18n:login.form_auth', + this.form_auth_msg); + + this.kerberos_msg = text.get(spec.kerberos_msg || '@i18n:login.krb_auth_msg', + this.kerberos_msg); + + this.kerberos_msg = this.kerberos_msg.replace('${host}', window.location.hostname); + + this.krb_auth_failed = text.get(spec.krb_auth_failed, this.krb_auth_failed); + } + }); + + LoginScreen.field_specs = [ + { + $type: 'text', + name: 'username', + label: text.get('@i18n:login.username', "Username"), + show_errors: false, + undo: false + }, + { + $type: 'password', + name: 'password', + label: text.get('@i18n:login.password', "Password"), + show_errors: false, + undo: false + }, + { + name: 'username_r', + read_only: true, + label: text.get('@i18n:login.username', "Username"), + show_errors: false, + undo: false + }, + { + name: 'new_password', + $type: 'password', + required: true, + label: text.get('@i18n:password.new_password)', "New Password"), + show_errors: false, + undo: false + }, + { + name: 'verify_password', + $type: 'password', + required: true, + label: text.get('@i18n:password.verify_password', "Verify Password"), + validators: [{ + $type: 'same_password', + other_field: 'new_password' + }], + show_errors: false, + undo: false + } + ]; + + return LoginScreen; +}); \ No newline at end of file -- cgit