mirror of https://github.com/D4-project/d4-core
commit
ff256984a3
|
@ -11,10 +11,10 @@ CYAN="\\033[1;36m"
|
||||||
|
|
||||||
. ./D4ENV/bin/activate
|
. ./D4ENV/bin/activate
|
||||||
|
|
||||||
isredis=`screen -ls | egrep '[0-9]+.Redis_D4' | cut -d. -f1`
|
isredis=`screen -ls | egrep '[0-9]+.Redis_D4 ' | cut -d. -f1`
|
||||||
isd4server=`screen -ls | egrep '[0-9]+.Server_D4' | cut -d. -f1`
|
isd4server=`screen -ls | egrep '[0-9]+.Server_D4 ' | cut -d. -f1`
|
||||||
isworker=`screen -ls | egrep '[0-9]+.Workers_D4' | cut -d. -f1`
|
isworker=`screen -ls | egrep '[0-9]+.Workers_D4 ' | cut -d. -f1`
|
||||||
isflask=`screen -ls | egrep '[0-9]+.Flask_D4' | cut -d. -f1`
|
isflask=`screen -ls | egrep '[0-9]+.Flask_D4 ' | cut -d. -f1`
|
||||||
|
|
||||||
function helptext {
|
function helptext {
|
||||||
echo -e $YELLOW"
|
echo -e $YELLOW"
|
||||||
|
@ -109,6 +109,18 @@ function checking_redis {
|
||||||
return $flag_redis;
|
return $flag_redis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function wait_until_redis_is_ready {
|
||||||
|
redis_not_ready=true
|
||||||
|
while $redis_not_ready; do
|
||||||
|
if checking_redis; then
|
||||||
|
redis_not_ready=false;
|
||||||
|
else
|
||||||
|
sleep 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo -e $YELLOW"\t* Redis Launched"$DEFAULT
|
||||||
|
}
|
||||||
|
|
||||||
function launch_redis {
|
function launch_redis {
|
||||||
if [[ ! $isredis ]]; then
|
if [[ ! $isredis ]]; then
|
||||||
launching_redis;
|
launching_redis;
|
||||||
|
@ -275,16 +287,19 @@ function launch_all {
|
||||||
|
|
||||||
while [ "$1" != "" ]; do
|
while [ "$1" != "" ]; do
|
||||||
case $1 in
|
case $1 in
|
||||||
-l | --launchAuto ) launch_all;
|
-l | --launchAuto ) launch_all;
|
||||||
;;
|
;;
|
||||||
-k | --killAll ) helptext;
|
-k | --killAll ) helptext;
|
||||||
killall;
|
killall;
|
||||||
;;
|
;;
|
||||||
-h | --help ) helptext;
|
-lrv | --launchRedisVerify ) launch_redis;
|
||||||
exit
|
wait_until_redis_is_ready;
|
||||||
;;
|
;;
|
||||||
* ) helptext
|
-h | --help ) helptext;
|
||||||
exit 1
|
exit
|
||||||
|
;;
|
||||||
|
* ) helptext
|
||||||
|
exit 1
|
||||||
esac
|
esac
|
||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
|
|
|
@ -42,6 +42,11 @@ cd web
|
||||||
./update_web.sh
|
./update_web.sh
|
||||||
~~~~
|
~~~~
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
[API Documentation](https://github.com/D4-project/d4-core/tree/master/server/documentation/README.md)
|
||||||
|
|
||||||
|
|
||||||
### Notes
|
### Notes
|
||||||
|
|
||||||
- All server logs are located in ``d4-core/server/logs/``
|
- All server logs are located in ``d4-core/server/logs/``
|
||||||
|
|
|
@ -7,12 +7,36 @@
|
||||||
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 3 - Flask Cache
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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 |
|
||||||
|
@ -63,8 +87,10 @@ sensor registrations, management of decoding protocols and dispatching to adequa
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| metadata_uuid:**uuid** | first_seen | **epoch** |
|
| metadata_uuid:**uuid** | first_seen | **epoch** |
|
||||||
| | last_seen | **epoch** |
|
| | last_seen | **epoch** |
|
||||||
| | description | **description** |
|
| | description | **description** | (optionnal)
|
||||||
| | Error | **error message** |
|
| | Error | **error message** | (optionnal)
|
||||||
|
| | hmac_key | **hmac_key** | (optionnal)
|
||||||
|
| | user_id | **user_id** | (optionnal)
|
||||||
|
|
||||||
###### Last IP
|
###### Last IP
|
||||||
| List Key | Value |
|
| List Key | Value |
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
# API DOCUMENTATION
|
||||||
|
|
||||||
|
## General
|
||||||
|
|
||||||
|
### Automation key
|
||||||
|
|
||||||
|
The authentication of the automation is performed via a secure key available in the D4 UI interface. Make sure you keep that key secret. It gives access to the entire database! The API key is available in the ``Settings`` menu under ``My Profile``.
|
||||||
|
|
||||||
|
The authorization is performed by using the following header:
|
||||||
|
|
||||||
|
~~~~
|
||||||
|
Authorization: YOUR_API_KEY
|
||||||
|
~~~~
|
||||||
|
### Accept and Content-Type headers
|
||||||
|
|
||||||
|
When submitting data in a POST, PUT or DELETE operation you need to specify in what content-type you encoded the payload. This is done by setting the below Content-Type headers:
|
||||||
|
|
||||||
|
~~~~
|
||||||
|
Content-Type: application/json
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
~~~~
|
||||||
|
curl --header "Authorization: YOUR_API_KEY" --header "Content-Type: application/json" https://D4_URL/
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
## Sensor Registration
|
||||||
|
|
||||||
|
### Register a sensor: `api/v1/add/sensor/register`<a name="add_sensor_register"></a>
|
||||||
|
|
||||||
|
#### Description
|
||||||
|
Register a sensor.
|
||||||
|
|
||||||
|
**Method** : `POST`
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
- `uuid`
|
||||||
|
- sensor uuid
|
||||||
|
- *uuid4*
|
||||||
|
- mandatory
|
||||||
|
|
||||||
|
- `hmac_key`
|
||||||
|
- sensor secret key
|
||||||
|
- *binary*
|
||||||
|
- mandatory
|
||||||
|
|
||||||
|
- `description`
|
||||||
|
- sensor description
|
||||||
|
- *str*
|
||||||
|
|
||||||
|
- `mail`
|
||||||
|
- user mail
|
||||||
|
- *str*
|
||||||
|
|
||||||
|
#### JSON response
|
||||||
|
- `uuid`
|
||||||
|
- sensor uuid
|
||||||
|
- *uuid4*
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```
|
||||||
|
curl https://127.0.0.1:7000/api/v1/add/sensor/register --header "Authorization: iHc1_ChZxj1aXmiFiF1mkxxQkzawwriEaZpPqyTQj " -H "Content-Type: application/json" --data @input.json -X POST
|
||||||
|
```
|
||||||
|
|
||||||
|
#### input.json Example
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"uuid": "ff7ba400-e76c-4053-982d-feec42bdef38",
|
||||||
|
"hmac_key": "...HMAC_KEY..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Expected Success Response
|
||||||
|
**HTTP Status Code** : `200`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"uuid": "ff7ba400-e76c-4053-982d-feec42bdef38",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Expected Fail Response
|
||||||
|
|
||||||
|
**HTTP Status Code** : `400`
|
||||||
|
```json
|
||||||
|
{"status": "error", "reason": "Mandatory parameter(s) not provided"}
|
||||||
|
{"status": "error", "reason": "Invalid uuid"}
|
||||||
|
```
|
||||||
|
|
||||||
|
**HTTP Status Code** : `409`
|
||||||
|
```json
|
||||||
|
{"status": "error", "reason": "Sensor already registred"}
|
||||||
|
```
|
|
@ -25,3 +25,17 @@ pushd redis/
|
||||||
git checkout 5.0
|
git checkout 5.0
|
||||||
make
|
make
|
||||||
popd
|
popd
|
||||||
|
|
||||||
|
# LAUNCH Redis
|
||||||
|
bash ${AIL_BIN}LAUNCH.sh -lrv &
|
||||||
|
wait
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# create default users
|
||||||
|
pushd web/
|
||||||
|
./create_default_user.py
|
||||||
|
popd
|
||||||
|
|
||||||
|
bash LAUNCH.sh -k &
|
||||||
|
wait
|
||||||
|
echo ""
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*-coding:UTF-8 -*
|
||||||
|
|
||||||
|
import os
|
||||||
|
import uuid
|
||||||
|
import redis
|
||||||
|
|
||||||
|
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=0)
|
||||||
|
|
||||||
|
def is_valid_uuid_v4(UUID):
|
||||||
|
UUID = UUID.replace('-', '')
|
||||||
|
try:
|
||||||
|
uuid_test = uuid.UUID(hex=UUID, version=4)
|
||||||
|
return uuid_test.hex == UUID
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
## TODO: add description
|
||||||
|
def register_sensor(req_dict):
|
||||||
|
sensor_uuid = req_dict.get('uuid', None)
|
||||||
|
hmac_key = req_dict.get('hmac_key', None)
|
||||||
|
user_id = req_dict.get('mail', None)
|
||||||
|
# verify uuid
|
||||||
|
if not is_valid_uuid_v4(sensor_uuid):
|
||||||
|
return ({"status": "error", "reason": "Invalid uuid"}, 400)
|
||||||
|
sensor_uuid = sensor_uuid.replace('-', '')
|
||||||
|
# sensor already exist
|
||||||
|
if r_serv_db.exists('metadata_uuid:{}'.format(sensor_uuid)):
|
||||||
|
return ({"status": "error", "reason": "Sensor already registred"}, 409)
|
||||||
|
|
||||||
|
res = _register_sensor(sensor_uuid, hmac_key, user_id=user_id, description=None)
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def _register_sensor(sensor_uuid, secret_key, user_id=None, description=None):
|
||||||
|
r_serv_db.hset('metadata_uuid:{}'.format(sensor_uuid), 'hmac_key', secret_key)
|
||||||
|
if user_id:
|
||||||
|
r_serv_db.hset('metadata_uuid:{}'.format(sensor_uuid), 'user_mail', user_id)
|
||||||
|
if description:
|
||||||
|
r_serv_db.hset('metadata_uuid:{}'.format(sensor_uuid), 'description', description)
|
||||||
|
return ({'uuid': sensor_uuid}, 200)
|
|
@ -0,0 +1,69 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*-coding:UTF-8 -*
|
||||||
|
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import redis
|
||||||
|
import bcrypt
|
||||||
|
import random
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
rand_sleep = random.randint(1,300)/1000
|
||||||
|
time.sleep(rand_sleep)
|
||||||
|
|
||||||
|
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,19 +3,35 @@
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
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, Response
|
||||||
|
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_user_basic
|
||||||
|
|
||||||
|
sys.path.append(os.path.join(os.environ['D4_HOME'], 'lib'))
|
||||||
|
from User import User
|
||||||
|
|
||||||
|
# Import Blueprint
|
||||||
|
from blueprints.restApi import restApi
|
||||||
|
from blueprints.settings import settings
|
||||||
|
|
||||||
baseUrl = ''
|
baseUrl = ''
|
||||||
if baseUrl != '':
|
if baseUrl != '':
|
||||||
|
@ -59,21 +75,60 @@ 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,
|
||||||
db=2,
|
db=2,
|
||||||
decode_responses=True)
|
decode_responses=True)
|
||||||
|
|
||||||
|
r_cache = redis.StrictRedis(
|
||||||
|
host=host_redis_metadata,
|
||||||
|
port=port_redis_metadata,
|
||||||
|
db=3,
|
||||||
|
decode_responses=True)
|
||||||
|
|
||||||
with open(json_type_description_path, 'r') as f:
|
with open(json_type_description_path, 'r') as f:
|
||||||
json_type = json.loads(f.read())
|
json_type = json.loads(f.read())
|
||||||
json_type_description = {}
|
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)
|
||||||
|
# ========= =========#
|
||||||
|
|
||||||
|
# ========= BLUEPRINT =========#
|
||||||
|
app.register_blueprint(restApi)
|
||||||
|
app.register_blueprint(settings)
|
||||||
|
# ========= =========#
|
||||||
|
|
||||||
|
# ========= 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 +250,131 @@ 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('/test')
|
@app.route('/login', methods=['POST', 'GET'])
|
||||||
def test():
|
def login():
|
||||||
return 'test'
|
|
||||||
|
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('/')
|
@app.route('/')
|
||||||
|
@login_required
|
||||||
|
@login_user_basic
|
||||||
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
|
||||||
|
@login_user_basic
|
||||||
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 +386,8 @@ 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
|
||||||
|
@login_user_basic
|
||||||
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 +404,8 @@ 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
|
||||||
|
@login_user_basic
|
||||||
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 +485,8 @@ 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
|
||||||
|
@login_user_basic
|
||||||
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 +501,8 @@ 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
|
||||||
|
@login_user_basic
|
||||||
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 +573,8 @@ 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
|
||||||
|
@login_user_basic
|
||||||
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 +647,8 @@ def uuid_management():
|
||||||
return 'Invalid uuid'
|
return 'Invalid uuid'
|
||||||
|
|
||||||
@app.route('/blacklisted_ip')
|
@app.route('/blacklisted_ip')
|
||||||
|
@login_required
|
||||||
|
@login_user_basic
|
||||||
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 +674,8 @@ 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
|
||||||
|
@login_user_basic
|
||||||
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 +702,8 @@ def blacklisted_uuid():
|
||||||
|
|
||||||
|
|
||||||
@app.route('/uuid_change_stream_max_size')
|
@app.route('/uuid_change_stream_max_size')
|
||||||
|
@login_required
|
||||||
|
@login_user_basic
|
||||||
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 +722,8 @@ def uuid_change_stream_max_size():
|
||||||
return 'Invalid uuid'
|
return 'Invalid uuid'
|
||||||
|
|
||||||
@app.route('/uuid_change_description')
|
@app.route('/uuid_change_description')
|
||||||
|
@login_required
|
||||||
|
@login_user_basic
|
||||||
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 +735,8 @@ 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
|
||||||
|
@login_user_basic
|
||||||
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 +763,8 @@ def add_new_analyzer():
|
||||||
return 'Invalid uuid'
|
return 'Invalid uuid'
|
||||||
|
|
||||||
@app.route('/empty_analyzer_queue')
|
@app.route('/empty_analyzer_queue')
|
||||||
|
@login_required
|
||||||
|
@login_user_basic
|
||||||
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 +787,8 @@ def empty_analyzer_queue():
|
||||||
return 'Invalid uuid'
|
return 'Invalid uuid'
|
||||||
|
|
||||||
@app.route('/remove_analyzer')
|
@app.route('/remove_analyzer')
|
||||||
|
@login_required
|
||||||
|
@login_user_basic
|
||||||
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 +814,8 @@ def remove_analyzer():
|
||||||
return 'Invalid uuid'
|
return 'Invalid uuid'
|
||||||
|
|
||||||
@app.route('/analyzer_change_max_size')
|
@app.route('/analyzer_change_max_size')
|
||||||
|
@login_required
|
||||||
|
@login_user_basic
|
||||||
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 +834,8 @@ def analyzer_change_max_size():
|
||||||
return 'Invalid uuid'
|
return 'Invalid uuid'
|
||||||
|
|
||||||
@app.route('/kick_uuid')
|
@app.route('/kick_uuid')
|
||||||
|
@login_required
|
||||||
|
@login_user_basic
|
||||||
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 +845,8 @@ def kick_uuid():
|
||||||
return 'Invalid uuid'
|
return 'Invalid uuid'
|
||||||
|
|
||||||
@app.route('/blacklist_uuid')
|
@app.route('/blacklist_uuid')
|
||||||
|
@login_required
|
||||||
|
@login_user_basic
|
||||||
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 +867,8 @@ def blacklist_uuid():
|
||||||
return 'Invalid uuid'
|
return 'Invalid uuid'
|
||||||
|
|
||||||
@app.route('/unblacklist_uuid')
|
@app.route('/unblacklist_uuid')
|
||||||
|
@login_required
|
||||||
|
@login_user_basic
|
||||||
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 +892,8 @@ def unblacklist_uuid():
|
||||||
return 'Invalid uuid'
|
return 'Invalid uuid'
|
||||||
|
|
||||||
@app.route('/blacklist_ip')
|
@app.route('/blacklist_ip')
|
||||||
|
@login_required
|
||||||
|
@login_user_basic
|
||||||
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 +919,8 @@ def blacklist_ip():
|
||||||
return 'Invalid ip'
|
return 'Invalid ip'
|
||||||
|
|
||||||
@app.route('/unblacklist_ip')
|
@app.route('/unblacklist_ip')
|
||||||
|
@login_required
|
||||||
|
@login_user_basic
|
||||||
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 +948,8 @@ def unblacklist_ip():
|
||||||
return 'Invalid ip'
|
return 'Invalid ip'
|
||||||
|
|
||||||
@app.route('/blacklist_ip_by_uuid')
|
@app.route('/blacklist_ip_by_uuid')
|
||||||
|
@login_required
|
||||||
|
@login_user_basic
|
||||||
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 +961,8 @@ 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
|
||||||
|
@login_user_basic
|
||||||
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 +974,8 @@ def unblacklist_ip_by_uuid():
|
||||||
return 'Invalid uuid'
|
return 'Invalid uuid'
|
||||||
|
|
||||||
@app.route('/add_accepted_type')
|
@app.route('/add_accepted_type')
|
||||||
|
@login_required
|
||||||
|
@login_user_basic
|
||||||
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 +995,8 @@ def add_accepted_type():
|
||||||
return 'Invalid type'
|
return 'Invalid type'
|
||||||
|
|
||||||
@app.route('/remove_accepted_type')
|
@app.route('/remove_accepted_type')
|
||||||
|
@login_required
|
||||||
|
@login_user_basic
|
||||||
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 +1009,8 @@ def remove_accepted_type():
|
||||||
return 'Invalid type'
|
return 'Invalid type'
|
||||||
|
|
||||||
@app.route('/remove_accepted_extended_type')
|
@app.route('/remove_accepted_extended_type')
|
||||||
|
@login_required
|
||||||
|
@login_user_basic
|
||||||
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 +1018,8 @@ def remove_accepted_extended_type():
|
||||||
|
|
||||||
# demo function
|
# demo function
|
||||||
@app.route('/delete_data')
|
@app.route('/delete_data')
|
||||||
|
@login_required
|
||||||
|
@login_user_basic
|
||||||
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 +1028,8 @@ def delete_data():
|
||||||
|
|
||||||
# demo function
|
# demo function
|
||||||
@app.route('/set_uuid_hmac_key')
|
@app.route('/set_uuid_hmac_key')
|
||||||
|
@login_required
|
||||||
|
@login_user_basic
|
||||||
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 +1041,8 @@ def set_uuid_hmac_key():
|
||||||
|
|
||||||
# demo function
|
# demo function
|
||||||
@app.route('/whois_data')
|
@app.route('/whois_data')
|
||||||
|
@login_required
|
||||||
|
@login_user_basic
|
||||||
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 +1051,15 @@ def whois_data():
|
||||||
return 'Invalid IP'
|
return 'Invalid IP'
|
||||||
|
|
||||||
@app.route('/generate_uuid')
|
@app.route('/generate_uuid')
|
||||||
|
@login_required
|
||||||
|
@login_user_basic
|
||||||
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
|
||||||
|
@login_user_basic
|
||||||
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 +1087,8 @@ 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
|
||||||
|
@login_user_basic
|
||||||
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 +1119,8 @@ 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
|
||||||
|
@login_user_basic
|
||||||
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 +1152,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_user_basic(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('user')):
|
||||||
|
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.zrangebyscore('d4:all_role', current_role_val, 50)
|
||||||
|
|
||||||
|
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,168 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*-coding:UTF-8 -*
|
||||||
|
|
||||||
|
'''
|
||||||
|
Flask functions and routes for the rest api
|
||||||
|
'''
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import uuid
|
||||||
|
import json
|
||||||
|
import redis
|
||||||
|
import random
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from flask import Flask, render_template, jsonify, request, Blueprint, redirect, url_for, Response
|
||||||
|
from flask_login import login_required
|
||||||
|
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
sys.path.append(os.path.join(os.environ['D4_HOME'], 'lib'))
|
||||||
|
import Sensor
|
||||||
|
|
||||||
|
# ============ BLUEPRINT ============
|
||||||
|
|
||||||
|
restApi = Blueprint('restApi', __name__, template_folder='templates')
|
||||||
|
|
||||||
|
# ============ VARIABLES ============
|
||||||
|
|
||||||
|
host_redis_metadata = os.getenv('D4_REDIS_METADATA_HOST', "localhost")
|
||||||
|
port_redis_metadata = int(os.getenv('D4_REDIS_METADATA_PORT', 6380))
|
||||||
|
|
||||||
|
r_serv_metadata = redis.StrictRedis(
|
||||||
|
host=host_redis_metadata,
|
||||||
|
port=port_redis_metadata,
|
||||||
|
db=0,
|
||||||
|
decode_responses=True)
|
||||||
|
|
||||||
|
r_serv_db = redis.StrictRedis(
|
||||||
|
host=host_redis_metadata,
|
||||||
|
port=port_redis_metadata,
|
||||||
|
db=1,
|
||||||
|
decode_responses=True)
|
||||||
|
|
||||||
|
r_cache = redis.StrictRedis(
|
||||||
|
host=host_redis_metadata,
|
||||||
|
port=port_redis_metadata,
|
||||||
|
db=3,
|
||||||
|
decode_responses=True)
|
||||||
|
|
||||||
|
# ============ AUTH FUNCTIONS ============
|
||||||
|
|
||||||
|
def check_token_format(strg, search=re.compile(r'[^a-zA-Z0-9_-]').search):
|
||||||
|
return not bool(search(strg))
|
||||||
|
|
||||||
|
def verify_token(token):
|
||||||
|
if len(token) != 41:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not check_token_format(token):
|
||||||
|
return False
|
||||||
|
|
||||||
|
rand_sleep = random.randint(1,300)/1000
|
||||||
|
time.sleep(rand_sleep)
|
||||||
|
if r_serv_db.hexists('user:tokens', token):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_user_from_token(token):
|
||||||
|
return r_serv_db.hget('user:tokens', token)
|
||||||
|
|
||||||
|
def verify_user_role(role, token):
|
||||||
|
user_id = get_user_from_token(token)
|
||||||
|
if user_id:
|
||||||
|
if is_in_role(user_id, role):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_in_role(user_id, role):
|
||||||
|
if r_serv_db.sismember('user_role:{}'.format(role), user_id):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# ============ DECORATOR ============
|
||||||
|
|
||||||
|
def token_required(user_role):
|
||||||
|
def actual_decorator(funct):
|
||||||
|
@wraps(funct)
|
||||||
|
def api_token(*args, **kwargs):
|
||||||
|
data = authErrors(user_role)
|
||||||
|
if data:
|
||||||
|
return Response(json.dumps(data[0], indent=2, sort_keys=True), mimetype='application/json'), data[1]
|
||||||
|
else:
|
||||||
|
return funct(*args, **kwargs)
|
||||||
|
return api_token
|
||||||
|
return actual_decorator
|
||||||
|
|
||||||
|
def get_auth_from_header():
|
||||||
|
token = request.headers.get('Authorization').replace(' ', '') # remove space
|
||||||
|
return token
|
||||||
|
|
||||||
|
def authErrors(user_role):
|
||||||
|
# Check auth
|
||||||
|
if not request.headers.get('Authorization'):
|
||||||
|
return ({'status': 'error', 'reason': 'Authentication needed'}, 401)
|
||||||
|
token = get_auth_from_header()
|
||||||
|
data = None
|
||||||
|
# verify token format
|
||||||
|
|
||||||
|
# brute force protection
|
||||||
|
current_ip = request.remote_addr
|
||||||
|
login_failed_ip = r_cache.get('failed_login_ip_api:{}'.format(current_ip))
|
||||||
|
# brute force by ip
|
||||||
|
if login_failed_ip:
|
||||||
|
login_failed_ip = int(login_failed_ip)
|
||||||
|
if login_failed_ip >= 5:
|
||||||
|
return ({'status': 'error', 'reason': 'Max Connection Attempts reached, Please wait {}s'.format(r_cache.ttl('failed_login_ip_api:{}'.format(current_ip)))}, 401)
|
||||||
|
|
||||||
|
try:
|
||||||
|
authenticated = False
|
||||||
|
if verify_token(token):
|
||||||
|
authenticated = True
|
||||||
|
|
||||||
|
# check user role
|
||||||
|
if not verify_user_role(user_role, token):
|
||||||
|
data = ({'status': 'error', 'reason': 'Access Forbidden'}, 403)
|
||||||
|
|
||||||
|
if not authenticated:
|
||||||
|
r_cache.incr('failed_login_ip_api:{}'.format(current_ip))
|
||||||
|
r_cache.expire('failed_login_ip_api:{}'.format(current_ip), 300)
|
||||||
|
data = ({'status': 'error', 'reason': 'Authentication failed'}, 401)
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
data = ({'status': 'error', 'reason': 'Malformed Authentication String'}, 400)
|
||||||
|
if data:
|
||||||
|
return data
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# ============ FUNCTIONS ============
|
||||||
|
|
||||||
|
def is_valid_uuid_v4(header_uuid):
|
||||||
|
try:
|
||||||
|
header_uuid=header_uuid.replace('-', '')
|
||||||
|
uuid_test = uuid.UUID(hex=header_uuid, version=4)
|
||||||
|
return uuid_test.hex == header_uuid
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def one():
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# ============= ROUTES ==============
|
||||||
|
|
||||||
|
|
||||||
|
@restApi.route("/api/v1/add/sensor/register", methods=['POST'])
|
||||||
|
@token_required('sensor_register')
|
||||||
|
def add_sensor_register():
|
||||||
|
data = request.get_json()
|
||||||
|
res = Sensor.register_sensor(data)
|
||||||
|
return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
|
|
@ -0,0 +1,188 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*-coding:UTF-8 -*
|
||||||
|
|
||||||
|
'''
|
||||||
|
Flask functions and routes for the rest api
|
||||||
|
'''
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import redis
|
||||||
|
|
||||||
|
from flask import Flask, render_template, jsonify, request, Blueprint, redirect, url_for, Response
|
||||||
|
from flask_login import login_required, current_user
|
||||||
|
|
||||||
|
from Role_Manager import login_admin, login_user_basic
|
||||||
|
from Role_Manager import create_user_db, edit_user_db, delete_user_db, check_password_strength, generate_new_token, gen_password, get_all_role
|
||||||
|
|
||||||
|
# ============ BLUEPRINT ============
|
||||||
|
|
||||||
|
settings = Blueprint('settings', __name__, template_folder='templates')
|
||||||
|
|
||||||
|
# ============ VARIABLES ============
|
||||||
|
|
||||||
|
email_regex = r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}'
|
||||||
|
email_regex = re.compile(email_regex)
|
||||||
|
host_redis_metadata = os.getenv('D4_REDIS_METADATA_HOST', "localhost")
|
||||||
|
port_redis_metadata = int(os.getenv('D4_REDIS_METADATA_PORT', 6380))
|
||||||
|
|
||||||
|
r_serv_metadata = redis.StrictRedis(
|
||||||
|
host=host_redis_metadata,
|
||||||
|
port=port_redis_metadata,
|
||||||
|
db=0,
|
||||||
|
decode_responses=True)
|
||||||
|
|
||||||
|
r_serv_db = redis.StrictRedis(
|
||||||
|
host=host_redis_metadata,
|
||||||
|
port=port_redis_metadata,
|
||||||
|
db=1,
|
||||||
|
decode_responses=True)
|
||||||
|
|
||||||
|
# ============ FUNCTIONS ============
|
||||||
|
|
||||||
|
def one():
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def check_email(email):
|
||||||
|
return email_regex.match(email)
|
||||||
|
|
||||||
|
def get_user_metadata(user_id):
|
||||||
|
user_metadata = {}
|
||||||
|
user_metadata['email'] = user_id
|
||||||
|
user_metadata['role'] = r_serv_db.hget('user_metadata:{}'.format(user_id), 'role')
|
||||||
|
user_metadata['api_key'] = r_serv_db.hget('user_metadata:{}'.format(user_id), 'token')
|
||||||
|
return user_metadata
|
||||||
|
|
||||||
|
def get_users_metadata(list_users):
|
||||||
|
users = []
|
||||||
|
for user in list_users:
|
||||||
|
users.append(get_user_metadata(user))
|
||||||
|
return users
|
||||||
|
|
||||||
|
def get_all_users():
|
||||||
|
return r_serv_db.hkeys('user:all')
|
||||||
|
|
||||||
|
|
||||||
|
# ============= ROUTES ==============
|
||||||
|
|
||||||
|
@settings.route("/settings/", methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@login_user_basic
|
||||||
|
def settings_page():
|
||||||
|
return redirect(url_for('settings.edit_profile'))
|
||||||
|
|
||||||
|
@settings.route("/settings/edit_profile", methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@login_user_basic
|
||||||
|
def edit_profile():
|
||||||
|
user_metadata = get_user_metadata(current_user.get_id())
|
||||||
|
admin_level = current_user.is_in_role('admin')
|
||||||
|
return render_template("edit_profile.html", user_metadata=user_metadata,
|
||||||
|
admin_level=admin_level)
|
||||||
|
|
||||||
|
@settings.route("/settings/new_token", methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@login_user_basic
|
||||||
|
def new_token():
|
||||||
|
generate_new_token(current_user.get_id())
|
||||||
|
return redirect(url_for('settings.edit_profile'))
|
||||||
|
|
||||||
|
@settings.route("/settings/new_token_user", methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@login_admin
|
||||||
|
def new_token_user():
|
||||||
|
user_id = request.args.get('user_id')
|
||||||
|
if r_serv_db.exists('user_metadata:{}'.format(user_id)):
|
||||||
|
generate_new_token(user_id)
|
||||||
|
return redirect(url_for('settings.users_list'))
|
||||||
|
|
||||||
|
@settings.route("/settings/create_user", methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@login_admin
|
||||||
|
def create_user():
|
||||||
|
user_id = request.args.get('user_id')
|
||||||
|
error = request.args.get('error')
|
||||||
|
error_mail = request.args.get('error_mail')
|
||||||
|
role = None
|
||||||
|
if r_serv_db.exists('user_metadata:{}'.format(user_id)):
|
||||||
|
role = r_serv_db.hget('user_metadata:{}'.format(user_id), 'role')
|
||||||
|
else:
|
||||||
|
user_id = None
|
||||||
|
all_roles = get_all_role()
|
||||||
|
return render_template("create_user.html", all_roles=all_roles, user_id=user_id, user_role=role,
|
||||||
|
error=error, error_mail=error_mail,
|
||||||
|
admin_level=True)
|
||||||
|
|
||||||
|
@settings.route("/settings/create_user_post", methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
@login_admin
|
||||||
|
def create_user_post():
|
||||||
|
email = request.form.get('username')
|
||||||
|
role = request.form.get('user_role')
|
||||||
|
password1 = request.form.get('password1')
|
||||||
|
password2 = request.form.get('password2')
|
||||||
|
|
||||||
|
all_roles = get_all_role()
|
||||||
|
|
||||||
|
if email and len(email)< 300 and check_email(email) and role:
|
||||||
|
if role in all_roles:
|
||||||
|
# password set
|
||||||
|
if password1 and password2:
|
||||||
|
if password1==password2:
|
||||||
|
if check_password_strength(password1):
|
||||||
|
password = password1
|
||||||
|
else:
|
||||||
|
return render_template("create_user.html", all_roles=all_roles, error="Incorrect Password", admin_level=True)
|
||||||
|
else:
|
||||||
|
return render_template("create_user.html", all_roles=all_roles, error="Passwords don't match", admin_level=True)
|
||||||
|
# generate password
|
||||||
|
else:
|
||||||
|
password = gen_password()
|
||||||
|
|
||||||
|
if current_user.is_in_role('admin'):
|
||||||
|
# edit user
|
||||||
|
if r_serv_db.exists('user_metadata:{}'.format(email)):
|
||||||
|
if password1 and password2:
|
||||||
|
edit_user_db(email, password=password, role=role)
|
||||||
|
return redirect(url_for('settings.users_list', new_user=email, new_user_password=password, new_user_edited=True))
|
||||||
|
else:
|
||||||
|
edit_user_db(email, role=role)
|
||||||
|
return redirect(url_for('settings.users_list', new_user=email, new_user_password='Password not changed', new_user_edited=True))
|
||||||
|
# create user
|
||||||
|
else:
|
||||||
|
create_user_db(email, password, default=True, role=role)
|
||||||
|
return redirect(url_for('settings.users_list', new_user=email, new_user_password=password, new_user_edited=False))
|
||||||
|
|
||||||
|
else:
|
||||||
|
return render_template("create_user.html", all_roles=all_roles, admin_level=True)
|
||||||
|
else:
|
||||||
|
return render_template("create_user.html", all_roles=all_roles, error_mail=True, admin_level=True)
|
||||||
|
|
||||||
|
@settings.route("/settings/users_list", methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@login_admin
|
||||||
|
def users_list():
|
||||||
|
all_users = get_users_metadata(get_all_users())
|
||||||
|
new_user = request.args.get('new_user')
|
||||||
|
new_user_dict = {}
|
||||||
|
if new_user:
|
||||||
|
new_user_dict['email'] = new_user
|
||||||
|
new_user_dict['edited'] = request.args.get('new_user_edited')
|
||||||
|
new_user_dict['password'] = request.args.get('new_user_password')
|
||||||
|
return render_template("users_list.html", all_users=all_users, new_user=new_user_dict, admin_level=True)
|
||||||
|
|
||||||
|
@settings.route("/settings/edit_user", methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@login_admin
|
||||||
|
def edit_user():
|
||||||
|
user_id = request.args.get('user_id')
|
||||||
|
return redirect(url_for('settings.create_user', user_id=user_id))
|
||||||
|
|
||||||
|
@settings.route("/settings/delete_user", methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@login_admin
|
||||||
|
def delete_user():
|
||||||
|
user_id = request.args.get('user_id')
|
||||||
|
delete_user_db(user_id)
|
||||||
|
return redirect(url_for('settings.users_list'))
|
|
@ -0,0 +1,56 @@
|
||||||
|
#!/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': 2, '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)
|
||||||
|
|
||||||
|
|
||||||
|
username2 = 'config_generator@register.test'
|
||||||
|
password2 = gen_password()
|
||||||
|
if r_serv.exists('user_metadata:config_generator@register.test'):
|
||||||
|
edit_user_db(username2, password=password2, role='sensor_register')
|
||||||
|
else:
|
||||||
|
create_user_db(username2, password2, role='sensor_register', 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,58 @@
|
||||||
|
<!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>
|
||||||
|
|
||||||
|
{% include 'navbar.html' %}
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
$("#nav-home").addClass("active");
|
||||||
|
} );
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
|
@ -23,22 +23,7 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<nav class="navbar navbar-expand-sm navbar-dark bg-dark">
|
{% include 'navbar.html' %}
|
||||||
<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 class="d-flex justify-content-center">
|
<div class="d-flex justify-content-center">
|
||||||
<pre>
|
<pre>
|
||||||
|
@ -68,3 +53,11 @@
|
||||||
|
|
||||||
{% include 'navfooter.html' %}
|
{% include 'navfooter.html' %}
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
$("#nav-home").addClass("active");
|
||||||
|
} );
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
|
@ -23,22 +23,7 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<nav class="navbar navbar-expand-sm navbar-dark bg-dark">
|
{% include 'navbar.html' %}
|
||||||
<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 class="card-deck justify-content-center ml-0 mr-0">
|
<div class="card-deck justify-content-center ml-0 mr-0">
|
||||||
<div class="card border-dark mt-3 ml-4 mr-4">
|
<div class="card border-dark mt-3 ml-4 mr-4">
|
||||||
|
|
|
@ -23,22 +23,7 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<nav class="navbar navbar-expand-sm navbar-dark bg-dark">
|
{% include 'navbar.html' %}
|
||||||
<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 class="card-deck justify-content-center ml-0 mr-0">
|
<div class="card-deck justify-content-center ml-0 mr-0">
|
||||||
<div class="card border-dark mt-3 ml-4 mr-4">
|
<div class="card border-dark mt-3 ml-4 mr-4">
|
||||||
|
|
|
@ -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,156 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<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">
|
||||||
|
<link href="{{ url_for('static', filename='css/dataTables.bootstrap.min.css') }}" rel="stylesheet">
|
||||||
|
|
||||||
|
<!-- JS -->
|
||||||
|
<script src="{{ url_for('static', filename='js/jquery.js')}}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/popper.min.js')}}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/bootstrap.min.js')}}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/jquery.dataTables.min.js')}}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/dataTables.bootstrap.min.js')}}"></script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
{% include 'navbar.html' %}
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
{% include 'sidebar_settings.html' %}
|
||||||
|
|
||||||
|
<div class="col-12 col-lg-10" id="core_content">
|
||||||
|
|
||||||
|
<form class="form-signin" action="{{ url_for('settings.create_user_post')}}" autocomplete="off" method="post">
|
||||||
|
|
||||||
|
<h1 class="h3 mt-1 mb-3 text-center text-secondary">Create User</h1>
|
||||||
|
<label for="inputEmail" class="sr-only">Email address</label>
|
||||||
|
<input type="email" id="inputEmail" name="username" class="form-control {% if error_mail %}is-invalid{% endif %}" placeholder="Email address" autocomplete="off" required {% if user_id %}value="{{user_id}}"{% else %}{% endif %}>
|
||||||
|
{% if error_mail %}
|
||||||
|
<div class="invalid-feedback">
|
||||||
|
Please provide a valid email address
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<label class="mt-3" for="role_selector">User Role</label>
|
||||||
|
<select class="custom-select" id="role_selector" name="user_role">
|
||||||
|
{% for role in all_roles %}
|
||||||
|
{% if role == user_role %}
|
||||||
|
<option value="{{role}}" selected>{{role}}</option>
|
||||||
|
{% else %}
|
||||||
|
<option value="{{role}}">{{role}}</option>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<div class="custom-control custom-switch mt-4 mb-3">
|
||||||
|
<input type="checkbox" class="custom-control-input" id="set_manual_password" value="" onclick="toggle_password_fields();">
|
||||||
|
<label class="custom-control-label" for="set_manual_password">Set Password</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="password-section">
|
||||||
|
<h1 class="h3 mb-3 text-center text-secondary">Create 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">
|
||||||
|
<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">
|
||||||
|
{% if error %}
|
||||||
|
<div class="invalid-feedback">
|
||||||
|
{{error}}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-lg btn-primary btn-block mt-3" type="submit">Submit</button>
|
||||||
|
|
||||||
|
<div id="password-section-info">
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<h5 class="h3 mb-3 text-center 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>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% include 'navfooter.html' %}
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
$("#password-section").hide();
|
||||||
|
$("#password-section-info").hide();
|
||||||
|
$("#nav-settings").addClass("active");
|
||||||
|
$("#nav_create_user").addClass("active");
|
||||||
|
$("#nav_user_management").removeClass("text-muted");
|
||||||
|
|
||||||
|
{% if error %}
|
||||||
|
toggle_password_fields();
|
||||||
|
{% endif %}
|
||||||
|
} );
|
||||||
|
|
||||||
|
function toggle_sidebar(){
|
||||||
|
if($('#nav_menu').is(':visible')){
|
||||||
|
$('#nav_menu').hide();
|
||||||
|
$('#side_menu').removeClass('border-right')
|
||||||
|
$('#side_menu').removeClass('col-lg-2')
|
||||||
|
$('#core_content').removeClass('col-lg-10')
|
||||||
|
}else{
|
||||||
|
$('#nav_menu').show();
|
||||||
|
$('#side_menu').addClass('border-right')
|
||||||
|
$('#side_menu').addClass('col-lg-2')
|
||||||
|
$('#core_content').addClass('col-lg-10')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle_password_fields() {
|
||||||
|
var password_div = $("#password-section");
|
||||||
|
if(password_div.is(":visible")){
|
||||||
|
$("#password-section").hide();
|
||||||
|
$("#password-section-info").hide();
|
||||||
|
$("#inputPassword1").prop('required',false);
|
||||||
|
$("#inputPassword2").prop('required',false);
|
||||||
|
} else {
|
||||||
|
$("#password-section").show();
|
||||||
|
$("#password-section-info").show();
|
||||||
|
$("#inputPassword1").prop('required',true);
|
||||||
|
$("#inputPassword2").prop('required',true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
|
@ -0,0 +1,99 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<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">
|
||||||
|
<link href="{{ url_for('static', filename='css/dataTables.bootstrap.min.css') }}" rel="stylesheet">
|
||||||
|
|
||||||
|
<!-- JS -->
|
||||||
|
<script src="{{ url_for('static', filename='js/jquery.js')}}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/popper.min.js')}}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/bootstrap.min.js')}}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/jquery.dataTables.min.js')}}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/dataTables.bootstrap.min.js')}}"></script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
{% include 'navbar.html' %}
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
{% include 'sidebar_settings.html' %}
|
||||||
|
|
||||||
|
<div class="col-12 col-lg-10" id="core_content">
|
||||||
|
|
||||||
|
<div class="card mb-3 mt-1">
|
||||||
|
<div class="card-header text-white bg-dark pb-1">
|
||||||
|
<h5 class="card-title">My Profile :</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xl-6">
|
||||||
|
<div class="card text-center border-secondary">
|
||||||
|
<div class="card-body px-1 py-0">
|
||||||
|
<table class="table table-sm">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Email</td>
|
||||||
|
<td>{{user_metadata['email']}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Role</td>
|
||||||
|
<td>{{user_metadata['role']}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>API Key</td>
|
||||||
|
<td>
|
||||||
|
{{user_metadata['api_key']}}
|
||||||
|
<a class="ml-3" href="{{url_for('settings.new_token')}}"><i class="fa fa-random"></i></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% include 'navfooter.html' %}
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
$("#nav-settings").addClass("active");
|
||||||
|
$("#nav_edit_profile").addClass("active");
|
||||||
|
$("#nav_my_profile").removeClass("text-muted");
|
||||||
|
} );
|
||||||
|
|
||||||
|
function toggle_sidebar(){
|
||||||
|
if($('#nav_menu').is(':visible')){
|
||||||
|
$('#nav_menu').hide();
|
||||||
|
$('#side_menu').removeClass('border-right')
|
||||||
|
$('#side_menu').removeClass('col-lg-2')
|
||||||
|
$('#core_content').removeClass('col-lg-10')
|
||||||
|
}else{
|
||||||
|
$('#nav_menu').show();
|
||||||
|
$('#side_menu').addClass('border-right')
|
||||||
|
$('#side_menu').addClass('col-lg-2')
|
||||||
|
$('#core_content').addClass('col-lg-10')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
|
@ -58,22 +58,7 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<nav class="navbar navbar-expand-sm navbar-dark bg-dark">
|
{% include 'navbar.html' %}
|
||||||
<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 active">
|
|
||||||
<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 class="row mr-0">
|
<div class="row mr-0">
|
||||||
|
@ -109,6 +94,9 @@
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
$("#nav-home").addClass("active");
|
||||||
|
} );
|
||||||
|
|
||||||
////
|
////
|
||||||
//http://bl.ocks.org/charlesdguthrie/11356441, updated and modified
|
//http://bl.ocks.org/charlesdguthrie/11356441, updated and modified
|
||||||
|
|
|
@ -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>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<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" id="nav-home" 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" id="nav-sensor" href="{{ url_for('sensors_status') }}">Sensors Status</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item mr-3">
|
||||||
|
<a class="nav-link" id="nav-server" href="{{ url_for('server_management') }}" tabindex="-1" aria-disabled="true">Server Management</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item mr-3">
|
||||||
|
<a class="nav-link" id="nav-settings" href="{{ url_for('settings.settings_page') }}" tabindex="-1" aria-disabled="true">Settings</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item mr-3">
|
||||||
|
<a class="nav-link" href="{{ url_for('logout') }}" tabindex="-1" aria-disabled="true"><i class="fa fa-sign-out"></i>Log Out</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
|
@ -27,22 +27,7 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
{% include 'navbar.html' %}
|
||||||
<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 active 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 class="card mt-2 mb-2">
|
<div class="card mt-2 mb-2">
|
||||||
<div class="card-body bg-dark text-white">
|
<div class="card-body bg-dark text-white">
|
||||||
|
@ -129,6 +114,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
|
$("#nav-sensor").addClass("active");
|
||||||
table = $('#myTable_1').DataTable(
|
table = $('#myTable_1').DataTable(
|
||||||
{
|
{
|
||||||
"aLengthMenu": [[5, 10, 15, 20, -1], [5, 10, 15, 20, "All"]],
|
"aLengthMenu": [[5, 10, 15, 20, -1], [5, 10, 15, 20, "All"]],
|
||||||
|
|
|
@ -19,22 +19,7 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<nav class="navbar navbar-expand-sm navbar-dark bg-dark">
|
{% include 'navbar.html' %}
|
||||||
<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 active" href="{{ url_for('server_management') }}" tabindex="-1" aria-disabled="true">Server Management</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div class="card-deck ml-0 mr-0">
|
<div class="card-deck ml-0 mr-0">
|
||||||
<div class="card text-center mt-3 ml-xl-4">
|
<div class="card text-center mt-3 ml-xl-4">
|
||||||
|
@ -384,6 +369,7 @@ var table
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
$('#extended_type_name').hide()
|
$('#extended_type_name').hide()
|
||||||
$('#analyzer_metatype_name').hide()
|
$('#analyzer_metatype_name').hide()
|
||||||
|
$("#nav-server").addClass("active");
|
||||||
|
|
||||||
table = $('#myTable_').DataTable(
|
table = $('#myTable_').DataTable(
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
<div class="col-12 col-lg-2 p-0 bg-light border-right" id="side_menu">
|
||||||
|
|
||||||
|
<button type="button" class="btn btn-outline-secondary mt-1 ml-3" onclick="toggle_sidebar();">
|
||||||
|
<i class="fa align-left"></i>
|
||||||
|
<span>Toggle Sidebar</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<nav class="navbar navbar-expand navbar-light bg-light flex-md-column flex-row align-items-start py-2" id="nav_menu">
|
||||||
|
<h5 class="d-flex text-muted w-100 py-2" id="nav_my_profile">
|
||||||
|
<span>My Profile</span>
|
||||||
|
</h5>
|
||||||
|
<ul class="nav flex-md-column flex-row navbar-nav justify-content-between w-100"> <!--nav-pills-->
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{url_for('settings.edit_profile')}}" id="nav_edit_profile">
|
||||||
|
<i class="fa fa-user"></i>
|
||||||
|
<span>My Profile</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{url_for('change_password')}}" id="nav_dashboard">
|
||||||
|
<i class="fa fa-key"></i>
|
||||||
|
<span>Change Password</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
{% if admin_level %}
|
||||||
|
<h5 class="d-flex text-muted w-100 py-2" id="nav_user_management">
|
||||||
|
<span>User Management</span>
|
||||||
|
</h5>
|
||||||
|
<ul class="nav flex-md-column flex-row navbar-nav justify-content-between w-100"> <!--nav-pills-->
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{url_for('settings.create_user')}}" id="nav_create_user">
|
||||||
|
<i class="fa fa-user-plus"></i>
|
||||||
|
<span>Create User</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{url_for('settings.users_list')}}" id="nav_users_list">
|
||||||
|
<i class="fa fa-users"></i>
|
||||||
|
<span>Users List</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</nav>
|
||||||
|
</div>
|
|
@ -0,0 +1,125 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<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">
|
||||||
|
<link href="{{ url_for('static', filename='css/dataTables.bootstrap.min.css') }}" rel="stylesheet">
|
||||||
|
|
||||||
|
<!-- JS -->
|
||||||
|
<script src="{{ url_for('static', filename='js/jquery.js')}}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/popper.min.js')}}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/bootstrap.min.js')}}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/jquery.dataTables.min.js')}}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/dataTables.bootstrap.min.js')}}"></script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.edit_icon:hover{
|
||||||
|
cursor: pointer;
|
||||||
|
color: #17a2b8;
|
||||||
|
}
|
||||||
|
.trash_icon:hover{
|
||||||
|
cursor: pointer;
|
||||||
|
color: #c82333;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
{% include 'navbar.html' %}
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
{% include 'sidebar_settings.html' %}
|
||||||
|
|
||||||
|
<div class="col-12 col-lg-10" id="core_content">
|
||||||
|
|
||||||
|
{% if new_user %}
|
||||||
|
<div class="text-center my-3 ">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
{% if new_user['edited']=='True' %}
|
||||||
|
<h5 class="card-title">User Edited</h5>
|
||||||
|
{% else %}
|
||||||
|
<h5 class="card-title">User Created</h5>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<p>User: {{new_user['email']}}</p>
|
||||||
|
<p>Password: {{new_user['password']}}</p>
|
||||||
|
<a href="{{url_for('settings.users_list')}}" class="btn btn-primary"><i class="fa fa-eye-slash"></i> Hide</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="table-responsive mt-1 table-hover table-borderless table-striped">
|
||||||
|
<table class="table">
|
||||||
|
<thead class="thead-dark">
|
||||||
|
<tr>
|
||||||
|
<th>Email</th>
|
||||||
|
<th>Role</th>
|
||||||
|
<th>Api Key</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="tbody_last_crawled">
|
||||||
|
{% for user in all_users %}
|
||||||
|
<tr>
|
||||||
|
<td>{{user['email']}}</td>
|
||||||
|
<td>{{user['role']}}</td>
|
||||||
|
<td>
|
||||||
|
{{user['api_key']}}
|
||||||
|
<a class="ml-3" href="{{url_for('settings.new_token_user')}}?user_id={{user['email']}}"><i class="fa fa-random"></i></a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('settings.edit_user')}}?user_id={{user['email']}}">
|
||||||
|
<i class="fa fa-pencil edit_icon"></i>
|
||||||
|
</a>
|
||||||
|
<a href="{{ url_for('settings.delete_user')}}?user_id={{user['email']}}" class="ml-4">
|
||||||
|
<i class="fa fa-trash trash_icon"></i>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% include 'navfooter.html' %}
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
$("#nav-settings").addClass("active");
|
||||||
|
$("#nav_users_list").addClass("active");
|
||||||
|
$("#nav_user_management").removeClass("text-muted");
|
||||||
|
} );
|
||||||
|
|
||||||
|
function toggle_sidebar(){
|
||||||
|
if($('#nav_menu').is(':visible')){
|
||||||
|
$('#nav_menu').hide();
|
||||||
|
$('#side_menu').removeClass('border-right')
|
||||||
|
$('#side_menu').removeClass('col-lg-2')
|
||||||
|
$('#core_content').removeClass('col-lg-10')
|
||||||
|
}else{
|
||||||
|
$('#nav_menu').show();
|
||||||
|
$('#side_menu').addClass('border-right')
|
||||||
|
$('#side_menu').addClass('col-lg-2')
|
||||||
|
$('#core_content').addClass('col-lg-10')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
|
@ -28,22 +28,7 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<nav class="navbar navbar-expand-sm navbar-dark bg-dark">
|
{% include 'navbar.html' %}
|
||||||
<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 class="card text-center mt-3 ml-2 mr-2">
|
<div class="card text-center mt-3 ml-2 mr-2">
|
||||||
<div class="card-header bg-dark text-white">
|
<div class="card-header bg-dark text-white">
|
||||||
|
|
Loading…
Reference in New Issue