flask server
This commit is contained in:
parent
5acb2992ce
commit
0a71a6c840
54 changed files with 5876 additions and 0 deletions
0
server/flask/application/backend/__init__.py
Normal file
0
server/flask/application/backend/__init__.py
Normal file
93
server/flask/application/backend/admin_requests.py
Normal file
93
server/flask/application/backend/admin_requests.py
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
from flask import request, jsonify
|
||||
from .require_decorators import get_cropped_username
|
||||
from .require_decorators import get_cropped_otp
|
||||
from .require_decorators import get_cropped_token
|
||||
from . import token_generator_util
|
||||
from .data import dao_registration_tokens
|
||||
from .data import dao_reset_password_tokens
|
||||
from .data import dao_users
|
||||
from .data import dao_session
|
||||
from .data.data_models import DataError
|
||||
from .data.data_models import User
|
||||
from .data.data_models import ResponseCode
|
||||
|
||||
def handle_create_registration_token(user: User):
|
||||
new_registration_token = get_cropped_otp(request.form.get('registration_token'))
|
||||
if new_registration_token is None or new_registration_token.strip() == '':
|
||||
errorResponse = jsonify({'message':'Invalid Registration Token given!','code':ResponseCode.INVALID_REGISTRATION_TOKEN})
|
||||
return errorResponse, 400
|
||||
|
||||
result = dao_registration_tokens.insert_token(new_registration_token)
|
||||
if (result is DataError.REGISTRATION_CODE_ALREADY_EXISTS):
|
||||
errorResponse = jsonify({'message':'Invalid Registration Token given!','code':ResponseCode.INVALID_REGISTRATION_TOKEN})
|
||||
return errorResponse, 400
|
||||
|
||||
return jsonify({'message':'Registration token Saved!','code':ResponseCode.SUCCESS_SAVED_REGISTRATION_TOKEN}), 200
|
||||
|
||||
def handle_create_reset_password_token(user: User):
|
||||
reset_password_token = get_cropped_otp(request.form.get('reset_password_token'))
|
||||
username_to_reset = get_cropped_username(request.form.get('username_to_reset'))
|
||||
if reset_password_token is None or reset_password_token.strip() == '':
|
||||
errorResponse = jsonify({'message':'Invalid Reset Password Token given!','code':ResponseCode.INVALID_RESET_PASSWORD_TOKEN})
|
||||
return errorResponse, 400
|
||||
|
||||
if username_to_reset is None or username_to_reset.strip() == '':
|
||||
errorResponse = jsonify({'message':'username_to_reset cannot be empty!','code':ResponseCode.INVALID_USERNAME_TO_EDIT})
|
||||
return errorResponse, 400
|
||||
|
||||
expires_at = token_generator_util.generate_reset_password_expires_at()
|
||||
|
||||
dao_reset_password_tokens.insert_token(token = reset_password_token, username = username_to_reset, expires_at = expires_at)
|
||||
|
||||
return jsonify({'message':'Reset Password token Saved!','code':ResponseCode.SUCCESS_SAVED_RESET_PASSWORD_TOKEN}), 200
|
||||
|
||||
def handle_reset_user_otp_verification(user: User):
|
||||
username = get_cropped_username(request.form.get('username_to_reset'))
|
||||
if username is None or username.strip() == '':
|
||||
errorResponse = jsonify({'message':'username_to_reset cannot be empty!','code':ResponseCode.INVALID_USERNAME_TO_EDIT})
|
||||
return errorResponse, 400
|
||||
|
||||
user_to_update = dao_users.get_user_by_name(username)
|
||||
if user_to_update is None:
|
||||
errorResponse = jsonify({'message':'User cannot be found!','code':ResponseCode.NOT_FOUND_USER})
|
||||
return errorResponse, 400
|
||||
|
||||
dao_users.update_user_otp_verification(user_to_update.id, False)
|
||||
|
||||
return jsonify({'message':'OTP Verification Reset!','code':ResponseCode.SUCCESS_RESET_OTP_VERIFICATION}), 200
|
||||
|
||||
def handle_get_users(user: User):
|
||||
users = dao_users.get_users()
|
||||
simplified_users = map(lambda user: {'name':user.name,'privileged':user.privileged}, users)
|
||||
|
||||
return jsonify({'users': list(simplified_users)}), 200
|
||||
|
||||
def handle_get_registration_tokens(user: User):
|
||||
tokens = dao_registration_tokens.get_tokens()
|
||||
|
||||
return jsonify({'registration_tokens': list(tokens)}), 200
|
||||
|
||||
def handle_delete_user_by_name(user: User):
|
||||
user_name_to_delete = get_cropped_username(request.form.get('username_to_delete'))
|
||||
success_response = jsonify({'message':'User deleted!','code':ResponseCode.SUCCESS_DELETED_USER})
|
||||
if user_name_to_delete is None:
|
||||
return success_response, 200
|
||||
|
||||
user_to_delete = dao_users.get_user_by_name(user_name_to_delete)
|
||||
if user_to_delete is None:
|
||||
return success_response, 200
|
||||
|
||||
dao_session.delete_all_user_session_by_user_id(user_to_delete.id)
|
||||
dao_users.delete_user_by_id(user_to_delete.id)
|
||||
|
||||
return success_response, 200
|
||||
|
||||
def handle_delete_registration_token(user: User):
|
||||
registration_token_to_delete = get_cropped_otp(request.form.get('registration_token'))
|
||||
success_response = jsonify({'message':'Token deleted!','code':ResponseCode.SUCCESS_DELETED_TOKEN})
|
||||
if registration_token_to_delete is None:
|
||||
return success_response, 200
|
||||
|
||||
dao_registration_tokens.delete_token(registration_token_to_delete)
|
||||
|
||||
return success_response, 200
|
||||
82
server/flask/application/backend/auth_requests.py
Normal file
82
server/flask/application/backend/auth_requests.py
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
from flask import request, jsonify
|
||||
from .require_decorators import get_cropped_otp
|
||||
from .require_decorators import get_cropped_token
|
||||
from . import token_generator_util
|
||||
from .data import dao_registration_tokens
|
||||
from .data import dao_users
|
||||
from .data import dao_session
|
||||
from .data.data_models import DataError
|
||||
from .data.data_models import RegisteringUser
|
||||
from .data.data_models import User
|
||||
from .data.data_models import ResponseCode
|
||||
|
||||
def handle_register(username, password):
|
||||
one_time_password = get_cropped_otp(request.form.get('otp') or '')
|
||||
if not dao_registration_tokens.is_valid_token(one_time_password):
|
||||
errorResponse = jsonify({'message':'Invalid Token!','code':ResponseCode.UNKNOWN_REGISTRATION_TOKEN})
|
||||
return errorResponse, 400
|
||||
|
||||
one_time_password_secret = token_generator_util.generate_otp_secret()
|
||||
user = RegisteringUser(name = username, password = password, otp_secret = one_time_password_secret)
|
||||
result = dao_users.insert_user(user)
|
||||
if (result is DataError.USER_NAME_NOT_VALID):
|
||||
errorResponse = jsonify({'message':'Username is already taken!','code':ResponseCode.ALREADY_TAKEN_USERNAME})
|
||||
return errorResponse, 400
|
||||
|
||||
dao_registration_tokens.delete_token(one_time_password)
|
||||
secret_url = token_generator_util.get_url(user.name, one_time_password_secret)
|
||||
return jsonify({'otp_secret': secret_url}), 200
|
||||
|
||||
def handle_login(user: User):
|
||||
if user.was_otp_verified:
|
||||
successResponse = jsonify({'message':'User found!','code':ResponseCode.SUCCESS_FOUND_USER})
|
||||
return successResponse, 200
|
||||
else:
|
||||
secret_url = token_generator_util.get_url(username=user.name, secret=user.otp_secret)
|
||||
return jsonify({'otp_secret': secret_url}), 200
|
||||
|
||||
def handle_otp_verification(user: User):
|
||||
one_time_password = get_cropped_otp(request.form.get('otp') or '')
|
||||
is_otp_ok = token_generator_util.verify_otp(user.otp_secret, one_time_password)
|
||||
if (is_otp_ok):
|
||||
dao_users.update_user_otp_verification(user.id, True)
|
||||
session = token_generator_util.generate_session(user.id)
|
||||
dao_session.insert_user_session(session)
|
||||
sessionResponse = jsonify({
|
||||
'access_token': session.access_token,
|
||||
'refresh_token': session.refresh_token,
|
||||
'expires_at': session.access_expires_at
|
||||
})
|
||||
return sessionResponse, 200
|
||||
else:
|
||||
errorResponse = jsonify({'message':'Invalid Token!','code':ResponseCode.INVALID_OTP})
|
||||
return errorResponse, 400
|
||||
|
||||
def handle_logout():
|
||||
access_token = get_cropped_token(request.headers.get('Authorization'))
|
||||
if (access_token is None):
|
||||
return '', 200
|
||||
else:
|
||||
dao_session.delete_user_session(access_token = access_token)
|
||||
return '', 200
|
||||
|
||||
def handle_refresh_token():
|
||||
refresh_token = get_cropped_token(request.form.get('refresh_token'))
|
||||
if refresh_token is None:
|
||||
errorResponse = jsonify({'message':'Invalid Refresh Token!','code':ResponseCode.INVALID_REFRESH_TOKEN})
|
||||
return errorResponse, 400
|
||||
|
||||
user_id = dao_session.get_user_for_refresh_token(refresh_token)
|
||||
if user_id is None:
|
||||
errorResponse = jsonify({'message':'Invalid Refresh Token!','code':ResponseCode.INVALID_REFRESH_TOKEN})
|
||||
return errorResponse, 400
|
||||
|
||||
new_session = token_generator_util.generate_session(user_id)
|
||||
dao_session.swap_refresh_session(refresh_token = refresh_token, session = new_session)
|
||||
|
||||
sessionResponse = jsonify({
|
||||
'access_token': new_session.access_token,
|
||||
'refresh_token': new_session.refresh_token,
|
||||
'expires_at': new_session.access_expires_at
|
||||
})
|
||||
return sessionResponse, 200
|
||||
12
server/flask/application/backend/config.json
Normal file
12
server/flask/application/backend/config.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"SECRECT_BYTE_COUNT": 64,
|
||||
"SESSION_ACCESS_EXPIRATION_IN_SECONDS": 86400,
|
||||
"SESSION_REFRESH_EXPIRATION_IN_SECONDS": 259200,
|
||||
"RESET_PASSWORD_EXPIRATION_IN_SECONDS": 432000,
|
||||
"MAX_PASSWORD_LENGTH": 64,
|
||||
"MAX_USERNAME_LENGTH": 64,
|
||||
"MAX_TOKEN_LENGTH": 200,
|
||||
"KEY_LENGTH": 150,
|
||||
"MAX_OTP_LENGTH": 16,
|
||||
"DATABASE_NAME": "sqlitedb"
|
||||
}
|
||||
0
server/flask/application/backend/data/__init__.py
Normal file
0
server/flask/application/backend/data/__init__.py
Normal file
33
server/flask/application/backend/data/dao_file_metadata.py
Normal file
33
server/flask/application/backend/data/dao_file_metadata.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
from .db import get_db
|
||||
from .data_models import DataError
|
||||
from sqlite3 import IntegrityError
|
||||
|
||||
_INSER_METADATA_SQL = "INSERT INTO file_metadata(file_key,metadata) "\
|
||||
"VALUES(:file_key, :metadata)"
|
||||
_DELETE_METADATA_SQL = "DELETE FROM file_metadata WHERE file_key=:file_key"
|
||||
def insert_metadata(metadata: dict):
|
||||
db = get_db()
|
||||
db_cursor = db.cursor()
|
||||
|
||||
delete_params = map(lambda key: {'file_key':key}, metadata.keys())
|
||||
delete_params = list(delete_params)
|
||||
db_cursor.executemany(_DELETE_METADATA_SQL, delete_params)
|
||||
|
||||
insert_params = map(lambda item: {'file_key':item[0], 'metadata':item[1]}, metadata.items())
|
||||
insert_params = list(insert_params)
|
||||
|
||||
|
||||
db_cursor.executemany(_INSER_METADATA_SQL, insert_params)
|
||||
db.commit()
|
||||
|
||||
def get_metadata(file_key: str):
|
||||
db = get_db()
|
||||
db_cursor = db.cursor()
|
||||
db_cursor.execute("SELECT metadata FROM file_metadata WHERE file_key=:file_key",{"file_key":file_key})
|
||||
rows = db_cursor.fetchone()
|
||||
|
||||
if (rows is None):
|
||||
return dict()
|
||||
|
||||
converted_tuple_array = map(lambda metadata: (file_key, metadata), rows)
|
||||
return dict(converted_tuple_array)
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
from .db import get_db
|
||||
from .data_models import DataError
|
||||
from sqlite3 import IntegrityError
|
||||
|
||||
_INSER_METADATA_SQL = "INSERT INTO file_metadata_of_user(user_id,file_key,metadata) "\
|
||||
"VALUES(:user_id, :file_key, :metadata)"
|
||||
_DELETE_METADATA_SQL = "DELETE FROM file_metadata_of_user WHERE user_id=:user_id AND file_key=:file_key"
|
||||
def insert_metadata(user_id: str, metadata: dict):
|
||||
db = get_db()
|
||||
db_cursor = db.cursor()
|
||||
|
||||
delete_params = map(lambda key: {'user_id':user_id, 'file_key':key}, metadata.keys())
|
||||
delete_params = list(delete_params)
|
||||
db_cursor.executemany(_DELETE_METADATA_SQL, delete_params)
|
||||
|
||||
insert_params = map(lambda item: {'user_id':user_id, 'file_key':item[0], 'metadata':item[1]}, metadata.items())
|
||||
insert_params = list(insert_params)
|
||||
|
||||
|
||||
db_cursor.executemany(_INSER_METADATA_SQL, insert_params)
|
||||
db.commit()
|
||||
|
||||
def get_metadata(user_id: str):
|
||||
db = get_db()
|
||||
db_cursor = db.cursor()
|
||||
db_cursor.execute("SELECT file_key, metadata FROM file_metadata_of_user WHERE user_id=:user_id",{"user_id":user_id})
|
||||
rows = db_cursor.fetchall()
|
||||
|
||||
converted_tuple_array = map(lambda row: (row['file_key'], row['metadata']), rows)
|
||||
return dict(converted_tuple_array)
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
from .db import get_db
|
||||
from .data_models import DataError
|
||||
from sqlite3 import IntegrityError
|
||||
|
||||
def is_valid_token(token):
|
||||
db = get_db()
|
||||
db_cursor = db.cursor()
|
||||
db_cursor.execute("SELECT COUNT(*) FROM registration_token where token = :token", {"token": token})
|
||||
rows = db_cursor.fetchone()
|
||||
|
||||
return rows[0] == 1
|
||||
|
||||
def insert_token(token):
|
||||
db = get_db()
|
||||
db_cursor = db.cursor()
|
||||
try:
|
||||
db_cursor.execute("INSERT INTO registration_token(token) VALUES(:token)", {"token": token})
|
||||
except IntegrityError as e:
|
||||
return DataError.REGISTRATION_CODE_ALREADY_EXISTS
|
||||
db.commit()
|
||||
|
||||
def delete_token(token):
|
||||
db = get_db()
|
||||
db_cursor = db.cursor()
|
||||
db_cursor.execute("DELETE FROM registration_token WHERE token=:token", {"token": token})
|
||||
db.commit()
|
||||
|
||||
def get_tokens():
|
||||
db = get_db()
|
||||
db_cursor = db.cursor()
|
||||
db_cursor.execute("SELECT * FROM registration_token")
|
||||
rows = db_cursor.fetchall()
|
||||
|
||||
return list(map(lambda row: row['token'],rows))
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
from .db import get_db
|
||||
import time
|
||||
|
||||
_DELETE_EXPIRED_SESSION_SQL = "DELETE FROM reset_password_token where expires_at <= :time"
|
||||
def _delete_expired_tokens(db):
|
||||
db_cursor = db.cursor()
|
||||
db_cursor.execute(_DELETE_EXPIRED_SESSION_SQL, {"time": time.time()})
|
||||
db.commit()
|
||||
|
||||
def is_valid_token(token, username):
|
||||
db = get_db()
|
||||
_delete_expired_tokens(db)
|
||||
db_cursor = db.cursor()
|
||||
db_cursor.execute("SELECT COUNT(*) FROM reset_password_token where token = :token AND username = :username", {"token": token, "username": username})
|
||||
rows = db_cursor.fetchone()
|
||||
|
||||
return rows[0] == 1
|
||||
|
||||
def insert_token(token, username, expires_at):
|
||||
db = get_db()
|
||||
db_cursor = db.cursor()
|
||||
params = {
|
||||
"token": token,
|
||||
"username": username,
|
||||
"expires_at": expires_at
|
||||
}
|
||||
db_cursor.execute("INSERT INTO reset_password_token(token, username, expires_at) VALUES(:token, :username, :expires_at)", params)
|
||||
db.commit()
|
||||
|
||||
def delete_tokens(username):
|
||||
db = get_db()
|
||||
db_cursor = db.cursor()
|
||||
db_cursor.execute("DELETE FROM reset_password_token WHERE username=:username", {"username": username})
|
||||
db.commit()
|
||||
78
server/flask/application/backend/data/dao_session.py
Normal file
78
server/flask/application/backend/data/dao_session.py
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
from .db import get_db
|
||||
from .data_models import Session
|
||||
import time
|
||||
|
||||
_DELETE_EXPIRED_SESSION_SQL = "DELETE FROM session where refresh_expires_at <= :time"
|
||||
def _delete_expired_tokens(db):
|
||||
db_cursor = db.cursor()
|
||||
db_cursor.execute(_DELETE_EXPIRED_SESSION_SQL, {"time": time.time()})
|
||||
db.commit()
|
||||
|
||||
_GET_USER_FOR_ACCESS_TOKEN_SQL = "SELECT user_id FROM session where access_token = :token and access_expires_at >= :time"
|
||||
def get_user_for_token(access_token: str):
|
||||
db = get_db()
|
||||
_delete_expired_tokens(db)
|
||||
db_cursor = db.cursor()
|
||||
db_cursor.execute(_GET_USER_FOR_ACCESS_TOKEN_SQL, {"token": access_token, "time": time.time()})
|
||||
rows = db_cursor.fetchall()
|
||||
|
||||
if (len(rows) == 1):
|
||||
return rows[0][0]
|
||||
return None
|
||||
|
||||
_INSER_SESSION_SQL = "INSERT INTO session(user_id, access_token, refresh_token, access_expires_at, refresh_expires_at)"\
|
||||
"VALUES(:user_id, :access_token, :refresh_token, :access_expires_at, :refresh_expires_at)"
|
||||
|
||||
def _session_insert(db_cursor, session: Session):
|
||||
params = {
|
||||
"user_id": session.user_id,
|
||||
"access_token": session.access_token,
|
||||
"refresh_token": session.refresh_token,
|
||||
"access_expires_at": session.access_expires_at,
|
||||
"refresh_expires_at": session.refresh_expires_at,
|
||||
}
|
||||
db_cursor.execute(_INSER_SESSION_SQL, params)
|
||||
|
||||
def insert_user_session(session: Session):
|
||||
db = get_db()
|
||||
db_cursor = db.cursor()
|
||||
_session_insert(db_cursor, session)
|
||||
db.commit()
|
||||
|
||||
def delete_user_session(access_token: str):
|
||||
db = get_db()
|
||||
db_cursor = db.cursor()
|
||||
db_cursor.execute("DELETE FROM session WHERE access_token = :token", {"token": access_token})
|
||||
db.commit()
|
||||
|
||||
def delete_all_user_session_by_user_id(user_id: int):
|
||||
db = get_db()
|
||||
db_cursor = db.cursor()
|
||||
db_cursor.execute("DELETE FROM session WHERE user_id = :id", {"id": user_id})
|
||||
db.commit()
|
||||
|
||||
def create_new_single_session(session: Session):
|
||||
db = get_db()
|
||||
db_cursor = db.cursor()
|
||||
db_cursor.execute("DELETE FROM session where user_id = :id", {"id": session.user_id})
|
||||
_session_insert(db_cursor, session)
|
||||
db.commit()
|
||||
|
||||
def get_user_for_refresh_token(refresh_token: str):
|
||||
db = get_db()
|
||||
_delete_expired_tokens(db)
|
||||
db_cursor = db.cursor()
|
||||
db_cursor.execute("SELECT user_id FROM session where refresh_token = :token", {"token": refresh_token})
|
||||
rows = db_cursor.fetchall()
|
||||
|
||||
if (len(rows) == 1):
|
||||
return rows[0][0]
|
||||
return None
|
||||
|
||||
def swap_refresh_session(refresh_token: str, session: Session):
|
||||
db = get_db()
|
||||
_delete_expired_tokens(db)
|
||||
db_cursor = db.cursor()
|
||||
db_cursor.execute("DELETE FROM session WHERE refresh_token = :token", {"token": refresh_token})
|
||||
_session_insert(db_cursor, session)
|
||||
db.commit()
|
||||
107
server/flask/application/backend/data/dao_users.py
Normal file
107
server/flask/application/backend/data/dao_users.py
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
from .data_models import User
|
||||
from .data_models import RegisteringUser
|
||||
from .data_models import DataError
|
||||
from .db import get_db
|
||||
from sqlite3 import IntegrityError
|
||||
from passlib.hash import sha256_crypt
|
||||
|
||||
def _user_from_row(row):
|
||||
return User(
|
||||
id = row['id'],
|
||||
name = row['username'],
|
||||
otp_secret = row['otp_secret'],
|
||||
was_otp_verified = row['was_otp_verified'] != 0,
|
||||
privileged = row['privileged'] != 0,
|
||||
)
|
||||
|
||||
def get_user_by_id(user_id: int):
|
||||
db = get_db()
|
||||
db_cursor = db.cursor()
|
||||
db_cursor.execute("SELECT * FROM user where id = :user_id", {"user_id": user_id})
|
||||
row = db_cursor.fetchone()
|
||||
|
||||
if (row is None):
|
||||
return None
|
||||
|
||||
return _user_from_row(row)
|
||||
|
||||
def get_user_by_name(username: str):
|
||||
db = get_db()
|
||||
db_cursor = db.cursor()
|
||||
db_cursor.execute("SELECT * FROM user where username = :name", {"name": username})
|
||||
row = db_cursor.fetchone()
|
||||
|
||||
if (row is None):
|
||||
return None
|
||||
|
||||
return _user_from_row(row)
|
||||
|
||||
def get_user_by_name_and_password(user_name: str, password: str):
|
||||
db = get_db()
|
||||
db_cursor = db.cursor()
|
||||
db_cursor.execute("SELECT * FROM user where username = :user_name", {"user_name": user_name})
|
||||
row = db_cursor.fetchone()
|
||||
|
||||
if (row is None):
|
||||
sha256_crypt.hash('') # do hashing even if no user is found
|
||||
return None
|
||||
|
||||
is_password_wrong = not sha256_crypt.verify(password, row['password'] or '')
|
||||
if is_password_wrong:
|
||||
return None
|
||||
|
||||
return _user_from_row(row)
|
||||
|
||||
def get_users():
|
||||
db = get_db()
|
||||
db_cursor = db.cursor()
|
||||
db_cursor.execute("SELECT * FROM user")
|
||||
rows = db_cursor.fetchall()
|
||||
|
||||
return map(_user_from_row, rows)
|
||||
|
||||
_INSER_USER_SQL = "INSERT INTO user(username, password, otp_secret, privileged, was_otp_verified)"\
|
||||
"VALUES(:name, :pass, :otp, :privileged, :otp_verified)"
|
||||
def insert_user(user: RegisteringUser):
|
||||
db = get_db()
|
||||
db_cursor = db.cursor()
|
||||
hashed_password = sha256_crypt.hash(user.password)
|
||||
|
||||
params = {
|
||||
"name": user.name,
|
||||
"pass": hashed_password,
|
||||
"otp": user.otp_secret,
|
||||
"privileged": 1 if user.privileged else 0,
|
||||
"otp_verified": 1 if user.was_otp_verified else 0,
|
||||
}
|
||||
try:
|
||||
db_cursor.execute(_INSER_USER_SQL, params)
|
||||
except IntegrityError as e:
|
||||
return DataError.USER_NAME_NOT_VALID
|
||||
db.commit()
|
||||
return db_cursor.lastrowid
|
||||
|
||||
def update_user_privilige(user_id: int, privileged: bool):
|
||||
db = get_db()
|
||||
db_cursor = db.cursor()
|
||||
db_cursor.execute('UPDATE user SET privileged = :privileged WHERE id=:id',{'id':user_id, 'privileged': privileged})
|
||||
db.commit()
|
||||
|
||||
def update_user_otp_verification(user_id: int, was_otp_verified: bool):
|
||||
db = get_db()
|
||||
db_cursor = db.cursor()
|
||||
db_cursor.execute('UPDATE user SET was_otp_verified = :otp_verified WHERE id=:id',{'id':user_id, 'otp_verified': was_otp_verified})
|
||||
db.commit()
|
||||
|
||||
def update_user_password(user_id: int, new_password: str):
|
||||
hashed_password = sha256_crypt.hash(new_password)
|
||||
db = get_db()
|
||||
db_cursor = db.cursor()
|
||||
db_cursor.execute('UPDATE user SET password = :pass WHERE id=:id',{'id':user_id, 'pass': hashed_password})
|
||||
db.commit()
|
||||
|
||||
def delete_user_by_id(user_id: int):
|
||||
db = get_db()
|
||||
db_cursor = db.cursor()
|
||||
db_cursor.execute('DELETE FROM user WHERE id=:id',{'id':user_id})
|
||||
db.commit()
|
||||
106
server/flask/application/backend/data/data_models.py
Normal file
106
server/flask/application/backend/data/data_models.py
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
from enum import Enum
|
||||
from enum import IntEnum
|
||||
|
||||
class Session:
|
||||
def __init__(self, user_id, access_token, refresh_token, access_expires_at, refresh_expires_at):
|
||||
self.user_id = user_id
|
||||
self.access_token = access_token
|
||||
self.refresh_token = refresh_token
|
||||
self.access_expires_at = access_expires_at
|
||||
self.refresh_expires_at = refresh_expires_at
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, Session):
|
||||
return False
|
||||
return self.user_id == other.user_id \
|
||||
and self.access_token == other.access_token \
|
||||
and self.refresh_token == other.refresh_token \
|
||||
and self.access_expires_at == other.access_expires_at \
|
||||
and self.refresh_expires_at == other.refresh_expires_at \
|
||||
|
||||
def __str__(self):
|
||||
return 'Session(user_id={},access_token={},refresh_token={},access_expires_at={},refresh_expires_at={})'.format(self.user_id, self.access_token, self.refresh_token, self.access_expires_at, self.refresh_expires_at)
|
||||
|
||||
def __repr__(self):
|
||||
return self.__str__()
|
||||
|
||||
class RegisteringUser:
|
||||
def __init__(self, name, password, otp_secret, privileged = False, was_otp_verified = False):
|
||||
self.name = name
|
||||
self.password = password
|
||||
self.otp_secret = otp_secret
|
||||
self.privileged = privileged
|
||||
self.was_otp_verified = was_otp_verified
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, User):
|
||||
return False
|
||||
|
||||
return self.name == other.name \
|
||||
and self.password == other.password \
|
||||
and self.otp_secret == other.otp_secret \
|
||||
and self.privileged == other.privileged \
|
||||
and self.was_otp_verified == other.was_otp_verified \
|
||||
|
||||
def __str__(self):
|
||||
return 'User(name={},pass={},otp={},privileged={},otp_active={})'\
|
||||
.format(self.name, self.password, self.otp_secret, self.privileged, self.was_otp_verified)
|
||||
|
||||
def __repr__(self):
|
||||
return self.__str__()
|
||||
|
||||
class User:
|
||||
def __init__(self, id, name, otp_secret, privileged, was_otp_verified):
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.otp_secret = otp_secret
|
||||
self.privileged = privileged
|
||||
self.was_otp_verified = was_otp_verified
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, User):
|
||||
return False
|
||||
|
||||
return self.id == other.id \
|
||||
and self.name == other.name \
|
||||
and self.otp_secret == other.otp_secret \
|
||||
and self.privileged == other.privileged \
|
||||
and self.was_otp_verified == other.was_otp_verified \
|
||||
|
||||
def __str__(self):
|
||||
return 'User(id={},name={},otp={},privileged={},otp_active={})'\
|
||||
.format(self.id, self.name, self.otp_secret, self.privileged, self.was_otp_verified)
|
||||
|
||||
def __repr__(self):
|
||||
return self.__str__()
|
||||
|
||||
class DataError(Enum):
|
||||
USER_NAME_NOT_VALID = 1
|
||||
REGISTRATION_CODE_ALREADY_EXISTS = 2
|
||||
|
||||
class ResponseCode(IntEnum):
|
||||
SUCCESS_FOUND_USER = 200
|
||||
SUCCESS_SAVED_PASSWORD = 201
|
||||
SUCCESS_SAVED_USER_FILE_METADATA = 202
|
||||
SUCCESS_SAVED_FILE_METADATA = 203
|
||||
SUCCESS_SAVED_REGISTRATION_TOKEN = 205
|
||||
SUCCESS_DELETED_USER = 206
|
||||
SUCCESS_RESET_OTP_VERIFICATION = 207
|
||||
SUCCESS_SAVED_RESET_PASSWORD_TOKEN = 208
|
||||
SUCCESS_DELETED_TOKEN = 209
|
||||
|
||||
|
||||
ALREADY_TAKEN_USERNAME = 411
|
||||
NOT_FOUND_USER = 412
|
||||
INVALID_USERNAME_TO_EDIT = 413
|
||||
CANT_SAVE_USER_FILE_METADATA = 414
|
||||
CANT_SAVE_FILE_METADATA = 415
|
||||
INVALID_FILE_KEY = 416
|
||||
INVALID_PASSWORD = 421
|
||||
INVALID_NEW_PASSWORD = 422
|
||||
UNKNOWN_REGISTRATION_TOKEN = 430
|
||||
INVALID_OTP = 431
|
||||
INVALID_REFRESH_TOKEN = 450
|
||||
INVALID_RESET_PASSWORD_TOKEN = 459
|
||||
INVALID_REGISTRATION_TOKEN = 460
|
||||
UNKNOWN_RESET_PASSWORD_TOKEN = 461
|
||||
82
server/flask/application/backend/data/db.py
Normal file
82
server/flask/application/backend/data/db.py
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import sqlite3
|
||||
from os import path
|
||||
import argparse
|
||||
from flask import current_app, g
|
||||
from passlib.hash import sha256_crypt
|
||||
|
||||
default_database_name = "sqlitedb"
|
||||
|
||||
def get_db():
|
||||
current_app.config.get('DATABASE_PATH')
|
||||
if 'db' not in g:
|
||||
db_path = current_app.config.get('DATABASE_PATH')
|
||||
if (db_path is None):
|
||||
db_path = path.join(current_app.instance_path, current_app.config['DATABASE_NAME'])
|
||||
g.db = sqlite3.connect(db_path, detect_types=sqlite3.PARSE_DECLTYPES)
|
||||
g.db.row_factory = sqlite3.Row
|
||||
|
||||
return g.db
|
||||
|
||||
def close_db(e=None):
|
||||
db = g.pop('db', None)
|
||||
|
||||
if db is not None:
|
||||
db.close()
|
||||
|
||||
def init_db(db_path = None, schema_path = None):
|
||||
if db_path is None:
|
||||
db = get_db()
|
||||
else:
|
||||
db = sqlite3.connect(db_path, detect_types=sqlite3.PARSE_DECLTYPES)
|
||||
|
||||
if schema_path is None:
|
||||
with current_app.open_resource('data/schema.sql') as f:
|
||||
script = f.read().decode('UTF-8')
|
||||
else:
|
||||
with open(schema_path, "r") as f:
|
||||
script = f.read()
|
||||
|
||||
db.executescript(script)
|
||||
db.commit()
|
||||
db.close()
|
||||
|
||||
def init_app(app):
|
||||
app.teardown_appcontext(close_db)
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="DB Init ArgumentParser", formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
parser.add_argument("-u", "--username", type=str, help="username of adming user", required=True)
|
||||
parser.add_argument("-p", "--password", type=str, help="password of adming user", required=True)
|
||||
parser.add_argument("-s", "--otp-secret", type=str, help="otp secret of admin user, python pyotp.random_base32()", required=False)
|
||||
args = parser.parse_args()
|
||||
config = vars(args)
|
||||
username = config['username']
|
||||
password = config['password']
|
||||
otp_secret = config['otp_secret']
|
||||
if (otp_secret is None):
|
||||
import pyotp
|
||||
otp_secret = pyotp.random_base32()
|
||||
|
||||
db_path = path.join('/server/instance/', default_database_name)
|
||||
if path.exists(db_path):
|
||||
print('Database already exists at {}. Will NOT override, if necessary first delete first then restart initialization!'.format(db_path))
|
||||
exit(1)
|
||||
schema_path = path.join(path.dirname(__file__), 'schema.sql')
|
||||
init_db(db_path = db_path, schema_path = schema_path)
|
||||
db = sqlite3.connect(db_path, detect_types=sqlite3.PARSE_DECLTYPES)
|
||||
db.row_factory = sqlite3.Row
|
||||
db_cursor = db.cursor()
|
||||
|
||||
hashed_password = sha256_crypt.hash(password)
|
||||
sql = "INSERT INTO user(username, password, otp_secret, privileged, was_otp_verified)"\
|
||||
"VALUES(:name, :pass, :otp, :privileged, :otp_verified)"
|
||||
params = {
|
||||
"name": username,
|
||||
"pass": hashed_password,
|
||||
"otp": otp_secret,
|
||||
"privileged": True,
|
||||
"otp_verified": False,
|
||||
}
|
||||
db_cursor.execute(sql, params)
|
||||
db.commit()
|
||||
db.close()
|
||||
47
server/flask/application/backend/data/schema.sql
Normal file
47
server/flask/application/backend/data/schema.sql
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
DROP TABLE IF EXISTS user;
|
||||
DROP TABLE IF EXISTS registration_token;
|
||||
DROP TABLE IF EXISTS reset_password_token;
|
||||
DROP TABLE IF EXISTS session;
|
||||
DROP TABLE IF EXISTS file_metadata_of_user;
|
||||
|
||||
CREATE TABLE user (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT UNIQUE NOT NULL,
|
||||
password TEXT NOT NULL,
|
||||
otp_secret TEXT NOT NULL,
|
||||
privileged INTEGER NOT NULL,
|
||||
was_otp_verified INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE registration_token (
|
||||
token TEXT PRIMARY KEY NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE reset_password_token (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
token TEXT NOT NULL,
|
||||
username TEXT NOT NULL,
|
||||
expires_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE session (
|
||||
user_id INTEGER NOT NULL,
|
||||
access_token TEXT NOT NULL,
|
||||
refresh_token TEXT NOT NULL,
|
||||
access_expires_at INTEGER NOT NULL,
|
||||
refresh_expires_at INTEGER NOT NULL,
|
||||
FOREIGN KEY (user_id) REFERENCES user (id)
|
||||
);
|
||||
|
||||
CREATE TABLE file_metadata_of_user (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
file_key TEXT NOT NULL,
|
||||
metadata TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE file_metadata (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
file_key TEXT NOT NULL,
|
||||
metadata TEXT NOT NULL
|
||||
);
|
||||
144
server/flask/application/backend/flask_project.py
Normal file
144
server/flask/application/backend/flask_project.py
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
from os import path
|
||||
|
||||
from flask import Flask
|
||||
import json
|
||||
from .data import db as db
|
||||
from .data.data_models import User
|
||||
from .require_decorators import require_username_and_password
|
||||
from .require_decorators import require_user_exists_by_username_and_password
|
||||
from .require_decorators import requires_session
|
||||
from .require_decorators import require_otp_verification_after_session
|
||||
from .require_decorators import require_user_priviliged_after_session
|
||||
from . import auth_requests as auth_requests_handler
|
||||
from . import admin_requests as admin_requests_handler
|
||||
from . import user_action_requests as user_action_requests_handler
|
||||
|
||||
# for chrome to accept the certificate run in console `endCommand(SecurityInterstitialCommandId.CMD_PROCEED)`
|
||||
# to restart = `uwsgi --ini home-vod-server.ini` like in Dockerimage
|
||||
def create_app(test_config=None):
|
||||
app = Flask(__name__)
|
||||
if (test_config == None):
|
||||
app.config.from_file('config.json', silent=True, load=json.load)
|
||||
else:
|
||||
app.config.from_mapping(test_config)
|
||||
db.init_app(app)
|
||||
|
||||
# region auth requests
|
||||
@app.route("/register", methods=['POST'])
|
||||
@require_username_and_password
|
||||
def register(username, password):
|
||||
return auth_requests_handler.handle_register(username = username, password = password)
|
||||
|
||||
@app.route("/otp_verification", methods=['POST'])
|
||||
@require_username_and_password
|
||||
@require_user_exists_by_username_and_password
|
||||
def otp_verification(user: User):
|
||||
return auth_requests_handler.handle_otp_verification(user = user)
|
||||
|
||||
@app.route("/login", methods=['POST'])
|
||||
@require_username_and_password
|
||||
@require_user_exists_by_username_and_password
|
||||
def login(user: User):
|
||||
return auth_requests_handler.handle_login(user = user)
|
||||
|
||||
@app.route("/logout", methods=['POST'])
|
||||
def logout():
|
||||
return auth_requests_handler.handle_logout()
|
||||
|
||||
@app.route("/refresh/token", methods=['POST'])
|
||||
def refresh_token():
|
||||
return auth_requests_handler.handle_refresh_token()
|
||||
# endregion
|
||||
|
||||
# region user_actions
|
||||
@app.route("/user/is_privileged", methods=['GET'])
|
||||
@requires_session
|
||||
def get_is_user_priviliged(user: User):
|
||||
return user_action_requests_handler.handle_get_is_user_priviliged(user = user)
|
||||
|
||||
@app.route("/change/password", methods=['POST'])
|
||||
@requires_session
|
||||
@require_otp_verification_after_session
|
||||
def change_password(user: User):
|
||||
return user_action_requests_handler.handle_change_password(user = user)
|
||||
|
||||
@app.route("/reset/password", methods=['POST'])
|
||||
@require_username_and_password
|
||||
def reset_password(username, password):
|
||||
return user_action_requests_handler.handle_reset_password(username = username, password = password)
|
||||
|
||||
@app.route("/user/file/metadata", methods=['POST'])
|
||||
@requires_session
|
||||
def add_user_file_data(user: User):
|
||||
return user_action_requests_handler.handle_add_user_file_data(user = user)
|
||||
|
||||
@app.route("/user/file/metadata", methods=['GET'])
|
||||
@requires_session
|
||||
def get_user_file_data(user: User):
|
||||
return user_action_requests_handler.handle_get_user_file_data(user = user)
|
||||
|
||||
@app.route("/file/metadata", methods=['POST'])
|
||||
@requires_session
|
||||
def add_file_metadata(user: User):
|
||||
return user_action_requests_handler.handle_add_file_metadata(user = user)
|
||||
|
||||
@app.route("/file/metadata", methods=['GET'])
|
||||
@requires_session
|
||||
def get_file_metadata(user: User):
|
||||
return user_action_requests_handler.handle_get_file_metadata(user = user)
|
||||
# endregion
|
||||
|
||||
# region admin requests
|
||||
@app.route("/admin/registration_token", methods=['POST'])
|
||||
@requires_session
|
||||
@require_otp_verification_after_session
|
||||
@require_user_priviliged_after_session
|
||||
def create_registration_token(user: User):
|
||||
return admin_requests_handler.handle_create_registration_token(user = user)
|
||||
|
||||
@app.route("/admin/reset_password_token", methods=['POST'])
|
||||
@requires_session
|
||||
@require_otp_verification_after_session
|
||||
@require_user_priviliged_after_session
|
||||
def create_reset_password_token(user: User):
|
||||
return admin_requests_handler.handle_create_reset_password_token(user = user)
|
||||
|
||||
@app.route("/admin/reset_otp_verification", methods=['POST'])
|
||||
@requires_session
|
||||
@require_otp_verification_after_session
|
||||
@require_user_priviliged_after_session
|
||||
def reset_user_otp_verification(user: User):
|
||||
return admin_requests_handler.handle_reset_user_otp_verification(user = user)
|
||||
|
||||
@app.route("/admin/get_users", methods=['GET'])
|
||||
@requires_session
|
||||
@require_user_priviliged_after_session
|
||||
def get_users(user: User):
|
||||
return admin_requests_handler.handle_get_users(user = user)
|
||||
|
||||
@app.route("/admin/get_registration_tokens", methods=['GET'])
|
||||
@requires_session
|
||||
@require_user_priviliged_after_session
|
||||
def get_registration_tokens(user: User):
|
||||
return admin_requests_handler.handle_get_registration_tokens(user = user)
|
||||
|
||||
@app.route("/admin/delete/user", methods=['POST'])
|
||||
@requires_session
|
||||
@require_otp_verification_after_session
|
||||
@require_user_priviliged_after_session
|
||||
def delete_user_by_name(user: User):
|
||||
return admin_requests_handler.handle_delete_user_by_name(user = user)
|
||||
|
||||
@app.route("/admin/delete/registration_token", methods=['POST'])
|
||||
@requires_session
|
||||
@require_otp_verification_after_session
|
||||
@require_user_priviliged_after_session
|
||||
def delete_registration_token(user: User):
|
||||
return admin_requests_handler.handle_delete_registration_token(user = user)
|
||||
# endregion
|
||||
|
||||
return app
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = create_app()
|
||||
app.run(host='0.0.0.0')
|
||||
108
server/flask/application/backend/require_decorators.py
Normal file
108
server/flask/application/backend/require_decorators.py
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
from flask import request, jsonify, current_app
|
||||
import functools
|
||||
from . import token_generator_util
|
||||
from .data import dao_users
|
||||
from .data.data_models import User
|
||||
from .data import dao_session
|
||||
|
||||
def get_cropped_username(username: str):
|
||||
max_length = current_app.config['MAX_USERNAME_LENGTH']
|
||||
if (isinstance(username, str)):
|
||||
return username[0:max_length]
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_cropped_password(password: str):
|
||||
max_length = current_app.config['MAX_PASSWORD_LENGTH']
|
||||
if (isinstance(password, str)):
|
||||
return password[0:max_length]
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_cropped_otp(otp: str):
|
||||
max_length = current_app.config['MAX_OTP_LENGTH']
|
||||
if (isinstance(otp, str)):
|
||||
return otp[0:max_length]
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_cropped_token(token: str):
|
||||
max_length = current_app.config['MAX_TOKEN_LENGTH']
|
||||
if (isinstance(token, str)):
|
||||
return token[0:max_length]
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_cropped_key(key: str):
|
||||
max_length = current_app.config['KEY_LENGTH']
|
||||
if (isinstance(key, str)):
|
||||
return key[0:max_length]
|
||||
else:
|
||||
return None
|
||||
|
||||
def require_username_and_password(request_processor):
|
||||
@functools.wraps(request_processor)
|
||||
def do_require_username_and_password():
|
||||
username = request.form.get('username')
|
||||
if username is None:
|
||||
errorResponse = jsonify({'message':'Username cannot be empty!','code':410})
|
||||
return errorResponse, 400
|
||||
|
||||
password = request.form.get('password')
|
||||
if password is None:
|
||||
errorResponse = jsonify({'message':'Password cannot be empty!','code':420})
|
||||
return errorResponse, 400
|
||||
return request_processor(get_cropped_username(username), get_cropped_password(password))
|
||||
return do_require_username_and_password
|
||||
|
||||
def require_user_exists_by_username_and_password(request_processor):
|
||||
@functools.wraps(request_processor)
|
||||
def do_require_user(username, password):
|
||||
user = dao_users.get_user_by_name_and_password(user_name = username, password = password)
|
||||
if user is None:
|
||||
errorResponse = jsonify({'message':'User cannot be found!','code':412})
|
||||
return errorResponse, 400
|
||||
return request_processor(user)
|
||||
return do_require_user
|
||||
|
||||
def requires_session(request_processor):
|
||||
@functools.wraps(request_processor)
|
||||
def do_require_session():
|
||||
access_token = get_cropped_token(request.headers.get('Authorization'))
|
||||
if (access_token is None):
|
||||
errorResponse = jsonify({'message':'Missing Authorization!','code':440})
|
||||
return errorResponse, 401
|
||||
|
||||
user_id = dao_session.get_user_for_token(access_token)
|
||||
if (user_id is None):
|
||||
errorResponse = jsonify({'message':'Invalid Authorization!','code':441})
|
||||
return errorResponse, 401
|
||||
|
||||
user = dao_users.get_user_by_id(user_id)
|
||||
if user is None:
|
||||
errorResponse = jsonify({'message':'Invalid Authorization!','code':442})
|
||||
return errorResponse, 400
|
||||
return request_processor(user)
|
||||
return do_require_session
|
||||
|
||||
def require_otp_verification_after_session(request_processor):
|
||||
@functools.wraps(request_processor)
|
||||
def do_require_otp(user: User):
|
||||
one_time_password = get_cropped_otp(request.form.get('otp') or '')
|
||||
is_otp_ok = token_generator_util.verify_otp(user.otp_secret, one_time_password)
|
||||
if not is_otp_ok:
|
||||
errorResponse = jsonify({'message':'Invalid Token!','code':431})
|
||||
return errorResponse, 400
|
||||
|
||||
return request_processor(user)
|
||||
return do_require_otp
|
||||
|
||||
def require_user_priviliged_after_session(request_processor):
|
||||
@functools.wraps(request_processor)
|
||||
def do_require_user_priviliged(user: User):
|
||||
if not user.privileged:
|
||||
errorResponse = jsonify({'message':'Not Authorized!','code':460})
|
||||
return errorResponse, 400
|
||||
return request_processor(user)
|
||||
|
||||
return do_require_user_priviliged
|
||||
47
server/flask/application/backend/token_generator_util.py
Normal file
47
server/flask/application/backend/token_generator_util.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
from secrets import token_urlsafe
|
||||
from flask import current_app
|
||||
import time
|
||||
import pyotp
|
||||
from .data.data_models import Session
|
||||
|
||||
def _get_byte_count():
|
||||
return current_app.config.get('SECRECT_BYTE_COUNT') or 64
|
||||
|
||||
def _get_access_expires_in():
|
||||
return current_app.config.get('SESSION_ACCESS_EXPIRATION_IN_SECONDS') or 86400
|
||||
|
||||
def _get_refresh_expires_in():
|
||||
return current_app.config.get('SESSION_REFRESH_EXPIRATION_IN_SECONDS') or 2*86400
|
||||
|
||||
def _get_reset_password_expires_in():
|
||||
return current_app.config.get('RESET_PASSWORD_EXPIRATION_IN_SECONDS') or 2*86400
|
||||
|
||||
def generate_session(user_id, byte_count = None, access_expires_in = None, refresh_expires_in = None):
|
||||
byte_count = byte_count or _get_byte_count()
|
||||
access_expires_in = access_expires_in or _get_access_expires_in()
|
||||
refresh_expires_in = refresh_expires_in or _get_refresh_expires_in()
|
||||
current_time = time.time()
|
||||
return Session(
|
||||
user_id = user_id,
|
||||
access_token = token_urlsafe(byte_count),
|
||||
refresh_token = token_urlsafe(byte_count),
|
||||
access_expires_at = access_expires_in + current_time,
|
||||
refresh_expires_at = refresh_expires_in + current_time,
|
||||
)
|
||||
|
||||
def generate_reset_password_expires_at(reset_password_expires_in = None):
|
||||
current_time = time.time()
|
||||
reset_password_expires_in = reset_password_expires_in or _get_reset_password_expires_in()
|
||||
return reset_password_expires_in + current_time
|
||||
|
||||
def generate_otp_secret():
|
||||
return pyotp.random_base32()
|
||||
|
||||
def verify_otp(secret, otp_code):
|
||||
totp = pyotp.TOTP(secret)
|
||||
timestampNow = time.time()
|
||||
return totp.verify(otp_code, time.time(), 2)
|
||||
|
||||
def get_url(secret, username):
|
||||
return pyotp.totp.TOTP(secret).provisioning_uri(name=username,issuer_name='FnivesVOD')
|
||||
|
||||
88
server/flask/application/backend/user_action_requests.py
Normal file
88
server/flask/application/backend/user_action_requests.py
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
from flask import request, jsonify
|
||||
import json
|
||||
from .require_decorators import get_cropped_password
|
||||
from .require_decorators import get_cropped_otp
|
||||
from .require_decorators import get_cropped_key
|
||||
from . import token_generator_util
|
||||
from .data import dao_users
|
||||
from .data import dao_session
|
||||
from .data import dao_reset_password_tokens
|
||||
from .data import dao_file_metadata_of_user
|
||||
from .data import dao_file_metadata
|
||||
from .data.data_models import User
|
||||
from .data.data_models import ResponseCode
|
||||
|
||||
def handle_change_password(user: User):
|
||||
password = get_cropped_password(request.form.get('password'))
|
||||
if password is None:
|
||||
errorResponse = jsonify({'message':'Invalid Password!','code':ResponseCode.INVALID_PASSWORD})
|
||||
return errorResponse, 400
|
||||
|
||||
new_password = get_cropped_password(request.form.get('new_password'))
|
||||
if new_password is None:
|
||||
errorResponse = jsonify({'message':'New Password cannot be empty!','code':ResponseCode.INVALID_NEW_PASSWORD})
|
||||
return errorResponse, 400
|
||||
|
||||
foundUser = dao_users.get_user_by_name_and_password(user_name = user.name, password = password)
|
||||
if (foundUser is None):
|
||||
errorResponse = jsonify({'message':'Invalid Password!','code':ResponseCode.INVALID_PASSWORD})
|
||||
return errorResponse, 400
|
||||
|
||||
session = token_generator_util.generate_session(user.id)
|
||||
dao_users.update_user_password(user_id = user.id, new_password = new_password)
|
||||
dao_session.create_new_single_session(session = session)
|
||||
sessionResponse = jsonify({
|
||||
'access_token': session.access_token,
|
||||
'refresh_token': session.refresh_token,
|
||||
'expires_at': session.access_expires_at
|
||||
})
|
||||
return sessionResponse, 200
|
||||
|
||||
def handle_reset_password(username: str, password: str):
|
||||
reset_password_token = get_cropped_otp(request.form.get('reset_password_token'))
|
||||
if reset_password_token is None:
|
||||
errorResponse = jsonify({'message':'Invalid Reset Password Token given!','code':ResponseCode.UNKNOWN_RESET_PASSWORD_TOKEN})
|
||||
return errorResponse, 400
|
||||
|
||||
if dao_reset_password_tokens.is_valid_token(token = reset_password_token, username = username) is False:
|
||||
errorResponse = jsonify({'message':'Invalid Reset Password Token given!','code':ResponseCode.UNKNOWN_RESET_PASSWORD_TOKEN})
|
||||
return errorResponse, 400
|
||||
|
||||
foundUser = dao_users.get_user_by_name(username = username)
|
||||
if (foundUser is None):
|
||||
errorResponse = jsonify({'message':'User cannot be found!','code':ResponseCode.NOT_FOUND_USER})
|
||||
return errorResponse, 400
|
||||
|
||||
dao_users.update_user_password(user_id = foundUser.id, new_password = password)
|
||||
|
||||
dao_reset_password_tokens.delete_tokens(username = username)
|
||||
|
||||
return jsonify({'message':'Password was Saved!','code':ResponseCode.SUCCESS_SAVED_PASSWORD}), 200
|
||||
|
||||
def handle_get_is_user_priviliged(user: User):
|
||||
return jsonify({'is_privileged': user.privileged}), 200
|
||||
|
||||
def handle_add_user_file_data(user: User):
|
||||
metadata_to_save = request.get_json(force=True, silent = True)
|
||||
if (metadata_to_save is not None and isinstance(metadata_to_save,dict)):
|
||||
dao_file_metadata_of_user.insert_metadata(user_id = user.id, metadata = metadata_to_save)
|
||||
return jsonify({'message': 'User\'s File MetaData Saved!', 'code': ResponseCode.SUCCESS_SAVED_USER_FILE_METADATA}), 200
|
||||
return jsonify({'message': 'Couldn\'t save user\'s metadata!', 'code': ResponseCode.CANT_SAVE_USER_FILE_METADATA}), 400
|
||||
|
||||
def handle_get_user_file_data(user: User):
|
||||
return jsonify(dao_file_metadata_of_user.get_metadata(user_id = user.id)), 200
|
||||
|
||||
|
||||
def handle_add_file_metadata(user: User):
|
||||
metadata_to_save = request.get_json(force=True, silent = True)
|
||||
if (metadata_to_save is not None and isinstance(metadata_to_save,dict)):
|
||||
dao_file_metadata.insert_metadata(metadata = metadata_to_save)
|
||||
return jsonify({'message': 'File MetaData Saved!', 'code': ResponseCode.SUCCESS_SAVED_FILE_METADATA}), 200
|
||||
return jsonify({'message': 'Couldn\'t save metadata!', 'code': ResponseCode.CANT_SAVE_FILE_METADATA}), 400
|
||||
|
||||
|
||||
def handle_get_file_metadata(user: User):
|
||||
file_key = get_cropped_key(request.args.get('file_key'))
|
||||
if (file_key is None):
|
||||
return jsonify({'message': 'Invalid FileKey (file_key)!', 'code': ResponseCode.INVALID_FILE_KEY}), 400
|
||||
return jsonify(dao_file_metadata.get_metadata(file_key = file_key)), 200
|
||||
Loading…
Add table
Add a link
Reference in a new issue