diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f59063..9df2fb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## Unreleased + +- Fix login welcome message +- Clarify "set new password" button (#94) +- Show error messages when login fails, for example when a wrong password was + entered (#96) + ## [0.5.1] - Fix bug of missing "Monitoring" app access when creating a new user. diff --git a/backend/web/login/login.py b/backend/web/login/login.py index 8ea982c..3601a00 100644 --- a/backend/web/login/login.py +++ b/backend/web/login/login.py @@ -118,7 +118,13 @@ def login(): identity = get_auth() if identity: - return render_template("loggedin.html", api_url=KRATOS_PUBLIC_URL, id=id) + if 'name' in identity['traits']: + # Add a space in front of the "name" so the template can put it + # between "Welcome" and the comma + name = " " + identity['traits']['name'] + else: + name = "" + return render_template("loggedin.html", api_url=KRATOS_PUBLIC_URL, name=name) flow = request.args.get("flow") diff --git a/backend/web/static/base.js b/backend/web/static/base.js index 4424760..0e142ed 100644 --- a/backend/web/static/base.js +++ b/backend/web/static/base.js @@ -16,257 +16,244 @@ */ - // Check if an auth flow is configured and redirect to auth page in that // case. function check_flow_auth() { - var state = Cookies.get('flow_state'); - var url = Cookies.get('auth_url'); + var state = Cookies.get('flow_state'); + var url = Cookies.get('auth_url'); - if (state == 'auth') { - Cookies.set('flow_state',''); - window.location.href = url; - } + if (state == 'auth') { + Cookies.set('flow_state', ''); + window.location.href = url; + } } // Check if there if the flow is expired, if so, reset the cookie function check_flow_expired() { - var state = Cookies.get('flow_state'); + var state = Cookies.get('flow_state'); - if (state == 'flow_expired') { - Cookies.set('flow_state',''); - $("#contentFlowExpired").show(); - } + if (state == 'flow_expired') { + Cookies.set('flow_state', ''); + $('#contentFlowExpired').show(); + } } - - // The script executed on login flows function flow_login() { + var flow = $.urlParam('flow'); + var uri = api_url + 'self-service/login/flows?id=' + flow; - var flow = $.urlParam('flow'); - var uri = api_url + 'self-service/login/flows?id=' + flow; + // Query the Kratos backend to know what fields to render for the + // current flow + $.ajax({ + type: 'GET', + url: uri, + success: function (data) { + // Render login form (group: password) + var form_html = render_form(data, 'password'); + $('#contentLogin').html(form_html); - // Query the Kratos backend to know what fields to render for the - // current flow - $.ajax({ - type: "GET", - url: uri, - success: function(data) { - - // Render login form (group: password) - var html = render_form(data, 'password'); - $("#contentLogin").html(html); - - }, - complete: function(obj) { - - // If we get a 410, the flow is expired, need to refresh the flow - if (obj.status == 410) { - Cookies.set('flow_state','flow_expired'); - // If we call the page without arguments, we get a new flow - window.location.href = 'login'; - } - } - }); + var messages_html = render_messages(data); + $('#contentMessages').html(messages_html); + }, + complete: function (obj) { + // If we get a 410, the flow is expired, need to refresh the flow + if (obj.status == 410) { + Cookies.set('flow_state', 'flow_expired'); + // If we call the page without arguments, we get a new flow + window.location.href = 'login'; + } + }, + }); } -// This is called after a POST on settings. It tells if the save was +// This is called after a POST on settings. It tells if the save was // successful and display / handles based on that outcome function flow_settings_validate() { + var flow = $.urlParam('flow'); + var uri = api_url + 'self-service/settings/flows?id=' + flow; - var flow = $.urlParam('flow'); - var uri = api_url + 'self-service/settings/flows?id=' + flow; + $.ajax({ + type: 'GET', + url: uri, + success: function (data) { + // We had success. We save that fact in our flow_state + // cookie and regenerate a new flow + if (data.state == 'success') { + Cookies.set('flow_state', 'settings_saved'); - $.ajax( { - type: "GET", - url: uri, - success: function(data) { + // Redirect to generate new flow ID + window.location.href = 'settings'; + } else { + // There was an error, Kratos does not specify what is + // wrong. So we just show the general error message and + // let the user figure it out. We can re-use the flow-id + $('#contentProfileSaveFailed').show(); - // We had success. We save that fact in our flow_state - // cookie and regenerate a new flow - if (data.state == 'success') { - Cookies.set('flow_state', 'settings_saved'); - - // Redirect to generate new flow ID - window.location.href = 'settings'; - } - else { - // There was an error, Kratos does not specify what is - // wrong. So we just show the general error message and - // let the user figure it out. We can re-use the flow-id - $("#contentProfileSaveFailed").show(); - - // For now, this code assumes that only the password can fail - // validation. Other forms might need to be added in the future. - html = render_form(data, 'password') - $("#contentPassword").html(html) - } - } - }); + // For now, this code assumes that only the password can fail + // validation. Other forms might need to be added in the future. + html = render_form(data, 'password'); + $('#contentPassword').html(html); + } + }, + }); } - // Render the settings flow, this is where users can change their personal // settings, like name and password. The form contents are defined by Kratos function flow_settings() { + // Get the details from the current flow from kratos + var flow = $.urlParam('flow'); + var uri = api_url + 'self-service/settings/flows?id=' + flow; + $.ajax({ + type: 'GET', + url: uri, + success: function (data) { + var state = Cookies.get('flow_state'); - // Get the details from the current flow from kratos - var flow = $.urlParam('flow'); - var uri = api_url + 'self-service/settings/flows?id=' + flow; - $.ajax({ - type: "GET", - url: uri, - success: function(data) { + // If we have confirmation the settings are saved, show the + // notification + if (state == 'settings_saved') { + $('#contentProfileSaved').show(); + Cookies.set('flow_state', 'settings'); + } - var state = Cookies.get('flow_state') + // Hide prfile section if we are in recovery state + // so the user is not confused by other fields. The user + // probably want to setup a password only first. + if (state == 'recovery') { + $('#contentProfile').hide(); + } - // If we have confirmation the settings are saved, show the - // notification - if (state == 'settings_saved') { - $("#contentProfileSaved").show(); - Cookies.set('flow_state', 'settings'); - } + // Render the password & profile form based on the fields we got + // from the API + var html = render_form(data, 'password'); + $('#contentPassword').html(html); - // Hide prfile section if we are in recovery state - // so the user is not confused by other fields. The user - // probably want to setup a password only first. - if (state == 'recovery') { - $("#contentProfile").hide(); - } + html = render_form(data, 'profile'); + $('#contentProfile').html(html); + // If the submit button is hit, execute the POST with Ajax. + $('#formpassword').submit(function (e) { + // avoid to execute the actual submit of the form. + e.preventDefault(); - // Render the password & profile form based on the fields we got - // from the API - var html = render_form(data, 'password'); - $("#contentPassword").html(html); - - html = render_form(data, 'profile'); - $("#contentProfile").html(html); - - // If the submit button is hit, execute the POST with Ajax. - $("#formpassword").submit(function(e) { - - // avoid to execute the actual submit of the form. - e.preventDefault(); - - var form = $(this); - var url = form.attr('action'); - - $.ajax({ - type: "POST", - url: url, - data: form.serialize(), - complete: function(obj) { - // Validate the settings - flow_settings_validate(); - }, - }); - }); - - - - }, - complete: function(obj) { - - // If we get a 410, the flow is expired, need to refresh the flow - if (obj.status == 410) { - Cookies.set('flow_state','flow_expired'); - window.location.href = 'settings'; - } - - } - }); + var form = $(this); + var url = form.attr('action'); + $.ajax({ + type: 'POST', + url: url, + data: form.serialize(), + complete: function (obj) { + // Validate the settings + flow_settings_validate(); + }, + }); + }); + }, + complete: function (obj) { + // If we get a 410, the flow is expired, need to refresh the flow + if (obj.status == 410) { + Cookies.set('flow_state', 'flow_expired'); + window.location.href = 'settings'; + } + }, + }); } function flow_recover() { - var flow = $.urlParam('flow'); - var uri = api_url + 'self-service/recovery/flows?id=' + flow; + var flow = $.urlParam('flow'); + var uri = api_url + 'self-service/recovery/flows?id=' + flow; - $.ajax( { - type: "GET", - url: uri, - success: function(data) { + $.ajax({ + type: 'GET', + url: uri, + success: function (data) { + // Render the recover form, method 'link' + var html = render_form(data, 'link'); + $('#contentRecover').html(html); - // Render the recover form, method 'link' - var html = render_form(data, 'link'); - $("#contentRecover").html(html); + // Do form post as an AJAX call + $('#formlink').submit(function (e) { + // avoid to execute the actual submit of the form. + e.preventDefault(); - // Do form post as an AJAX call - $("#formlink").submit(function(e) { + var form = $(this); + var url = form.attr('action'); - // avoid to execute the actual submit of the form. - e.preventDefault(); - - var form = $(this); - var url = form.attr('action'); - - // keep stat we are in recovery - Cookies.set('flow_state', 'recovery'); - $.ajax({ - type: "POST", - url: url, - data: form.serialize(), // serializes the form's elements. - success: function(data) - { - - // Show the request is sent out - $("#contentRecover").hide(); - $("#contentRecoverRequested").show(); - } - }); - }); - - - }, - complete: function(obj) { - - // If we get a 410, the flow is expired, need to refresh the flow - if (obj.status == 410) { - Cookies.set('flow_state','flow_expired'); - window.location.href = 'recovery'; - - } - } - }); + // keep stat we are in recovery + Cookies.set('flow_state', 'recovery'); + $.ajax({ + type: 'POST', + url: url, + data: form.serialize(), // serializes the form's elements. + success: function (data) { + // Show the request is sent out + $('#contentRecover').hide(); + $('#contentRecoverRequested').show(); + }, + }); + }); + }, + complete: function (obj) { + // If we get a 410, the flow is expired, need to refresh the flow + if (obj.status == 410) { + Cookies.set('flow_state', 'flow_expired'); + window.location.href = 'recovery'; + } + }, + }); } // Based on Kratos UI data and a group name, get the full form for that group. -// kratos groups elements which belongs together in a group and should be posted +// kratos groups elements which belongs together in a group and should be posted // at once. The elements in the default group should be part of all other // groups. // // data: data object as returned form the API // group: group to render. -function render_form(data, group) { +function render_form(data, group) { + // Create form + var action = data.ui.action; + var method = data.ui.method; + var form = "
"; - return form; + } + form += ''; + return form; +} +// Check if there are any general messages to show to the user and render them +function render_messages(data) { + var messages = data.ui.messages; + if (messages == []) { + return ''; + } + var html = '