src/wok/API.json | 25 +++++++++++++++++- src/wok/i18n.py | 5 +++- src/wok/root.py | 69 +++++++++++++++++++++++++++++++++++++++++------- ui/js/src/wok.login.js | 19 ++++++++----- ui/pages/i18n.json.tmpl | 5 +++- ui/pages/login.html.tmpl | 6 ++--- 6 files changed, 106 insertions(+), 23 deletions(-)
From: Ramon Medeiros <ramonn@linux.vnet.ibm.com>
To prevent brute force attack, creates a mechanism to allow 3 tries
first. After that, a timeout will start and will be added 30 seconds for
each failed try in a row.
Signed-off-by: Ramon Medeiros <ramonn@linux.vnet.ibm.com>
---
Changes:
v4:
Use API.json for input validation
v3:
Improve error handling on login page
v2:
Set timeout by user, ip and session id. This will avoid trouble with
users using the same ip, like NAT.
src/wok/API.json | 25 +++++++++++++++++-
src/wok/i18n.py | 5 +++-
src/wok/root.py | 69 +++++++++++++++++++++++++++++++++++++++++-------
ui/js/src/wok.login.js | 19 ++++++++-----
ui/pages/i18n.json.tmpl | 5 +++-
ui/pages/login.html.tmpl | 6 ++---
6 files changed, 106 insertions(+), 23 deletions(-)
diff --git a/src/wok/API.json b/src/wok/API.json
index 8965db9..3f7bfd7 100644
--- a/src/wok/API.json
+++ b/src/wok/API.json
@@ -2,5 +2,28 @@
"$schema": "http://json-schema.org/draft-03/schema#",
"title": "Wok API",
"description": "Json schema for Wok API",
- "type": "object"
+ "type": "object",
+ "properties": {
+ "wokroot_login": {
+ "type": "object",
+ "properties": {
+ "username": {
+ "description": "Username",
+ "required": true,
+ "type": "string",
+ "minLength": 1,
+ "error": "WOKAUTH0003E"
+ },
+ "password": {
+ "description": "Password",
+ "required": true,
+ "type": "string",
+ "minLength": 1,
+ "error": "WOKAUTH0006E"
+ }
+ },
+ "additionalProperties": false,
+ "error": "WOKAUTH0007E"
+ }
+ }
}
diff --git a/src/wok/i18n.py b/src/wok/i18n.py
index 935c9c1..5ad5e57 100644
--- a/src/wok/i18n.py
+++ b/src/wok/i18n.py
@@ -40,8 +40,11 @@ messages = {
"WOKAUTH0001E": _("Authentication failed for user '%(username)s'. [Error code: %(code)s]"),
"WOKAUTH0002E": _("You are not authorized to access Wok. Please, login first."),
- "WOKAUTH0003E": _("Specify %(item)s to login into Wok."),
+ "WOKAUTH0003E": _("Specify username to login into Wok."),
+ "WOKAUTH0004E": _("You have failed to login in too much attempts. Please, wait for %(seconds)s seconds to try again."),
"WOKAUTH0005E": _("Invalid LDAP configuration: %(item)s : %(value)s"),
+ "WOKAUTH0006E": _("Specify password to login into Wok."),
+ "WOKAUTH0007E": _("You need to specify username and password to login into Wok."),
"WOKLOG0001E": _("Invalid filter parameter. Filter parameters allowed: %(filters)s"),
"WOKLOG0002E": _("Creation of log file failed: %(err)s"),
diff --git a/src/wok/root.py b/src/wok/root.py
index 080b7f0..9f6b7b3 100644
--- a/src/wok/root.py
+++ b/src/wok/root.py
@@ -1,7 +1,7 @@
#
# Project Wok
#
-# Copyright IBM Corp, 2015-2016
+# Copyright IBM Corp, 2015-2017
#
# Code derived from Project Kimchi
#
@@ -21,7 +21,9 @@
import cherrypy
import json
+import re
import os
+import time
from distutils.version import LooseVersion
from wok import auth
@@ -30,8 +32,8 @@ from wok.i18n import messages
from wok.config import paths as wok_paths
from wok.control import sub_nodes
from wok.control.base import Resource
-from wok.control.utils import parse_request
-from wok.exception import MissingParameter
+from wok.control.utils import parse_request, validate_params
+from wok.exception import UnauthorizedError, WokException
from wok.reqlogger import log_request
@@ -48,7 +50,8 @@ class Root(Resource):
super(Root, self).__init__(model)
self._handled_error = ['error_page.400', 'error_page.404',
'error_page.405', 'error_page.406',
- 'error_page.415', 'error_page.500']
+ 'error_page.415', 'error_page.500',
+ 'error_page.403', 'error_page.401']
if not dev_env:
self._cp_config = dict([(key, self.error_production_handler)
@@ -146,6 +149,7 @@ class WokRoot(Root):
self.domain = 'wok'
self.messages = messages
self.extends = None
+ self.failed_logins = {}
# set user log messages and make sure all parameters are present
self.log_map = ROOT_REQUESTS
@@ -153,24 +157,71 @@ class WokRoot(Root):
@cherrypy.expose
def login(self, *args):
+ def _raise_timeout(user_id):
+ length = self.failed_logins[user_ip_sid]["count"]
+ timeout = (length - 3) * 30
+ details = e = UnauthorizedError("WOKAUTH0004E",
+ {"seconds": timeout})
+ log_request(code, params, details, method, 403)
+ raise cherrypy.HTTPError(403, e.message)
details = None
method = 'POST'
code = self.getRequestMessage(method, 'login')
try:
params = parse_request()
+ validate_params(params, self, "login")
username = params['username']
password = params['password']
- except KeyError, item:
- details = e = MissingParameter('WOKAUTH0003E', {'item': str(item)})
- log_request(code, params, details, method, 400)
- raise cherrypy.HTTPError(400, e.message)
+ except WokException, e:
+ details = e
+ status = e.getHttpStatusCode()
+ raise cherrypy.HTTPError(status, e.message)
+
+ # get authentication info
+ remote_ip = cherrypy.request.remote.ip
+ session_id = str(cherrypy.session.originalid)
+ user_ip_sid = re.escape(username + remote_ip + session_id)
+
+ # check for repetly
+ count = self.failed_logins.get(user_ip_sid, {"count": 0}).get("count")
+ if count > 3:
+
+ # verify if timeout is still valid
+ last_try = self.failed_logins[user_ip_sid]["time"]
+ if time.time() < (last_try + ((count - 3) * 30)):
+ _raise_timeout(user_ip_sid)
+ else:
+ self.failed_logins.pop(user_ip_sid)
try:
status = 200
user_info = auth.login(username, password)
+
+ # user logged sucessfuly: reset counters
+ if self.failed_logins.get(user_ip_sid) != None:
+ self.failed_logins.pop(user_ip_sid)
except cherrypy.HTTPError, e:
- status = e.status
+
+ # store time and prevent too much tries
+ if self.failed_logins.get(user_ip_sid) == None:
+ self.failed_logins[user_ip_sid] = {"time": time.time(),
+ "ip": remote_ip,
+ "session_id": session_id,
+ "username": username,
+ "count": 1}
+ else:
+ # tries take more than 30 seconds between each one: do not
+ # increase count
+ if (time.time() -
+ self.failed_logins[user_ip_sid]["time"]) < 30:
+
+ self.failed_logins[user_ip_sid]["time"] = time.time()
+ self.failed_logins[user_ip_sid]["count"] += 1
+
+ # more than 3 fails: raise error
+ if self.failed_logins[user_ip_sid]["count"] > 3:
+ _raise_timeout(user_ip_sid)
raise
finally:
log_request(code, params, details, method, status)
diff --git a/ui/js/src/wok.login.js b/ui/js/src/wok.login.js
index 666a339..9e2a392 100644
--- a/ui/js/src/wok.login.js
+++ b/ui/js/src/wok.login.js
@@ -19,6 +19,10 @@
*/
wok.login_main = function() {
"use strict";
+ var i18n;
+ wok.getI18n(function(i18nObj){
+ i18n = i18nObj;
+ }, false, "i18n.json", true);
// verify if language is available
var selectedLanguage = wok.lang.get();
@@ -50,7 +54,8 @@ wok.login_main = function() {
var query = window.location.search;
var error = /.*error=(.*?)(&|$)/g.exec(query);
if (error && error[1] === "sessionTimeout") {
- $("#messSession").show();
+ $("#errorArea").html(i18n["WOKAUT0001E"]);
+ $("#errorArea").show();
}
var userNameBox = $('#username');
@@ -82,13 +87,13 @@ wok.login_main = function() {
window.location.replace(window.location.pathname.replace(/\/+login.html/, '') + next_url);
}, function(jqXHR, textStatus, errorThrown) {
if (jqXHR.responseText == "") {
- $("#messUserPass").hide();
- $("#missServer").show();
- } else {
- $("#missServer").hide();
- $("#messUserPass").show();
+ $("#errorArea").html(i18n["WOKAUT0002E"]);
+ $("#errorArea").show();
+ } else if ((jqXHR.responseJSON != undefined) &&
+ ! (jqXHR.responseJSON["reason"] == undefined)) {
+ $("#errorArea").html(jqXHR.responseJSON["reason"]);
+ $("#errorArea").show();
}
- $("#messSession").hide();
$("#logging").hide();
$("#login").show();
});
diff --git a/ui/pages/i18n.json.tmpl b/ui/pages/i18n.json.tmpl
index ba29532..4329ad0 100644
--- a/ui/pages/i18n.json.tmpl
+++ b/ui/pages/i18n.json.tmpl
@@ -1,7 +1,7 @@
#*
* Project Wok
*
- * Copyright IBM Corp, 2014-2016
+ * Copyright IBM Corp, 2014-2017
*
* Code derived from Project Kimchi
*
@@ -39,6 +39,9 @@
"WOKHOST6001M": "$_("Max:")",
+ "WOKAUT0001E": "$_("Session timeout, please re-login.")",
+ "WOKAUT0002E": "$_("Server unreachable")",
+
"WOKSETT0001M": "$_("Application")",
"WOKSETT0002M": "$_("User")",
"WOKSETT0003M": "$_("Request")",
diff --git a/ui/pages/login.html.tmpl b/ui/pages/login.html.tmpl
index f5a4b2d..6f967cf 100644
--- a/ui/pages/login.html.tmpl
+++ b/ui/pages/login.html.tmpl
@@ -1,7 +1,7 @@
#*
* Project Wok
*
- * Copyright IBM Corp, 2014-2016
+ * Copyright IBM Corp, 2014-2017
*
* Code derived from Project Kimchi
*
@@ -104,9 +104,7 @@
<div class="container">
<div id="login-window" class="login-area row">
<div class="err-area">
- <div id="messUserPass" class="alert alert-danger" style="display: none;">$_("The username or password you entered is incorrect. Please try again.")</div>
- <div id="messSession" class="alert alert-danger" style="display: none;">$_("Session timeout, please re-login.")</div>
- <div id="missServer" class="alert alert-danger" style="display: none;">$_("Server unreachable.")</div>
+ <div id="errorArea" class="alert alert-danger" style="display: none;"></div>
</div>
<form id="form-login" class="form-horizontal" method="post">
<div class="form-group">
--
2.10.1 (Apple Git-78)
_______________________________________________
Kimchi-devel mailing list
Kimchi-devel@ovirt.org
http://lists.ovirt.org/mailman/listinfo/kimchi-devel
Also, when I enter invalid credentials, I got the error: Authentication failed for user '%(username)s'. [Error code: %(code)s] I know it is correct, but it seems not a friendly message. Maybe we could log that message (to use when debugging) and show on UI: The username or password you entered is incorrect. Please try again. (the same message we had before this patch) On 02/03/2017 03:34 PM, ramonn@linux.vnet.ibm.com wrote: > From: Ramon Medeiros <ramonn@linux.vnet.ibm.com> > > To prevent brute force attack, creates a mechanism to allow 3 tries > first. After that, a timeout will start and will be added 30 seconds for > each failed try in a row. > > Signed-off-by: Ramon Medeiros <ramonn@linux.vnet.ibm.com> > --- > Changes: > > v4: > Use API.json for input validation > > v3: > Improve error handling on login page > > v2: > Set timeout by user, ip and session id. This will avoid trouble with > users using the same ip, like NAT. > > src/wok/API.json | 25 +++++++++++++++++- > src/wok/i18n.py | 5 +++- > src/wok/root.py | 69 +++++++++++++++++++++++++++++++++++++++++------- > ui/js/src/wok.login.js | 19 ++++++++----- > ui/pages/i18n.json.tmpl | 5 +++- > ui/pages/login.html.tmpl | 6 ++--- > 6 files changed, 106 insertions(+), 23 deletions(-) > > diff --git a/src/wok/API.json b/src/wok/API.json > index 8965db9..3f7bfd7 100644 > --- a/src/wok/API.json > +++ b/src/wok/API.json > @@ -2,5 +2,28 @@ > "$schema": "http://json-schema.org/draft-03/schema#", > "title": "Wok API", > "description": "Json schema for Wok API", > - "type": "object" > + "type": "object", > + "properties": { > + "wokroot_login": { > + "type": "object", > + "properties": { > + "username": { > + "description": "Username", > + "required": true, > + "type": "string", > + "minLength": 1, > + "error": "WOKAUTH0003E" > + }, > + "password": { > + "description": "Password", > + "required": true, > + "type": "string", > + "minLength": 1, > + "error": "WOKAUTH0006E" > + } > + }, > + "additionalProperties": false, > + "error": "WOKAUTH0007E" > + } > + } > } > diff --git a/src/wok/i18n.py b/src/wok/i18n.py > index 935c9c1..5ad5e57 100644 > --- a/src/wok/i18n.py > +++ b/src/wok/i18n.py > @@ -40,8 +40,11 @@ messages = { > > "WOKAUTH0001E": _("Authentication failed for user '%(username)s'. [Error code: %(code)s]"), > "WOKAUTH0002E": _("You are not authorized to access Wok. Please, login first."), > - "WOKAUTH0003E": _("Specify %(item)s to login into Wok."), > + "WOKAUTH0003E": _("Specify username to login into Wok."), > + "WOKAUTH0004E": _("You have failed to login in too much attempts. Please, wait for %(seconds)s seconds to try again."), > "WOKAUTH0005E": _("Invalid LDAP configuration: %(item)s : %(value)s"), > + "WOKAUTH0006E": _("Specify password to login into Wok."), > + "WOKAUTH0007E": _("You need to specify username and password to login into Wok."), > > "WOKLOG0001E": _("Invalid filter parameter. Filter parameters allowed: %(filters)s"), > "WOKLOG0002E": _("Creation of log file failed: %(err)s"), > diff --git a/src/wok/root.py b/src/wok/root.py > index 080b7f0..9f6b7b3 100644 > --- a/src/wok/root.py > +++ b/src/wok/root.py > @@ -1,7 +1,7 @@ > # > # Project Wok > # > -# Copyright IBM Corp, 2015-2016 > +# Copyright IBM Corp, 2015-2017 > # > # Code derived from Project Kimchi > # > @@ -21,7 +21,9 @@ > > import cherrypy > import json > +import re > import os > +import time > from distutils.version import LooseVersion > > from wok import auth > @@ -30,8 +32,8 @@ from wok.i18n import messages > from wok.config import paths as wok_paths > from wok.control import sub_nodes > from wok.control.base import Resource > -from wok.control.utils import parse_request > -from wok.exception import MissingParameter > +from wok.control.utils import parse_request, validate_params > +from wok.exception import UnauthorizedError, WokException > from wok.reqlogger import log_request > > > @@ -48,7 +50,8 @@ class Root(Resource): > super(Root, self).__init__(model) > self._handled_error = ['error_page.400', 'error_page.404', > 'error_page.405', 'error_page.406', > - 'error_page.415', 'error_page.500'] > + 'error_page.415', 'error_page.500', > + 'error_page.403', 'error_page.401'] > > if not dev_env: > self._cp_config = dict([(key, self.error_production_handler) > @@ -146,6 +149,7 @@ class WokRoot(Root): > self.domain = 'wok' > self.messages = messages > self.extends = None > + self.failed_logins = {} > > # set user log messages and make sure all parameters are present > self.log_map = ROOT_REQUESTS > @@ -153,24 +157,71 @@ class WokRoot(Root): > > @cherrypy.expose > def login(self, *args): > + def _raise_timeout(user_id): > + length = self.failed_logins[user_ip_sid]["count"] > + timeout = (length - 3) * 30 > + details = e = UnauthorizedError("WOKAUTH0004E", > + {"seconds": timeout}) > + log_request(code, params, details, method, 403) > + raise cherrypy.HTTPError(403, e.message) > details = None > method = 'POST' > code = self.getRequestMessage(method, 'login') > > try: > params = parse_request() > + validate_params(params, self, "login") > username = params['username'] > password = params['password'] > - except KeyError, item: > - details = e = MissingParameter('WOKAUTH0003E', {'item': str(item)}) > - log_request(code, params, details, method, 400) > - raise cherrypy.HTTPError(400, e.message) > + except WokException, e: > + details = e > + status = e.getHttpStatusCode() > + raise cherrypy.HTTPError(status, e.message) > + > + # get authentication info > + remote_ip = cherrypy.request.remote.ip > + session_id = str(cherrypy.session.originalid) > + user_ip_sid = re.escape(username + remote_ip + session_id) > + > + # check for repetly > + count = self.failed_logins.get(user_ip_sid, {"count": 0}).get("count") > + if count > 3: > + > + # verify if timeout is still valid > + last_try = self.failed_logins[user_ip_sid]["time"] > + if time.time() < (last_try + ((count - 3) * 30)): > + _raise_timeout(user_ip_sid) > + else: > + self.failed_logins.pop(user_ip_sid) > > try: > status = 200 > user_info = auth.login(username, password) > + > + # user logged sucessfuly: reset counters > + if self.failed_logins.get(user_ip_sid) != None: > + self.failed_logins.pop(user_ip_sid) > except cherrypy.HTTPError, e: > - status = e.status > + > + # store time and prevent too much tries > + if self.failed_logins.get(user_ip_sid) == None: > + self.failed_logins[user_ip_sid] = {"time": time.time(), > + "ip": remote_ip, > + "session_id": session_id, > + "username": username, > + "count": 1} > + else: > + # tries take more than 30 seconds between each one: do not > + # increase count > + if (time.time() - > + self.failed_logins[user_ip_sid]["time"]) < 30: > + > + self.failed_logins[user_ip_sid]["time"] = time.time() > + self.failed_logins[user_ip_sid]["count"] += 1 > + > + # more than 3 fails: raise error > + if self.failed_logins[user_ip_sid]["count"] > 3: > + _raise_timeout(user_ip_sid) > raise > finally: > log_request(code, params, details, method, status) > diff --git a/ui/js/src/wok.login.js b/ui/js/src/wok.login.js > index 666a339..9e2a392 100644 > --- a/ui/js/src/wok.login.js > +++ b/ui/js/src/wok.login.js > @@ -19,6 +19,10 @@ > */ > wok.login_main = function() { > "use strict"; > + var i18n; > + wok.getI18n(function(i18nObj){ > + i18n = i18nObj; > + }, false, "i18n.json", true); > > // verify if language is available > var selectedLanguage = wok.lang.get(); > @@ -50,7 +54,8 @@ wok.login_main = function() { > var query = window.location.search; > var error = /.*error=(.*?)(&|$)/g.exec(query); > if (error && error[1] === "sessionTimeout") { > - $("#messSession").show(); > + $("#errorArea").html(i18n["WOKAUT0001E"]); > + $("#errorArea").show(); > } > > var userNameBox = $('#username'); > @@ -82,13 +87,13 @@ wok.login_main = function() { > window.location.replace(window.location.pathname.replace(/\/+login.html/, '') + next_url); > }, function(jqXHR, textStatus, errorThrown) { > if (jqXHR.responseText == "") { > - $("#messUserPass").hide(); > - $("#missServer").show(); > - } else { > - $("#missServer").hide(); > - $("#messUserPass").show(); > + $("#errorArea").html(i18n["WOKAUT0002E"]); > + $("#errorArea").show(); > + } else if ((jqXHR.responseJSON != undefined) && > + ! (jqXHR.responseJSON["reason"] == undefined)) { > + $("#errorArea").html(jqXHR.responseJSON["reason"]); > + $("#errorArea").show(); > } > - $("#messSession").hide(); > $("#logging").hide(); > $("#login").show(); > }); > diff --git a/ui/pages/i18n.json.tmpl b/ui/pages/i18n.json.tmpl > index ba29532..4329ad0 100644 > --- a/ui/pages/i18n.json.tmpl > +++ b/ui/pages/i18n.json.tmpl > @@ -1,7 +1,7 @@ > #* > * Project Wok > * > - * Copyright IBM Corp, 2014-2016 > + * Copyright IBM Corp, 2014-2017 > * > * Code derived from Project Kimchi > * > @@ -39,6 +39,9 @@ > > "WOKHOST6001M": "$_("Max:")", > > + "WOKAUT0001E": "$_("Session timeout, please re-login.")", > + "WOKAUT0002E": "$_("Server unreachable")", > + > "WOKSETT0001M": "$_("Application")", > "WOKSETT0002M": "$_("User")", > "WOKSETT0003M": "$_("Request")", > diff --git a/ui/pages/login.html.tmpl b/ui/pages/login.html.tmpl > index f5a4b2d..6f967cf 100644 > --- a/ui/pages/login.html.tmpl > +++ b/ui/pages/login.html.tmpl > @@ -1,7 +1,7 @@ > #* > * Project Wok > * > - * Copyright IBM Corp, 2014-2016 > + * Copyright IBM Corp, 2014-2017 > * > * Code derived from Project Kimchi > * > @@ -104,9 +104,7 @@ > <div class="container"> > <div id="login-window" class="login-area row"> > <div class="err-area"> > - <div id="messUserPass" class="alert alert-danger" style="display: none;">$_("The username or password you entered is incorrect. Please try again.")</div> > - <div id="messSession" class="alert alert-danger" style="display: none;">$_("Session timeout, please re-login.")</div> > - <div id="missServer" class="alert alert-danger" style="display: none;">$_("Server unreachable.")</div> > + <div id="errorArea" class="alert alert-danger" style="display: none;"></div> > </div> > <form id="form-login" class="form-horizontal" method="post"> > <div class="form-group"> _______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel
Ramon, There is a test failing: ====================================================================== FAIL: test_user_log (test_api.APITests) ---------------------------------------------------------------------- Traceback (most recent call last): File "test_api.py", line 73, in test_user_log self.assertIn('records', conf) AssertionError: 'records' not found in {u'reason': u'The server encountered an unexpected condition which prevented it from fulfilling the request.', u'code': u'500 Internal Server Error', u'call_stack': u'Traceback (most recent call last):\n File "/usr/lib/python2.7/site-packages/cherrypy/_cprequest.py", line 670, in respond\n response.body = self.handler()\n File "/usr/lib/python2.7/site-packages/cherrypy/lib/encoding.py", line 217, in __call__\n self.body = self.oldhandler(*args, **kwargs)\n File "/usr/lib/python2.7/site-packages/cherrypy/_cpdispatch.py", line 61, in __call__\n return self.callable(*self.args, **self.kwargs)\n File "/home/alinefm/wok/src/wok/control/base.py", line 438, in index\n return self.get(params)\n File "/home/alinefm/wok/src/wok/control/logs.py", line 38, in get\n res_list = get_list(filter_params)\n File "/home/alinefm/wok/src/wok/model/logs.py", line 29, in get_list\n return RequestParser().getFilteredRecords(filter_params)\n File "/home/alinefm/wok/src/wok/reqlogger.py", line 264, in getFilteredRecords\n records = self.getRecords()\n File "/home/alinefm/wok/src/wok/reqlogger.py", line 206, in getRecords\n text = self.getTranslatedMessage(message, error, uri)\n File "/home/alinefm/wok/src/wok/reqlogger.py", line 175, in getTranslatedMessage\n text = msg.get_text(prepend_code=False, translate=True)\n File "/home/alinefm/wok/src/wok/message.py", line 89, in get_text\n msg = decode_value(msg) % self.args\nKeyError: u\'username\'\n'} On 02/03/2017 03:34 PM, ramonn@linux.vnet.ibm.com wrote: > From: Ramon Medeiros <ramonn@linux.vnet.ibm.com> > > To prevent brute force attack, creates a mechanism to allow 3 tries > first. After that, a timeout will start and will be added 30 seconds for > each failed try in a row. > > Signed-off-by: Ramon Medeiros <ramonn@linux.vnet.ibm.com> > --- > Changes: > > v4: > Use API.json for input validation > > v3: > Improve error handling on login page > > v2: > Set timeout by user, ip and session id. This will avoid trouble with > users using the same ip, like NAT. > > src/wok/API.json | 25 +++++++++++++++++- > src/wok/i18n.py | 5 +++- > src/wok/root.py | 69 +++++++++++++++++++++++++++++++++++++++++------- > ui/js/src/wok.login.js | 19 ++++++++----- > ui/pages/i18n.json.tmpl | 5 +++- > ui/pages/login.html.tmpl | 6 ++--- > 6 files changed, 106 insertions(+), 23 deletions(-) > > diff --git a/src/wok/API.json b/src/wok/API.json > index 8965db9..3f7bfd7 100644 > --- a/src/wok/API.json > +++ b/src/wok/API.json > @@ -2,5 +2,28 @@ > "$schema": "http://json-schema.org/draft-03/schema#", > "title": "Wok API", > "description": "Json schema for Wok API", > - "type": "object" > + "type": "object", > + "properties": { > + "wokroot_login": { > + "type": "object", > + "properties": { > + "username": { > + "description": "Username", > + "required": true, > + "type": "string", > + "minLength": 1, > + "error": "WOKAUTH0003E" > + }, > + "password": { > + "description": "Password", > + "required": true, > + "type": "string", > + "minLength": 1, > + "error": "WOKAUTH0006E" > + } > + }, > + "additionalProperties": false, > + "error": "WOKAUTH0007E" > + } > + } > } > diff --git a/src/wok/i18n.py b/src/wok/i18n.py > index 935c9c1..5ad5e57 100644 > --- a/src/wok/i18n.py > +++ b/src/wok/i18n.py > @@ -40,8 +40,11 @@ messages = { > > "WOKAUTH0001E": _("Authentication failed for user '%(username)s'. [Error code: %(code)s]"), > "WOKAUTH0002E": _("You are not authorized to access Wok. Please, login first."), > - "WOKAUTH0003E": _("Specify %(item)s to login into Wok."), > + "WOKAUTH0003E": _("Specify username to login into Wok."), > + "WOKAUTH0004E": _("You have failed to login in too much attempts. Please, wait for %(seconds)s seconds to try again."), > "WOKAUTH0005E": _("Invalid LDAP configuration: %(item)s : %(value)s"), > + "WOKAUTH0006E": _("Specify password to login into Wok."), > + "WOKAUTH0007E": _("You need to specify username and password to login into Wok."), > > "WOKLOG0001E": _("Invalid filter parameter. Filter parameters allowed: %(filters)s"), > "WOKLOG0002E": _("Creation of log file failed: %(err)s"), > diff --git a/src/wok/root.py b/src/wok/root.py > index 080b7f0..9f6b7b3 100644 > --- a/src/wok/root.py > +++ b/src/wok/root.py > @@ -1,7 +1,7 @@ > # > # Project Wok > # > -# Copyright IBM Corp, 2015-2016 > +# Copyright IBM Corp, 2015-2017 > # > # Code derived from Project Kimchi > # > @@ -21,7 +21,9 @@ > > import cherrypy > import json > +import re > import os > +import time > from distutils.version import LooseVersion > > from wok import auth > @@ -30,8 +32,8 @@ from wok.i18n import messages > from wok.config import paths as wok_paths > from wok.control import sub_nodes > from wok.control.base import Resource > -from wok.control.utils import parse_request > -from wok.exception import MissingParameter > +from wok.control.utils import parse_request, validate_params > +from wok.exception import UnauthorizedError, WokException > from wok.reqlogger import log_request > > > @@ -48,7 +50,8 @@ class Root(Resource): > super(Root, self).__init__(model) > self._handled_error = ['error_page.400', 'error_page.404', > 'error_page.405', 'error_page.406', > - 'error_page.415', 'error_page.500'] > + 'error_page.415', 'error_page.500', > + 'error_page.403', 'error_page.401'] > > if not dev_env: > self._cp_config = dict([(key, self.error_production_handler) > @@ -146,6 +149,7 @@ class WokRoot(Root): > self.domain = 'wok' > self.messages = messages > self.extends = None > + self.failed_logins = {} > > # set user log messages and make sure all parameters are present > self.log_map = ROOT_REQUESTS > @@ -153,24 +157,71 @@ class WokRoot(Root): > > @cherrypy.expose > def login(self, *args): > + def _raise_timeout(user_id): > + length = self.failed_logins[user_ip_sid]["count"] > + timeout = (length - 3) * 30 > + details = e = UnauthorizedError("WOKAUTH0004E", > + {"seconds": timeout}) > + log_request(code, params, details, method, 403) > + raise cherrypy.HTTPError(403, e.message) > details = None > method = 'POST' > code = self.getRequestMessage(method, 'login') > > try: > params = parse_request() > + validate_params(params, self, "login") > username = params['username'] > password = params['password'] > - except KeyError, item: > - details = e = MissingParameter('WOKAUTH0003E', {'item': str(item)}) > - log_request(code, params, details, method, 400) > - raise cherrypy.HTTPError(400, e.message) > + except WokException, e: > + details = e > + status = e.getHttpStatusCode() > + raise cherrypy.HTTPError(status, e.message) > + > + # get authentication info > + remote_ip = cherrypy.request.remote.ip > + session_id = str(cherrypy.session.originalid) > + user_ip_sid = re.escape(username + remote_ip + session_id) > + > + # check for repetly > + count = self.failed_logins.get(user_ip_sid, {"count": 0}).get("count") > + if count > 3: > + > + # verify if timeout is still valid > + last_try = self.failed_logins[user_ip_sid]["time"] > + if time.time() < (last_try + ((count - 3) * 30)): > + _raise_timeout(user_ip_sid) > + else: > + self.failed_logins.pop(user_ip_sid) > > try: > status = 200 > user_info = auth.login(username, password) > + > + # user logged sucessfuly: reset counters > + if self.failed_logins.get(user_ip_sid) != None: > + self.failed_logins.pop(user_ip_sid) > except cherrypy.HTTPError, e: > - status = e.status > + > + # store time and prevent too much tries > + if self.failed_logins.get(user_ip_sid) == None: > + self.failed_logins[user_ip_sid] = {"time": time.time(), > + "ip": remote_ip, > + "session_id": session_id, > + "username": username, > + "count": 1} > + else: > + # tries take more than 30 seconds between each one: do not > + # increase count > + if (time.time() - > + self.failed_logins[user_ip_sid]["time"]) < 30: > + > + self.failed_logins[user_ip_sid]["time"] = time.time() > + self.failed_logins[user_ip_sid]["count"] += 1 > + > + # more than 3 fails: raise error > + if self.failed_logins[user_ip_sid]["count"] > 3: > + _raise_timeout(user_ip_sid) > raise > finally: > log_request(code, params, details, method, status) > diff --git a/ui/js/src/wok.login.js b/ui/js/src/wok.login.js > index 666a339..9e2a392 100644 > --- a/ui/js/src/wok.login.js > +++ b/ui/js/src/wok.login.js > @@ -19,6 +19,10 @@ > */ > wok.login_main = function() { > "use strict"; > + var i18n; > + wok.getI18n(function(i18nObj){ > + i18n = i18nObj; > + }, false, "i18n.json", true); > > // verify if language is available > var selectedLanguage = wok.lang.get(); > @@ -50,7 +54,8 @@ wok.login_main = function() { > var query = window.location.search; > var error = /.*error=(.*?)(&|$)/g.exec(query); > if (error && error[1] === "sessionTimeout") { > - $("#messSession").show(); > + $("#errorArea").html(i18n["WOKAUT0001E"]); > + $("#errorArea").show(); > } > > var userNameBox = $('#username'); > @@ -82,13 +87,13 @@ wok.login_main = function() { > window.location.replace(window.location.pathname.replace(/\/+login.html/, '') + next_url); > }, function(jqXHR, textStatus, errorThrown) { > if (jqXHR.responseText == "") { > - $("#messUserPass").hide(); > - $("#missServer").show(); > - } else { > - $("#missServer").hide(); > - $("#messUserPass").show(); > + $("#errorArea").html(i18n["WOKAUT0002E"]); > + $("#errorArea").show(); > + } else if ((jqXHR.responseJSON != undefined) && > + ! (jqXHR.responseJSON["reason"] == undefined)) { > + $("#errorArea").html(jqXHR.responseJSON["reason"]); > + $("#errorArea").show(); > } > - $("#messSession").hide(); > $("#logging").hide(); > $("#login").show(); > }); > diff --git a/ui/pages/i18n.json.tmpl b/ui/pages/i18n.json.tmpl > index ba29532..4329ad0 100644 > --- a/ui/pages/i18n.json.tmpl > +++ b/ui/pages/i18n.json.tmpl > @@ -1,7 +1,7 @@ > #* > * Project Wok > * > - * Copyright IBM Corp, 2014-2016 > + * Copyright IBM Corp, 2014-2017 > * > * Code derived from Project Kimchi > * > @@ -39,6 +39,9 @@ > > "WOKHOST6001M": "$_("Max:")", > > + "WOKAUT0001E": "$_("Session timeout, please re-login.")", > + "WOKAUT0002E": "$_("Server unreachable")", > + > "WOKSETT0001M": "$_("Application")", > "WOKSETT0002M": "$_("User")", > "WOKSETT0003M": "$_("Request")", > diff --git a/ui/pages/login.html.tmpl b/ui/pages/login.html.tmpl > index f5a4b2d..6f967cf 100644 > --- a/ui/pages/login.html.tmpl > +++ b/ui/pages/login.html.tmpl > @@ -1,7 +1,7 @@ > #* > * Project Wok > * > - * Copyright IBM Corp, 2014-2016 > + * Copyright IBM Corp, 2014-2017 > * > * Code derived from Project Kimchi > * > @@ -104,9 +104,7 @@ > <div class="container"> > <div id="login-window" class="login-area row"> > <div class="err-area"> > - <div id="messUserPass" class="alert alert-danger" style="display: none;">$_("The username or password you entered is incorrect. Please try again.")</div> > - <div id="messSession" class="alert alert-danger" style="display: none;">$_("Session timeout, please re-login.")</div> > - <div id="missServer" class="alert alert-danger" style="display: none;">$_("Server unreachable.")</div> > + <div id="errorArea" class="alert alert-danger" style="display: none;"></div> > </div> > <form id="form-login" class="form-horizontal" method="post"> > <div class="form-group"> _______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel
Also, after logging, the user logs were not loaded and in the logs there are: 11; Fedora; Linux x86_64; rv:51.0) Gecko/20100101 Firefox/51.0" [03/Feb/2017:16:52:43] HTTP Request Headers: COOKIE: wok=1780a4b70362519eb0a809690949fef39712e6cb; username=admin; user_role=admin; lastPage="/#/tabs/settings" Remote-Addr: 127.0.0.1 X-REAL-IP: 127.0.0.1 USER-AGENT: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:51.0) Gecko/20100101 Firefox/51.0 CONNECTION: close REFERER: https://localhost:8001/ X-REQUESTED-WITH: XMLHttpRequest DNT: 1 HOST: localhost ACCEPT: application/json, text/javascript, */*; q=0.01 ACCEPT-LANGUAGE: en-US,en;q=0.5 X-FORWARDED-FOR: 127.0.0.1 Content-Type: application/json ACCEPT-ENCODING: gzip, deflate, br INFO:cherrypy.error.140313708401360:[03/Feb/2017:16:52:43] HTTP Request Headers: COOKIE: wok=1780a4b70362519eb0a809690949fef39712e6cb; username=admin; user_role=admin; lastPage="/#/tabs/settings" Remote-Addr: 127.0.0.1 X-REAL-IP: 127.0.0.1 USER-AGENT: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:51.0) Gecko/20100101 Firefox/51.0 CONNECTION: close REFERER: https://localhost:8001/ X-REQUESTED-WITH: XMLHttpRequest DNT: 1 HOST: localhost ACCEPT: application/json, text/javascript, */*; q=0.01 ACCEPT-LANGUAGE: en-US,en;q=0.5 X-FORWARDED-FOR: 127.0.0.1 Content-Type: application/json ACCEPT-ENCODING: gzip, deflate, br [03/Feb/2017:16:52:43] HTTP Traceback (most recent call last): File "/usr/lib/python2.7/site-packages/cherrypy/_cprequest.py", line 670, in respond response.body = self.handler() File "/usr/lib/python2.7/site-packages/cherrypy/lib/encoding.py", line 217, in __call__ self.body = self.oldhandler(*args, **kwargs) File "/usr/lib/python2.7/site-packages/cherrypy/_cpdispatch.py", line 61, in __call__ return self.callable(*self.args, **self.kwargs) File "/home/alinefm/wok/src/wok/control/base.py", line 438, in index return self.get(params) File "/home/alinefm/wok/src/wok/control/logs.py", line 38, in get res_list = get_list(filter_params) File "/home/alinefm/wok/src/wok/model/logs.py", line 31, in get_list return RequestParser().getRecords() File "/home/alinefm/wok/src/wok/reqlogger.py", line 206, in getRecords text = self.getTranslatedMessage(message, error, uri) File "/home/alinefm/wok/src/wok/reqlogger.py", line 175, in getTranslatedMessage text = msg.get_text(prepend_code=False, translate=True) File "/home/alinefm/wok/src/wok/message.py", line 89, in get_text msg = decode_value(msg) % self.args KeyError: u'username' ERROR:cherrypy.error.140313708401360:[03/Feb/2017:16:52:43] HTTP Traceback (most recent call last): File "/usr/lib/python2.7/site-packages/cherrypy/_cprequest.py", line 670, in respond response.body = self.handler() File "/usr/lib/python2.7/site-packages/cherrypy/lib/encoding.py", line 217, in __call__ self.body = self.oldhandler(*args, **kwargs) File "/usr/lib/python2.7/site-packages/cherrypy/_cpdispatch.py", line 61, in __call__ return self.callable(*self.args, **self.kwargs) File "/home/alinefm/wok/src/wok/control/base.py", line 438, in index return self.get(params) File "/home/alinefm/wok/src/wok/control/logs.py", line 38, in get res_list = get_list(filter_params) File "/home/alinefm/wok/src/wok/model/logs.py", line 31, in get_list return RequestParser().getRecords() File "/home/alinefm/wok/src/wok/reqlogger.py", line 206, in getRecords text = self.getTranslatedMessage(message, error, uri) File "/home/alinefm/wok/src/wok/reqlogger.py", line 175, in getTranslatedMessage text = msg.get_text(prepend_code=False, translate=True) File "/home/alinefm/wok/src/wok/message.py", line 89, in get_text msg = decode_value(msg) % self.args KeyError: u'username' On 02/03/2017 04:44 PM, Aline Manera wrote: > Ramon, > > There is a test failing: > > ====================================================================== > FAIL: test_user_log (test_api.APITests) > ---------------------------------------------------------------------- > Traceback (most recent call last): > File "test_api.py", line 73, in test_user_log > self.assertIn('records', conf) > AssertionError: 'records' not found in {u'reason': u'The server > encountered an unexpected condition which prevented it from fulfilling > the request.', u'code': u'500 Internal Server Error', u'call_stack': > u'Traceback (most recent call last):\n File > "/usr/lib/python2.7/site-packages/cherrypy/_cprequest.py", line 670, > in respond\n response.body = self.handler()\n File > "/usr/lib/python2.7/site-packages/cherrypy/lib/encoding.py", line 217, > in __call__\n self.body = self.oldhandler(*args, **kwargs)\n File > "/usr/lib/python2.7/site-packages/cherrypy/_cpdispatch.py", line 61, > in __call__\n return self.callable(*self.args, **self.kwargs)\n > File "/home/alinefm/wok/src/wok/control/base.py", line 438, in > index\n return self.get(params)\n File > "/home/alinefm/wok/src/wok/control/logs.py", line 38, in get\n > res_list = get_list(filter_params)\n File > "/home/alinefm/wok/src/wok/model/logs.py", line 29, in get_list\n > return RequestParser().getFilteredRecords(filter_params)\n File > "/home/alinefm/wok/src/wok/reqlogger.py", line 264, in > getFilteredRecords\n records = self.getRecords()\n File > "/home/alinefm/wok/src/wok/reqlogger.py", line 206, in getRecords\n > text = self.getTranslatedMessage(message, error, uri)\n File > "/home/alinefm/wok/src/wok/reqlogger.py", line 175, in > getTranslatedMessage\n text = msg.get_text(prepend_code=False, > translate=True)\n File "/home/alinefm/wok/src/wok/message.py", line > 89, in get_text\n msg = decode_value(msg) % self.args\nKeyError: > u\'username\'\n'} > > > > On 02/03/2017 03:34 PM, ramonn@linux.vnet.ibm.com wrote: >> From: Ramon Medeiros <ramonn@linux.vnet.ibm.com> >> >> To prevent brute force attack, creates a mechanism to allow 3 tries >> first. After that, a timeout will start and will be added 30 seconds for >> each failed try in a row. >> >> Signed-off-by: Ramon Medeiros <ramonn@linux.vnet.ibm.com> >> --- >> Changes: >> >> v4: >> Use API.json for input validation >> >> v3: >> Improve error handling on login page >> >> v2: >> Set timeout by user, ip and session id. This will avoid trouble with >> users using the same ip, like NAT. >> >> src/wok/API.json | 25 +++++++++++++++++- >> src/wok/i18n.py | 5 +++- >> src/wok/root.py | 69 >> +++++++++++++++++++++++++++++++++++++++++------- >> ui/js/src/wok.login.js | 19 ++++++++----- >> ui/pages/i18n.json.tmpl | 5 +++- >> ui/pages/login.html.tmpl | 6 ++--- >> 6 files changed, 106 insertions(+), 23 deletions(-) >> >> diff --git a/src/wok/API.json b/src/wok/API.json >> index 8965db9..3f7bfd7 100644 >> --- a/src/wok/API.json >> +++ b/src/wok/API.json >> @@ -2,5 +2,28 @@ >> "$schema": "http://json-schema.org/draft-03/schema#", >> "title": "Wok API", >> "description": "Json schema for Wok API", >> - "type": "object" >> + "type": "object", >> + "properties": { >> + "wokroot_login": { >> + "type": "object", >> + "properties": { >> + "username": { >> + "description": "Username", >> + "required": true, >> + "type": "string", >> + "minLength": 1, >> + "error": "WOKAUTH0003E" >> + }, >> + "password": { >> + "description": "Password", >> + "required": true, >> + "type": "string", >> + "minLength": 1, >> + "error": "WOKAUTH0006E" >> + } >> + }, >> + "additionalProperties": false, >> + "error": "WOKAUTH0007E" >> + } >> + } >> } >> diff --git a/src/wok/i18n.py b/src/wok/i18n.py >> index 935c9c1..5ad5e57 100644 >> --- a/src/wok/i18n.py >> +++ b/src/wok/i18n.py >> @@ -40,8 +40,11 @@ messages = { >> >> "WOKAUTH0001E": _("Authentication failed for user >> '%(username)s'. [Error code: %(code)s]"), >> "WOKAUTH0002E": _("You are not authorized to access Wok. >> Please, login first."), >> - "WOKAUTH0003E": _("Specify %(item)s to login into Wok."), >> + "WOKAUTH0003E": _("Specify username to login into Wok."), >> + "WOKAUTH0004E": _("You have failed to login in too much >> attempts. Please, wait for %(seconds)s seconds to try again."), >> "WOKAUTH0005E": _("Invalid LDAP configuration: %(item)s : >> %(value)s"), >> + "WOKAUTH0006E": _("Specify password to login into Wok."), >> + "WOKAUTH0007E": _("You need to specify username and password to >> login into Wok."), >> >> "WOKLOG0001E": _("Invalid filter parameter. Filter parameters >> allowed: %(filters)s"), >> "WOKLOG0002E": _("Creation of log file failed: %(err)s"), >> diff --git a/src/wok/root.py b/src/wok/root.py >> index 080b7f0..9f6b7b3 100644 >> --- a/src/wok/root.py >> +++ b/src/wok/root.py >> @@ -1,7 +1,7 @@ >> # >> # Project Wok >> # >> -# Copyright IBM Corp, 2015-2016 >> +# Copyright IBM Corp, 2015-2017 >> # >> # Code derived from Project Kimchi >> # >> @@ -21,7 +21,9 @@ >> >> import cherrypy >> import json >> +import re >> import os >> +import time >> from distutils.version import LooseVersion >> >> from wok import auth >> @@ -30,8 +32,8 @@ from wok.i18n import messages >> from wok.config import paths as wok_paths >> from wok.control import sub_nodes >> from wok.control.base import Resource >> -from wok.control.utils import parse_request >> -from wok.exception import MissingParameter >> +from wok.control.utils import parse_request, validate_params >> +from wok.exception import UnauthorizedError, WokException >> from wok.reqlogger import log_request >> >> >> @@ -48,7 +50,8 @@ class Root(Resource): >> super(Root, self).__init__(model) >> self._handled_error = ['error_page.400', 'error_page.404', >> 'error_page.405', 'error_page.406', >> - 'error_page.415', 'error_page.500'] >> + 'error_page.415', 'error_page.500', >> + 'error_page.403', 'error_page.401'] >> >> if not dev_env: >> self._cp_config = dict([(key, >> self.error_production_handler) >> @@ -146,6 +149,7 @@ class WokRoot(Root): >> self.domain = 'wok' >> self.messages = messages >> self.extends = None >> + self.failed_logins = {} >> >> # set user log messages and make sure all parameters are >> present >> self.log_map = ROOT_REQUESTS >> @@ -153,24 +157,71 @@ class WokRoot(Root): >> >> @cherrypy.expose >> def login(self, *args): >> + def _raise_timeout(user_id): >> + length = self.failed_logins[user_ip_sid]["count"] >> + timeout = (length - 3) * 30 >> + details = e = UnauthorizedError("WOKAUTH0004E", >> + {"seconds": timeout}) >> + log_request(code, params, details, method, 403) >> + raise cherrypy.HTTPError(403, e.message) >> details = None >> method = 'POST' >> code = self.getRequestMessage(method, 'login') >> >> try: >> params = parse_request() >> + validate_params(params, self, "login") >> username = params['username'] >> password = params['password'] >> - except KeyError, item: >> - details = e = MissingParameter('WOKAUTH0003E', {'item': >> str(item)}) >> - log_request(code, params, details, method, 400) >> - raise cherrypy.HTTPError(400, e.message) >> + except WokException, e: >> + details = e >> + status = e.getHttpStatusCode() >> + raise cherrypy.HTTPError(status, e.message) >> + >> + # get authentication info >> + remote_ip = cherrypy.request.remote.ip >> + session_id = str(cherrypy.session.originalid) >> + user_ip_sid = re.escape(username + remote_ip + session_id) >> + >> + # check for repetly >> + count = self.failed_logins.get(user_ip_sid, {"count": >> 0}).get("count") >> + if count > 3: >> + >> + # verify if timeout is still valid >> + last_try = self.failed_logins[user_ip_sid]["time"] >> + if time.time() < (last_try + ((count - 3) * 30)): >> + _raise_timeout(user_ip_sid) >> + else: >> + self.failed_logins.pop(user_ip_sid) >> >> try: >> status = 200 >> user_info = auth.login(username, password) >> + >> + # user logged sucessfuly: reset counters >> + if self.failed_logins.get(user_ip_sid) != None: >> + self.failed_logins.pop(user_ip_sid) >> except cherrypy.HTTPError, e: >> - status = e.status >> + >> + # store time and prevent too much tries >> + if self.failed_logins.get(user_ip_sid) == None: >> + self.failed_logins[user_ip_sid] = {"time": time.time(), >> + "ip": remote_ip, >> + "session_id": session_id, >> + "username": >> username, >> + "count": 1} >> + else: >> + # tries take more than 30 seconds between each one: >> do not >> + # increase count >> + if (time.time() - >> + self.failed_logins[user_ip_sid]["time"]) < 30: >> + >> + self.failed_logins[user_ip_sid]["time"] = >> time.time() >> + self.failed_logins[user_ip_sid]["count"] += 1 >> + >> + # more than 3 fails: raise error >> + if self.failed_logins[user_ip_sid]["count"] > 3: >> + _raise_timeout(user_ip_sid) >> raise >> finally: >> log_request(code, params, details, method, status) >> diff --git a/ui/js/src/wok.login.js b/ui/js/src/wok.login.js >> index 666a339..9e2a392 100644 >> --- a/ui/js/src/wok.login.js >> +++ b/ui/js/src/wok.login.js >> @@ -19,6 +19,10 @@ >> */ >> wok.login_main = function() { >> "use strict"; >> + var i18n; >> + wok.getI18n(function(i18nObj){ >> + i18n = i18nObj; >> + }, false, "i18n.json", true); >> >> // verify if language is available >> var selectedLanguage = wok.lang.get(); >> @@ -50,7 +54,8 @@ wok.login_main = function() { >> var query = window.location.search; >> var error = /.*error=(.*?)(&|$)/g.exec(query); >> if (error && error[1] === "sessionTimeout") { >> - $("#messSession").show(); >> + $("#errorArea").html(i18n["WOKAUT0001E"]); >> + $("#errorArea").show(); >> } >> >> var userNameBox = $('#username'); >> @@ -82,13 +87,13 @@ wok.login_main = function() { >> window.location.replace(window.location.pathname.replace(/\/+login.html/, >> '') + next_url); >> }, function(jqXHR, textStatus, errorThrown) { >> if (jqXHR.responseText == "") { >> - $("#messUserPass").hide(); >> - $("#missServer").show(); >> - } else { >> - $("#missServer").hide(); >> - $("#messUserPass").show(); >> + $("#errorArea").html(i18n["WOKAUT0002E"]); >> + $("#errorArea").show(); >> + } else if ((jqXHR.responseJSON != undefined) && >> + ! (jqXHR.responseJSON["reason"] == undefined)) { >> + $("#errorArea").html(jqXHR.responseJSON["reason"]); >> + $("#errorArea").show(); >> } >> - $("#messSession").hide(); >> $("#logging").hide(); >> $("#login").show(); >> }); >> diff --git a/ui/pages/i18n.json.tmpl b/ui/pages/i18n.json.tmpl >> index ba29532..4329ad0 100644 >> --- a/ui/pages/i18n.json.tmpl >> +++ b/ui/pages/i18n.json.tmpl >> @@ -1,7 +1,7 @@ >> #* >> * Project Wok >> * >> - * Copyright IBM Corp, 2014-2016 >> + * Copyright IBM Corp, 2014-2017 >> * >> * Code derived from Project Kimchi >> * >> @@ -39,6 +39,9 @@ >> >> "WOKHOST6001M": "$_("Max:")", >> >> + "WOKAUT0001E": "$_("Session timeout, please re-login.")", >> + "WOKAUT0002E": "$_("Server unreachable")", >> + >> "WOKSETT0001M": "$_("Application")", >> "WOKSETT0002M": "$_("User")", >> "WOKSETT0003M": "$_("Request")", >> diff --git a/ui/pages/login.html.tmpl b/ui/pages/login.html.tmpl >> index f5a4b2d..6f967cf 100644 >> --- a/ui/pages/login.html.tmpl >> +++ b/ui/pages/login.html.tmpl >> @@ -1,7 +1,7 @@ >> #* >> * Project Wok >> * >> - * Copyright IBM Corp, 2014-2016 >> + * Copyright IBM Corp, 2014-2017 >> * >> * Code derived from Project Kimchi >> * >> @@ -104,9 +104,7 @@ >> <div class="container"> >> <div id="login-window" class="login-area row"> >> <div class="err-area"> >> - <div id="messUserPass" class="alert >> alert-danger" style="display: none;">$_("The username or password you >> entered is incorrect. Please try again.")</div> >> - <div id="messSession" class="alert alert-danger" >> style="display: none;">$_("Session timeout, please re-login.")</div> >> - <div id="missServer" class="alert alert-danger" >> style="display: none;">$_("Server unreachable.")</div> >> + <div id="errorArea" class="alert alert-danger" >> style="display: none;"></div> >> </div> >> <form id="form-login" class="form-horizontal" >> method="post"> >> <div class="form-group"> > > _______________________________________________ > Kimchi-devel mailing list > Kimchi-devel@ovirt.org > http://lists.ovirt.org/mailman/listinfo/kimchi-devel > _______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel
© 2016 - 2024 Red Hat, Inc.