mirror of https://github.com/D4-project/d4-core
chg: [UI] add user management
parent
113159f820
commit
c8d2b8cb95
|
@ -7,12 +7,35 @@
|
||||||
D4 core server is a complete server to handle clients (sensors) including the decapsulation of the [D4 protocol](https://github.com/D4-project/architecture/tree/master/format), control of
|
D4 core server is a complete server to handle clients (sensors) including the decapsulation of the [D4 protocol](https://github.com/D4-project/architecture/tree/master/format), control of
|
||||||
sensor registrations, management of decoding protocols and dispatching to adequate decoders/analysers.
|
sensor registrations, management of decoding protocols and dispatching to adequate decoders/analysers.
|
||||||
|
|
||||||
## Database map
|
## Database map - Metadata
|
||||||
|
|
||||||
| Key | Value |
|
```
|
||||||
| --- | --- |
|
DB 0 - Stats + sensor configs
|
||||||
| | |
|
DB 1 - Users
|
||||||
| | | |
|
DB 2 - Analyzer queue
|
||||||
|
```
|
||||||
|
|
||||||
|
### DB 1
|
||||||
|
|
||||||
|
##### User Management:
|
||||||
|
| Hset Key | Field | Value |
|
||||||
|
| ------ | ------ | ------ |
|
||||||
|
| user:all | **user id** | **password hash** |
|
||||||
|
| | | |
|
||||||
|
| user:tokens | **token** | **user id** |
|
||||||
|
| | | |
|
||||||
|
| user_metadata:**user id** | token | **token** |
|
||||||
|
| | change_passwd | **boolean** |
|
||||||
|
| | role | **role** |
|
||||||
|
|
||||||
|
| Set Key | Value |
|
||||||
|
| ------ | ------ |
|
||||||
|
| user_role:**role** | **user id** |
|
||||||
|
|
||||||
|
|
||||||
|
| Zrank Key | Field | Value |
|
||||||
|
| ------ | ------ | ------ |
|
||||||
|
| ail:all_role | **role** | **int, role priority (1=admin)** |
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
| Key | Value |
|
| Key | Value |
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*-coding:UTF-8 -*
|
||||||
|
|
||||||
|
import os
|
||||||
|
import redis
|
||||||
|
import bcrypt
|
||||||
|
|
||||||
|
from flask_login import UserMixin
|
||||||
|
|
||||||
|
class User(UserMixin):
|
||||||
|
|
||||||
|
def __init__(self, id):
|
||||||
|
host_redis_metadata = os.getenv('D4_REDIS_METADATA_HOST', "localhost")
|
||||||
|
port_redis_metadata = int(os.getenv('D4_REDIS_METADATA_PORT', 6380))
|
||||||
|
|
||||||
|
self.r_serv_db = redis.StrictRedis(
|
||||||
|
host=host_redis_metadata,
|
||||||
|
port=port_redis_metadata,
|
||||||
|
db=1,
|
||||||
|
decode_responses=True)
|
||||||
|
|
||||||
|
if self.r_serv_db.hexists('user:all', id):
|
||||||
|
self.id = id
|
||||||
|
else:
|
||||||
|
self.id = "__anonymous__"
|
||||||
|
|
||||||
|
# return True or False
|
||||||
|
#def is_authenticated():
|
||||||
|
|
||||||
|
# return True or False
|
||||||
|
#def is_anonymous():
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get(self_class, id):
|
||||||
|
return self_class(id)
|
||||||
|
|
||||||
|
def user_is_anonymous(self):
|
||||||
|
if self.id == "__anonymous__":
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def check_password(self, password):
|
||||||
|
if self.user_is_anonymous():
|
||||||
|
return False
|
||||||
|
|
||||||
|
password = password.encode()
|
||||||
|
hashed_password = self.r_serv_db.hget('user:all', self.id).encode()
|
||||||
|
if bcrypt.checkpw(password, hashed_password):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def request_password_change(self):
|
||||||
|
if self.r_serv_db.hget('user_metadata:{}'.format(self.id), 'change_passwd') == 'True':
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_in_role(self, role):
|
||||||
|
if self.r_serv_db.sismember('user_role:{}'.format(role), self.id):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
|
@ -1,6 +1,8 @@
|
||||||
twisted[tls]
|
twisted[tls]
|
||||||
redis
|
redis
|
||||||
flask
|
flask
|
||||||
|
flask-login
|
||||||
|
bcrypt
|
||||||
|
|
||||||
#sudo python3 -m pip install --upgrade service_identity
|
#sudo python3 -m pip install --upgrade service_identity
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,14 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import ssl
|
||||||
import sys
|
import sys
|
||||||
import uuid
|
|
||||||
import time
|
|
||||||
import json
|
import json
|
||||||
import redis
|
import time
|
||||||
|
import uuid
|
||||||
import flask
|
import flask
|
||||||
|
import redis
|
||||||
|
import random
|
||||||
import datetime
|
import datetime
|
||||||
import ipaddress
|
import ipaddress
|
||||||
import configparser
|
import configparser
|
||||||
|
@ -16,6 +18,17 @@ import configparser
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from flask import Flask, render_template, jsonify, request, Blueprint, redirect, url_for
|
from flask import Flask, render_template, jsonify, request, Blueprint, redirect, url_for
|
||||||
|
from flask_login import LoginManager, current_user, login_user, logout_user, login_required
|
||||||
|
|
||||||
|
import bcrypt
|
||||||
|
|
||||||
|
# Import Role_Manager
|
||||||
|
from Role_Manager import create_user_db, check_password_strength, check_user_role_integrity
|
||||||
|
from Role_Manager import login_admin, login_analyst
|
||||||
|
|
||||||
|
sys.path.append(os.path.join(os.environ['D4_HOME'], 'lib'))
|
||||||
|
|
||||||
|
from User import User
|
||||||
|
|
||||||
baseUrl = ''
|
baseUrl = ''
|
||||||
if baseUrl != '':
|
if baseUrl != '':
|
||||||
|
@ -59,6 +72,12 @@ redis_server_metadata = redis.StrictRedis(
|
||||||
db=0,
|
db=0,
|
||||||
decode_responses=True)
|
decode_responses=True)
|
||||||
|
|
||||||
|
redis_users = redis.StrictRedis(
|
||||||
|
host=host_redis_metadata,
|
||||||
|
port=port_redis_metadata,
|
||||||
|
db=1,
|
||||||
|
decode_responses=True)
|
||||||
|
|
||||||
redis_server_analyzer = redis.StrictRedis(
|
redis_server_analyzer = redis.StrictRedis(
|
||||||
host=host_redis_metadata,
|
host=host_redis_metadata,
|
||||||
port=port_redis_metadata,
|
port=port_redis_metadata,
|
||||||
|
@ -71,9 +90,31 @@ json_type_description = {}
|
||||||
for type_info in json_type:
|
for type_info in json_type:
|
||||||
json_type_description[type_info['type']] = type_info
|
json_type_description[type_info['type']] = type_info
|
||||||
|
|
||||||
|
Flask_dir = os.path.join(os.environ['D4_HOME'], 'web')
|
||||||
|
|
||||||
|
# ========= TLS =========#
|
||||||
|
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
|
||||||
|
ssl_context.load_cert_chain(certfile=os.path.join(Flask_dir, 'server.crt'), keyfile=os.path.join(Flask_dir, 'server.key'))
|
||||||
|
#print(ssl_context.get_ciphers())
|
||||||
|
# ========= =========#
|
||||||
|
|
||||||
app = Flask(__name__, static_url_path=baseUrl+'/static/')
|
app = Flask(__name__, static_url_path=baseUrl+'/static/')
|
||||||
app.config['MAX_CONTENT_LENGTH'] = 900 * 1024 * 1024
|
app.config['MAX_CONTENT_LENGTH'] = 900 * 1024 * 1024
|
||||||
|
|
||||||
|
# ========= session ========
|
||||||
|
app.secret_key = str(random.getrandbits(256))
|
||||||
|
login_manager = LoginManager()
|
||||||
|
login_manager.login_view = 'login'
|
||||||
|
login_manager.init_app(app)
|
||||||
|
# ========= =========#
|
||||||
|
|
||||||
|
# ========= LOGIN MANAGER ========
|
||||||
|
|
||||||
|
@login_manager.user_loader
|
||||||
|
def load_user(user_id):
|
||||||
|
return User.get(user_id)
|
||||||
|
# ========= =========#
|
||||||
|
|
||||||
# ========== FUNCTIONS ============
|
# ========== FUNCTIONS ============
|
||||||
def is_valid_uuid_v4(header_uuid):
|
def is_valid_uuid_v4(header_uuid):
|
||||||
try:
|
try:
|
||||||
|
@ -195,19 +236,137 @@ def get_uuid_disk_statistics(uuid_name, date_day='', type='', all_types_on_disk=
|
||||||
|
|
||||||
@app.errorhandler(404)
|
@app.errorhandler(404)
|
||||||
def page_not_found(e):
|
def page_not_found(e):
|
||||||
return render_template('404.html'), 404
|
# API - JSON
|
||||||
|
if request.path.startswith('/api/'):
|
||||||
|
return Response(json.dumps({"status": "error", "reason": "404 Not Found"}, indent=2, sort_keys=True), mimetype='application/json'), 404
|
||||||
|
# UI - HTML Template
|
||||||
|
else:
|
||||||
|
return render_template('404.html'), 404
|
||||||
|
|
||||||
|
@app.errorhandler(405)
|
||||||
|
def _handle_client_error(e):
|
||||||
|
if request.path.startswith('/api/'):
|
||||||
|
res_dict = {"status": "error", "reason": "Method Not Allowed: The method is not allowed for the requested URL"}
|
||||||
|
anchor_id = request.path[8:]
|
||||||
|
anchor_id = anchor_id.replace('/', '_')
|
||||||
|
api_doc_url = 'https://d4-project.org#{}'.format(anchor_id)
|
||||||
|
res_dict['documentation'] = api_doc_url
|
||||||
|
return Response(json.dumps(res_dict, indent=2, sort_keys=True), mimetype='application/json'), 405
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
# ========== ROUTES ============
|
# ========== ROUTES ============
|
||||||
|
@app.route('/login', methods=['POST', 'GET'])
|
||||||
|
def login():
|
||||||
|
|
||||||
|
'''
|
||||||
|
current_ip = request.remote_addr
|
||||||
|
login_failed_ip = r_cache.get('failed_login_ip:{}'.format(current_ip))
|
||||||
|
|
||||||
|
# brute force by ip
|
||||||
|
if login_failed_ip:
|
||||||
|
login_failed_ip = int(login_failed_ip)
|
||||||
|
if login_failed_ip >= 5:
|
||||||
|
error = 'Max Connection Attempts reached, Please wait {}s'.format(r_cache.ttl('failed_login_ip:{}'.format(current_ip)))
|
||||||
|
return render_template("login.html", error=error)
|
||||||
|
'''
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
username = request.form.get('username')
|
||||||
|
password = request.form.get('password')
|
||||||
|
#next_page = request.form.get('next_page')
|
||||||
|
|
||||||
|
if username is not None:
|
||||||
|
user = User.get(username)
|
||||||
|
'''
|
||||||
|
login_failed_user_id = r_cache.get('failed_login_user_id:{}'.format(username))
|
||||||
|
# brute force by user_id
|
||||||
|
if login_failed_user_id:
|
||||||
|
login_failed_user_id = int(login_failed_user_id)
|
||||||
|
if login_failed_user_id >= 5:
|
||||||
|
error = 'Max Connection Attempts reached, Please wait {}s'.format(r_cache.ttl('failed_login_user_id:{}'.format(username)))
|
||||||
|
return render_template("login.html", error=error)
|
||||||
|
'''
|
||||||
|
|
||||||
|
if user and user.check_password(password):
|
||||||
|
#if not check_user_role_integrity(user.get_id()):
|
||||||
|
# error = 'Incorrect User ACL, Please contact your administrator'
|
||||||
|
# return render_template("login.html", error=error)
|
||||||
|
login_user(user) ## TODO: use remember me ?
|
||||||
|
if user.request_password_change():
|
||||||
|
return redirect(url_for('change_password'))
|
||||||
|
else:
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
# login failed
|
||||||
|
else:
|
||||||
|
# set brute force protection
|
||||||
|
#logger.warning("Login failed, ip={}, username={}".format(current_ip, username))
|
||||||
|
#r_cache.incr('failed_login_ip:{}'.format(current_ip))
|
||||||
|
#r_cache.expire('failed_login_ip:{}'.format(current_ip), 300)
|
||||||
|
#r_cache.incr('failed_login_user_id:{}'.format(username))
|
||||||
|
#r_cache.expire('failed_login_user_id:{}'.format(username), 300)
|
||||||
|
#
|
||||||
|
|
||||||
|
error = 'Password Incorrect'
|
||||||
|
return render_template("login.html", error=error)
|
||||||
|
|
||||||
|
return 'please provide a valid username'
|
||||||
|
|
||||||
|
else:
|
||||||
|
#next_page = request.args.get('next')
|
||||||
|
error = request.args.get('error')
|
||||||
|
return render_template("login.html" , error=error)
|
||||||
|
|
||||||
|
@app.route('/change_password', methods=['POST', 'GET'])
|
||||||
|
@login_required
|
||||||
|
def change_password():
|
||||||
|
password1 = request.form.get('password1')
|
||||||
|
password2 = request.form.get('password2')
|
||||||
|
error = request.args.get('error')
|
||||||
|
|
||||||
|
if error:
|
||||||
|
return render_template("change_password.html", error=error)
|
||||||
|
|
||||||
|
if current_user.is_authenticated and password1!=None:
|
||||||
|
if password1==password2:
|
||||||
|
if check_password_strength(password1):
|
||||||
|
user_id = current_user.get_id()
|
||||||
|
create_user_db(user_id , password1, update=True)
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
else:
|
||||||
|
error = 'Incorrect password'
|
||||||
|
return render_template("change_password.html", error=error)
|
||||||
|
else:
|
||||||
|
error = "Passwords don't match"
|
||||||
|
return render_template("change_password.html", error=error)
|
||||||
|
else:
|
||||||
|
error = 'Please choose a new password'
|
||||||
|
return render_template("change_password.html", error=error)
|
||||||
|
|
||||||
|
@app.route('/logout')
|
||||||
|
@login_required
|
||||||
|
def logout():
|
||||||
|
logout_user()
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
# role error template
|
||||||
|
@app.route('/role', methods=['POST', 'GET'])
|
||||||
|
@login_required
|
||||||
|
def role():
|
||||||
|
return render_template("error/403.html"), 403
|
||||||
|
|
||||||
@app.route('/test')
|
@app.route('/test')
|
||||||
def test():
|
def test():
|
||||||
return 'test'
|
return 'test'
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
|
@login_required
|
||||||
def index():
|
def index():
|
||||||
date = datetime.datetime.now().strftime("%Y/%m/%d")
|
date = datetime.datetime.now().strftime("%Y/%m/%d")
|
||||||
return render_template("index.html", date=date)
|
return render_template("index.html", date=date)
|
||||||
|
|
||||||
@app.route('/_json_daily_uuid_stats')
|
@app.route('/_json_daily_uuid_stats')
|
||||||
|
@login_required
|
||||||
def _json_daily_uuid_stats():
|
def _json_daily_uuid_stats():
|
||||||
date = datetime.datetime.now().strftime("%Y%m%d")
|
date = datetime.datetime.now().strftime("%Y%m%d")
|
||||||
daily_uuid = redis_server_metadata.zrange('daily_uuid:{}'.format(date), 0, -1, withscores=True)
|
daily_uuid = redis_server_metadata.zrange('daily_uuid:{}'.format(date), 0, -1, withscores=True)
|
||||||
|
@ -219,6 +378,7 @@ def _json_daily_uuid_stats():
|
||||||
return jsonify(data_daily_uuid)
|
return jsonify(data_daily_uuid)
|
||||||
|
|
||||||
@app.route('/_json_daily_type_stats')
|
@app.route('/_json_daily_type_stats')
|
||||||
|
@login_required
|
||||||
def _json_daily_type_stats():
|
def _json_daily_type_stats():
|
||||||
date = datetime.datetime.now().strftime("%Y%m%d")
|
date = datetime.datetime.now().strftime("%Y%m%d")
|
||||||
daily_uuid = redis_server_metadata.zrange('daily_type:{}'.format(date), 0, -1, withscores=True)
|
daily_uuid = redis_server_metadata.zrange('daily_type:{}'.format(date), 0, -1, withscores=True)
|
||||||
|
@ -235,6 +395,7 @@ def _json_daily_type_stats():
|
||||||
return jsonify(data_daily_uuid)
|
return jsonify(data_daily_uuid)
|
||||||
|
|
||||||
@app.route('/sensors_status')
|
@app.route('/sensors_status')
|
||||||
|
@login_required
|
||||||
def sensors_status():
|
def sensors_status():
|
||||||
active_connection_filter = request.args.get('active_connection_filter')
|
active_connection_filter = request.args.get('active_connection_filter')
|
||||||
if active_connection_filter is None:
|
if active_connection_filter is None:
|
||||||
|
@ -314,6 +475,7 @@ def sensors_status():
|
||||||
active_connection_filter=active_connection_filter)
|
active_connection_filter=active_connection_filter)
|
||||||
|
|
||||||
@app.route('/show_active_uuid')
|
@app.route('/show_active_uuid')
|
||||||
|
@login_required
|
||||||
def show_active_uuid():
|
def show_active_uuid():
|
||||||
#swap switch value
|
#swap switch value
|
||||||
active_connection_filter = request.args.get('show_active_connection')
|
active_connection_filter = request.args.get('show_active_connection')
|
||||||
|
@ -328,6 +490,7 @@ def show_active_uuid():
|
||||||
return redirect(url_for('sensors_status', active_connection_filter=active_connection_filter))
|
return redirect(url_for('sensors_status', active_connection_filter=active_connection_filter))
|
||||||
|
|
||||||
@app.route('/server_management')
|
@app.route('/server_management')
|
||||||
|
@login_required
|
||||||
def server_management():
|
def server_management():
|
||||||
blacklisted_ip = request.args.get('blacklisted_ip')
|
blacklisted_ip = request.args.get('blacklisted_ip')
|
||||||
unblacklisted_ip = request.args.get('unblacklisted_ip')
|
unblacklisted_ip = request.args.get('unblacklisted_ip')
|
||||||
|
@ -398,6 +561,7 @@ def server_management():
|
||||||
blacklisted_uuid=blacklisted_uuid, unblacklisted_uuid=unblacklisted_uuid)
|
blacklisted_uuid=blacklisted_uuid, unblacklisted_uuid=unblacklisted_uuid)
|
||||||
|
|
||||||
@app.route('/uuid_management')
|
@app.route('/uuid_management')
|
||||||
|
@login_required
|
||||||
def uuid_management():
|
def uuid_management():
|
||||||
uuid_sensor = request.args.get('uuid')
|
uuid_sensor = request.args.get('uuid')
|
||||||
if is_valid_uuid_v4(uuid_sensor):
|
if is_valid_uuid_v4(uuid_sensor):
|
||||||
|
@ -470,6 +634,7 @@ def uuid_management():
|
||||||
return 'Invalid uuid'
|
return 'Invalid uuid'
|
||||||
|
|
||||||
@app.route('/blacklisted_ip')
|
@app.route('/blacklisted_ip')
|
||||||
|
@login_required
|
||||||
def blacklisted_ip():
|
def blacklisted_ip():
|
||||||
blacklisted_ip = request.args.get('blacklisted_ip')
|
blacklisted_ip = request.args.get('blacklisted_ip')
|
||||||
unblacklisted_ip = request.args.get('unblacklisted_ip')
|
unblacklisted_ip = request.args.get('unblacklisted_ip')
|
||||||
|
@ -495,6 +660,7 @@ def blacklisted_ip():
|
||||||
unblacklisted_ip=unblacklisted_ip, blacklisted_ip=blacklisted_ip)
|
unblacklisted_ip=unblacklisted_ip, blacklisted_ip=blacklisted_ip)
|
||||||
|
|
||||||
@app.route('/blacklisted_uuid')
|
@app.route('/blacklisted_uuid')
|
||||||
|
@login_required
|
||||||
def blacklisted_uuid():
|
def blacklisted_uuid():
|
||||||
blacklisted_uuid = request.args.get('blacklisted_uuid')
|
blacklisted_uuid = request.args.get('blacklisted_uuid')
|
||||||
unblacklisted_uuid = request.args.get('unblacklisted_uuid')
|
unblacklisted_uuid = request.args.get('unblacklisted_uuid')
|
||||||
|
@ -521,6 +687,7 @@ def blacklisted_uuid():
|
||||||
|
|
||||||
|
|
||||||
@app.route('/uuid_change_stream_max_size')
|
@app.route('/uuid_change_stream_max_size')
|
||||||
|
@login_required
|
||||||
def uuid_change_stream_max_size():
|
def uuid_change_stream_max_size():
|
||||||
uuid_sensor = request.args.get('uuid')
|
uuid_sensor = request.args.get('uuid')
|
||||||
user = request.args.get('redirect')
|
user = request.args.get('redirect')
|
||||||
|
@ -539,6 +706,7 @@ def uuid_change_stream_max_size():
|
||||||
return 'Invalid uuid'
|
return 'Invalid uuid'
|
||||||
|
|
||||||
@app.route('/uuid_change_description')
|
@app.route('/uuid_change_description')
|
||||||
|
@login_required
|
||||||
def uuid_change_description():
|
def uuid_change_description():
|
||||||
uuid_sensor = request.args.get('uuid')
|
uuid_sensor = request.args.get('uuid')
|
||||||
description = request.args.get('description')
|
description = request.args.get('description')
|
||||||
|
@ -550,6 +718,7 @@ def uuid_change_description():
|
||||||
|
|
||||||
# # TODO: check analyser uuid dont exist
|
# # TODO: check analyser uuid dont exist
|
||||||
@app.route('/add_new_analyzer')
|
@app.route('/add_new_analyzer')
|
||||||
|
@login_required
|
||||||
def add_new_analyzer():
|
def add_new_analyzer():
|
||||||
type = request.args.get('type')
|
type = request.args.get('type')
|
||||||
user = request.args.get('redirect')
|
user = request.args.get('redirect')
|
||||||
|
@ -576,6 +745,7 @@ def add_new_analyzer():
|
||||||
return 'Invalid uuid'
|
return 'Invalid uuid'
|
||||||
|
|
||||||
@app.route('/empty_analyzer_queue')
|
@app.route('/empty_analyzer_queue')
|
||||||
|
@login_required
|
||||||
def empty_analyzer_queue():
|
def empty_analyzer_queue():
|
||||||
analyzer_uuid = request.args.get('analyzer_uuid')
|
analyzer_uuid = request.args.get('analyzer_uuid')
|
||||||
type = request.args.get('type')
|
type = request.args.get('type')
|
||||||
|
@ -598,6 +768,7 @@ def empty_analyzer_queue():
|
||||||
return 'Invalid uuid'
|
return 'Invalid uuid'
|
||||||
|
|
||||||
@app.route('/remove_analyzer')
|
@app.route('/remove_analyzer')
|
||||||
|
@login_required
|
||||||
def remove_analyzer():
|
def remove_analyzer():
|
||||||
analyzer_uuid = request.args.get('analyzer_uuid')
|
analyzer_uuid = request.args.get('analyzer_uuid')
|
||||||
type = request.args.get('type')
|
type = request.args.get('type')
|
||||||
|
@ -623,6 +794,7 @@ def remove_analyzer():
|
||||||
return 'Invalid uuid'
|
return 'Invalid uuid'
|
||||||
|
|
||||||
@app.route('/analyzer_change_max_size')
|
@app.route('/analyzer_change_max_size')
|
||||||
|
@login_required
|
||||||
def analyzer_change_max_size():
|
def analyzer_change_max_size():
|
||||||
analyzer_uuid = request.args.get('analyzer_uuid')
|
analyzer_uuid = request.args.get('analyzer_uuid')
|
||||||
user = request.args.get('redirect')
|
user = request.args.get('redirect')
|
||||||
|
@ -641,6 +813,7 @@ def analyzer_change_max_size():
|
||||||
return 'Invalid uuid'
|
return 'Invalid uuid'
|
||||||
|
|
||||||
@app.route('/kick_uuid')
|
@app.route('/kick_uuid')
|
||||||
|
@login_required
|
||||||
def kick_uuid():
|
def kick_uuid():
|
||||||
uuid_sensor = request.args.get('uuid')
|
uuid_sensor = request.args.get('uuid')
|
||||||
if is_valid_uuid_v4(uuid_sensor):
|
if is_valid_uuid_v4(uuid_sensor):
|
||||||
|
@ -650,6 +823,7 @@ def kick_uuid():
|
||||||
return 'Invalid uuid'
|
return 'Invalid uuid'
|
||||||
|
|
||||||
@app.route('/blacklist_uuid')
|
@app.route('/blacklist_uuid')
|
||||||
|
@login_required
|
||||||
def blacklist_uuid():
|
def blacklist_uuid():
|
||||||
uuid_sensor = request.args.get('uuid')
|
uuid_sensor = request.args.get('uuid')
|
||||||
user = request.args.get('redirect')
|
user = request.args.get('redirect')
|
||||||
|
@ -670,6 +844,7 @@ def blacklist_uuid():
|
||||||
return 'Invalid uuid'
|
return 'Invalid uuid'
|
||||||
|
|
||||||
@app.route('/unblacklist_uuid')
|
@app.route('/unblacklist_uuid')
|
||||||
|
@login_required
|
||||||
def unblacklist_uuid():
|
def unblacklist_uuid():
|
||||||
uuid_sensor = request.args.get('uuid')
|
uuid_sensor = request.args.get('uuid')
|
||||||
user = request.args.get('redirect')
|
user = request.args.get('redirect')
|
||||||
|
@ -693,6 +868,7 @@ def unblacklist_uuid():
|
||||||
return 'Invalid uuid'
|
return 'Invalid uuid'
|
||||||
|
|
||||||
@app.route('/blacklist_ip')
|
@app.route('/blacklist_ip')
|
||||||
|
@login_required
|
||||||
def blacklist_ip():
|
def blacklist_ip():
|
||||||
ip = request.args.get('ip')
|
ip = request.args.get('ip')
|
||||||
user = request.args.get('redirect')
|
user = request.args.get('redirect')
|
||||||
|
@ -718,6 +894,7 @@ def blacklist_ip():
|
||||||
return 'Invalid ip'
|
return 'Invalid ip'
|
||||||
|
|
||||||
@app.route('/unblacklist_ip')
|
@app.route('/unblacklist_ip')
|
||||||
|
@login_required
|
||||||
def unblacklist_ip():
|
def unblacklist_ip():
|
||||||
ip = request.args.get('ip')
|
ip = request.args.get('ip')
|
||||||
user = request.args.get('redirect')
|
user = request.args.get('redirect')
|
||||||
|
@ -745,6 +922,7 @@ def unblacklist_ip():
|
||||||
return 'Invalid ip'
|
return 'Invalid ip'
|
||||||
|
|
||||||
@app.route('/blacklist_ip_by_uuid')
|
@app.route('/blacklist_ip_by_uuid')
|
||||||
|
@login_required
|
||||||
def blacklist_ip_by_uuid():
|
def blacklist_ip_by_uuid():
|
||||||
uuid_sensor = request.args.get('uuid')
|
uuid_sensor = request.args.get('uuid')
|
||||||
user = request.args.get('redirect')
|
user = request.args.get('redirect')
|
||||||
|
@ -756,6 +934,7 @@ def blacklist_ip_by_uuid():
|
||||||
return 'Invalid uuid'
|
return 'Invalid uuid'
|
||||||
|
|
||||||
@app.route('/unblacklist_ip_by_uuid')
|
@app.route('/unblacklist_ip_by_uuid')
|
||||||
|
@login_required
|
||||||
def unblacklist_ip_by_uuid():
|
def unblacklist_ip_by_uuid():
|
||||||
uuid_sensor = request.args.get('uuid')
|
uuid_sensor = request.args.get('uuid')
|
||||||
user = request.args.get('redirect')
|
user = request.args.get('redirect')
|
||||||
|
@ -767,6 +946,7 @@ def unblacklist_ip_by_uuid():
|
||||||
return 'Invalid uuid'
|
return 'Invalid uuid'
|
||||||
|
|
||||||
@app.route('/add_accepted_type')
|
@app.route('/add_accepted_type')
|
||||||
|
@login_required
|
||||||
def add_accepted_type():
|
def add_accepted_type():
|
||||||
type = request.args.get('type')
|
type = request.args.get('type')
|
||||||
extended_type_name = request.args.get('extended_type_name')
|
extended_type_name = request.args.get('extended_type_name')
|
||||||
|
@ -786,6 +966,7 @@ def add_accepted_type():
|
||||||
return 'Invalid type'
|
return 'Invalid type'
|
||||||
|
|
||||||
@app.route('/remove_accepted_type')
|
@app.route('/remove_accepted_type')
|
||||||
|
@login_required
|
||||||
def remove_accepted_type():
|
def remove_accepted_type():
|
||||||
type = request.args.get('type')
|
type = request.args.get('type')
|
||||||
user = request.args.get('redirect')
|
user = request.args.get('redirect')
|
||||||
|
@ -798,6 +979,7 @@ def remove_accepted_type():
|
||||||
return 'Invalid type'
|
return 'Invalid type'
|
||||||
|
|
||||||
@app.route('/remove_accepted_extended_type')
|
@app.route('/remove_accepted_extended_type')
|
||||||
|
@login_required
|
||||||
def remove_accepted_extended_type():
|
def remove_accepted_extended_type():
|
||||||
type_name = request.args.get('type_name')
|
type_name = request.args.get('type_name')
|
||||||
redis_server_metadata.srem('server:accepted_extended_type', type_name)
|
redis_server_metadata.srem('server:accepted_extended_type', type_name)
|
||||||
|
@ -805,6 +987,7 @@ def remove_accepted_extended_type():
|
||||||
|
|
||||||
# demo function
|
# demo function
|
||||||
@app.route('/delete_data')
|
@app.route('/delete_data')
|
||||||
|
@login_required
|
||||||
def delete_data():
|
def delete_data():
|
||||||
date = datetime.datetime.now().strftime("%Y%m%d")
|
date = datetime.datetime.now().strftime("%Y%m%d")
|
||||||
redis_server_metadata.delete('daily_type:{}'.format(date))
|
redis_server_metadata.delete('daily_type:{}'.format(date))
|
||||||
|
@ -813,6 +996,7 @@ def delete_data():
|
||||||
|
|
||||||
# demo function
|
# demo function
|
||||||
@app.route('/set_uuid_hmac_key')
|
@app.route('/set_uuid_hmac_key')
|
||||||
|
@login_required
|
||||||
def set_uuid_hmac_key():
|
def set_uuid_hmac_key():
|
||||||
uuid_sensor = request.args.get('uuid')
|
uuid_sensor = request.args.get('uuid')
|
||||||
user = request.args.get('redirect')
|
user = request.args.get('redirect')
|
||||||
|
@ -824,6 +1008,7 @@ def set_uuid_hmac_key():
|
||||||
|
|
||||||
# demo function
|
# demo function
|
||||||
@app.route('/whois_data')
|
@app.route('/whois_data')
|
||||||
|
@login_required
|
||||||
def whois_data():
|
def whois_data():
|
||||||
ip = request.args.get('ip')
|
ip = request.args.get('ip')
|
||||||
if is_valid_ip:
|
if is_valid_ip:
|
||||||
|
@ -832,11 +1017,13 @@ def whois_data():
|
||||||
return 'Invalid IP'
|
return 'Invalid IP'
|
||||||
|
|
||||||
@app.route('/generate_uuid')
|
@app.route('/generate_uuid')
|
||||||
|
@login_required
|
||||||
def generate_uuid():
|
def generate_uuid():
|
||||||
new_uuid = uuid.uuid4()
|
new_uuid = uuid.uuid4()
|
||||||
return jsonify({'uuid': new_uuid})
|
return jsonify({'uuid': new_uuid})
|
||||||
|
|
||||||
@app.route('/get_analyser_sample')
|
@app.route('/get_analyser_sample')
|
||||||
|
@login_required
|
||||||
def get_analyser_sample():
|
def get_analyser_sample():
|
||||||
type = request.args.get('type')
|
type = request.args.get('type')
|
||||||
analyzer_uuid = request.args.get('analyzer_uuid')
|
analyzer_uuid = request.args.get('analyzer_uuid')
|
||||||
|
@ -864,6 +1051,7 @@ def get_analyser_sample():
|
||||||
return jsonify('Incorrect UUID')
|
return jsonify('Incorrect UUID')
|
||||||
|
|
||||||
@app.route('/get_uuid_type_history_json')
|
@app.route('/get_uuid_type_history_json')
|
||||||
|
@login_required
|
||||||
def get_uuid_type_history_json():
|
def get_uuid_type_history_json():
|
||||||
uuid_sensor = request.args.get('uuid_sensor')
|
uuid_sensor = request.args.get('uuid_sensor')
|
||||||
if is_valid_uuid_v4(uuid_sensor):
|
if is_valid_uuid_v4(uuid_sensor):
|
||||||
|
@ -894,6 +1082,7 @@ def get_uuid_type_history_json():
|
||||||
return jsonify('Incorrect UUID')
|
return jsonify('Incorrect UUID')
|
||||||
|
|
||||||
@app.route('/get_uuid_stats_history_json')
|
@app.route('/get_uuid_stats_history_json')
|
||||||
|
@login_required
|
||||||
def get_uuid_stats_history_json():
|
def get_uuid_stats_history_json():
|
||||||
uuid_sensor = request.args.get('uuid_sensor')
|
uuid_sensor = request.args.get('uuid_sensor')
|
||||||
stats = request.args.get('stats')
|
stats = request.args.get('stats')
|
||||||
|
@ -925,4 +1114,4 @@ def get_uuid_stats_history_json():
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app.run(host='0.0.0.0', port=7000, threaded=True)
|
app.run(host='0.0.0.0', port=7000, threaded=True, ssl_context=ssl_context)
|
||||||
|
|
|
@ -0,0 +1,183 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*-coding:UTF-8 -*
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import redis
|
||||||
|
import bcrypt
|
||||||
|
|
||||||
|
from functools import wraps
|
||||||
|
from flask_login import LoginManager, current_user, login_user, logout_user, login_required
|
||||||
|
|
||||||
|
from flask import request, current_app
|
||||||
|
|
||||||
|
login_manager = LoginManager()
|
||||||
|
login_manager.login_view = 'role'
|
||||||
|
|
||||||
|
host_redis_metadata = os.getenv('D4_REDIS_METADATA_HOST', "localhost")
|
||||||
|
port_redis_metadata = int(os.getenv('D4_REDIS_METADATA_PORT', 6380))
|
||||||
|
|
||||||
|
r_serv_db = redis.StrictRedis(
|
||||||
|
host=host_redis_metadata,
|
||||||
|
port=port_redis_metadata,
|
||||||
|
db=1,
|
||||||
|
decode_responses=True)
|
||||||
|
|
||||||
|
default_passwd_file = os.path.join(os.environ['D4_HOME'], 'DEFAULT_PASSWORD')
|
||||||
|
|
||||||
|
regex_password = r'^(?=(.*\d){2})(?=.*[a-z])(?=.*[A-Z]).{10,100}$'
|
||||||
|
regex_password = re.compile(regex_password)
|
||||||
|
|
||||||
|
###############################################################
|
||||||
|
############### CHECK ROLE ACCESS ##################
|
||||||
|
###############################################################
|
||||||
|
|
||||||
|
def login_admin(func):
|
||||||
|
@wraps(func)
|
||||||
|
def decorated_view(*args, **kwargs):
|
||||||
|
if not current_user.is_authenticated:
|
||||||
|
return login_manager.unauthorized()
|
||||||
|
elif (not current_user.is_in_role('admin')):
|
||||||
|
return login_manager.unauthorized()
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
return decorated_view
|
||||||
|
|
||||||
|
def login_analyst(func):
|
||||||
|
@wraps(func)
|
||||||
|
def decorated_view(*args, **kwargs):
|
||||||
|
if not current_user.is_authenticated:
|
||||||
|
return login_manager.unauthorized()
|
||||||
|
elif (not current_user.is_in_role('analyst')):
|
||||||
|
return login_manager.unauthorized()
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
return decorated_view
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################
|
||||||
|
###############################################################
|
||||||
|
###############################################################
|
||||||
|
|
||||||
|
def gen_password(length=30, charset="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_!@#$%^&*()"):
|
||||||
|
random_bytes = os.urandom(length)
|
||||||
|
len_charset = len(charset)
|
||||||
|
indices = [int(len_charset * (byte / 256.0)) for byte in random_bytes]
|
||||||
|
return "".join([charset[index] for index in indices])
|
||||||
|
|
||||||
|
def gen_token(length=41, charset="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"):
|
||||||
|
random_bytes = os.urandom(length)
|
||||||
|
len_charset = len(charset)
|
||||||
|
indices = [int(len_charset * (byte / 256.0)) for byte in random_bytes]
|
||||||
|
return "".join([charset[index] for index in indices])
|
||||||
|
|
||||||
|
def generate_new_token(user_id):
|
||||||
|
# create user token
|
||||||
|
current_token = r_serv_db.hget('user_metadata:{}'.format(user_id), 'token')
|
||||||
|
if current_token:
|
||||||
|
r_serv_db.hdel('user:tokens', current_token)
|
||||||
|
token = gen_token(41)
|
||||||
|
r_serv_db.hset('user:tokens', token, user_id)
|
||||||
|
r_serv_db.hset('user_metadata:{}'.format(user_id), 'token', token)
|
||||||
|
|
||||||
|
def get_default_admin_token():
|
||||||
|
if r_serv_db.exists('user_metadata:admin@admin.test'):
|
||||||
|
return r_serv_db.hget('user_metadata:admin@admin.test', 'token')
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def create_user_db(username_id , password, default=False, role=None, update=False):
|
||||||
|
password = password.encode()
|
||||||
|
password_hash = hashing_password(password)
|
||||||
|
|
||||||
|
# create user token
|
||||||
|
generate_new_token(username_id)
|
||||||
|
|
||||||
|
if update:
|
||||||
|
r_serv_db.hdel('user_metadata:{}'.format(username_id), 'change_passwd')
|
||||||
|
# remove default user password file
|
||||||
|
if username_id=='admin@admin.test':
|
||||||
|
os.remove(default_passwd_file)
|
||||||
|
else:
|
||||||
|
if default:
|
||||||
|
r_serv_db.hset('user_metadata:{}'.format(username_id), 'change_passwd', 'True')
|
||||||
|
if role:
|
||||||
|
if role in get_all_role():
|
||||||
|
for role_to_add in get_all_user_role(role):
|
||||||
|
r_serv_db.sadd('user_role:{}'.format(role_to_add), username_id)
|
||||||
|
r_serv_db.hset('user_metadata:{}'.format(username_id), 'role', role)
|
||||||
|
|
||||||
|
r_serv_db.hset('user:all', username_id, password_hash)
|
||||||
|
|
||||||
|
def edit_user_db(user_id, role, password=None):
|
||||||
|
if password:
|
||||||
|
password_hash = hashing_password(password.encode())
|
||||||
|
r_serv_db.hset('user:all', user_id, password_hash)
|
||||||
|
|
||||||
|
current_role = r_serv_db.hget('user_metadata:{}'.format(user_id), 'role')
|
||||||
|
if role != current_role:
|
||||||
|
request_level = get_role_level(role)
|
||||||
|
current_role = get_role_level(current_role)
|
||||||
|
|
||||||
|
if current_role < request_level:
|
||||||
|
role_to_remove = get_user_role_by_range(current_role -1, request_level - 2)
|
||||||
|
for role_id in role_to_remove:
|
||||||
|
r_serv_db.srem('user_role:{}'.format(role_id), user_id)
|
||||||
|
r_serv_db.hset('user_metadata:{}'.format(user_id), 'role', role)
|
||||||
|
else:
|
||||||
|
role_to_add = get_user_role_by_range(request_level -1, current_role)
|
||||||
|
for role_id in role_to_add:
|
||||||
|
r_serv_db.sadd('user_role:{}'.format(role_id), user_id)
|
||||||
|
r_serv_db.hset('user_metadata:{}'.format(user_id), 'role', role)
|
||||||
|
|
||||||
|
def delete_user_db(user_id):
|
||||||
|
if r_serv_db.exists('user_metadata:{}'.format(user_id)):
|
||||||
|
role_to_remove =get_all_role()
|
||||||
|
for role_id in role_to_remove:
|
||||||
|
r_serv_db.srem('user_role:{}'.format(role_id), user_id)
|
||||||
|
user_token = r_serv_db.hget('user_metadata:{}'.format(user_id), 'token')
|
||||||
|
r_serv_db.hdel('user:tokens', user_token)
|
||||||
|
r_serv_db.delete('user_metadata:{}'.format(user_id))
|
||||||
|
r_serv_db.hdel('user:all', user_id)
|
||||||
|
|
||||||
|
def hashing_password(bytes_password):
|
||||||
|
hashed = bcrypt.hashpw(bytes_password, bcrypt.gensalt())
|
||||||
|
return hashed
|
||||||
|
|
||||||
|
def check_password_strength(password):
|
||||||
|
result = regex_password.match(password)
|
||||||
|
if result:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_all_role():
|
||||||
|
return r_serv_db.zrange('d4:all_role', 0, -1)
|
||||||
|
|
||||||
|
def get_role_level(role):
|
||||||
|
return int(r_serv_db.zscore('d4:all_role', role))
|
||||||
|
|
||||||
|
def get_all_user_role(user_role):
|
||||||
|
current_role_val = get_role_level(user_role)
|
||||||
|
return r_serv_db.zrange('d4:all_role', current_role_val -1, -1)
|
||||||
|
|
||||||
|
def get_all_user_upper_role(user_role):
|
||||||
|
current_role_val = get_role_level(user_role)
|
||||||
|
# remove one rank
|
||||||
|
if current_role_val > 1:
|
||||||
|
return r_serv_db.zrange('d4:all_role', 0, current_role_val -2)
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def get_user_role_by_range(inf, sup):
|
||||||
|
return r_serv_db.zrange('d4:all_role', inf, sup)
|
||||||
|
|
||||||
|
def get_user_role(user_id):
|
||||||
|
return r_serv_db.hget('user_metadata:{}'.format(user_id), 'role')
|
||||||
|
|
||||||
|
def check_user_role_integrity(user_id):
|
||||||
|
user_role = get_user_role(user_id)
|
||||||
|
all_user_role = get_all_user_role(user_role)
|
||||||
|
res = True
|
||||||
|
if user_role not in all_user_role:
|
||||||
|
return False
|
||||||
|
return res
|
|
@ -0,0 +1,46 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*-coding:UTF-8 -*
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import redis
|
||||||
|
import configparser
|
||||||
|
|
||||||
|
sys.path.append(os.path.join(os.environ['D4_HOME'], 'lib'))
|
||||||
|
|
||||||
|
from Role_Manager import create_user_db, edit_user_db, get_default_admin_token, gen_password
|
||||||
|
|
||||||
|
host_redis_metadata = os.getenv('D4_REDIS_METADATA_HOST', "localhost")
|
||||||
|
port_redis_metadata = int(os.getenv('D4_REDIS_METADATA_HOST', 6380))
|
||||||
|
|
||||||
|
r_serv = redis.StrictRedis(
|
||||||
|
host=host_redis_metadata,
|
||||||
|
port=port_redis_metadata,
|
||||||
|
db=1,
|
||||||
|
decode_responses=True)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
# create role_list
|
||||||
|
if not r_serv.exists('d4:all_role'):
|
||||||
|
role_dict = {'admin': 1, 'user': 1, 'sensor_register': 20}
|
||||||
|
r_serv.zadd('d4:all_role', role_dict)
|
||||||
|
|
||||||
|
username = 'admin@admin.test'
|
||||||
|
password = gen_password()
|
||||||
|
if r_serv.exists('user_metadata:admin@admin.test'):
|
||||||
|
edit_user_db(username, password=password, role='admin')
|
||||||
|
else:
|
||||||
|
create_user_db(username, password, role='admin', default=True)
|
||||||
|
token = get_default_admin_token()
|
||||||
|
|
||||||
|
default_passwd_file = os.path.join(os.environ['D4_HOME'], 'DEFAULT_PASSWORD')
|
||||||
|
to_write_str = '# Password Generated by default\n# This file is deleted after the first login\n#\nemail=admin@admin.test\npassword='
|
||||||
|
to_write_str = to_write_str + password + '\nAPI_Key=' + token
|
||||||
|
with open(default_passwd_file, 'w') as f:
|
||||||
|
f.write(to_write_str)
|
||||||
|
|
||||||
|
print('new user created: {}'.format(username))
|
||||||
|
print('password: {}'.format(password))
|
||||||
|
print('token: {}'.format(token))
|
|
@ -0,0 +1,67 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>403 - D4-Project</title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='image/d4-logo.png') }}">
|
||||||
|
|
||||||
|
<!-- Core CSS -->
|
||||||
|
<link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet">
|
||||||
|
<link href="{{ url_for('static', filename='font-awesome/css/font-awesome.css') }}" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<nav class="navbar navbar-expand-sm navbar-dark bg-dark">
|
||||||
|
<a class="navbar-brand" href="{{ url_for('index') }}">
|
||||||
|
<img src="{{ url_for('static', filename='img/d4-logo.png')}}" alt="D4 Project" style="width:80px;">
|
||||||
|
</a>
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link mr-3" href="{{ url_for('index') }}">Home <span class="sr-only">(current)</span></a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" mr-3>
|
||||||
|
<a class="nav-link mr-3" href="{{ url_for('sensors_status') }}">Sensors Status</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item mr-3">
|
||||||
|
<a class="nav-link" href="{{ url_for('server_management') }}" tabindex="-1" aria-disabled="true">Server Management</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<h1 class="text-center">403 Forbidden</h1>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<div class="d-flex justify-content-center">
|
||||||
|
<pre>
|
||||||
|
,d8 ,a8888a, ad888888b,
|
||||||
|
,d888 ,8P"' `"Y8, d8" "88
|
||||||
|
,d8" 88 ,8P Y8, a8P
|
||||||
|
,d8" 88 88 88 aad8"
|
||||||
|
,d8" 88 88 88 ""Y8,
|
||||||
|
8888888888888 `8b d8' "8b
|
||||||
|
88 `8ba, ,ad8' Y8, a88
|
||||||
|
88 "Y8888P" "Y888888P'
|
||||||
|
|
||||||
|
88888888888 88 88 88 88
|
||||||
|
88 88 "" 88 88
|
||||||
|
88 88 88 88
|
||||||
|
88aaaaa ,adPPYba, 8b,dPPYba, 88,dPPYba, 88 ,adPPYb,88 ,adPPYb,88 ,adPPYba, 8b,dPPYba,
|
||||||
|
88""""" a8" "8a 88P' "Y8 88P' "8a 88 a8" `Y88 a8" `Y88 a8P_____88 88P' `"8a
|
||||||
|
88 8b d8 88 88 d8 88 8b 88 8b 88 8PP""""""" 88 88
|
||||||
|
88 "8a, ,a8" 88 88b, ,a8" 88 "8a, ,d88 "8a, ,d88 "8b, ,aa 88 88
|
||||||
|
88 `"YbbdP"' 88 8Y"Ybbd8"' 88 `"8bbdP"Y8 `"8bbdP"Y8 `"Ybbd8"' 88 88
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% include 'navfooter.html' %}
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -0,0 +1,108 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<title>D4-Project</title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='img/d4-logo.png')}}">
|
||||||
|
<!-- Core CSS -->
|
||||||
|
<link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet">
|
||||||
|
<link href="{{ url_for('static', filename='font-awesome/css/font-awesome.css') }}" rel="stylesheet">
|
||||||
|
|
||||||
|
<!-- JS -->
|
||||||
|
<script src="{{ url_for('static', filename='js/jquery.js')}}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/bootstrap.min.js')}}"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-ms-flex-align: center;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 40px;
|
||||||
|
padding-bottom: 40px;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-signin {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 330px;
|
||||||
|
padding: 15px;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
.form-signin .checkbox {
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
.form-signin .form-control {
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: auto;
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.form-signin .form-control:focus {
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
.form-signin input[type="password"] {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="text-center">
|
||||||
|
|
||||||
|
|
||||||
|
<form class="form-signin" action="{{ url_for('change_password')}}" autocomplete="off" method="post">
|
||||||
|
<img class="mb-4" src="{{ url_for('static', filename='img/d4-logo.png')}}" width="300">
|
||||||
|
<h1 class="h3 mb-3 text-secondary">Change Password</h1>
|
||||||
|
<label for="inputPassword1" class="sr-only">Password</label>
|
||||||
|
<input type="password" id="inputPassword1" name="password1" class="form-control {% if error %}is-invalid{% endif %}" placeholder="Password" autocomplete="new-password" required autofocus>
|
||||||
|
<label for="inputPassword2" class="sr-only">Confirm Password</label>
|
||||||
|
<input type="password" id="inputPassword2" name="password2" class="form-control {% if error %}is-invalid{% endif %}" placeholder="Confirm Password" value="" autocomplete="new-password" required>
|
||||||
|
{% if error %}
|
||||||
|
<div class="invalid-feedback">
|
||||||
|
{{error}}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<button class="btn btn-lg btn-primary btn-block" type="submit">Submit</button>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<h5 class="h3 mb-3 text-secondary">Password Requirements</h5>
|
||||||
|
<ul class="list-group">
|
||||||
|
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||||
|
Minimal length
|
||||||
|
<span class="badge badge-primary badge-pill">10</span>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||||
|
Upper characters: A-Z
|
||||||
|
<span class="badge badge-primary badge-pill">1</span>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||||
|
Lower characters: a-z
|
||||||
|
<span class="badge badge-primary badge-pill">1</span>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||||
|
Digits: 0-9
|
||||||
|
<span class="badge badge-primary badge-pill">2</span>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||||
|
Maximum length
|
||||||
|
<span class="badge badge-primary badge-pill">100</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
|
@ -0,0 +1,85 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<title>D4-Project</title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='img/d4-logo.png')}}">
|
||||||
|
<!-- Core CSS -->
|
||||||
|
<link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet">
|
||||||
|
<link href="{{ url_for('static', filename='font-awesome/css/font-awesome.css') }}" rel="stylesheet">
|
||||||
|
|
||||||
|
<!-- JS -->
|
||||||
|
<script src="{{ url_for('static', filename='js/jquery.js')}}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/bootstrap.min.js')}}"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-ms-flex-align: center;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 40px;
|
||||||
|
padding-bottom: 40px;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-signin {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 330px;
|
||||||
|
padding: 15px;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
.form-signin .checkbox {
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
.form-signin .form-control {
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: auto;
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.form-signin .form-control:focus {
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
.form-signin input[type="email"] {
|
||||||
|
margin-bottom: -1px;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
}
|
||||||
|
.form-signin input[type="password"] {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="text-center">
|
||||||
|
|
||||||
|
|
||||||
|
<form class="form-signin" action="{{ url_for('login')}}" method="post">
|
||||||
|
<img class="mb-4" src="{{ url_for('static', filename='img/d4-logo.png')}}" width="300">
|
||||||
|
<h1 class="h3 mb-3 text-secondary">Please sign in</h1>
|
||||||
|
<label for="inputEmail" class="sr-only">Email address</label>
|
||||||
|
<input type="email" id="inputEmail" name="username" class="form-control" placeholder="Email address" required autofocus>
|
||||||
|
<label for="inputPassword" class="sr-only">Password</label>
|
||||||
|
<input type="password" id="inputPassword" name="password" class="form-control {% if error %}is-invalid{% endif %}" placeholder="Password" required>
|
||||||
|
{% if error %}
|
||||||
|
<div class="invalid-feedback">
|
||||||
|
{{error}}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
Loading…
Reference in New Issue