chg: [login/diagnostic] Improved login errors feedback and adjusted

diagnostic
pull/135/head
mokaddem 2019-10-29 14:52:10 +01:00
parent d390a169b5
commit ce4aa82c0a
4 changed files with 135 additions and 17 deletions

View File

@ -6,7 +6,7 @@ import time
import signal
import functools
import configparser
from pprint import pprint
from urllib.parse import urlparse, parse_qs
import subprocess
import diagnostic_util
try:
@ -379,10 +379,22 @@ def check_server_listening(spinner):
r = requests.get(url)
except requests.exceptions.ConnectionError:
return (False, 'Can\'t connect to {}').format(url)
return (
r.status_code == 200,
'{} {}reached. Status code [{}]'.format(url, "not " if r.status_code != 200 else "", r.status_code)
)
if '/error_page' in r.url:
o = urlparse(r.url)
query = parse_qs(o.query)
error_code = query.get('error_code', '')
if error_code[0] == '1':
return (False, 'To many redirects. Server may not be properly configured\n\t➥ Try to correctly setup an HTTPS server or change the cookie policy in the configuration')
else:
error_message = query.get('error_message', '')[0]
return (False, 'Unkown error: {}\n{}'.format(error_code, error_message))
else:
return (
r.status_code == 200,
'{} {}reached. Status code [{}]'.format(url, "not " if r.status_code != 200 else "", r.status_code)
)
@add_spinner
@ -394,15 +406,46 @@ def check_server_dynamic_enpoint(spinner):
}
sleep_max = 15
start_time = time.time()
# Check MISP connectivity
url_misp = configuration_file.get("Auth", "misp_fqdn")
try:
r = requests.get(url_misp, verify=configuration_file.getboolean("Auth", "ssl_verify"))
except requests.exceptions.SSLError as e:
if 'CERTIFICATE_VERIFY_FAILED' in str(e):
return (False, 'SSL connection error certificate verify failed.\n\t➥ Review your configuration'.format(e))
else:
return (False, 'SSL connection error `{}`.\n\t➥ Review your configuration'.format(e))
except requests.exceptions.ConnectionError:
return (False, 'MISP `{}` cannot be reached.\n\t➥ Review your configuration'.format(url_misp))
url_login = '{}:{}/login'.format(HOST, PORT)
url = '{}:{}/_logs'.format(HOST, PORT)
session = requests.Session()
session.verify = False
session.verify = configuration_file.getboolean("Auth", "ssl_verify")
r_login = session.post(url_login, data=payload)
# Check if we ended up on the error page
if '/error_page' in r_login.url:
o = urlparse(r_login.url)
query = parse_qs(o.query)
error_code = query.get('error_code', '')
if error_code[0] == '2':
return (False, 'MISP cannot be reached for authentication\n\t➥ Review MISP fully qualified name and SSL settings')
else:
error_message = query.get('error_message', '')[0]
return (False, 'Unkown error: {}\n{}'.format(error_code, error_message))
# Recover error message from the url
if '/login' in r_login.url:
return_text = 'Invalid credential. Use valid credential to proceed.'
o = urlparse(r_login.url)
query = parse_qs(o.query)
error_message = query.get('auth_error_message', 'Unknown error')[0]
return_text = error_message
return (False, return_text)
# Connection seems to be successful, checking if we receive data from event-stream
r = session.get(url, stream=True, timeout=sleep_max, headers={'Accept': 'text/event-stream'})
return_flag = False
return_text = 'Dynamic endpoint returned data but not in the correct format.'

View File

@ -16,7 +16,7 @@ from time import sleep, strftime
import redis
import util
from flask import (Flask, Response, jsonify, render_template, request,
from flask import (Flask, Response, jsonify, render_template, request, make_response,
send_from_directory, stream_with_context, url_for, redirect)
from flask_login import (UserMixin, LoginManager, current_user, login_user, logout_user, login_required)
from helpers import (contributor_helper, geo_helper, live_helper,
@ -101,9 +101,11 @@ class User(UserMixin):
:return:
"""
post_data = {
"_method": "POST",
"data[_Token][key]": "",
"data[_Token][fields]": "",
"data[_Token][unlocked]": "",
"data[_Token][debug]": "",
"data[User][email]": self.id,
"data[User][password]": self.password,
}
@ -120,12 +122,17 @@ class User(UserMixin):
token_fields_exp = re.compile(r'name="data\[_Token]\[fields]" value="([^\s]+)"')
token_fields = token_fields_exp.search(login_page.text)
# This regex matches the "data[_Token][fields]" value needed to make a POST request on the MISP login page.
# This regex matches the "data[_Token][key]" value needed to make a POST request on the MISP login page.
token_key_exp = re.compile(r'name="data\[_Token]\[key]" value="([^\s]+)"')
token_key = token_key_exp.search(login_page.text)
# This regex matches the "data[_Token][debug]" value needed to make a POST request on the MISP login page.
token_key_exp = re.compile(r'name="data\[_Token]\[debug]" value="([^\s]+)"')
token_debug = token_key_exp.search(login_page.text)
post_data["data[_Token][fields]"] = token_fields.group(1)
post_data["data[_Token][key]"] = token_key.group(1)
post_data["data[_Token][debug]"] = token_debug.group(1)
# POST request with user credentials + hidden form values.
post_to_login_page = session.post(misp_login_page, data=post_data, allow_redirects=False)
@ -159,7 +166,24 @@ def unauthorized():
Redirect unauthorized user to login page.
:return:
"""
return redirect(url_for('login'))
redirectCount = int(request.cookies.get('redirectCount', '0'))
if redirectCount > 5:
response = make_response(redirect(url_for(
'error_page',
error_message='Too many redirects. This can be due to your brower not accepting cookies or the misp-dashboard website is badly configured',
error_code='1'
)))
response.set_cookie('redirectCount', '0', secure=False, httponly=True)
else:
response = make_response(redirect(url_for('login')))
response.set_cookie('redirectCount', str(redirectCount+1), secure=False, httponly=True)
return response
@app.route('/error_page')
def error_page():
error_message = request.args.get('error_message', False)
return render_template('error_page.html', error_message=error_message)
@app.route('/logout')
@ -173,7 +197,7 @@ def logout():
return redirect(url_for('login'))
@app.route('/login', methods=['GET','POST'])
@app.route('/login', methods=['GET', 'POST'])
def login():
"""
Login form route.
@ -192,14 +216,22 @@ def login():
if request.method == 'POST' and form.validate():
user = User(form.username.data, form.password.data)
if user.misp_login():
login_user(user)
return redirect(url_for('index'))
error_message = 'Username and Password does not match when connecting to MISP or incorrect MISP permission'
try:
is_logged_in, misp_error_message = user.misp_login()
if len(misp_error_message) > 0:
error_message = misp_error_message
if is_logged_in:
login_user(user)
return redirect(url_for('index'))
except requests.exceptions.SSLError:
return redirect(url_for('login', auth_error=True, auth_error_message='MISP cannot be reached for authentication'))
return redirect(url_for('login', auth_error=True))
return redirect(url_for('login', auth_error=True, auth_error_message=error_message))
else:
auth_error = request.args.get('auth_error', False)
return render_template('login.html', title='Login', form=form, authError=auth_error)
auth_error_message = request.args.get('auth_error_message', '')
return render_template('login.html', title='Login', form=form, authError=auth_error, authErrorMessage=auth_error_message)

43
templates/error_page.html Normal file
View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width" />
<title>
Users - MISP
</title>
<!-- jQuery -->
<script src="{{ url_for('static', filename='js/jquery.min.js') }}"></script>
<!-- Bootstrap Core CSS -->
<link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet">
<!-- Custom CSS -->
<link href="{{ url_for('static', filename='css/sb-admin-2.css') }}" rel="stylesheet">
<!-- Bootstrap Core JavaScript -->
<script src="{{ url_for('static', filename='js/bootstrap.js') }}"></script>
<link rel="stylesheet" href="{{ url_for('static', filename='css/font-awesome.min.css') }}" rel="text/css">
</head>
<body>
<div id="flashContainer" style="padding-top:50px; !important;">
<div id="main-view-container" class="container-fluid ">
</div>
</div>
<div style="width:100%;">
<table style="margin-left:auto;margin-right:auto;">
<tr>
<td style="text-align:right;width:250px;padding-right:50px"></td>
<td style="width:460px">
<div>
<img src="{{ url_for('static', filename='pics/misp-logo.png') }}" style="display:block; margin-left: auto; margin-right: auto;"/>
</div>
<div class="alert alert-danger" style="margin-top: 15px;">
{{ error_message }}
</div>
</td>
</tr>
</table>
</div>
</body>
</html>

View File

@ -36,7 +36,7 @@
</div>
{% if authError %}
<div class="alert alert-danger">
Username and Password does not match when connecting to MISP or incorrect MISP permission
{{ authErrorMessage }}
</div>
{% endif %}