flask server
This commit is contained in:
parent
5acb2992ce
commit
0a71a6c840
54 changed files with 5876 additions and 0 deletions
32
server/flask/Dockerfile
Normal file
32
server/flask/Dockerfile
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
FROM python:3.9-bullseye
|
||||||
|
|
||||||
|
# ENV setup
|
||||||
|
RUN ["python","--version"]
|
||||||
|
RUN ["apt-get", "update"]
|
||||||
|
RUN ["apt-get", "install", "nginx", "-y"]
|
||||||
|
RUN ["pip", "install", "uwsgi"]
|
||||||
|
RUN ["uwsgi", "--version"]
|
||||||
|
RUN ["pip", "install", "flask"]
|
||||||
|
RUN ["pip", "install", "passlib"]
|
||||||
|
RUN ["pip", "install", "pyotp"]
|
||||||
|
|
||||||
|
# NGINX configuration setup
|
||||||
|
COPY ./application/nginx-proxy-config /etc/nginx/sites-available/nginx-proxy-config
|
||||||
|
RUN ln -s /etc/nginx/sites-available/nginx-proxy-config /etc/nginx/sites-enabled
|
||||||
|
|
||||||
|
COPY ./certificate /certificate
|
||||||
|
|
||||||
|
WORKDIR $HOME/server
|
||||||
|
|
||||||
|
# load source files
|
||||||
|
COPY ./application $HOME/server
|
||||||
|
|
||||||
|
# run server commands
|
||||||
|
RUN nginx -t
|
||||||
|
RUN service nginx configtest
|
||||||
|
|
||||||
|
# create database - no longer needed, since binding volume
|
||||||
|
#RUN mkdir instance
|
||||||
|
#RUN python db.py
|
||||||
|
|
||||||
|
CMD service nginx restart & uwsgi --ini home-vod-server.ini
|
||||||
34
server/flask/README.md
Normal file
34
server/flask/README.md
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Home-VOD Flask Backend
|
||||||
|
|
||||||
|
Learning Python Flask Project.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
For Runtime Environment I have used a docker container. The `setup.sh` script should create it.
|
||||||
|
|
||||||
|
The source code is bound to the container so any changes are reflected inside the container as well.
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
### Just run all tests
|
||||||
|
|
||||||
|
You can run all tests just from docker, using docker exec:
|
||||||
|
|
||||||
|
`docker exec home-vod-server python -m unittest discover -v -s .`
|
||||||
|
|
||||||
|
This should run all the tests.
|
||||||
|
|
||||||
|
> Ensure the testdb is deleted if you have stopped the execution of tests.
|
||||||
|
|
||||||
|
### Interactive mode
|
||||||
|
|
||||||
|
While developing you may want to run a single test multiple times. Suggestion is to connect interactively with the container:
|
||||||
|
`docker exec -it home-vod-server /bin/bash`
|
||||||
|
|
||||||
|
Then use the following command to run the specific test:
|
||||||
|
`python -m unittest ./test/test_logout.py`
|
||||||
|
|
||||||
|
You can also run all the tests with the following at this point.
|
||||||
|
`python -m unittest discover -v -s .`
|
||||||
|
|
||||||
|
> Ensure you are in the `/server` folder
|
||||||
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
|
||||||
5
server/flask/application/flask-wsgi.py
Normal file
5
server/flask/application/flask-wsgi.py
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
from backend.flask_project import create_app
|
||||||
|
|
||||||
|
app = create_app()
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run()
|
||||||
11
server/flask/application/home-vod-server.conf
Normal file
11
server/flask/application/home-vod-server.conf
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
description "uWSGI instance to serve home-vod-server"
|
||||||
|
|
||||||
|
start on runlevel [2345]
|
||||||
|
stop on runlevel [!2345]
|
||||||
|
|
||||||
|
setgid www-data
|
||||||
|
|
||||||
|
script
|
||||||
|
cd $HOME/server
|
||||||
|
uwsgi --ini home-vod-server.ini
|
||||||
|
end-script
|
||||||
11
server/flask/application/home-vod-server.ini
Normal file
11
server/flask/application/home-vod-server.ini
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
[uwsgi]
|
||||||
|
module = flask-wsgi:app
|
||||||
|
|
||||||
|
master = true
|
||||||
|
processes = 5
|
||||||
|
|
||||||
|
socket = /tmp/myapp.sock
|
||||||
|
chmod-socket = 666
|
||||||
|
vacuum = true
|
||||||
|
|
||||||
|
die-on-term = true
|
||||||
64
server/flask/application/nginx-proxy-config
Normal file
64
server/flask/application/nginx-proxy-config
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
# rate limiting: https://www.nginx.com/blog/rate-limiting-nginx/
|
||||||
|
limit_req_zone $binary_remote_addr zone=ip:10m rate=5r/s;
|
||||||
|
limit_req_zone $binary_remote_addr zone=restricted_ip:10m rate=10r/m;
|
||||||
|
|
||||||
|
# http server
|
||||||
|
server {
|
||||||
|
server_name _;
|
||||||
|
listen 8080 default_server;
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
|
||||||
|
# https server
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name home_vod_server;
|
||||||
|
|
||||||
|
ssl_certificate /certificate/cert.pem;
|
||||||
|
ssl_certificate_key /certificate/key.pem;
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256;
|
||||||
|
ssl_prefer_server_ciphers on;
|
||||||
|
|
||||||
|
root /server;
|
||||||
|
|
||||||
|
# static media
|
||||||
|
location /media {
|
||||||
|
root /media-data/;
|
||||||
|
autoindex on;
|
||||||
|
auth_request /user/is_privileged;
|
||||||
|
limit_req zone=ip burst=12 delay=8;
|
||||||
|
|
||||||
|
# enable cache
|
||||||
|
expires 1d;
|
||||||
|
add_header Cache-Control "public, no-transform";
|
||||||
|
|
||||||
|
# kill cache
|
||||||
|
# add_header Last-Modified $date_gmt;
|
||||||
|
# add_header Cache-Control 'no-store, no-cache';
|
||||||
|
# if_modified_since off;
|
||||||
|
# expires off;
|
||||||
|
# etag off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# flask server
|
||||||
|
location / {
|
||||||
|
include uwsgi_params;
|
||||||
|
uwsgi_pass unix:///tmp/myapp.sock;
|
||||||
|
limit_req zone=ip burst=12 delay=8;
|
||||||
|
}
|
||||||
|
|
||||||
|
# flask server login
|
||||||
|
location /login {
|
||||||
|
include uwsgi_params;
|
||||||
|
uwsgi_pass unix:///tmp/myapp.sock;
|
||||||
|
limit_req zone=restricted_ip burst=4;
|
||||||
|
}
|
||||||
|
|
||||||
|
# flask server otp_verification
|
||||||
|
location /otp_verification {
|
||||||
|
include uwsgi_params;
|
||||||
|
uwsgi_pass unix:///tmp/myapp.sock;
|
||||||
|
limit_req zone=restricted_ip burst=4;
|
||||||
|
}
|
||||||
|
}
|
||||||
1
server/flask/application/start.sh
Executable file
1
server/flask/application/start.sh
Executable file
|
|
@ -0,0 +1 @@
|
||||||
|
uwsgi --ini home-vod-server.ini
|
||||||
5
server/flask/application/test/__init__.py
Normal file
5
server/flask/application/test/__init__.py
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# import os, sys
|
||||||
|
|
||||||
|
# currentDir = os.path.dirname(__file__)
|
||||||
|
# parentDir = os.path.join(currentDir, '..')
|
||||||
|
# sys.path.append(os.path.abspath(parentDir))
|
||||||
5
server/flask/application/test/backend/__init__.py
Normal file
5
server/flask/application/test/backend/__init__.py
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import os, sys
|
||||||
|
|
||||||
|
currentDir = os.path.dirname(__file__)
|
||||||
|
parentDir = os.path.join(currentDir, '..')
|
||||||
|
sys.path.append(os.path.abspath(parentDir))
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
import context
|
||||||
|
import unittest
|
||||||
|
import unittest.mock
|
||||||
|
from copy import copy
|
||||||
|
from flask import current_app
|
||||||
|
|
||||||
|
import backend.token_generator_util as sut
|
||||||
|
|
||||||
|
|
||||||
|
class SessionDAOTest(unittest.TestCase):
|
||||||
|
|
||||||
|
app = context.create_app({**context.default_test_config, "SECRECT_BYTE_COUNT": 52})
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_session_generated_is_proper(self, mock_time):
|
||||||
|
expected = None
|
||||||
|
user_id = 123
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
actual = sut.generate_session(user_id)
|
||||||
|
|
||||||
|
self.assertEqual(20000+1000, actual.access_expires_at)
|
||||||
|
self.assertEqual(50000+1000, actual.refresh_expires_at)
|
||||||
|
self.assertEqual(user_id, actual.user_id)
|
||||||
|
self.assertEqual(70, len(actual.access_token))
|
||||||
|
self.assertEqual(70, len(actual.refresh_token))
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_two_sessions_are_not_equal(self, mock_time):
|
||||||
|
expected = None
|
||||||
|
user_id = 123
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
session1 = sut.generate_session(user_id)
|
||||||
|
session2 = sut.generate_session(user_id)
|
||||||
|
|
||||||
|
session1Copy = copy(session1)
|
||||||
|
self.assertEqual(session1Copy, session1)
|
||||||
|
self.assertIsNot(session1Copy, session1)
|
||||||
|
self.assertNotEqual(session1, session2)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_two_sessions_are_not_equal(self, mock_time):
|
||||||
|
# how to get values = pyotp.TOTP('base32secret3232').at(1000+90)
|
||||||
|
secret = 'base32secret3232'
|
||||||
|
nowCode = 585501
|
||||||
|
nowMinus30SecCode = 572292
|
||||||
|
nowMinus60SecCode = 512128
|
||||||
|
nowMinus90SecCode = 440932
|
||||||
|
nowPlus30SecCode = 602066
|
||||||
|
nowPlus60SecCode = 893795
|
||||||
|
nowPlus90SecCode = 11418
|
||||||
|
with self.app.app_context():
|
||||||
|
actual = sut.verify_otp(secret, nowCode)
|
||||||
|
actual30SecBefore = sut.verify_otp(secret, nowMinus30SecCode)
|
||||||
|
actual60SecBefore = sut.verify_otp(secret, nowMinus60SecCode)
|
||||||
|
actual90SecBefore = sut.verify_otp(secret, nowMinus90SecCode)
|
||||||
|
actual30SecAfter = sut.verify_otp(secret, nowPlus30SecCode)
|
||||||
|
actual60SecAfter = sut.verify_otp(secret, nowPlus60SecCode)
|
||||||
|
actual90SecAfter = sut.verify_otp(secret, nowPlus90SecCode)
|
||||||
|
|
||||||
|
self.assertEqual(True, actual)
|
||||||
|
self.assertEqual(True, actual30SecBefore)
|
||||||
|
self.assertEqual(True, actual60SecBefore)
|
||||||
|
self.assertEqual(True, actual30SecAfter)
|
||||||
|
self.assertEqual(True, actual60SecAfter)
|
||||||
|
self.assertEqual(False, actual90SecBefore)
|
||||||
|
self.assertEqual(False, actual90SecAfter)
|
||||||
|
|
||||||
|
def test_url_is_proper(self):
|
||||||
|
secret = 'base32secret3232'
|
||||||
|
actual = sut.get_url(secret=secret, username='admin')
|
||||||
|
# URL can be verified by using text->QR code on https://www.the-qrcode-generator.com/
|
||||||
|
self.assertEqual('otpauth://totp/FnivesVOD:admin?secret=base32secret3232&issuer=FnivesVOD', actual)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
||||||
13
server/flask/application/test/context.py
Normal file
13
server/flask/application/test/context.py
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
from backend.flask_project import create_app
|
||||||
|
|
||||||
|
default_test_config = {
|
||||||
|
"DATABASE_PATH": "testdb",
|
||||||
|
"SESSION_ACCESS_EXPIRATION_IN_SECONDS": 20000,
|
||||||
|
"SESSION_REFRESH_EXPIRATION_IN_SECONDS": 50000,
|
||||||
|
"DATABASE_PATH": "testdb",
|
||||||
|
"MAX_PASSWORD_LENGTH": 64,
|
||||||
|
"MAX_USERNAME_LENGTH": 64,
|
||||||
|
"MAX_OTP_LENGTH": 16,
|
||||||
|
"MAX_TOKEN_LENGTH": 200,
|
||||||
|
"KEY_LENGTH": 30,
|
||||||
|
}
|
||||||
5
server/flask/application/test/data/__init__.py
Normal file
5
server/flask/application/test/data/__init__.py
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import os, sys
|
||||||
|
|
||||||
|
currentDir = os.path.dirname(__file__)
|
||||||
|
parentDir = os.path.join(currentDir, '..')
|
||||||
|
sys.path.append(os.path.abspath(parentDir))
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
import os,sys
|
||||||
|
sys.path.append('../')
|
||||||
|
import context
|
||||||
|
import unittest
|
||||||
|
import unittest.mock
|
||||||
|
import time
|
||||||
|
from flask import current_app
|
||||||
|
import backend.data.db as db
|
||||||
|
from backend.data.data_models import DataError
|
||||||
|
|
||||||
|
import backend.data.dao_registration_tokens as sut
|
||||||
|
|
||||||
|
class RegistrationTokenDAOTest(unittest.TestCase):
|
||||||
|
|
||||||
|
app = context.create_app(context.default_test_config)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.init_db()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.close_db()
|
||||||
|
os.remove("testdb")
|
||||||
|
|
||||||
|
def test_empty_db_contains_no_token(self):
|
||||||
|
token = "token"
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
actual = sut.is_valid_token(token)
|
||||||
|
|
||||||
|
self.assertEqual(False, actual)
|
||||||
|
|
||||||
|
def test_inserted_token_is_found(self):
|
||||||
|
token = "token"
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
sut.insert_token(token)
|
||||||
|
actual = sut.is_valid_token(token)
|
||||||
|
|
||||||
|
self.assertEqual(True, actual)
|
||||||
|
|
||||||
|
def test_same_token_cannot_be_inserted_twice(self):
|
||||||
|
token = "token"
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
sut.insert_token(token)
|
||||||
|
result = sut.insert_token(token)
|
||||||
|
|
||||||
|
self.assertEqual(DataError.REGISTRATION_CODE_ALREADY_EXISTS, result)
|
||||||
|
|
||||||
|
def test_token_deleted_is_not_found(self):
|
||||||
|
token = "token"
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
sut.insert_token(token)
|
||||||
|
sut.delete_token(token)
|
||||||
|
actual = sut.is_valid_token(token)
|
||||||
|
|
||||||
|
self.assertEqual(False, actual)
|
||||||
|
|
||||||
|
def test_tokens_can_be_requested(self):
|
||||||
|
expected = ['token-1', 'token-3']
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
sut.insert_token('token-1')
|
||||||
|
sut.insert_token('token-2')
|
||||||
|
sut.insert_token('token-3')
|
||||||
|
sut.delete_token('token-2')
|
||||||
|
actual = sut.get_tokens()
|
||||||
|
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
import os,sys
|
||||||
|
sys.path.append('../')
|
||||||
|
import context
|
||||||
|
import unittest
|
||||||
|
import unittest.mock
|
||||||
|
import time
|
||||||
|
from flask import current_app
|
||||||
|
import backend.data.db as db
|
||||||
|
|
||||||
|
import backend.data.dao_reset_password_tokens as sut
|
||||||
|
|
||||||
|
class ResetPasswordTokenDAOTest(unittest.TestCase):
|
||||||
|
|
||||||
|
app = context.create_app(context.default_test_config)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.init_db()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.close_db()
|
||||||
|
os.remove("testdb")
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_empty_db_contains_no_token(self, mock_time):
|
||||||
|
token = "token"
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
actual = sut.is_valid_token(token = token, username = "")
|
||||||
|
|
||||||
|
self.assertEqual(False, actual)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_inserted_token_is_found(self, mock_time):
|
||||||
|
token = "token"
|
||||||
|
username = "usr"
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
sut.insert_token(token = token, username = username, expires_at = 2000)
|
||||||
|
actual = sut.is_valid_token(token = token, username = username)
|
||||||
|
|
||||||
|
self.assertEqual(True, actual)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_inserted_expired_token_is_not_found(self, mock_time):
|
||||||
|
token = "token"
|
||||||
|
username = "usr"
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
sut.insert_token(token = token, username = username, expires_at = 1000)
|
||||||
|
actual = sut.is_valid_token(token = token, username = username)
|
||||||
|
|
||||||
|
self.assertEqual(False, actual)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_token_deleted_is_not_found(self, mock_time):
|
||||||
|
token = "token"
|
||||||
|
username = "usr"
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
sut.insert_token(token = token, username = username, expires_at = 2000)
|
||||||
|
sut.delete_tokens(username = username)
|
||||||
|
actual = sut.is_valid_token(token = token, username = username)
|
||||||
|
|
||||||
|
self.assertEqual(False, actual)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_only_given_user_tokens_are_deleted(self, mock_time):
|
||||||
|
token = "token"
|
||||||
|
username_given = "usr_given"
|
||||||
|
username = "usr_other"
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
sut.insert_token(token = token, username = username_given, expires_at = 2000)
|
||||||
|
sut.insert_token(token = token, username = username, expires_at = 2000)
|
||||||
|
sut.delete_tokens(username = username_given)
|
||||||
|
actual = sut.is_valid_token(token = token, username = username)
|
||||||
|
|
||||||
|
self.assertEqual(True, actual)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
||||||
247
server/flask/application/test/data/test_dao_session.py
Normal file
247
server/flask/application/test/data/test_dao_session.py
Normal file
|
|
@ -0,0 +1,247 @@
|
||||||
|
import os,sys
|
||||||
|
sys.path.append('../')
|
||||||
|
import context
|
||||||
|
import unittest
|
||||||
|
import unittest.mock
|
||||||
|
import time
|
||||||
|
from flask import current_app
|
||||||
|
from backend.data import db
|
||||||
|
from backend.data.data_models import Session
|
||||||
|
|
||||||
|
import backend.data.dao_session as sut
|
||||||
|
|
||||||
|
# Notes to myself:
|
||||||
|
# test nees tu start with test
|
||||||
|
# sys.path.append('../') appends the path so context can be imported
|
||||||
|
# with self.app.app_context(): is required to have current_app, if no request is running
|
||||||
|
|
||||||
|
class SessionDAOTest(unittest.TestCase):
|
||||||
|
|
||||||
|
app = context.create_app(context.default_test_config)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.init_db()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.close_db()
|
||||||
|
os.remove("testdb")
|
||||||
|
|
||||||
|
def test_empty_db_contains_no_token(self):
|
||||||
|
expected = None
|
||||||
|
token = "token"
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
actual = sut.get_user_for_token(token)
|
||||||
|
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_inserted_token_is_found(self, mock_time):
|
||||||
|
assert time.time() == 1000
|
||||||
|
expected = 13
|
||||||
|
token = "token"
|
||||||
|
session = Session(
|
||||||
|
user_id = 13,
|
||||||
|
access_token = token,
|
||||||
|
refresh_token = "refresh_token",
|
||||||
|
access_expires_at = 2000,
|
||||||
|
refresh_expires_at = 4000
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
sut.insert_user_session(session)
|
||||||
|
actual = sut.get_user_for_token(token)
|
||||||
|
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_same_token_results_in_not_found(self, mock_time):
|
||||||
|
assert time.time() == 1000
|
||||||
|
expected = None
|
||||||
|
token = "token"
|
||||||
|
session1 = Session(
|
||||||
|
user_id = 13,
|
||||||
|
access_token = token,
|
||||||
|
refresh_token = "refresh_token",
|
||||||
|
access_expires_at = 2000,
|
||||||
|
refresh_expires_at = 4000
|
||||||
|
)
|
||||||
|
session2 = Session(
|
||||||
|
user_id = 14,
|
||||||
|
access_token = token,
|
||||||
|
refresh_token = "refresh_token",
|
||||||
|
access_expires_at = 2000,
|
||||||
|
refresh_expires_at = 4000
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
sut.insert_user_session(session1)
|
||||||
|
sut.insert_user_session(session2)
|
||||||
|
actual = sut.get_user_for_token(token)
|
||||||
|
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_expired_access_token_isnt_returned(self, mock_time):
|
||||||
|
assert time.time() == 1000
|
||||||
|
expected = None
|
||||||
|
token = "token"
|
||||||
|
session = Session(
|
||||||
|
user_id = 13,
|
||||||
|
access_token = token,
|
||||||
|
refresh_token = "refresh_token",
|
||||||
|
access_expires_at = 500,
|
||||||
|
refresh_expires_at = 2000
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
sut.insert_user_session(session)
|
||||||
|
actual = sut.get_user_for_token(token)
|
||||||
|
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_expired_refresh_token_isnt_returned(self, mock_time):
|
||||||
|
assert time.time() == 1000
|
||||||
|
expected = None
|
||||||
|
token = "token"
|
||||||
|
session = Session(
|
||||||
|
user_id = 13,
|
||||||
|
access_token = token,
|
||||||
|
refresh_token = "refresh_token",
|
||||||
|
access_expires_at = 2500,
|
||||||
|
refresh_expires_at = 500
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
sut.insert_user_session(session)
|
||||||
|
actual = sut.get_user_for_token(token)
|
||||||
|
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_deleted_session_isnt_returned(self, mock_time):
|
||||||
|
assert time.time() == 1000
|
||||||
|
expected = None
|
||||||
|
token = "token"
|
||||||
|
session = Session(
|
||||||
|
user_id = 13,
|
||||||
|
access_token = token,
|
||||||
|
refresh_token = "refresh_token",
|
||||||
|
access_expires_at = 1500,
|
||||||
|
refresh_expires_at = 5000
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
sut.insert_user_session(session = session)
|
||||||
|
sut.delete_user_session(access_token = token)
|
||||||
|
actual = sut.get_user_for_token(access_token = token)
|
||||||
|
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_deleted_all_user_session_isnt_returned(self, mock_time):
|
||||||
|
assert time.time() == 1000
|
||||||
|
expected = None
|
||||||
|
session1 = Session(
|
||||||
|
user_id = 13,
|
||||||
|
access_token = "token1",
|
||||||
|
refresh_token = "refresh_token1",
|
||||||
|
access_expires_at = 1500,
|
||||||
|
refresh_expires_at = 5000
|
||||||
|
)
|
||||||
|
session2 = Session(
|
||||||
|
user_id = 13,
|
||||||
|
access_token = "token2",
|
||||||
|
refresh_token = "refresh_token2",
|
||||||
|
access_expires_at = 1500,
|
||||||
|
refresh_expires_at = 5000
|
||||||
|
)
|
||||||
|
session_of_different_user = Session(
|
||||||
|
user_id = 14,
|
||||||
|
access_token = "token3",
|
||||||
|
refresh_token = "refresh_token3",
|
||||||
|
access_expires_at = 1500,
|
||||||
|
refresh_expires_at = 5000
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
sut.insert_user_session(session = session1)
|
||||||
|
sut.insert_user_session(session = session2)
|
||||||
|
sut.insert_user_session(session = session_of_different_user)
|
||||||
|
sut.delete_all_user_session_by_user_id(user_id=13)
|
||||||
|
actual1 = sut.get_user_for_token(access_token = session1.access_token)
|
||||||
|
actual2 = sut.get_user_for_token(access_token = session2.access_token)
|
||||||
|
actual_of_different_user = sut.get_user_for_token(access_token = session_of_different_user.access_token)
|
||||||
|
|
||||||
|
self.assertEqual(expected, actual1)
|
||||||
|
self.assertEqual(expected, actual2)
|
||||||
|
self.assertEqual(session_of_different_user.user_id, actual_of_different_user)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_after_new_single_session_old_session_is_not_returned(self, mock_time):
|
||||||
|
assert time.time() == 1000
|
||||||
|
token = "token"
|
||||||
|
new_token = "new_token"
|
||||||
|
session = Session(
|
||||||
|
user_id = 13,
|
||||||
|
access_token = token,
|
||||||
|
refresh_token = "refresh_token",
|
||||||
|
access_expires_at = 1500,
|
||||||
|
refresh_expires_at = 5000
|
||||||
|
)
|
||||||
|
new_session = session = Session(
|
||||||
|
user_id = 13,
|
||||||
|
access_token = new_token,
|
||||||
|
refresh_token = "refresh_token",
|
||||||
|
access_expires_at = 1500,
|
||||||
|
refresh_expires_at = 5000
|
||||||
|
)
|
||||||
|
expected_old = None
|
||||||
|
expected_new = 13
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
sut.insert_user_session(session = session)
|
||||||
|
sut.create_new_single_session(session = new_session)
|
||||||
|
actual_old = sut.get_user_for_token(access_token = token)
|
||||||
|
actual_new = sut.get_user_for_token(access_token = new_token)
|
||||||
|
|
||||||
|
self.assertEqual(expected_old, actual_old)
|
||||||
|
self.assertEqual(expected_new, actual_new)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_after_swap_refresh_session_old_session_is_not_returned(self, mock_time):
|
||||||
|
assert time.time() == 1000
|
||||||
|
token = "token"
|
||||||
|
new_token = "new_token"
|
||||||
|
session = Session(
|
||||||
|
user_id = 13,
|
||||||
|
access_token = token,
|
||||||
|
refresh_token = "refresh_token",
|
||||||
|
access_expires_at = 1500,
|
||||||
|
refresh_expires_at = 5000
|
||||||
|
)
|
||||||
|
new_session = session = Session(
|
||||||
|
user_id = 13,
|
||||||
|
access_token = new_token,
|
||||||
|
refresh_token = "refresh_token2",
|
||||||
|
access_expires_at = 1500,
|
||||||
|
refresh_expires_at = 5000
|
||||||
|
)
|
||||||
|
expected_old = None
|
||||||
|
expected_new = 13
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
sut.insert_user_session(session = session)
|
||||||
|
sut.swap_refresh_session(refresh_token = session.refresh_token, session = new_session)
|
||||||
|
actual_old = sut.get_user_for_token(access_token = token)
|
||||||
|
actual_new = sut.get_user_for_token(access_token = new_token)
|
||||||
|
|
||||||
|
self.assertEqual(expected_old, actual_old)
|
||||||
|
self.assertEqual(expected_new, actual_new)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
||||||
301
server/flask/application/test/data/test_dao_users.py
Normal file
301
server/flask/application/test/data/test_dao_users.py
Normal file
|
|
@ -0,0 +1,301 @@
|
||||||
|
import os,sys
|
||||||
|
sys.path.append('../')
|
||||||
|
import context
|
||||||
|
import unittest
|
||||||
|
import unittest.mock
|
||||||
|
import time
|
||||||
|
from flask import current_app
|
||||||
|
from backend.data import db
|
||||||
|
from backend.data.data_models import RegisteringUser
|
||||||
|
from backend.data.data_models import User
|
||||||
|
from backend.data.data_models import DataError
|
||||||
|
|
||||||
|
import backend.data.dao_users as sut
|
||||||
|
|
||||||
|
class RegistrationTokenDAOTest(unittest.TestCase):
|
||||||
|
|
||||||
|
app = context.create_app(context.default_test_config)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.init_db()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.close_db()
|
||||||
|
os.remove("testdb")
|
||||||
|
|
||||||
|
def test_empty_db_contains_no_user(self):
|
||||||
|
user_id = 1
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
actual = sut.get_user_by_id(user_id)
|
||||||
|
|
||||||
|
self.assertEqual(None, actual)
|
||||||
|
|
||||||
|
def test_user_inserted_can_be_found_by_id(self):
|
||||||
|
inserted = RegisteringUser(
|
||||||
|
name = "admin",
|
||||||
|
password = "admin",
|
||||||
|
otp_secret = "secret",
|
||||||
|
privileged = True,
|
||||||
|
was_otp_verified = True
|
||||||
|
)
|
||||||
|
expected = User(
|
||||||
|
id = 1,
|
||||||
|
name = "admin",
|
||||||
|
otp_secret = "secret",
|
||||||
|
privileged = True,
|
||||||
|
was_otp_verified = True
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
user_id = sut.insert_user(inserted)
|
||||||
|
actual = sut.get_user_by_id(user_id)
|
||||||
|
|
||||||
|
self.assertEqual(expected.id, user_id)
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
def test_deleted_user_cannot_be_found(self):
|
||||||
|
inserted = RegisteringUser(
|
||||||
|
name = "admin",
|
||||||
|
password = "admin",
|
||||||
|
otp_secret = "secret",
|
||||||
|
privileged = True,
|
||||||
|
was_otp_verified = True
|
||||||
|
)
|
||||||
|
expected = None
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
user_id = sut.insert_user(inserted)
|
||||||
|
sut.delete_user_by_id(user_id)
|
||||||
|
actual = sut.get_user_by_id(user_id)
|
||||||
|
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
def test_user_inserted_can_be_found_by_name(self):
|
||||||
|
inserted = RegisteringUser(
|
||||||
|
name = "admin",
|
||||||
|
password = "admin",
|
||||||
|
otp_secret = "secret",
|
||||||
|
privileged = True,
|
||||||
|
was_otp_verified = True
|
||||||
|
)
|
||||||
|
expected = User(
|
||||||
|
id = 1,
|
||||||
|
name = "admin",
|
||||||
|
otp_secret = "secret",
|
||||||
|
privileged = True,
|
||||||
|
was_otp_verified = True
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
user_id = sut.insert_user(inserted)
|
||||||
|
actual = sut.get_user_by_name('admin')
|
||||||
|
|
||||||
|
self.assertEqual(expected.id, user_id)
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
def test_2_user_inserted_can_be_found_by_id(self):
|
||||||
|
inserted1 = RegisteringUser(
|
||||||
|
name = "admin",
|
||||||
|
password = "pass",
|
||||||
|
otp_secret = "secret",
|
||||||
|
privileged = True,
|
||||||
|
was_otp_verified = True
|
||||||
|
)
|
||||||
|
expected1 = User(
|
||||||
|
id = 1,
|
||||||
|
name = "admin",
|
||||||
|
otp_secret = "secret",
|
||||||
|
privileged = True,
|
||||||
|
was_otp_verified = True
|
||||||
|
)
|
||||||
|
inserted2 = RegisteringUser(
|
||||||
|
name = "admin2",
|
||||||
|
password = "pass",
|
||||||
|
otp_secret = "secret",
|
||||||
|
privileged = False,
|
||||||
|
)
|
||||||
|
expected2 = User(
|
||||||
|
id = 2,
|
||||||
|
name = "admin2",
|
||||||
|
otp_secret = "secret",
|
||||||
|
privileged = False,
|
||||||
|
was_otp_verified = False
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
user_id1 = sut.insert_user(inserted1)
|
||||||
|
actual1 = sut.get_user_by_id(user_id1)
|
||||||
|
user_id2 = sut.insert_user(inserted2)
|
||||||
|
actual2 = sut.get_user_by_id(user_id2)
|
||||||
|
|
||||||
|
self.assertEqual(expected1.id, user_id1)
|
||||||
|
self.assertEqual(expected1, actual1)
|
||||||
|
self.assertEqual(expected2.id, user_id2)
|
||||||
|
self.assertEqual(expected2, actual2)
|
||||||
|
|
||||||
|
def test_2_user_inserted_can_be_get(self):
|
||||||
|
inserted1 = RegisteringUser(
|
||||||
|
name = "admin",
|
||||||
|
password = "pass",
|
||||||
|
otp_secret = "secret",
|
||||||
|
privileged = True,
|
||||||
|
was_otp_verified = True
|
||||||
|
)
|
||||||
|
expected1 = User(
|
||||||
|
id = 1,
|
||||||
|
name = "admin",
|
||||||
|
otp_secret = "secret",
|
||||||
|
privileged = True,
|
||||||
|
was_otp_verified = True
|
||||||
|
)
|
||||||
|
inserted2 = RegisteringUser(
|
||||||
|
name = "admin2",
|
||||||
|
password = "pass",
|
||||||
|
otp_secret = "secret",
|
||||||
|
privileged = False,
|
||||||
|
)
|
||||||
|
expected2 = User(
|
||||||
|
id = 2,
|
||||||
|
name = "admin2",
|
||||||
|
otp_secret = "secret",
|
||||||
|
privileged = False,
|
||||||
|
was_otp_verified = False
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
user_id1 = sut.insert_user(inserted1)
|
||||||
|
user_id2 = sut.insert_user(inserted2)
|
||||||
|
actual = sut.get_users()
|
||||||
|
|
||||||
|
self.assertEqual([expected1,expected2], list(actual))
|
||||||
|
|
||||||
|
|
||||||
|
def test_user_inserted_can_not_be_found_by_good_name_and_wrong_password(self):
|
||||||
|
inserted = RegisteringUser(
|
||||||
|
name = "admin",
|
||||||
|
password = "pass",
|
||||||
|
otp_secret = "secret"
|
||||||
|
)
|
||||||
|
with self.app.app_context():
|
||||||
|
sut.insert_user(inserted)
|
||||||
|
actual = sut.get_user_by_name_and_password('admin', 'pass2')
|
||||||
|
|
||||||
|
self.assertEqual(None, actual)
|
||||||
|
|
||||||
|
def test_user_inserted_can_not_be_found_by_wrong_name_and_good_password(self):
|
||||||
|
inserted = RegisteringUser(
|
||||||
|
name = "admin",
|
||||||
|
password = "pass",
|
||||||
|
otp_secret = "secret"
|
||||||
|
)
|
||||||
|
with self.app.app_context():
|
||||||
|
sut.insert_user(inserted)
|
||||||
|
actual = sut.get_user_by_name_and_password('admin2', 'pass')
|
||||||
|
|
||||||
|
self.assertEqual(None, actual)
|
||||||
|
|
||||||
|
|
||||||
|
def test_user_inserted_can_be_found_by_name_and_password(self):
|
||||||
|
inserted = RegisteringUser(
|
||||||
|
name = "admin",
|
||||||
|
password = "pass",
|
||||||
|
otp_secret = "secret"
|
||||||
|
)
|
||||||
|
expected = User(
|
||||||
|
id = 1,
|
||||||
|
name = "admin",
|
||||||
|
otp_secret = "secret",
|
||||||
|
privileged = False,
|
||||||
|
was_otp_verified = False
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
sut.insert_user(inserted)
|
||||||
|
actual = sut.get_user_by_name_and_password('admin', 'pass')
|
||||||
|
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
def test_update_user_privilige(self):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = "admin2",
|
||||||
|
password = "pass",
|
||||||
|
otp_secret = "secret"
|
||||||
|
)
|
||||||
|
expected = User(
|
||||||
|
id = 1,
|
||||||
|
name = "admin2",
|
||||||
|
otp_secret = "secret",
|
||||||
|
privileged = True,
|
||||||
|
was_otp_verified = False
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
user_id = sut.insert_user(user)
|
||||||
|
sut.update_user_privilige(user_id, True)
|
||||||
|
actual = sut.get_user_by_id(user_id)
|
||||||
|
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
def test_update_user_otp_verification(self):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = "admin2",
|
||||||
|
password = "pass",
|
||||||
|
otp_secret = "secret"
|
||||||
|
)
|
||||||
|
expected = User(
|
||||||
|
id = 1,
|
||||||
|
name = "admin2",
|
||||||
|
otp_secret = "secret",
|
||||||
|
privileged = False,
|
||||||
|
was_otp_verified = True
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
user_id = sut.insert_user(user)
|
||||||
|
sut.update_user_otp_verification(user_id, True)
|
||||||
|
actual = sut.get_user_by_id(user_id)
|
||||||
|
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
def test_insert_user_twice(self):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = "admin2",
|
||||||
|
password = "pass",
|
||||||
|
otp_secret = "secret"
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
sut.insert_user(user)
|
||||||
|
actual = sut.insert_user(user)
|
||||||
|
|
||||||
|
self.assertEqual(DataError.USER_NAME_NOT_VALID, actual)
|
||||||
|
|
||||||
|
def test_update_user_password(self):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = "admin2",
|
||||||
|
password = "pass",
|
||||||
|
otp_secret = "secret"
|
||||||
|
)
|
||||||
|
expected_old = None
|
||||||
|
expected_new = User(
|
||||||
|
id = 1,
|
||||||
|
name = "admin2",
|
||||||
|
otp_secret = "secret",
|
||||||
|
privileged = False,
|
||||||
|
was_otp_verified = False
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.app.app_context():
|
||||||
|
user_id = sut.insert_user(user)
|
||||||
|
sut.update_user_password(user_id = user_id, new_password = "alma")
|
||||||
|
actual_old = sut.get_user_by_name_and_password(user_name = "admin2", password = "pass")
|
||||||
|
actual_new = sut.get_user_by_name_and_password(user_name = "admin2", password = "alma")
|
||||||
|
|
||||||
|
self.assertEqual(expected_old, actual_old)
|
||||||
|
self.assertEqual(expected_new, actual_new)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
||||||
238
server/flask/application/test/test_add_file_metadata.py
Normal file
238
server/flask/application/test/test_add_file_metadata.py
Normal file
|
|
@ -0,0 +1,238 @@
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import unittest.mock
|
||||||
|
import json
|
||||||
|
from .context import create_app, default_test_config
|
||||||
|
from backend.data import db
|
||||||
|
from backend.data import dao_users
|
||||||
|
from backend.data import dao_session
|
||||||
|
from backend.data.data_models import Session
|
||||||
|
from backend.data.data_models import RegisteringUser
|
||||||
|
|
||||||
|
class AddFileMetadataUnitTest(unittest.TestCase):
|
||||||
|
|
||||||
|
url_path = '/file/metadata'
|
||||||
|
app = create_app(default_test_config)
|
||||||
|
client = app.test_client()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.init_db()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.close_db()
|
||||||
|
os.remove("testdb")
|
||||||
|
|
||||||
|
def insert_user(self, user: RegisteringUser):
|
||||||
|
with self.app.app_context():
|
||||||
|
user_id = dao_users.insert_user(user)
|
||||||
|
return user_id
|
||||||
|
|
||||||
|
def insert_session(self, session: Session):
|
||||||
|
with self.app.app_context():
|
||||||
|
dao_session.insert_user_session(session)
|
||||||
|
|
||||||
|
def test_no_headers_returns_unauthorized(self):
|
||||||
|
expected = {'message':'Missing Authorization!','code':440}
|
||||||
|
|
||||||
|
response = self.client.post(self.url_path)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_not_saved_access_token_headers_returns_unauthorized(self):
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
response = self.client.post(self.url_path, headers={'Authorization': 'token'})
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_expired_access_token_headers_returns_unauthorized(self):
|
||||||
|
session = Session(
|
||||||
|
user_id=2,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=950,
|
||||||
|
refresh_expires_at=1050,
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
response = self.client.post(self.url_path, headers={'Authorization': 'token'})
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_sending_non_saved_user_error_is_shown(self):
|
||||||
|
session = Session(
|
||||||
|
user_id=2,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_given_authenticated_user_sending_data_shows_success(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'File MetaData Saved!','code':203}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
data = json.dumps({'key': 'value'})
|
||||||
|
response = self.client.post(self.url_path, headers = header, data = data, content_type='application/json')
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_given_authenticated_user_sending_invalid_data_shows_error(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Couldn\'t save metadata!','code':415}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
data = ""
|
||||||
|
response = self.client.post(self.url_path, headers = header, data = data, content_type='application/json')
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_given_authenticated_user_sending_data_can_be_read(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected_of_key_query = {'key': 'value'}
|
||||||
|
expected_of_key2_query = {'key2':'value2'}
|
||||||
|
|
||||||
|
data = json.dumps({'key': 'value', 'key2': 'value2'})
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
self.client.post(self.url_path, headers = header, data = data, content_type='application/json')
|
||||||
|
get_query_key1 = {'file_key': 'key'}
|
||||||
|
response_of_key1 = self.client.get(self.url_path, query_string = get_query_key1, headers = header)
|
||||||
|
get_query_key2 = {'file_key': 'key2'}
|
||||||
|
response_of_key2 = self.client.get(self.url_path, query_string = get_query_key2, headers = header)
|
||||||
|
|
||||||
|
self.assertEqual(200, response_of_key1.status_code)
|
||||||
|
actual_response_json = json.loads(response_of_key1.data.decode())
|
||||||
|
self.assertEqual(expected_of_key_query, actual_response_json)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response_of_key2.data.decode())
|
||||||
|
self.assertEqual(200, response_of_key2.status_code)
|
||||||
|
self.assertEqual(expected_of_key2_query, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_given_authenticated_user_data_can_be_overwritten_and_extended(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'key': 'value1'}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
data = json.dumps({'key': 'value', 'key2': 'value2'})
|
||||||
|
self.client.post(self.url_path, headers = header, data = data, content_type='application/json')
|
||||||
|
data = json.dumps({'key': 'value1', 'key3': 'value3'})
|
||||||
|
self.client.post(self.url_path, headers = header, data = data, content_type='application/json')
|
||||||
|
get_query = {'file_key': 'key'}
|
||||||
|
response = self.client.get(self.url_path, query_string=get_query, headers = header)
|
||||||
|
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_given_authenticated_user_invalid_data_is_not_saved(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'key': 'value'}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
data = json.dumps({'key': 'value', 'key2': 'value2'})
|
||||||
|
self.client.post(self.url_path, headers = header, data = data, content_type='application/json')
|
||||||
|
invalid_data = json.dumps([{'key': 'value1', 'key3': 'value3'}])
|
||||||
|
self.client.post(self.url_path, headers = header, data = invalid_data, content_type='application/json')
|
||||||
|
get_query={'file_key':'key'}
|
||||||
|
response = self.client.get(self.url_path, query_string = get_query, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
||||||
228
server/flask/application/test/test_add_file_metadata_of_user.py
Normal file
228
server/flask/application/test/test_add_file_metadata_of_user.py
Normal file
|
|
@ -0,0 +1,228 @@
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import unittest.mock
|
||||||
|
import json
|
||||||
|
from .context import create_app, default_test_config
|
||||||
|
from backend.data import db
|
||||||
|
from backend.data import dao_users
|
||||||
|
from backend.data import dao_session
|
||||||
|
from backend.data.data_models import Session
|
||||||
|
from backend.data.data_models import RegisteringUser
|
||||||
|
|
||||||
|
class AddFileMetadataOfUserUnitTest(unittest.TestCase):
|
||||||
|
|
||||||
|
url_path = '/user/file/metadata'
|
||||||
|
app = create_app(default_test_config)
|
||||||
|
client = app.test_client()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.init_db()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.close_db()
|
||||||
|
os.remove("testdb")
|
||||||
|
|
||||||
|
def insert_user(self, user: RegisteringUser):
|
||||||
|
with self.app.app_context():
|
||||||
|
user_id = dao_users.insert_user(user)
|
||||||
|
return user_id
|
||||||
|
|
||||||
|
def insert_session(self, session: Session):
|
||||||
|
with self.app.app_context():
|
||||||
|
dao_session.insert_user_session(session)
|
||||||
|
|
||||||
|
def test_no_headers_returns_unauthorized(self):
|
||||||
|
expected = {'message':'Missing Authorization!','code':440}
|
||||||
|
|
||||||
|
response = self.client.post(self.url_path)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_not_saved_access_token_headers_returns_unauthorized(self):
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
response = self.client.post(self.url_path, headers={'Authorization': 'token'})
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_expired_access_token_headers_returns_unauthorized(self):
|
||||||
|
session = Session(
|
||||||
|
user_id=2,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=950,
|
||||||
|
refresh_expires_at=1050,
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
response = self.client.post(self.url_path, headers={'Authorization': 'token'})
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_sending_non_saved_user_error_is_shown(self):
|
||||||
|
session = Session(
|
||||||
|
user_id=2,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_given_authenticated_user_sending_data_shows_success(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'User\'s File MetaData Saved!','code':202}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
data = json.dumps({'key': 'value'})
|
||||||
|
response = self.client.post(self.url_path, headers = header, data = data, content_type='application/json')
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_given_authenticated_user_sending_invalid_data_shows_error(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Couldn\'t save user\'s metadata!','code':414}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
data = ""
|
||||||
|
response = self.client.post(self.url_path, headers = header, data = data, content_type='application/json')
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_given_authenticated_user_sending_data_can_be_read(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'key': 'value', 'key2':'value2'}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
data = json.dumps({'key': 'value', 'key2': 'value2'})
|
||||||
|
self.client.post(self.url_path, headers = header, data = data, content_type='application/json')
|
||||||
|
response = self.client.get(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_given_authenticated_user_data_can_be_overwritten_and_extended(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'key': 'value1', 'key2':'value2', 'key3': 'value3'}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
data = json.dumps({'key': 'value', 'key2': 'value2'})
|
||||||
|
self.client.post(self.url_path, headers = header, data = data, content_type='application/json')
|
||||||
|
data = json.dumps({'key': 'value1', 'key3': 'value3'})
|
||||||
|
self.client.post(self.url_path, headers = header, data = data, content_type='application/json')
|
||||||
|
response = self.client.get(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_given_authenticated_user_invalid_data_is_not_saved(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'key': 'value', 'key2':'value2'}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
data = json.dumps({'key': 'value', 'key2': 'value2'})
|
||||||
|
self.client.post(self.url_path, headers = header, data = data, content_type='application/json')
|
||||||
|
invalid_data = json.dumps([{'key': 'value1', 'key3': 'value3'}])
|
||||||
|
self.client.post(self.url_path, headers = header, data = invalid_data, content_type='application/json')
|
||||||
|
response = self.client.get(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
||||||
315
server/flask/application/test/test_change_password.py
Normal file
315
server/flask/application/test/test_change_password.py
Normal file
|
|
@ -0,0 +1,315 @@
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import unittest.mock
|
||||||
|
import json
|
||||||
|
from .context import create_app, default_test_config
|
||||||
|
from backend.data import db
|
||||||
|
from backend.data import dao_users
|
||||||
|
from backend.data import dao_session
|
||||||
|
from backend.data.data_models import Session
|
||||||
|
from backend.data.data_models import RegisteringUser
|
||||||
|
|
||||||
|
class PasswordChangeUnitTest(unittest.TestCase):
|
||||||
|
|
||||||
|
url_path = '/change/password'
|
||||||
|
app = create_app(default_test_config)
|
||||||
|
client = app.test_client()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.init_db()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.close_db()
|
||||||
|
os.remove("testdb")
|
||||||
|
|
||||||
|
def insert_user(self, user: RegisteringUser):
|
||||||
|
with self.app.app_context():
|
||||||
|
user_id = dao_users.insert_user(user)
|
||||||
|
return user_id
|
||||||
|
|
||||||
|
def insert_session(self, session: Session):
|
||||||
|
with self.app.app_context():
|
||||||
|
dao_session.insert_user_session(session)
|
||||||
|
|
||||||
|
def assertHasSession(self, access_token, user_id):
|
||||||
|
with self.app.app_context():
|
||||||
|
actual = dao_session.get_user_for_token(access_token)
|
||||||
|
self.assertEqual(user_id, actual)
|
||||||
|
|
||||||
|
def test_no_headers_returns_unauthorized(self):
|
||||||
|
expected = {'message':'Missing Authorization!','code':440}
|
||||||
|
|
||||||
|
response = self.client.post(self.url_path)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_not_saved_access_token_headers_returns_unauthorized(self):
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
response = self.client.post(self.url_path, headers={'Authorization': 'token'})
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_expired_access_token_headers_returns_unauthorized(self, mock_time):
|
||||||
|
session = Session(
|
||||||
|
user_id=2,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=950,
|
||||||
|
refresh_expires_at=1050,
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
response = self.client.post(self.url_path, headers={'Authorization': 'token'})
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_non_saved_user_error_is_shown(self, mock_time):
|
||||||
|
session = Session(
|
||||||
|
user_id=2,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Authorization!','code':442}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_no_otp_token_then_error_is_shown(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Token!','code':431}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_invalid_otp_token_then_error_is_shown(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Token!','code':431}
|
||||||
|
|
||||||
|
data = {'otp': 0}
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, data=data, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_auth_correct_but_no_password_shows_error(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
correct_code = 585501 #for 1000 and base32secret3232
|
||||||
|
expected = {'message':'Invalid Password!','code':421}
|
||||||
|
|
||||||
|
data = {'otp': correct_code}
|
||||||
|
response = self.client.post(self.url_path, data = data, headers = {'Authorization': 'token'})
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_auth_correct_but_no_new_password_shows_error(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
correct_code = 585501 #for 1000 and base32secret3232
|
||||||
|
expected = {'message':'New Password cannot be empty!','code':422}
|
||||||
|
|
||||||
|
data = {'otp': correct_code, 'password':'pass'}
|
||||||
|
response = self.client.post(self.url_path, data=data, headers={'Authorization': 'token'})
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_auth_correct_but_invalid_current_password_shows_error(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
correct_code = 585501 #for 1000 and base32secret3232
|
||||||
|
expected = {'message':'Invalid Password!','code':421}
|
||||||
|
|
||||||
|
data = {'otp': correct_code, 'password':'pass', 'new_password': 'new_pass'}
|
||||||
|
response = self.client.post(self.url_path, data=data, headers={'Authorization': 'token'})
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_valid_password_change_results_in_new_session(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'alma',
|
||||||
|
password = 'pass',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected_keys = {'access_token', 'refresh_token', 'expires_at'}
|
||||||
|
|
||||||
|
correct_code = 585501 #for 1000 and base32secret3232
|
||||||
|
data = {'password':'pass', 'new_password': 'pass2', 'otp': correct_code}
|
||||||
|
response = self.client.post(self.url_path, data=data, headers={'Authorization': 'token'})
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected_keys, set(actual_response_json.keys()))
|
||||||
|
self.assertEqual(actual_response_json['expires_at'], 1000+20000)
|
||||||
|
self.assertHasSession(access_token = actual_response_json['access_token'], user_id = user_id)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_after_password_change_old_session_is_invalid(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'alma',
|
||||||
|
password = 'pass',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
correct_code = 585501 #for 1000 and base32secret3232
|
||||||
|
change_pass_data = {'password':'pass', 'new_password': 'pass2', 'otp': correct_code}
|
||||||
|
response = self.client.post(self.url_path, data=change_pass_data, headers={'Authorization': 'token'})
|
||||||
|
json.loads(response.data.decode())
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
data = {'password':'pass2', 'new_password': 'pass3', 'otp': correct_code}
|
||||||
|
response = self.client.post(self.url_path, data=data, headers={'Authorization': 'token'})
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_after_password_change_new_session_is_valid(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'alma',
|
||||||
|
password = 'pass',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
correct_code = 585501 #for 1000 and base32secret3232
|
||||||
|
change_pass_data = {'password':'pass', 'new_password': 'pass2', 'otp': correct_code}
|
||||||
|
response = self.client.post(self.url_path, data=change_pass_data, headers={'Authorization': 'token'})
|
||||||
|
session_response = json.loads(response.data.decode())
|
||||||
|
expected_keys = {'access_token', 'refresh_token', 'expires_at'}
|
||||||
|
|
||||||
|
data = {'password':'pass2', 'new_password': 'pass3', 'otp': correct_code}
|
||||||
|
response = self.client.post(self.url_path, data=data, headers={'Authorization': session_response['access_token']})
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected_keys, set(actual_response_json.keys()))
|
||||||
|
self.assertEqual(actual_response_json['expires_at'], 1000+20000)
|
||||||
|
self.assertHasSession(access_token = actual_response_json['access_token'], user_id = user_id)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
||||||
295
server/flask/application/test/test_create_registration_token.py
Normal file
295
server/flask/application/test/test_create_registration_token.py
Normal file
|
|
@ -0,0 +1,295 @@
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import unittest.mock
|
||||||
|
import json
|
||||||
|
from .context import create_app, default_test_config
|
||||||
|
from backend.data import db
|
||||||
|
from backend.data import dao_users
|
||||||
|
from backend.data import dao_registration_tokens
|
||||||
|
from backend.data import dao_session
|
||||||
|
from backend.data.data_models import RegisteringUser
|
||||||
|
from backend.data.data_models import Session
|
||||||
|
|
||||||
|
class CreateRegistrationTokenUnitTest(unittest.TestCase):
|
||||||
|
|
||||||
|
url_path = '/admin/registration_token'
|
||||||
|
app = create_app(default_test_config)
|
||||||
|
client = app.test_client()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.init_db()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.close_db()
|
||||||
|
os.remove("testdb")
|
||||||
|
|
||||||
|
def insert_user(self, user: RegisteringUser):
|
||||||
|
with self.app.app_context():
|
||||||
|
user_id = dao_users.insert_user(user)
|
||||||
|
return user_id
|
||||||
|
|
||||||
|
def insert_session(self, session: Session):
|
||||||
|
with self.app.app_context():
|
||||||
|
dao_session.insert_user_session(session)
|
||||||
|
|
||||||
|
def assertRegistrationTokenIs(self, token: str, is_valid: bool):
|
||||||
|
with self.app.app_context():
|
||||||
|
actual = dao_registration_tokens.is_valid_token(token)
|
||||||
|
self.assertEqual(is_valid, actual)
|
||||||
|
|
||||||
|
def test_no_headers_returns_unauthorized(self):
|
||||||
|
expected = {'message':'Missing Authorization!','code':440}
|
||||||
|
|
||||||
|
response = self.client.post(self.url_path)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_not_saved_access_token_headers_returns_unauthorized(self):
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, headers=header)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_expired_access_token_headers_returns_unauthorized(self, mock_time):
|
||||||
|
session = Session(
|
||||||
|
user_id=2,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=950,
|
||||||
|
refresh_expires_at=1050,
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, headers=header)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_non_saved_user_error_is_shown(self, mock_time):
|
||||||
|
session = Session(
|
||||||
|
user_id=2,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Authorization!','code':442}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_no_otp_token_then_error_is_shown(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Token!','code':431}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_invalid_otp_token_then_error_is_shown(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Token!','code':431}
|
||||||
|
|
||||||
|
data = {'otp': 0}
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, data=data, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_correct_data_but_not_priviliged_user_shows_error(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
correct_code = 585501 #for 1000 and base32secret3232
|
||||||
|
expected = {'message':'Not Authorized!','code':460}
|
||||||
|
|
||||||
|
data = {'otp': correct_code}
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, data=data, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_no_registration_token_returns_error(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232',
|
||||||
|
privileged = True
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
correct_code = 585501 #for 1000 and base32secret3232
|
||||||
|
expected = {'message':'Invalid Registration Token given!','code':460}
|
||||||
|
|
||||||
|
data = {'otp': correct_code}
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, data = data, headers = header)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_blank_registration_token_returns_error(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232',
|
||||||
|
privileged = True
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
correct_code = 585501 #for 1000 and base32secret3232
|
||||||
|
expected = {'message':'Invalid Registration Token given!','code':460}
|
||||||
|
|
||||||
|
data = {'registration_token': ' ', 'otp': correct_code}
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, data = data, headers = header)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
self.assertRegistrationTokenIs(token = ' ', is_valid = False)
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_priviliged_user_sending_correct_data_token_is_saved(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232',
|
||||||
|
privileged = True
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
correct_code = 585501 #for 1000 and base32secret3232
|
||||||
|
expected = {'message':'Registration token Saved!','code':205}
|
||||||
|
|
||||||
|
data = {'registration_token': '123456', 'otp': correct_code}
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, data=data, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
self.assertRegistrationTokenIs(token = '123456', is_valid = True)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_priviliged_user_authenticated_sending_same_token_shows_ok(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232',
|
||||||
|
privileged = True
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
correct_code = 585501 #for 1000 and base32secret3232
|
||||||
|
data = {'registration_token': '123456', 'otp': correct_code}
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
self.client.post(self.url_path, data=data, headers = header)
|
||||||
|
expected = {'message':'Invalid Registration Token given!','code':460}
|
||||||
|
|
||||||
|
response = self.client.post(self.url_path, data=data, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
self.assertRegistrationTokenIs(token = '123456', is_valid = True)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
||||||
|
|
@ -0,0 +1,327 @@
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import unittest.mock
|
||||||
|
import json
|
||||||
|
from .context import create_app, default_test_config
|
||||||
|
from backend.data import db
|
||||||
|
from backend.data import dao_users
|
||||||
|
from backend.data import dao_registration_tokens
|
||||||
|
from backend.data import dao_session
|
||||||
|
from backend.data.data_models import RegisteringUser
|
||||||
|
from backend.data.data_models import Session
|
||||||
|
|
||||||
|
class CreateResetPasswordTokenTest(unittest.TestCase):
|
||||||
|
|
||||||
|
url_path = '/admin/reset_password_token'
|
||||||
|
app = create_app(default_test_config)
|
||||||
|
client = app.test_client()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.init_db()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.close_db()
|
||||||
|
os.remove("testdb")
|
||||||
|
|
||||||
|
def insert_user(self, user: RegisteringUser):
|
||||||
|
with self.app.app_context():
|
||||||
|
user_id = dao_users.insert_user(user)
|
||||||
|
return user_id
|
||||||
|
|
||||||
|
def insert_session(self, session: Session):
|
||||||
|
with self.app.app_context():
|
||||||
|
dao_session.insert_user_session(session)
|
||||||
|
|
||||||
|
def assertResetPasswordTokenIs(self, token: str, is_valid: bool):
|
||||||
|
with self.app.app_context():
|
||||||
|
actual = dao_registration_tokens.is_valid_token(token)
|
||||||
|
self.assertEqual(is_valid, actual)
|
||||||
|
|
||||||
|
def test_no_headers_returns_unauthorized(self):
|
||||||
|
expected = {'message':'Missing Authorization!','code':440}
|
||||||
|
|
||||||
|
response = self.client.post(self.url_path)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_not_saved_access_token_headers_returns_unauthorized(self):
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, headers=header)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_expired_access_token_headers_returns_unauthorized(self, mock_time):
|
||||||
|
session = Session(
|
||||||
|
user_id=2,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=950,
|
||||||
|
refresh_expires_at=1050,
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, headers=header)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_non_saved_user_error_is_shown(self, mock_time):
|
||||||
|
session = Session(
|
||||||
|
user_id=2,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Authorization!','code':442}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_no_otp_token_then_error_is_shown(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Token!','code':431}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_invalid_otp_token_then_error_is_shown(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Token!','code':431}
|
||||||
|
|
||||||
|
data = {'otp': 0}
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, data=data, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_correct_data_but_not_priviliged_user_shows_error(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
correct_code = 585501 #for 1000 and base32secret3232
|
||||||
|
expected = {'message':'Not Authorized!','code':460}
|
||||||
|
|
||||||
|
data = {'otp': correct_code}
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, data=data, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_no_reset_token_returns_error(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232',
|
||||||
|
privileged = True
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
correct_code = 585501 #for 1000 and base32secret3232
|
||||||
|
expected = {'message':'Invalid Reset Password Token given!','code':459}
|
||||||
|
|
||||||
|
data = {'otp': correct_code}
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, data = data, headers = header)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_blank_reset_token_returns_error(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232',
|
||||||
|
privileged = True
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
correct_code = 585501 #for 1000 and base32secret3232
|
||||||
|
expected = {'message':'Invalid Reset Password Token given!','code':459}
|
||||||
|
|
||||||
|
data = {'reset_password_token':' ','otp': correct_code}
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, data = data, headers = header)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_usernametoreset_not_send_shows_error(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232',
|
||||||
|
privileged = True
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
correct_code = 585501 #for 1000 and base32secret3232
|
||||||
|
expected = {'message':'username_to_reset cannot be empty!','code':413}
|
||||||
|
|
||||||
|
data = {'reset_password_token':'a','otp': correct_code}
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, data = data, headers = header)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_as_authenticated_privilidged_proper_data_then_reset_password_token_is_saved(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232',
|
||||||
|
privileged = True
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
correct_code = 585501 #for 1000 and base32secret3232
|
||||||
|
expected = {'message':'Reset Password token Saved!','code':208}
|
||||||
|
|
||||||
|
data = {'reset_password_token':'a', 'username_to_reset': 'c','otp': correct_code}
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, data = data, headers = header)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_resetted_password_token_can_be_user_to_change_password(self, mock_time):
|
||||||
|
admin = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232',
|
||||||
|
privileged = True
|
||||||
|
)
|
||||||
|
admin_id = self.insert_user(admin)
|
||||||
|
session = Session(
|
||||||
|
user_id=admin_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'alma',
|
||||||
|
password = '',
|
||||||
|
otp_secret = ''
|
||||||
|
)
|
||||||
|
self.insert_user(user)
|
||||||
|
correct_code = 585501 #for 1000 and base32secret3232
|
||||||
|
reset_token = 'a'
|
||||||
|
data = {'reset_password_token':reset_token, 'username_to_reset': user.name,'otp': correct_code}
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
self.client.post(self.url_path, data = data, headers = header)
|
||||||
|
expected = {'message':'Password was Saved!','code':201}
|
||||||
|
|
||||||
|
reset_passwod_data = {'reset_password_token':reset_token, 'username': user.name,'password': 'a'}
|
||||||
|
response = self.client.post('/reset/password', data = reset_passwod_data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=1)
|
||||||
214
server/flask/application/test/test_delete_registration_token.py
Normal file
214
server/flask/application/test/test_delete_registration_token.py
Normal file
|
|
@ -0,0 +1,214 @@
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import unittest.mock
|
||||||
|
import json
|
||||||
|
from .context import create_app, default_test_config
|
||||||
|
from backend.data import db
|
||||||
|
from backend.data import dao_users
|
||||||
|
from backend.data import dao_registration_tokens
|
||||||
|
from backend.data import dao_session
|
||||||
|
from backend.data.data_models import RegisteringUser
|
||||||
|
from backend.data.data_models import Session
|
||||||
|
|
||||||
|
class DeleteRegistrationTokenUnitTest(unittest.TestCase):
|
||||||
|
|
||||||
|
url_path = '/admin/delete/registration_token'
|
||||||
|
app = create_app(default_test_config)
|
||||||
|
client = app.test_client()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.init_db()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.close_db()
|
||||||
|
os.remove("testdb")
|
||||||
|
|
||||||
|
def insert_user(self, user: RegisteringUser):
|
||||||
|
with self.app.app_context():
|
||||||
|
user_id = dao_users.insert_user(user)
|
||||||
|
return user_id
|
||||||
|
|
||||||
|
def insert_session(self, session: Session):
|
||||||
|
with self.app.app_context():
|
||||||
|
dao_session.insert_user_session(session)
|
||||||
|
|
||||||
|
def insert_registration_token(self, token: str):
|
||||||
|
with self.app.app_context():
|
||||||
|
dao_registration_tokens.insert_token(token)
|
||||||
|
|
||||||
|
def assertRegistrationTokenIs(self, token: str, is_valid: bool):
|
||||||
|
with self.app.app_context():
|
||||||
|
actual = dao_registration_tokens.is_valid_token(token)
|
||||||
|
self.assertEqual(is_valid, actual)
|
||||||
|
|
||||||
|
def test_no_headers_returns_unauthorized(self):
|
||||||
|
expected = {'message':'Missing Authorization!','code':440}
|
||||||
|
|
||||||
|
response = self.client.post(self.url_path)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_not_saved_access_token_headers_returns_unauthorized(self):
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, headers=header)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_expired_access_token_headers_returns_unauthorized(self, mock_time):
|
||||||
|
session = Session(
|
||||||
|
user_id=2,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=950,
|
||||||
|
refresh_expires_at=1050,
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, headers=header)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_non_saved_user_error_is_shown(self, mock_time):
|
||||||
|
session = Session(
|
||||||
|
user_id=2,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Authorization!','code':442}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_no_otp_token_then_error_is_shown(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Token!','code':431}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_invalid_otp_token_then_error_is_shown(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Token!','code':431}
|
||||||
|
|
||||||
|
data = {'otp': 0}
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, data=data, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_correct_data_but_not_priviliged_user_shows_error(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
correct_code = 585501 #for 1000 and base32secret3232
|
||||||
|
expected = {'message':'Not Authorized!','code':460}
|
||||||
|
|
||||||
|
data = {'otp': correct_code}
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, data=data, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_priviliged_user_sending_correct_data_token_is_deleted(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232',
|
||||||
|
privileged = True
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
self.insert_registration_token('123456')
|
||||||
|
self.insert_registration_token('abcdef')
|
||||||
|
correct_code = 585501 #for 1000 and base32secret3232
|
||||||
|
expected = {'message':'Token deleted!','code':209}
|
||||||
|
|
||||||
|
data = {'registration_token': '123456', 'otp': correct_code}
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, data=data, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
self.assertRegistrationTokenIs(token = '123456', is_valid = False)
|
||||||
|
self.assertRegistrationTokenIs(token = 'abcdef', is_valid = True)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
||||||
234
server/flask/application/test/test_delete_user.py
Normal file
234
server/flask/application/test/test_delete_user.py
Normal file
|
|
@ -0,0 +1,234 @@
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import unittest.mock
|
||||||
|
import json
|
||||||
|
from .context import create_app, default_test_config
|
||||||
|
from backend.data import db
|
||||||
|
from backend.data import dao_users
|
||||||
|
from backend.data import dao_registration_tokens
|
||||||
|
from backend.data import dao_session
|
||||||
|
from backend.data.data_models import RegisteringUser
|
||||||
|
from backend.data.data_models import Session
|
||||||
|
|
||||||
|
class DeleteTokenUnitTest(unittest.TestCase):
|
||||||
|
|
||||||
|
url_path = '/admin/delete/user'
|
||||||
|
app = create_app(default_test_config)
|
||||||
|
client = app.test_client()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.init_db()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.close_db()
|
||||||
|
os.remove("testdb")
|
||||||
|
|
||||||
|
def insert_user(self, user: RegisteringUser):
|
||||||
|
with self.app.app_context():
|
||||||
|
user_id = dao_users.insert_user(user)
|
||||||
|
return user_id
|
||||||
|
|
||||||
|
def insert_session(self, session: Session):
|
||||||
|
with self.app.app_context():
|
||||||
|
dao_session.insert_user_session(session)
|
||||||
|
|
||||||
|
def assert_user(self, user_id: str, exists: bool):
|
||||||
|
with self.app.app_context():
|
||||||
|
actual = dao_users.get_user_by_id(user_id)
|
||||||
|
self.assertEqual(exists, actual is not None)
|
||||||
|
|
||||||
|
def assert_session(self, access_token: str, exists: bool):
|
||||||
|
with self.app.app_context():
|
||||||
|
actual = dao_session.get_user_for_token(access_token)
|
||||||
|
self.assertEqual(exists, actual is not None)
|
||||||
|
|
||||||
|
def test_no_headers_returns_unauthorized(self):
|
||||||
|
expected = {'message':'Missing Authorization!','code':440}
|
||||||
|
|
||||||
|
response = self.client.post(self.url_path)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_not_saved_access_token_headers_returns_unauthorized(self):
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, headers=header)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_expired_access_token_headers_returns_unauthorized(self, mock_time):
|
||||||
|
session = Session(
|
||||||
|
user_id=2,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=950,
|
||||||
|
refresh_expires_at=1050,
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, headers=header)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_non_saved_user_error_is_shown(self, mock_time):
|
||||||
|
session = Session(
|
||||||
|
user_id=2,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Authorization!','code':442}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_no_otp_token_then_error_is_shown(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Token!','code':431}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_invalid_otp_token_then_error_is_shown(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Token!','code':431}
|
||||||
|
|
||||||
|
data = {'otp': 0}
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, data=data, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_correct_data_but_not_priviliged_user_shows_error(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
correct_code = 585501 #for 1000 and base32secret3232
|
||||||
|
expected = {'message':'Not Authorized!','code':460}
|
||||||
|
|
||||||
|
data = {'username_to_delete': 'guest-2', 'otp': correct_code}
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, data=data, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_priviliged_user_sending_correct_data_user_is_deleted(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232',
|
||||||
|
privileged = True
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
guest1_user_id=self.insert_user(RegisteringUser(name = 'guest-1',password = '123',otp_secret = '',))
|
||||||
|
guest2_user_id=self.insert_user(RegisteringUser(name = 'guest-2',password = '123',otp_secret = '',))
|
||||||
|
guest1_session = Session(
|
||||||
|
user_id=guest1_user_id,
|
||||||
|
access_token='a-1',
|
||||||
|
refresh_token='r-1',
|
||||||
|
access_expires_at=2000,
|
||||||
|
refresh_expires_at=3000
|
||||||
|
)
|
||||||
|
guest2_session = Session(
|
||||||
|
user_id=guest2_user_id,
|
||||||
|
access_token='a-2',
|
||||||
|
refresh_token='r-2',
|
||||||
|
access_expires_at=2000,
|
||||||
|
refresh_expires_at=3000
|
||||||
|
)
|
||||||
|
self.insert_session(guest1_session)
|
||||||
|
self.insert_session(guest2_session)
|
||||||
|
correct_code = 585501 #for 1000 and base32secret3232
|
||||||
|
expected = {'message':'User deleted!','code':206}
|
||||||
|
|
||||||
|
data = {'username_to_delete': 'guest-2', 'otp': correct_code}
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, data=data, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
self.assert_user(user_id = guest1_user_id, exists = True)
|
||||||
|
self.assert_user(user_id = guest2_user_id, exists = False)
|
||||||
|
self.assert_session(access_token = 'a-1', exists = True)
|
||||||
|
self.assert_session(access_token = 'a-2', exists = False)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
||||||
187
server/flask/application/test/test_get_file_metadata.py
Normal file
187
server/flask/application/test/test_get_file_metadata.py
Normal file
|
|
@ -0,0 +1,187 @@
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import unittest.mock
|
||||||
|
import json
|
||||||
|
from .context import create_app, default_test_config
|
||||||
|
from backend.data import db
|
||||||
|
from backend.data import dao_users
|
||||||
|
from backend.data import dao_session
|
||||||
|
from backend.data.data_models import Session
|
||||||
|
from backend.data.data_models import RegisteringUser
|
||||||
|
|
||||||
|
class GetFileMetadataOfUserUnitTest(unittest.TestCase):
|
||||||
|
|
||||||
|
url_path = '/file/metadata'
|
||||||
|
app = create_app(default_test_config)
|
||||||
|
client = app.test_client()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.init_db()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.close_db()
|
||||||
|
os.remove("testdb")
|
||||||
|
|
||||||
|
def insert_user(self, user: RegisteringUser):
|
||||||
|
with self.app.app_context():
|
||||||
|
user_id = dao_users.insert_user(user)
|
||||||
|
return user_id
|
||||||
|
|
||||||
|
def insert_session(self, session: Session):
|
||||||
|
with self.app.app_context():
|
||||||
|
dao_session.insert_user_session(session)
|
||||||
|
|
||||||
|
def test_no_headers_returns_unauthorized(self):
|
||||||
|
expected = {'message':'Missing Authorization!','code':440}
|
||||||
|
|
||||||
|
response = self.client.get(self.url_path)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_not_saved_access_token_headers_returns_unauthorized(self):
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
response = self.client.get(self.url_path, headers={'Authorization': 'token'})
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_expired_access_token_headers_returns_unauthorized(self):
|
||||||
|
session = Session(
|
||||||
|
user_id=2,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=950,
|
||||||
|
refresh_expires_at=1050,
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
response = self.client.get(self.url_path, headers={'Authorization': 'token'})
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_sending_non_saved_user_error_is_shown(self):
|
||||||
|
session = Session(
|
||||||
|
user_id=2,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.get(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_given_authenticated_user_without_parameter_error_is_shown(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message': 'Invalid FileKey (file_key)!', 'code': 416}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.get(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_given_authenticated_user_with_no_data_returns_empty(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
get_query = {'file_key': 'a'}
|
||||||
|
response = self.client.get(self.url_path, headers = header, query_string = get_query)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_given_one_users_saved_data_other_can_access_it(self, mock_time):
|
||||||
|
user_with_metadata = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_with_metadata_id = self.insert_user(user_with_metadata)
|
||||||
|
usersession_with_metadata = Session(
|
||||||
|
user_id=user_with_metadata_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(usersession_with_metadata)
|
||||||
|
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'other',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token2',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'key': 'value'}
|
||||||
|
|
||||||
|
post_header = {'Authorization': 'token'}
|
||||||
|
data = json.dumps({'key': 'value'})
|
||||||
|
self.client.post(self.url_path, headers = post_header, data = data, content_type='application/json')
|
||||||
|
|
||||||
|
get_header = {'Authorization': 'token2'}
|
||||||
|
get_query = {'file_key': 'key'}
|
||||||
|
response = self.client.get(self.url_path, headers = get_header, query_string=get_query, content_type='application/json')
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
||||||
159
server/flask/application/test/test_get_file_metadata_of_user.py
Normal file
159
server/flask/application/test/test_get_file_metadata_of_user.py
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import unittest.mock
|
||||||
|
import json
|
||||||
|
from .context import create_app, default_test_config
|
||||||
|
from backend.data import db
|
||||||
|
from backend.data import dao_users
|
||||||
|
from backend.data import dao_session
|
||||||
|
from backend.data.data_models import Session
|
||||||
|
from backend.data.data_models import RegisteringUser
|
||||||
|
|
||||||
|
class GetFileMetadataOfUserUnitTest(unittest.TestCase):
|
||||||
|
|
||||||
|
url_path = '/user/file/metadata'
|
||||||
|
app = create_app(default_test_config)
|
||||||
|
client = app.test_client()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.init_db()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.close_db()
|
||||||
|
os.remove("testdb")
|
||||||
|
|
||||||
|
def insert_user(self, user: RegisteringUser):
|
||||||
|
with self.app.app_context():
|
||||||
|
user_id = dao_users.insert_user(user)
|
||||||
|
return user_id
|
||||||
|
|
||||||
|
def insert_session(self, session: Session):
|
||||||
|
with self.app.app_context():
|
||||||
|
dao_session.insert_user_session(session)
|
||||||
|
|
||||||
|
def test_no_headers_returns_unauthorized(self):
|
||||||
|
expected = {'message':'Missing Authorization!','code':440}
|
||||||
|
|
||||||
|
response = self.client.get(self.url_path)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_not_saved_access_token_headers_returns_unauthorized(self):
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
response = self.client.get(self.url_path, headers={'Authorization': 'token'})
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_expired_access_token_headers_returns_unauthorized(self):
|
||||||
|
session = Session(
|
||||||
|
user_id=2,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=950,
|
||||||
|
refresh_expires_at=1050,
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
response = self.client.get(self.url_path, headers={'Authorization': 'token'})
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_sending_non_saved_user_error_is_shown(self):
|
||||||
|
session = Session(
|
||||||
|
user_id=2,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.get(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_given_authenticated_user_with_no_data_returns_empty(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.get(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_given_one_users_saved_data_other_cant_access_it(self, mock_time):
|
||||||
|
user_with_metadata = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_with_metadata_id = self.insert_user(user_with_metadata)
|
||||||
|
usersession_with_metadata = Session(
|
||||||
|
user_id=user_with_metadata_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(usersession_with_metadata)
|
||||||
|
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'other',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token2',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {}
|
||||||
|
|
||||||
|
post_header = {'Authorization': 'token'}
|
||||||
|
data = json.dumps({'key': 'value'})
|
||||||
|
self.client.post(self.url_path, headers = post_header, data = data, content_type='application/json')
|
||||||
|
|
||||||
|
get_header = {'Authorization': 'token2'}
|
||||||
|
response = self.client.get(self.url_path, headers = get_header, data = data, content_type='application/json')
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
||||||
154
server/flask/application/test/test_get_registration_tokens.py
Normal file
154
server/flask/application/test/test_get_registration_tokens.py
Normal file
|
|
@ -0,0 +1,154 @@
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import unittest.mock
|
||||||
|
import json
|
||||||
|
from .context import create_app, default_test_config
|
||||||
|
from backend.data import db
|
||||||
|
from backend.data import dao_users
|
||||||
|
from backend.data import dao_registration_tokens
|
||||||
|
from backend.data import dao_session
|
||||||
|
from backend.data.data_models import RegisteringUser
|
||||||
|
from backend.data.data_models import Session
|
||||||
|
|
||||||
|
class GetRegistrationTokensUnitTest(unittest.TestCase):
|
||||||
|
|
||||||
|
url_path = '/admin/get_registration_tokens'
|
||||||
|
app = create_app(default_test_config)
|
||||||
|
client = app.test_client()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.init_db()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.close_db()
|
||||||
|
os.remove("testdb")
|
||||||
|
|
||||||
|
def insert_user(self, user: RegisteringUser):
|
||||||
|
with self.app.app_context():
|
||||||
|
user_id = dao_users.insert_user(user)
|
||||||
|
return user_id
|
||||||
|
|
||||||
|
def insert_session(self, session: Session):
|
||||||
|
with self.app.app_context():
|
||||||
|
dao_session.insert_user_session(session)
|
||||||
|
|
||||||
|
def inser_registration_token(self, token: str):
|
||||||
|
with self.app.app_context():
|
||||||
|
dao_registration_tokens.insert_token(token)
|
||||||
|
|
||||||
|
def test_no_headers_returns_unauthorized(self):
|
||||||
|
expected = {'message':'Missing Authorization!','code':440}
|
||||||
|
|
||||||
|
response = self.client.get(self.url_path)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_not_saved_access_token_headers_returns_unauthorized(self):
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.get(self.url_path, headers=header)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_expired_access_token_headers_returns_unauthorized(self, mock_time):
|
||||||
|
session = Session(
|
||||||
|
user_id=2,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=950,
|
||||||
|
refresh_expires_at=1050,
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.get(self.url_path, headers=header)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_non_saved_user_error_is_shown(self, mock_time):
|
||||||
|
session = Session(
|
||||||
|
user_id=2,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Authorization!','code':442}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.get(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_correct_data_but_not_priviliged_user_shows_error(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Not Authorized!','code':460}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.get(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_priviliged_user_with_correct_session_returns_tokens(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232',
|
||||||
|
privileged = True
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
self.inser_registration_token('token-abc-1')
|
||||||
|
self.inser_registration_token('token-abc-2')
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {
|
||||||
|
'registration_tokens':['token-abc-1','token-abc-2']
|
||||||
|
}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.get(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
||||||
153
server/flask/application/test/test_get_users.py
Normal file
153
server/flask/application/test/test_get_users.py
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import unittest.mock
|
||||||
|
import json
|
||||||
|
from .context import create_app, default_test_config
|
||||||
|
from backend.data import db
|
||||||
|
from backend.data import dao_users
|
||||||
|
from backend.data import dao_session
|
||||||
|
from backend.data.data_models import RegisteringUser
|
||||||
|
from backend.data.data_models import Session
|
||||||
|
|
||||||
|
class GetUsersUnitTest(unittest.TestCase):
|
||||||
|
|
||||||
|
url_path = '/admin/get_users'
|
||||||
|
app = create_app(default_test_config)
|
||||||
|
client = app.test_client()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.init_db()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.close_db()
|
||||||
|
os.remove("testdb")
|
||||||
|
|
||||||
|
def insert_user(self, user: RegisteringUser):
|
||||||
|
with self.app.app_context():
|
||||||
|
user_id = dao_users.insert_user(user)
|
||||||
|
return user_id
|
||||||
|
|
||||||
|
def insert_session(self, session: Session):
|
||||||
|
with self.app.app_context():
|
||||||
|
dao_session.insert_user_session(session)
|
||||||
|
|
||||||
|
def test_no_headers_returns_unauthorized(self):
|
||||||
|
expected = {'message':'Missing Authorization!','code':440}
|
||||||
|
|
||||||
|
response = self.client.get(self.url_path)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_not_saved_access_token_headers_returns_unauthorized(self):
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.get(self.url_path, headers=header)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_expired_access_token_headers_returns_unauthorized(self, mock_time):
|
||||||
|
session = Session(
|
||||||
|
user_id=2,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=950,
|
||||||
|
refresh_expires_at=1050,
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.get(self.url_path, headers=header)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_non_saved_user_error_is_shown(self, mock_time):
|
||||||
|
session = Session(
|
||||||
|
user_id=2,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Authorization!','code':442}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.get(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_correct_data_but_not_priviliged_user_shows_error(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Not Authorized!','code':460}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.get(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_priviliged_user_with_correct_session_returns_users(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232',
|
||||||
|
privileged = True
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
self.insert_user(RegisteringUser(name = 'guest-1',password = 'citrom',otp_secret = ''))
|
||||||
|
self.insert_user(RegisteringUser(name = 'guest-2',password = 'citrom',otp_secret = ''))
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {
|
||||||
|
'users':[
|
||||||
|
{'name': 'banan', 'privileged': True},
|
||||||
|
{'name': 'guest-1', 'privileged': False},
|
||||||
|
{'name': 'guest-2', 'privileged': False},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.get(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
||||||
142
server/flask/application/test/test_is_user_priviliged.py
Normal file
142
server/flask/application/test/test_is_user_priviliged.py
Normal file
|
|
@ -0,0 +1,142 @@
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import unittest.mock
|
||||||
|
import json
|
||||||
|
from .context import create_app, default_test_config
|
||||||
|
from backend.data import db
|
||||||
|
from backend.data import dao_users
|
||||||
|
from backend.data import dao_session
|
||||||
|
from backend.data.data_models import Session
|
||||||
|
from backend.data.data_models import RegisteringUser
|
||||||
|
|
||||||
|
class IsUserPriviligedcUnitTest(unittest.TestCase):
|
||||||
|
|
||||||
|
url_path = '/user/is_privileged'
|
||||||
|
app = create_app(default_test_config)
|
||||||
|
client = app.test_client()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.init_db()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.close_db()
|
||||||
|
os.remove("testdb")
|
||||||
|
|
||||||
|
def insert_user(self, user: RegisteringUser):
|
||||||
|
with self.app.app_context():
|
||||||
|
user_id = dao_users.insert_user(user)
|
||||||
|
return user_id
|
||||||
|
|
||||||
|
def insert_session(self, session: Session):
|
||||||
|
with self.app.app_context():
|
||||||
|
dao_session.insert_user_session(session)
|
||||||
|
|
||||||
|
def test_no_headers_returns_unauthorized(self):
|
||||||
|
expected = {'message':'Missing Authorization!','code':440}
|
||||||
|
|
||||||
|
response = self.client.get(self.url_path)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_not_saved_access_token_headers_returns_unauthorized(self):
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
response = self.client.get(self.url_path, headers={'Authorization': 'token'})
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_expired_access_token_headers_returns_unauthorized(self):
|
||||||
|
session = Session(
|
||||||
|
user_id=2,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=950,
|
||||||
|
refresh_expires_at=1050,
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
response = self.client.get(self.url_path, headers={'Authorization': 'token'})
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_sending_non_saved_user_error_is_shown(self):
|
||||||
|
session = Session(
|
||||||
|
user_id=2,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.get(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_given_priviliged_user_sending_correct_data_result_is_shown(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232',
|
||||||
|
privileged = True
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'is_privileged': True}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.get(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_given_non_priviliged_user_sending_correct_data_result_is_shown(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232',
|
||||||
|
privileged = False
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'is_privileged': False}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.get(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
||||||
129
server/flask/application/test/test_login.py
Normal file
129
server/flask/application/test/test_login.py
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import json
|
||||||
|
from .context import create_app, default_test_config
|
||||||
|
from backend.data import db
|
||||||
|
from backend.data import dao_users
|
||||||
|
from backend.data.data_models import RegisteringUser
|
||||||
|
|
||||||
|
class LoginUnitTest(unittest.TestCase):
|
||||||
|
|
||||||
|
url_path = '/login'
|
||||||
|
app = create_app(default_test_config)
|
||||||
|
client = app.test_client()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.init_db()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.close_db()
|
||||||
|
os.remove("testdb")
|
||||||
|
|
||||||
|
def insert_user(self, user: RegisteringUser):
|
||||||
|
with self.app.app_context():
|
||||||
|
user_id = dao_users.insert_user(user)
|
||||||
|
return user_id
|
||||||
|
|
||||||
|
def test_without_username_error_is_shown(self):
|
||||||
|
expected = {'message':'Username cannot be empty!','code':410}
|
||||||
|
|
||||||
|
response = self.client.post(self.url_path)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_without_password_error_is_shown(self):
|
||||||
|
expected = {'message':'Password cannot be empty!','code':420}
|
||||||
|
|
||||||
|
data = {'username': 'myname'}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_sending_non_saved_user_error_is_shown(self):
|
||||||
|
expected = {'message':'User cannot be found!','code':412}
|
||||||
|
|
||||||
|
data = {'username': 'myname', 'password': 'mypass', 'otp': '124'}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_sending_non_found_name_by_name_error_is_shown(self):
|
||||||
|
password = 'mypass'
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = "myname",
|
||||||
|
password = password,
|
||||||
|
otp_secret = "base32secret3232"
|
||||||
|
)
|
||||||
|
self.insert_user(user)
|
||||||
|
expected = {'message':'User cannot be found!','code':412}
|
||||||
|
|
||||||
|
data = {'username': 'myname2', 'password': password, 'otp': '124'}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_sending_non_found_user_by_password_error_is_shown(self):
|
||||||
|
username = 'myname'
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = username,
|
||||||
|
password = 'pass',
|
||||||
|
otp_secret = "base32secret3232"
|
||||||
|
)
|
||||||
|
self.insert_user(user)
|
||||||
|
expected = {'message':'User cannot be found!','code':412}
|
||||||
|
|
||||||
|
data = {'username': username, 'password': 'pass2', 'otp': '124'}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_without_otp_verified_then_secret_is_returned(self):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = "myname",
|
||||||
|
password = "mypass",
|
||||||
|
otp_secret = "base32secret3232",
|
||||||
|
was_otp_verified = False
|
||||||
|
)
|
||||||
|
self.insert_user(user)
|
||||||
|
expected_keys = {'otp_secret'}
|
||||||
|
expected_format = r'^otpauth://totp/FnivesVOD:myname\?secret=[^&]*&issuer=FnivesVOD$'
|
||||||
|
|
||||||
|
data = {'username': 'myname', 'password': 'mypass'}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected_keys, set(actual_response_json.keys()))
|
||||||
|
self.assertRegex(actual_response_json['otp_secret'], expected_format)
|
||||||
|
|
||||||
|
def test_with_otp_verified_success_is_returned(self):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = "myname",
|
||||||
|
password = "mypass",
|
||||||
|
otp_secret = "base32secret3232",
|
||||||
|
was_otp_verified = True
|
||||||
|
)
|
||||||
|
self.insert_user(user)
|
||||||
|
expected = {'message':'User found!','code':200}
|
||||||
|
|
||||||
|
data = {'username': 'myname', 'password': 'mypass'}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
||||||
71
server/flask/application/test/test_logout.py
Normal file
71
server/flask/application/test/test_logout.py
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import unittest.mock
|
||||||
|
from .context import create_app, default_test_config
|
||||||
|
from backend.data import db
|
||||||
|
from backend.data import dao_users
|
||||||
|
from backend.data import dao_session
|
||||||
|
from backend.data.data_models import Session
|
||||||
|
from backend.data.data_models import RegisteringUser
|
||||||
|
|
||||||
|
class LogoutUnitTest(unittest.TestCase):
|
||||||
|
|
||||||
|
url_path = '/logout'
|
||||||
|
app = create_app(default_test_config)
|
||||||
|
client = app.test_client()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.init_db()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.close_db()
|
||||||
|
os.remove("testdb")
|
||||||
|
|
||||||
|
def insert_session(self, session: Session):
|
||||||
|
with self.app.app_context():
|
||||||
|
dao_session.insert_user_session(session)
|
||||||
|
|
||||||
|
def assertSession(self, access_token, user_id):
|
||||||
|
with self.app.app_context():
|
||||||
|
actual_user_id = dao_session.get_user_for_token(access_token)
|
||||||
|
self.assertEqual(actual_user_id, user_id)
|
||||||
|
|
||||||
|
def test_no_headers_returns_ok(self):
|
||||||
|
expected = ''
|
||||||
|
response = self.client.post(self.url_path)
|
||||||
|
actual_response = response.data.decode()
|
||||||
|
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response)
|
||||||
|
|
||||||
|
def test_given_invalid_session_returns_ok(self):
|
||||||
|
expected = ''
|
||||||
|
response = self.client.post(self.url_path, headers={'Authorization': 'invalid_token'})
|
||||||
|
actual_response = response.data.decode()
|
||||||
|
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_given_valid_session_returns_and_session_is_invalidated_ok(self, mock_time):
|
||||||
|
session = Session(
|
||||||
|
user_id=2,
|
||||||
|
access_token='access',
|
||||||
|
refresh_token='refresh',
|
||||||
|
access_expires_at=1010,
|
||||||
|
refresh_expires_at=1020
|
||||||
|
)
|
||||||
|
self.insert_session(session = session)
|
||||||
|
self.assertSession(access_token = 'access', user_id = 2)
|
||||||
|
expected = ''
|
||||||
|
response = self.client.post(self.url_path, headers={'Authorization': 'access'})
|
||||||
|
actual_response = response.data.decode()
|
||||||
|
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response)
|
||||||
|
self.assertSession(access_token = 'access', user_id = None)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
||||||
165
server/flask/application/test/test_otp_verification.py
Normal file
165
server/flask/application/test/test_otp_verification.py
Normal file
|
|
@ -0,0 +1,165 @@
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import unittest.mock
|
||||||
|
import json
|
||||||
|
from .context import create_app, default_test_config
|
||||||
|
from backend.data import db
|
||||||
|
from backend.data import dao_users
|
||||||
|
from backend.data import dao_session
|
||||||
|
from backend.data.data_models import Session
|
||||||
|
from backend.data.data_models import RegisteringUser
|
||||||
|
|
||||||
|
class OTP_VerificationUnitTest(unittest.TestCase):
|
||||||
|
|
||||||
|
url_path = '/otp_verification'
|
||||||
|
app = create_app(default_test_config)
|
||||||
|
client = app.test_client()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.init_db()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.close_db()
|
||||||
|
os.remove("testdb")
|
||||||
|
|
||||||
|
def insert_user(self, user):
|
||||||
|
with self.app.app_context():
|
||||||
|
user_id = dao_users.insert_user(user)
|
||||||
|
return user_id
|
||||||
|
|
||||||
|
def assertHasSession(self, access_token, user_id):
|
||||||
|
with self.app.app_context():
|
||||||
|
actual = dao_session.get_user_for_token(access_token)
|
||||||
|
self.assertEqual(user_id, actual)
|
||||||
|
|
||||||
|
def assertUserOTPIs(self, user_id, was_otp_verified):
|
||||||
|
with self.app.app_context():
|
||||||
|
actual = dao_users.get_user_by_id(user_id)
|
||||||
|
self.assertEqual(was_otp_verified, actual.was_otp_verified)
|
||||||
|
|
||||||
|
def test_without_username_error_is_shown(self):
|
||||||
|
expected = {'message':'Username cannot be empty!','code':410}
|
||||||
|
|
||||||
|
response = self.client.post(self.url_path)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_without_password_error_is_shown(self):
|
||||||
|
expected = {'message':'Password cannot be empty!','code':420}
|
||||||
|
|
||||||
|
data = {'username': 'myname'}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_sending_non_saved_user_error_is_shown(self):
|
||||||
|
expected = {'message':'User cannot be found!','code':412}
|
||||||
|
|
||||||
|
data = {'username': 'myname', 'password': 'mypass', 'otp': '124'}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_sending_non_found_user_by_name_error_is_shown(self):
|
||||||
|
password = 'mypass'
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = "myname",
|
||||||
|
password = password,
|
||||||
|
otp_secret = "base32secret3232"
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
expected = {'message':'User cannot be found!','code':412}
|
||||||
|
|
||||||
|
data = {'username': 'myname2', 'password': password, 'otp': '124'}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
self.assertUserOTPIs(user_id = user_id, was_otp_verified = False)
|
||||||
|
|
||||||
|
def test_sending_non_found_user_by_password_error_is_shown(self):
|
||||||
|
username = 'myname'
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = username,
|
||||||
|
password = 'pass',
|
||||||
|
otp_secret = "base32secret3232"
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
expected = {'message':'User cannot be found!','code':412}
|
||||||
|
|
||||||
|
data = {'username': username, 'password': 'pass2', 'otp': '124'}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
self.assertUserOTPIs(user_id = user_id, was_otp_verified = False)
|
||||||
|
|
||||||
|
def test_without_otp_error_is_shown(self):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = "myname",
|
||||||
|
password = "mypass",
|
||||||
|
otp_secret = "base32secret3232"
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
expected = {'message':'Invalid Token!','code':431}
|
||||||
|
|
||||||
|
data = {'username': 'myname', 'password': 'mypass'}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
self.assertUserOTPIs(user_id = user_id, was_otp_verified = False)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_invalid_otp_token_then_error_is_shown(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = "myname",
|
||||||
|
password = "mypass",
|
||||||
|
otp_secret = "base32secret3232"
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
invalid_code = 1
|
||||||
|
expected = {'message':'Invalid Token!','code':431}
|
||||||
|
|
||||||
|
data = {'username': 'myname', 'password': 'mypass', 'otp': '{}'.format(invalid_code)}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
self.assertUserOTPIs(user_id = user_id, was_otp_verified = False)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_proper_otp_token_then_session_is_returned(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = "myname",
|
||||||
|
password = "mypass",
|
||||||
|
otp_secret = "base32secret3232"
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
correct_code = 585501 #for 1000 and base32secret3232
|
||||||
|
expected_keys = {'access_token', 'refresh_token', 'expires_at'}
|
||||||
|
|
||||||
|
data = {'username': 'myname', 'password': 'mypass', 'otp': '{}'.format(correct_code)}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected_keys, set(actual_response_json.keys()))
|
||||||
|
self.assertEqual(actual_response_json['expires_at'], 1000+20000)
|
||||||
|
self.assertHasSession(access_token = actual_response_json['access_token'], user_id = user_id)
|
||||||
|
self.assertUserOTPIs(user_id = user_id, was_otp_verified = True)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
||||||
118
server/flask/application/test/test_refresh_token.py
Normal file
118
server/flask/application/test/test_refresh_token.py
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import unittest.mock
|
||||||
|
import json
|
||||||
|
from .context import create_app, default_test_config
|
||||||
|
from backend.data import db
|
||||||
|
from backend.data import dao_session
|
||||||
|
from backend.data.data_models import RegisteringUser
|
||||||
|
from backend.data.data_models import Session
|
||||||
|
|
||||||
|
class RefreshTokenUnitTest(unittest.TestCase):
|
||||||
|
|
||||||
|
url_path = '/refresh/token'
|
||||||
|
app = create_app(default_test_config)
|
||||||
|
client = app.test_client()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.init_db()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.close_db()
|
||||||
|
os.remove("testdb")
|
||||||
|
|
||||||
|
def insert_session(self, session: Session):
|
||||||
|
with self.app.app_context():
|
||||||
|
dao_session.insert_user_session(session)
|
||||||
|
|
||||||
|
def assertHasSession(self, access_token, user_id):
|
||||||
|
with self.app.app_context():
|
||||||
|
actual = dao_session.get_user_for_token(access_token)
|
||||||
|
self.assertEqual(user_id, actual)
|
||||||
|
|
||||||
|
def test_no_refresh_token_returns_error(self):
|
||||||
|
expected = {'message':'Invalid Refresh Token!','code':450}
|
||||||
|
|
||||||
|
response = self.client.post(self.url_path)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_not_saved_refresh_token_returns_error(self):
|
||||||
|
expected = {'message':'Invalid Refresh Token!','code':450}
|
||||||
|
|
||||||
|
response = self.client.post(self.url_path, data={'refresh_token': 'token'})
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_if_token_is_exists_2_times_then_invalid(self, mock_time):
|
||||||
|
session1 = Session(
|
||||||
|
user_id = 1,
|
||||||
|
access_token = "a",
|
||||||
|
refresh_token = "token",
|
||||||
|
access_expires_at = 5000,
|
||||||
|
refresh_expires_at = 5000,
|
||||||
|
)
|
||||||
|
session2 = Session(
|
||||||
|
user_id = 2,
|
||||||
|
access_token = "b",
|
||||||
|
refresh_token = "token",
|
||||||
|
access_expires_at = 6000,
|
||||||
|
refresh_expires_at = 6000,
|
||||||
|
)
|
||||||
|
self.insert_session(session1)
|
||||||
|
self.insert_session(session2)
|
||||||
|
expected = {'message':'Invalid Refresh Token!','code':450}
|
||||||
|
|
||||||
|
response = self.client.post(self.url_path, data={'refresh_token': 'token'})
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_expired_refresh_token_then_invalid(self, mock_time):
|
||||||
|
session = Session(
|
||||||
|
user_id = 1,
|
||||||
|
access_token = "a",
|
||||||
|
refresh_token = "token",
|
||||||
|
access_expires_at = 6000,
|
||||||
|
refresh_expires_at = 900,
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Refresh Token!','code':450}
|
||||||
|
|
||||||
|
response = self.client.post(self.url_path, data={'refresh_token': 'token'})
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_expired_access_token_but_non_expires_refresh_token_then_new_is_returned(self, mock_time):
|
||||||
|
session = Session(
|
||||||
|
user_id = 1,
|
||||||
|
access_token = "a",
|
||||||
|
refresh_token = "token",
|
||||||
|
access_expires_at = 900,
|
||||||
|
refresh_expires_at = 6000,
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected_keys = {'access_token', 'refresh_token', 'expires_at'}
|
||||||
|
|
||||||
|
response = self.client.post(self.url_path, data={'refresh_token': 'token'})
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected_keys, set(actual_response_json.keys()))
|
||||||
|
self.assertEqual(actual_response_json['expires_at'], 1000+20000)
|
||||||
|
self.assertHasSession(access_token = actual_response_json['access_token'], user_id = 1)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
||||||
136
server/flask/application/test/test_registration.py
Normal file
136
server/flask/application/test/test_registration.py
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
import os,re
|
||||||
|
import unittest
|
||||||
|
import json
|
||||||
|
from .context import create_app, default_test_config
|
||||||
|
from backend.data import db
|
||||||
|
from backend.data import dao_registration_tokens
|
||||||
|
|
||||||
|
class RegistrationUnitTest(unittest.TestCase):
|
||||||
|
|
||||||
|
url_path = '/register'
|
||||||
|
app = create_app(default_test_config)
|
||||||
|
client = app.test_client()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.init_db()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.close_db()
|
||||||
|
os.remove("testdb")
|
||||||
|
|
||||||
|
def save_token(self, token):
|
||||||
|
with self.app.app_context():
|
||||||
|
dao_registration_tokens.insert_token(token)
|
||||||
|
|
||||||
|
def test_without_username_error_is_shown(self):
|
||||||
|
expected = {'message':'Username cannot be empty!','code':410}
|
||||||
|
|
||||||
|
response = self.client.post(self.url_path)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_without_password_error_is_shown(self):
|
||||||
|
expected = {'message':'Password cannot be empty!','code':420}
|
||||||
|
|
||||||
|
data = {'username': 'myname'}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_without_otp_error_is_shown(self):
|
||||||
|
expected = {'message':'Invalid Token!','code':430}
|
||||||
|
|
||||||
|
data = {'username': 'myname', 'password': 'mypass'}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_sending_non_saved_otp_token_then_error_is_shown(self):
|
||||||
|
expected = {'message':'Invalid Token!','code':430}
|
||||||
|
|
||||||
|
data = {'username': 'myname', 'password': 'mypass', 'otp': '124'}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_sending_saved_token_with_nonexistent_username_then_otp_secret_is_revealed(self):
|
||||||
|
token = '124'
|
||||||
|
name = 'myname'
|
||||||
|
self.save_token(token)
|
||||||
|
expected_keys = {'otp_secret'}
|
||||||
|
expected_format = r'^otpauth://totp/FnivesVOD:[^?]*\?secret='+re.escape(name)+r'&issuer=FnivesVOD$'
|
||||||
|
|
||||||
|
data = {'username': 'myname', 'password': 'mypass', 'otp': token}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected_keys, set(actual_response_json.keys()))
|
||||||
|
self.assertRegex(actual_response_json['otp_secret'], expected_format)
|
||||||
|
|
||||||
|
def test_reusing_token_doesnt_work(self):
|
||||||
|
token = '124'
|
||||||
|
self.save_token(token)
|
||||||
|
first_user_data = {'username': 'myname', 'password': 'mypass', 'otp': token}
|
||||||
|
self.client.post(self.url_path, data=first_user_data)
|
||||||
|
expected = {'message':'Invalid Token!','code':430}
|
||||||
|
|
||||||
|
data = {'username': 'myname2', 'password': 'mypass', 'otp': token}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_sending_saved_otp_token_but_existing_username_then_error_is_shown(self):
|
||||||
|
first_user_token = '124'
|
||||||
|
second_user_token = '125'
|
||||||
|
same_user_name = 'myname'
|
||||||
|
self.save_token(first_user_token)
|
||||||
|
self.save_token(second_user_token)
|
||||||
|
first_user_data = {'username': same_user_name, 'password': 'mypass', 'otp': first_user_token}
|
||||||
|
self.client.post(self.url_path, data=first_user_data)
|
||||||
|
expected = {'message':'Username is already taken!','code':411}
|
||||||
|
|
||||||
|
data = {'username': same_user_name, 'password': 'mypass', 'otp': second_user_token}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_reusing_token_after_existing_user_error_works(self):
|
||||||
|
first_user_token = '124'
|
||||||
|
second_user_token = '125'
|
||||||
|
same_user_name = 'myname'
|
||||||
|
different_user_name = 'myname2'
|
||||||
|
self.save_token(first_user_token)
|
||||||
|
self.save_token(second_user_token)
|
||||||
|
first_user_data = {'username': same_user_name, 'password': 'mypass', 'otp': first_user_token}
|
||||||
|
self.client.post(self.url_path, data=first_user_data)
|
||||||
|
second_user_data = {'username': same_user_name, 'password': 'mypass', 'otp': second_user_token}
|
||||||
|
self.client.post(self.url_path, data=second_user_data)
|
||||||
|
expected_keys = {'otp_secret'}
|
||||||
|
expected_format = r'^otpauth://totp/FnivesVOD:[^?]*\?secret='+re.escape(different_user_name)+r'&issuer=FnivesVOD$'
|
||||||
|
|
||||||
|
data = {'username': different_user_name, 'password': 'mypass', 'otp': second_user_token}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected_keys, set(actual_response_json.keys()))
|
||||||
|
self.assertRegex(actual_response_json['otp_secret'], expected_format)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
||||||
204
server/flask/application/test/test_reset_password.py
Normal file
204
server/flask/application/test/test_reset_password.py
Normal file
|
|
@ -0,0 +1,204 @@
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import unittest.mock
|
||||||
|
import json
|
||||||
|
from .context import create_app, default_test_config
|
||||||
|
from backend.data import db
|
||||||
|
from backend.data import dao_reset_password_tokens
|
||||||
|
from backend.data import dao_users
|
||||||
|
from backend.data.data_models import RegisteringUser
|
||||||
|
|
||||||
|
class ResetPasswordUnitTest(unittest.TestCase):
|
||||||
|
|
||||||
|
url_path = '/reset/password'
|
||||||
|
app = create_app(default_test_config)
|
||||||
|
client = app.test_client()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.init_db()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.close_db()
|
||||||
|
os.remove("testdb")
|
||||||
|
|
||||||
|
def save_token(self, token, username, expires_at):
|
||||||
|
with self.app.app_context():
|
||||||
|
dao_reset_password_tokens.insert_token(token=token,username=username,expires_at=expires_at)
|
||||||
|
|
||||||
|
def insert_user(self, user: RegisteringUser):
|
||||||
|
with self.app.app_context():
|
||||||
|
user_id = dao_users.insert_user(user)
|
||||||
|
return user_id
|
||||||
|
|
||||||
|
def test_without_username_error_is_shown(self):
|
||||||
|
expected = {'message':'Username cannot be empty!','code':410}
|
||||||
|
|
||||||
|
response = self.client.post(self.url_path)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_without_password_error_is_shown(self):
|
||||||
|
expected = {'message':'Password cannot be empty!','code':420}
|
||||||
|
|
||||||
|
data = {'username': 'myname'}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_without_reset_password_token_error_is_shown(self):
|
||||||
|
expected = {'message': 'Invalid Reset Password Token given!','code':461}
|
||||||
|
|
||||||
|
data = {'username': 'myname', 'password': 'mypass'}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_without_valid_reset_password_token_error_is_shown(self):
|
||||||
|
expected = {'message': 'Invalid Reset Password Token given!','code':461}
|
||||||
|
|
||||||
|
data = {'username': 'myname', 'password': 'mypass', 'reset_password_token': 'alma'}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_without_saved_reset_password_token_error_is_shown(self):
|
||||||
|
expected = {'message': 'Invalid Reset Password Token given!','code':461}
|
||||||
|
|
||||||
|
data = {'username': 'myname', 'password': 'mypass', 'reset_password_token': 'alma'}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_with_expired_reset_password_token_error_is_shown(self, mock_time):
|
||||||
|
expected = {'message': 'Invalid Reset Password Token given!','code':461}
|
||||||
|
token = 'alma'
|
||||||
|
username = 'myname'
|
||||||
|
self.save_token(token = token, username = username, expires_at = 10)
|
||||||
|
|
||||||
|
data = {'username': username, 'password': 'mypass', 'reset_password_token': token}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_with_valid_registration_token_but_no_user_error_is_shown(self, mock_time):
|
||||||
|
expected = {'message': 'User cannot be found!','code':412}
|
||||||
|
token = 'alma'
|
||||||
|
username = 'myname'
|
||||||
|
self.save_token(token = token, username = username, expires_at = 1100)
|
||||||
|
|
||||||
|
data = {'username': username, 'password': 'mypass', 'reset_password_token': token}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_with_valid_token_and_user_success_is_shown(self, mock_time):
|
||||||
|
expected = {'message': 'Password was Saved!','code':201}
|
||||||
|
token = 'alma'
|
||||||
|
username = 'myname'
|
||||||
|
self.save_token(token = token, username = username, expires_at = 1100)
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = username,
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
|
||||||
|
data = {'username': username, 'password': 'mypass', 'reset_password_token': token}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_reset_password_can_be_used(self, mock_time):
|
||||||
|
expected_keys = {'otp_secret'}
|
||||||
|
expected_format = r'^otpauth://totp/FnivesVOD:myname\?secret=[^&]*&issuer=FnivesVOD$'
|
||||||
|
token = 'alma'
|
||||||
|
username = 'myname'
|
||||||
|
self.save_token(token = token, username = username, expires_at = 1100)
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = username,
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
self.insert_user(user)
|
||||||
|
data = {'username': username, 'password': 'mypass', 'reset_password_token': token}
|
||||||
|
self.client.post(self.url_path, data=data)
|
||||||
|
|
||||||
|
login_data = {'username': username, 'password': 'mypass'}
|
||||||
|
response = self.client.post('/login', data=login_data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(expected_keys, set(actual_response_json.keys()))
|
||||||
|
self.assertRegex(actual_response_json['otp_secret'], expected_format)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_same_token_cannot_be_reused(self, mock_time):
|
||||||
|
expected = {'message': 'Invalid Reset Password Token given!','code':461}
|
||||||
|
token = 'alma'
|
||||||
|
username = 'myname'
|
||||||
|
self.save_token(token = token, username = username, expires_at = 1100)
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = username,
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
self.insert_user(user)
|
||||||
|
data = {'username': username, 'password': 'mypass', 'reset_password_token': token}
|
||||||
|
self.client.post(self.url_path, data=data)
|
||||||
|
|
||||||
|
data = {'username': username, 'password': 'mypass', 'reset_password_token': token}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_no_previous_token_can_be_reused_after_reset(self, mock_time):
|
||||||
|
expected = {'message': 'Invalid Reset Password Token given!','code':461}
|
||||||
|
token = 'alma'
|
||||||
|
token2 = 'alma2'
|
||||||
|
username = 'myname'
|
||||||
|
self.save_token(token = token, username = username, expires_at = 1100)
|
||||||
|
self.save_token(token = token2, username = username, expires_at = 1100)
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = username,
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
self.insert_user(user)
|
||||||
|
data = {'username': username, 'password': 'mypass', 'reset_password_token': token}
|
||||||
|
self.client.post(self.url_path, data=data)
|
||||||
|
|
||||||
|
data = {'username': username, 'password': 'mypass', 'reset_password_token': token2}
|
||||||
|
response = self.client.post(self.url_path, data=data)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
||||||
|
|
@ -0,0 +1,243 @@
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import unittest.mock
|
||||||
|
import json
|
||||||
|
from .context import create_app, default_test_config
|
||||||
|
from backend.data import db
|
||||||
|
from backend.data import dao_users
|
||||||
|
from backend.data import dao_registration_tokens
|
||||||
|
from backend.data import dao_session
|
||||||
|
from backend.data.data_models import RegisteringUser
|
||||||
|
from backend.data.data_models import Session
|
||||||
|
|
||||||
|
class ResetUserOTPVerificationTest(unittest.TestCase):
|
||||||
|
|
||||||
|
url_path = '/admin/reset_otp_verification'
|
||||||
|
app = create_app(default_test_config)
|
||||||
|
client = app.test_client()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.init_db()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
with self.app.app_context():
|
||||||
|
db.close_db()
|
||||||
|
os.remove("testdb")
|
||||||
|
|
||||||
|
def insert_user(self, user: RegisteringUser):
|
||||||
|
with self.app.app_context():
|
||||||
|
user_id = dao_users.insert_user(user)
|
||||||
|
return user_id
|
||||||
|
|
||||||
|
def insert_session(self, session: Session):
|
||||||
|
with self.app.app_context():
|
||||||
|
dao_session.insert_user_session(session)
|
||||||
|
|
||||||
|
def inser_registration_token(self, token: str):
|
||||||
|
with self.app.app_context():
|
||||||
|
dao_registration_tokens.insert_token(token)
|
||||||
|
|
||||||
|
def test_no_headers_returns_unauthorized(self):
|
||||||
|
expected = {'message':'Missing Authorization!','code':440}
|
||||||
|
|
||||||
|
response = self.client.post(self.url_path)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
def test_not_saved_access_token_headers_returns_unauthorized(self):
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, headers=header)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_expired_access_token_headers_returns_unauthorized(self, mock_time):
|
||||||
|
session = Session(
|
||||||
|
user_id=2,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=950,
|
||||||
|
refresh_expires_at=1050,
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Authorization!','code':441}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, headers=header)
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
|
||||||
|
self.assertEqual(401, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_non_saved_user_error_is_shown(self, mock_time):
|
||||||
|
session = Session(
|
||||||
|
user_id=2,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'message':'Invalid Authorization!','code':442}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_correct_authentication_but_no_otp_shows_error(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232'
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
expected = {'code': 431, 'message': 'Invalid Token!'}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
response = self.client.post(self.url_path, headers = header)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_correct_authentication_and_otp_but_not_priviliged_shows_error(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232',
|
||||||
|
privileged = False
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
correct_code = 585501 #for 1000 and base32secret3232
|
||||||
|
expected = {'code': 460, 'message': 'Not Authorized!'}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
data = {'otp': '{}'.format(correct_code)}
|
||||||
|
response = self.client.post(self.url_path, headers = header, data = data)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_correct_privilidged_authentication_and_otp_without_usertoreset_shows_error(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232',
|
||||||
|
privileged = True
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
correct_code = 585501 #for 1000 and base32secret3232
|
||||||
|
expected = {'code': 413, 'message': 'username_to_reset cannot be empty!'}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
data = {'otp': '{}'.format(correct_code)}
|
||||||
|
response = self.client.post(self.url_path, headers = header, data = data)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_correct_privilidged_authentication_and_otp_but_nonexistent_usertoreset_shows_error(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232',
|
||||||
|
privileged = True
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
correct_code = 585501 #for 1000 and base32secret3232
|
||||||
|
expected = {'message':'User cannot be found!','code':412}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
data = {'username_to_reset': 'alma', 'otp': '{}'.format(correct_code)}
|
||||||
|
response = self.client.post(self.url_path, headers = header, data = data)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(400, response.status_code)
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
|
||||||
|
@unittest.mock.patch('time.time', return_value=1000)
|
||||||
|
def test_sending_correct_data_otp_verification_is_updated(self, mock_time):
|
||||||
|
user = RegisteringUser(
|
||||||
|
name = 'banan',
|
||||||
|
password = 'citrom',
|
||||||
|
otp_secret = 'base32secret3232',
|
||||||
|
privileged = True
|
||||||
|
)
|
||||||
|
user_id = self.insert_user(user)
|
||||||
|
user_to_reset = RegisteringUser(
|
||||||
|
name = 'alma',
|
||||||
|
password = '',
|
||||||
|
otp_secret = '',
|
||||||
|
was_otp_verified = True
|
||||||
|
)
|
||||||
|
self.insert_user(user_to_reset)
|
||||||
|
session = Session(
|
||||||
|
user_id=user_id,
|
||||||
|
access_token='token',
|
||||||
|
refresh_token='',
|
||||||
|
access_expires_at=1050,
|
||||||
|
refresh_expires_at=2000
|
||||||
|
)
|
||||||
|
self.insert_session(session)
|
||||||
|
correct_code = 585501 #for 1000 and base32secret3232
|
||||||
|
expected = {'code': 207, 'message': 'OTP Verification Reset!'}
|
||||||
|
|
||||||
|
header = {'Authorization': 'token'}
|
||||||
|
data = {'username_to_reset': '{}'.format(user_to_reset.name), 'otp': '{}'.format(correct_code)}
|
||||||
|
response = self.client.post(self.url_path, headers = header, data = data)
|
||||||
|
|
||||||
|
actual_response_json = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(expected, actual_response_json)
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
||||||
69
server/flask/setup.sh
Executable file
69
server/flask/setup.sh
Executable file
|
|
@ -0,0 +1,69 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [ -f "certificate/cert.pem" ] && [ -f "certificate/key.pem" ]; then
|
||||||
|
echo "Certificates found!"
|
||||||
|
else
|
||||||
|
echo "Certificates not found! Make sure you have cert.pem and key.pem ready in folder $(pwd)/certificate/" >&2
|
||||||
|
echo "To generate certificate use following command: \`openssl req -x509 -newkey rsa:4096 -keyout key.pem -nodes -out cert.pem -sha256 -days 365 -addext \"subjectAltName = IP:<ipaddress>\"\`" >&2
|
||||||
|
echo "If -addext option doesn't work and you use libressl (to verify use openssl version) checkout https://github.com/libressl/portable/issues/544"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
while getopts ":u:p:m:" opt; do
|
||||||
|
case $opt in
|
||||||
|
u) admin_username="$OPTARG"
|
||||||
|
;;
|
||||||
|
p) password="$OPTARG"
|
||||||
|
;;
|
||||||
|
m) mount_folder="$OPTARG"
|
||||||
|
;;
|
||||||
|
\?) echo "Invalid option -$OPTARG" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$admin_username" ]; then
|
||||||
|
echo "-u Admin Username is required! ">&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ -z "$password" ]; then
|
||||||
|
echo "-p Admin Password is required! ">&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ -z "$mount_folder" ]; then
|
||||||
|
echo "-p Media Folder absolute path is required! ">&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Admin username of backend: $admin_username"
|
||||||
|
echo "Admin password of backend: $password"
|
||||||
|
echo "Media folder mounted: $mount_folder"
|
||||||
|
|
||||||
|
IMG_NAME="home-vod-server:1.0"
|
||||||
|
CONTAINER_NAME="home-vod-server"
|
||||||
|
|
||||||
|
# build docker image if doesn't exists already
|
||||||
|
if [[ "$(docker images -q $IMG_NAME 2> /dev/null)" == "" ]]; then
|
||||||
|
echo "Building Docker Image..."
|
||||||
|
docker build -t $IMG_NAME .
|
||||||
|
else
|
||||||
|
echo "Docker image $IMG_NAME already exists, skipping build."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# delete container if it exists
|
||||||
|
echo "Deleting existing container($CONTAINER_NAME)..."
|
||||||
|
docker stop $CONTAINER_NAME 2> /dev/null
|
||||||
|
docker rm $CONTAINER_NAME 2> /dev/null
|
||||||
|
|
||||||
|
# start container binding the source and media files
|
||||||
|
echo "Creating container($CONTAINER_NAME)..."
|
||||||
|
docker run -d --name "$CONTAINER_NAME" -p443:443 --restart=always --mount type=bind,source="$(pwd)"/application,target=/server --mount type=bind,source="$mount_folder",target="/media-data/media",readonly $IMG_NAME
|
||||||
|
|
||||||
|
# initialize database with admin credentials
|
||||||
|
echo "Initializing database with adming user..."
|
||||||
|
docker exec $CONTAINER_NAME python backend/data/db.py -u "$admin_username" -p "$password"
|
||||||
|
|
||||||
|
echo "Docker container should be ready to use."
|
||||||
|
echo "To test server, you may use \`curl -k -d \"username=$admin_username&password=$password\" https://localhost:443/login\`"
|
||||||
|
echo "To tests use \`docker exec home-vod-server python -m unittest discover -v -s .\`."
|
||||||
Loading…
Add table
Add a link
Reference in a new issue