mirror of
https://github.com/fuckpiracyshield/service.git
synced 2024-11-21 12:49:46 +01:00
Initial commit.
This commit is contained in:
commit
b43282e1a7
461 changed files with 11679 additions and 0 deletions
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
__pycache__/
|
||||||
|
build/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
*.egg
|
||||||
|
*.egg-info/
|
3
README.md
Normal file
3
README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
### Service
|
||||||
|
|
||||||
|
Main logic of the application.
|
12
pyproject.toml
Normal file
12
pyproject.toml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
[build-system]
|
||||||
|
requires = [
|
||||||
|
"setuptools>=54",
|
||||||
|
"setuptools-rust"
|
||||||
|
]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[[tool.setuptools-rust.ext-modules]]
|
||||||
|
target = "rs_cidr_verifier"
|
||||||
|
path = "src/piracyshield_service/whitelist/cidr/Cargo.toml"
|
||||||
|
binding = "PyO3"
|
||||||
|
py-limited-api = "auto"
|
16
setup.cfg
Normal file
16
setup.cfg
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
[metadata]
|
||||||
|
name = piracyshield_service
|
||||||
|
version = 1.0.0
|
||||||
|
description = Flow Management
|
||||||
|
|
||||||
|
[options]
|
||||||
|
package_dir=
|
||||||
|
=src
|
||||||
|
packages = find:
|
||||||
|
python_requires = >= 3.10
|
||||||
|
install_requires =
|
||||||
|
redis
|
||||||
|
rq
|
||||||
|
|
||||||
|
[options.packages.find]
|
||||||
|
where = src
|
1
src/piracyshield_service/__init__.py
Normal file
1
src/piracyshield_service/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
1
src/piracyshield_service/account/__init__.py
Normal file
1
src/piracyshield_service/account/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
167
src/piracyshield_service/account/change_password.py
Normal file
167
src/piracyshield_service/account/change_password.py
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.config import Config
|
||||||
|
from piracyshield_component.security.hasher import Hasher, HasherNonValidException
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_model.account.model import (
|
||||||
|
AccountModel,
|
||||||
|
AccountModelPasswordException,
|
||||||
|
AccountModelConfirmPasswordException,
|
||||||
|
AccountModelConfirmPasswordMismatchException
|
||||||
|
)
|
||||||
|
|
||||||
|
from piracyshield_service.account.set_flag import AccountSetFlagService
|
||||||
|
|
||||||
|
from piracyshield_data_storage.account.storage import AccountStorageUpdateException
|
||||||
|
|
||||||
|
from piracyshield_service.account.errors import AccountErrorCode, AccountErrorMessage
|
||||||
|
|
||||||
|
class AccountChangePasswordService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Changes account password.
|
||||||
|
"""
|
||||||
|
|
||||||
|
hasher = None
|
||||||
|
|
||||||
|
hasher_config = None
|
||||||
|
|
||||||
|
data_model = None
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self, data_storage: AccountStorage):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# child data storage class
|
||||||
|
self.data_storage = data_storage()
|
||||||
|
|
||||||
|
self.account_set_flag_service = AccountSetFlagService(data_storage)
|
||||||
|
|
||||||
|
self._prepare_configs()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def execute(self, account_id: str, current_password: str, new_password: str, confirm_password: str) -> bool | Exception:
|
||||||
|
account_data = self._get_account_data(account_id)
|
||||||
|
|
||||||
|
self._verify_current_password(current_password, account_data.get('password'))
|
||||||
|
|
||||||
|
self._validate_parameters(account_data, new_password, confirm_password)
|
||||||
|
|
||||||
|
if current_password == new_password:
|
||||||
|
raise ApplicationException(AccountErrorCode.PASSWORD_DIFF, AccountErrorMessage.PASSWORD_DIFF)
|
||||||
|
|
||||||
|
hashed_new_password = self.hasher.encode_string(new_password)
|
||||||
|
|
||||||
|
try:
|
||||||
|
affected_rows = self.data_storage.change_password(
|
||||||
|
account_id = account_id,
|
||||||
|
password = hashed_new_password
|
||||||
|
)
|
||||||
|
|
||||||
|
if not len(affected_rows):
|
||||||
|
self.logger.debug(f'Could not change password for account `{account_id}`')
|
||||||
|
|
||||||
|
raise ApplicationException(AccountErrorCode.GENERIC, AccountErrorMessage.GENERIC)
|
||||||
|
|
||||||
|
except AccountStorageUpdateException as e:
|
||||||
|
self.logger.error(f'Could not update the account `{account_id}`')
|
||||||
|
|
||||||
|
raise ApplicationException(AccountErrorCode.GENERIC, AccountErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
self.account_set_flag_service.execute(
|
||||||
|
account_id = account_id,
|
||||||
|
flag = 'change_password',
|
||||||
|
value = False
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _get_account_data(self, account_id: str) -> dict | Exception:
|
||||||
|
try:
|
||||||
|
response = self.data_storage.get_complete(account_id)
|
||||||
|
|
||||||
|
if response.empty():
|
||||||
|
self.logger.debug(f'Could not find any account for `{account_id}`')
|
||||||
|
|
||||||
|
raise ApplicationException(AccountErrorCode.ACCOUNT_NOT_FOUND, AccountErrorMessage.ACCOUNT_NOT_FOUND)
|
||||||
|
|
||||||
|
document = next(response, None)
|
||||||
|
|
||||||
|
return document
|
||||||
|
|
||||||
|
except AccountStorageGetException as e:
|
||||||
|
self.logger.error(f'Could not retrieve account `{account_id}`')
|
||||||
|
|
||||||
|
raise ApplicationException(AccountErrorCode.GENERIC, AccountErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _verify_current_password(self, password: str, hashed_password: str) -> bool | Exception:
|
||||||
|
"""
|
||||||
|
Checks the hashed password against the plain text password.
|
||||||
|
|
||||||
|
:param hashed_password: Argon2 hash string.
|
||||||
|
:param password: plain text password to verify.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.hasher.verify_hash(password, hashed_password)
|
||||||
|
|
||||||
|
except HasherNonValidException:
|
||||||
|
raise ApplicationException(AccountErrorCode.PASSWORD_CHANGE_MISMATCH, AccountErrorMessage.PASSWORD_CHANGE_MISMATCH)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self, account_data: dict, new_password: str, confirm_password: str) -> bool | Exception:
|
||||||
|
try:
|
||||||
|
# we currently need to re-validate everything to have a password validation
|
||||||
|
self.data_model(
|
||||||
|
account_id = account_data.get('account_id'),
|
||||||
|
name = account_data.get('name'),
|
||||||
|
email = account_data.get('email'),
|
||||||
|
password = new_password,
|
||||||
|
confirm_password = confirm_password,
|
||||||
|
role = account_data.get('role'),
|
||||||
|
is_active = account_data.get('is_active')
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
except AccountModelPasswordException:
|
||||||
|
raise ApplicationException(AccountErrorCode.PASSWORD_ERROR, AccountErrorMessage.PASSWORD_ERROR)
|
||||||
|
|
||||||
|
except AccountModelConfirmPasswordException:
|
||||||
|
raise ApplicationException(AccountErrorCode.PASSWORD_ERROR, AccountErrorMessage.PASSWORD_ERROR)
|
||||||
|
|
||||||
|
except AccountModelConfirmPasswordMismatchException:
|
||||||
|
raise ApplicationException(AccountErrorCode.PASSWORD_MISMATCH_ERROR, AccountErrorMessage.PASSWORD_MISMATCH_ERROR)
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
"""
|
||||||
|
Loads the configs.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.hasher_config = Config('security/token').get('hasher')
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
"""
|
||||||
|
Initialize and set the instances.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.data_model = AccountModel
|
||||||
|
|
||||||
|
self.hasher = Hasher(
|
||||||
|
time_cost = self.hasher_config.get('time_cost'),
|
||||||
|
memory_cost = self.hasher_config.get('memory_cost'),
|
||||||
|
parallelism = self.hasher_config.get('parallelism'),
|
||||||
|
hash_length = self.hasher_config.get('hash_length'),
|
||||||
|
salt_length = self.hasher_config.get('salt_length')
|
||||||
|
)
|
246
src/piracyshield_service/account/create.py
Normal file
246
src/piracyshield_service/account/create.py
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.config import Config
|
||||||
|
from piracyshield_component.utils.time import Time
|
||||||
|
from piracyshield_component.security.hasher import Hasher, HasherGenericException
|
||||||
|
from piracyshield_component.security.identifier import Identifier
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_model.account.model import (
|
||||||
|
AccountModel,
|
||||||
|
AccountModelNameException,
|
||||||
|
AccountModelEmailException,
|
||||||
|
AccountModelPasswordException,
|
||||||
|
AccountModelConfirmPasswordException,
|
||||||
|
AccountModelConfirmPasswordMismatchException,
|
||||||
|
AccountModelRoleException
|
||||||
|
)
|
||||||
|
|
||||||
|
from piracyshield_data_model.account.flags.model import (
|
||||||
|
AccountFlagsModel,
|
||||||
|
AccountFlagsModelUnknownFlagException,
|
||||||
|
AccountFlagsModelValueException
|
||||||
|
)
|
||||||
|
|
||||||
|
from piracyshield_data_storage.authentication.storage import AuthenticationStorage, AuthenticationStorageGetException
|
||||||
|
from piracyshield_data_storage.account.storage import AccountStorage, AccountStorageCreateException
|
||||||
|
|
||||||
|
from piracyshield_service.authentication.exists_by_email import AuthenticationExistsByEmailService
|
||||||
|
|
||||||
|
from piracyshield_service.account.errors import AccountErrorCode, AccountErrorMessage
|
||||||
|
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
|
class AccountCreateService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Account creation class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
authentication_exists_by_email_service = None
|
||||||
|
|
||||||
|
authentication_storage = None
|
||||||
|
|
||||||
|
flags_data_model = None
|
||||||
|
|
||||||
|
data_model = None
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
hasher = None
|
||||||
|
|
||||||
|
hasher_config = None
|
||||||
|
|
||||||
|
identifier = None
|
||||||
|
|
||||||
|
def __init__(self, data_model: AccountModel, data_storage: AccountStorage):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.data_model = data_model
|
||||||
|
|
||||||
|
self.data_storage = data_storage()
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_configs()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, name: str, email: str, password: str, confirm_password: str, flags: dict, created_by: str) -> str | Exception:
|
||||||
|
"""
|
||||||
|
:param name: a string that identificates the real name (and, eventually, sourname) of the user.
|
||||||
|
:param email: e-mail address, used in conjunction with a password to authenticate the user.
|
||||||
|
:param password: a string.
|
||||||
|
:param confirm_password: must be the same as `password`.
|
||||||
|
:param flags: flags of the account.
|
||||||
|
:param created_by: account id of the creator.
|
||||||
|
:return account id of the created user.
|
||||||
|
"""
|
||||||
|
|
||||||
|
model = self._validate_parameters(
|
||||||
|
account_id = self._generate_account_id(),
|
||||||
|
name = name,
|
||||||
|
email = email,
|
||||||
|
password = password,
|
||||||
|
confirm_password = confirm_password,
|
||||||
|
is_active = True
|
||||||
|
)
|
||||||
|
|
||||||
|
# check for duplicates
|
||||||
|
if self.authentication_exists_by_email_service.execute(
|
||||||
|
email = model.get('email')
|
||||||
|
) == True:
|
||||||
|
raise ApplicationException(AccountErrorCode.EMAIL_EXISTS, AccountErrorMessage.EMAIL_EXISTS)
|
||||||
|
|
||||||
|
flags_model = self._validate_flags(
|
||||||
|
flags = flags
|
||||||
|
)
|
||||||
|
|
||||||
|
document = self._build_document(
|
||||||
|
model = model,
|
||||||
|
encoded_password = self.hasher.encode_string(model.get('password')),
|
||||||
|
created_by = created_by,
|
||||||
|
flags = flags_model,
|
||||||
|
now = Time.now_iso8601()
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# insert the data into the database
|
||||||
|
self.data_storage.insert(document)
|
||||||
|
|
||||||
|
except AccountStorageCreateException as e:
|
||||||
|
self.logger.error(f'Could not create the account')
|
||||||
|
|
||||||
|
raise ApplicationException(AccountErrorCode.GENERIC, AccountErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
self.logger.info(f'Account `{document.get("email")}` created with id `{document.get("account_id")}`')
|
||||||
|
|
||||||
|
# return the pre-generated user_id
|
||||||
|
return document.get('account_id')
|
||||||
|
|
||||||
|
def _generate_account_id(self) -> str:
|
||||||
|
"""
|
||||||
|
Generates a UUIDv4 to use as a main account identifier without exposing the true ID in the database.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.identifier.generate()
|
||||||
|
|
||||||
|
def _encode_password(self, password: str) -> str | Exception:
|
||||||
|
"""
|
||||||
|
Attempts to encode the plain test password.
|
||||||
|
|
||||||
|
:param password: plain text password.
|
||||||
|
:return: a string containing the encoded password.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.hasher.encode_string(password)
|
||||||
|
|
||||||
|
except HasherGenericException as e:
|
||||||
|
self.logger.error(f'Could not encode password `{password}`')
|
||||||
|
|
||||||
|
raise ApplicationException(AccountErrorCode.GENERIC, AccountErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _build_document(self, model: dict, encoded_password: str, created_by: str, flags: dict, now: str) -> dict:
|
||||||
|
return {
|
||||||
|
'account_id': model.get('account_id'),
|
||||||
|
'name': model.get('name'),
|
||||||
|
'email': model.get('email'),
|
||||||
|
'password': encoded_password,
|
||||||
|
'role': model.get('role'),
|
||||||
|
'is_active': model.get('is_active'),
|
||||||
|
'flags': flags.get('flags'),
|
||||||
|
'metadata': {
|
||||||
|
# creation date
|
||||||
|
'created_at': now,
|
||||||
|
|
||||||
|
# same as creation date
|
||||||
|
'updated_at': now,
|
||||||
|
|
||||||
|
# who created this item
|
||||||
|
'created_by': created_by
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_flags(self, flags: dict) -> dict:
|
||||||
|
try:
|
||||||
|
# validate flags
|
||||||
|
model = self.flags_data_model(
|
||||||
|
flags = flags
|
||||||
|
)
|
||||||
|
|
||||||
|
return model.to_dict()
|
||||||
|
|
||||||
|
except AccountFlagsModelUnknownFlagException:
|
||||||
|
raise ApplicationException(AccountErrorCode.FLAG_UNKNOWN, AccountErrorMessage.FLAG_UNKNOWN)
|
||||||
|
|
||||||
|
except AccountFlagsModelValueException:
|
||||||
|
raise ApplicationException(AccountErrorCode.FLAG_NON_VALID_VALUE, AccountErrorMessage.FLAG_NON_VALID_VALUE)
|
||||||
|
|
||||||
|
def _validate_parameters(self, account_id: str, name: str, email: str, password: str, confirm_password: str, is_active: bool) -> dict:
|
||||||
|
try:
|
||||||
|
# validate given parameters
|
||||||
|
model = self.data_model(
|
||||||
|
account_id = account_id,
|
||||||
|
name = name,
|
||||||
|
email = email,
|
||||||
|
password = password,
|
||||||
|
confirm_password = confirm_password,
|
||||||
|
is_active = True
|
||||||
|
)
|
||||||
|
|
||||||
|
return model.to_dict()
|
||||||
|
|
||||||
|
except AccountModelNameException:
|
||||||
|
raise ApplicationException(AccountErrorCode.NAME_ERROR, AccountErrorMessage.NAME_ERROR)
|
||||||
|
|
||||||
|
except AccountModelEmailException:
|
||||||
|
raise ApplicationException(AccountErrorCode.EMAIL_ERROR, AccountErrorMessage.EMAIL_ERROR)
|
||||||
|
|
||||||
|
except AccountModelPasswordException:
|
||||||
|
raise ApplicationException(AccountErrorCode.PASSWORD_ERROR, AccountErrorMessage.PASSWORD_ERROR)
|
||||||
|
|
||||||
|
except AccountModelConfirmPasswordException:
|
||||||
|
raise ApplicationException(AccountErrorCode.PASSWORD_ERROR, AccountErrorMessage.PASSWORD_ERROR)
|
||||||
|
|
||||||
|
except AccountModelConfirmPasswordMismatchException:
|
||||||
|
raise ApplicationException(AccountErrorCode.PASSWORD_MISMATCH_ERROR, AccountErrorMessage.PASSWORD_MISMATCH_ERROR)
|
||||||
|
|
||||||
|
# this is implicitly passed by the child model
|
||||||
|
except AccountModelRoleException:
|
||||||
|
raise ApplicationException(AccountErrorCode.ROLE_ERROR, AccountErrorMessage.ROLE_ERROR)
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
"""
|
||||||
|
Loads the configs.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.hasher_config = Config('security/token').get('hasher')
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
"""
|
||||||
|
Initialize and set the instances.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.flags_data_model = AccountFlagsModel
|
||||||
|
|
||||||
|
self.hasher = Hasher(
|
||||||
|
time_cost = self.hasher_config.get('time_cost'),
|
||||||
|
memory_cost = self.hasher_config.get('memory_cost'),
|
||||||
|
parallelism = self.hasher_config.get('parallelism'),
|
||||||
|
hash_length = self.hasher_config.get('hash_length'),
|
||||||
|
salt_length = self.hasher_config.get('salt_length')
|
||||||
|
)
|
||||||
|
|
||||||
|
self.identifier = Identifier()
|
||||||
|
|
||||||
|
self.authentication_storage = AuthenticationStorage()
|
||||||
|
|
||||||
|
self.authentication_exists_by_email_service = AuthenticationExistsByEmailService()
|
60
src/piracyshield_service/account/errors.py
Normal file
60
src/piracyshield_service/account/errors.py
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
|
||||||
|
class AccountErrorCode:
|
||||||
|
|
||||||
|
GENERIC = '3001'
|
||||||
|
|
||||||
|
EMAIL_EXISTS = '3002'
|
||||||
|
|
||||||
|
NAME_ERROR = '3003'
|
||||||
|
|
||||||
|
EMAIL_ERROR = '3004'
|
||||||
|
|
||||||
|
PASSWORD_ERROR = '3005'
|
||||||
|
|
||||||
|
PASSWORD_MISMATCH_ERROR = '3006'
|
||||||
|
|
||||||
|
ROLE_ERROR = '3007'
|
||||||
|
|
||||||
|
FLAG_UNKNOWN = '3008'
|
||||||
|
|
||||||
|
FLAG_NON_VALID_VALUE = '3009'
|
||||||
|
|
||||||
|
FLAG_NOT_FOUND = '3010'
|
||||||
|
|
||||||
|
ACCOUNT_NOT_FOUND = '3011'
|
||||||
|
|
||||||
|
PASSWORD_CHANGE_NON_VALID = '3012'
|
||||||
|
|
||||||
|
PASSWORD_CHANGE_MISMATCH = '3013'
|
||||||
|
|
||||||
|
PASSWORD_DIFF = '3014'
|
||||||
|
|
||||||
|
class AccountErrorMessage:
|
||||||
|
|
||||||
|
GENERIC = 'Error during the creation of the account.'
|
||||||
|
|
||||||
|
EMAIL_EXISTS = 'The e-mail address already exists.'
|
||||||
|
|
||||||
|
NAME_ERROR = 'The name should be a string between 3 and 32 characters.'
|
||||||
|
|
||||||
|
EMAIL_ERROR = 'Non valid e-mail address.'
|
||||||
|
|
||||||
|
PASSWORD_ERROR = 'The password should be a string between 8 and 32 characters.'
|
||||||
|
|
||||||
|
PASSWORD_MISMATCH_ERROR = 'The password confirmation should be equal as the password field.'
|
||||||
|
|
||||||
|
ROLE_ERROR = 'The role should be a valid role type.'
|
||||||
|
|
||||||
|
FLAG_UNKNOWN = 'Unknown flag.'
|
||||||
|
|
||||||
|
FLAG_NON_VALID_VALUE = 'Flag value non valid.'
|
||||||
|
|
||||||
|
FLAG_NOT_FOUND = 'Flag not found.'
|
||||||
|
|
||||||
|
ACCOUNT_NOT_FOUND = 'No account found.'
|
||||||
|
|
||||||
|
PASSWORD_CHANGE_NON_VALID = 'Current or new password non valid.'
|
||||||
|
|
||||||
|
PASSWORD_CHANGE_MISMATCH = 'Current password is wrong.'
|
||||||
|
|
||||||
|
PASSWORD_DIFF = 'The new password should be different from the current password.'
|
57
src/piracyshield_service/account/exists_by_identifier.py
Normal file
57
src/piracyshield_service/account/exists_by_identifier.py
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.account.storage import AccountStorageGetException
|
||||||
|
|
||||||
|
from piracyshield_service.account.errors import AccountErrorCode, AccountErrorMessage
|
||||||
|
|
||||||
|
class AccountExistsByIdentifierService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Checks if an account with this identifier exists.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self, data_storage: AccountStorage):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# child data storage class
|
||||||
|
self.data_storage = data_storage()
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def execute(self, account_id: str) -> bool | Exception:
|
||||||
|
try:
|
||||||
|
response = self.data_storage.exists_by_identifier(
|
||||||
|
account_id = account_id
|
||||||
|
)
|
||||||
|
|
||||||
|
batch = response.batch()
|
||||||
|
|
||||||
|
if len(batch):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
except AccountStorageGetException as e:
|
||||||
|
self.logger.error(f'Could not verify if an account exists with the identifier `{account_id}`')
|
||||||
|
|
||||||
|
raise ApplicationException(AccountErrorCode.GENERIC, AccountErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
pass
|
1
src/piracyshield_service/account/general/__init__.py
Normal file
1
src/piracyshield_service/account/general/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
54
src/piracyshield_service/account/general/get.py
Normal file
54
src/piracyshield_service/account/general/get.py
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_data_storage.account.general.storage import GeneralAccountStorage, GeneralAccountStorageGetException
|
||||||
|
|
||||||
|
from piracyshield_service.account.errors import AccountErrorCode, AccountErrorMessage
|
||||||
|
|
||||||
|
class GeneralAccountGetService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Fetches any type of account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, account_id: str) -> dict:
|
||||||
|
try:
|
||||||
|
response = self.data_storage.get(account_id)
|
||||||
|
|
||||||
|
if response.empty():
|
||||||
|
self.logger.debug(f'Could not find any account for `{account_id}`')
|
||||||
|
|
||||||
|
raise ApplicationException(AccountErrorCode.ACCOUNT_NOT_FOUND, AccountErrorMessage.ACCOUNT_NOT_FOUND)
|
||||||
|
|
||||||
|
document = next(response, None)
|
||||||
|
|
||||||
|
return document
|
||||||
|
|
||||||
|
except GeneralAccountStorageGetException:
|
||||||
|
self.logger.error(f'Could not get the account `{account_id}`')
|
||||||
|
|
||||||
|
raise ApplicationException(AccountErrorCode.GENERIC, AccountErrorMessage.GENERIC)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
self.data_storage = GeneralAccountStorage()
|
54
src/piracyshield_service/account/general/get_all.py
Normal file
54
src/piracyshield_service/account/general/get_all.py
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_data_storage.account.general.storage import GeneralAccountStorage, GeneralAccountStorageGetException
|
||||||
|
|
||||||
|
from piracyshield_service.account.errors import AccountErrorCode, AccountErrorMessage
|
||||||
|
|
||||||
|
class GeneralAccountGetAllService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Fetches all the accounts.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self) -> list:
|
||||||
|
try:
|
||||||
|
response = self.data_storage.get_all()
|
||||||
|
|
||||||
|
if response.empty():
|
||||||
|
self.logger.debug(f'No account found')
|
||||||
|
|
||||||
|
raise ApplicationException(AccountErrorCode.ACCOUNT_NOT_FOUND, AccountErrorMessage.ACCOUNT_NOT_FOUND)
|
||||||
|
|
||||||
|
batch = response.batch()
|
||||||
|
|
||||||
|
return list(batch)
|
||||||
|
|
||||||
|
except GeneralAccountStorageGetException:
|
||||||
|
self.logger.error(f'Could not get the account `{account_id}`')
|
||||||
|
|
||||||
|
raise ApplicationException(AccountErrorCode.GENERIC, AccountErrorMessage.GENERIC)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
self.data_storage = GeneralAccountStorage()
|
57
src/piracyshield_service/account/get.py
Normal file
57
src/piracyshield_service/account/get.py
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.account.storage import AccountStorageGetException
|
||||||
|
|
||||||
|
from piracyshield_service.account.errors import AccountErrorCode, AccountErrorMessage
|
||||||
|
|
||||||
|
class AccountGetService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Fetches account data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self, data_storage: AccountStorage):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# child data storage class
|
||||||
|
self.data_storage = data_storage()
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def execute(self, account_id: str) -> dict | Exception:
|
||||||
|
try:
|
||||||
|
response = self.data_storage.get(account_id)
|
||||||
|
|
||||||
|
if response.empty():
|
||||||
|
self.logger.debug(f'Could not find any account for `{account_id}`')
|
||||||
|
|
||||||
|
raise ApplicationException(AccountErrorCode.ACCOUNT_NOT_FOUND, AccountErrorMessage.ACCOUNT_NOT_FOUND)
|
||||||
|
|
||||||
|
document = next(response, None)
|
||||||
|
|
||||||
|
return document
|
||||||
|
|
||||||
|
except AccountStorageGetException as e:
|
||||||
|
self.logger.error(f'Could not retrieve account `{account_id}`')
|
||||||
|
|
||||||
|
raise ApplicationException(AccountErrorCode.GENERIC, AccountErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
pass
|
57
src/piracyshield_service/account/get_all.py
Normal file
57
src/piracyshield_service/account/get_all.py
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.account.storage import AccountStorageGetException
|
||||||
|
|
||||||
|
from piracyshield_service.account.errors import AccountErrorCode, AccountErrorMessage
|
||||||
|
|
||||||
|
class AccountGetAllService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Fetches all accounts.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self, data_storage: AccountStorage):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# child data storage class
|
||||||
|
self.data_storage = data_storage()
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def execute(self) -> list | Exception:
|
||||||
|
try:
|
||||||
|
response = self.data_storage.get_all()
|
||||||
|
|
||||||
|
batch = response.batch()
|
||||||
|
|
||||||
|
if not len(batch):
|
||||||
|
self.logger.debug(f'No account found')
|
||||||
|
|
||||||
|
raise ApplicationException(AccountErrorCode.ACCOUNT_NOT_FOUND, AccountErrorMessage.ACCOUNT_NOT_FOUND)
|
||||||
|
|
||||||
|
return list(batch)
|
||||||
|
|
||||||
|
except AccountStorageGetException as e:
|
||||||
|
self.logger.error(f'Could not retrieve any account')
|
||||||
|
|
||||||
|
raise ApplicationException(AccountErrorCode.GENERIC, AccountErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
pass
|
52
src/piracyshield_service/account/get_total.py
Normal file
52
src/piracyshield_service/account/get_total.py
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.account.storage import AccountStorageGetException
|
||||||
|
|
||||||
|
from piracyshield_service.account.errors import AccountErrorCode, AccountErrorMessage
|
||||||
|
|
||||||
|
class AccountGetTotalService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Retrieves the total number of accounts.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self, data_storage: AccountStorage):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# child data storage class
|
||||||
|
self.data_storage = data_storage()
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def execute(self) -> list | Exception:
|
||||||
|
try:
|
||||||
|
response = self.data_storage.get_total()
|
||||||
|
|
||||||
|
batch = response.batch()
|
||||||
|
|
||||||
|
return len(batch)
|
||||||
|
|
||||||
|
except AccountStorageGetException as e:
|
||||||
|
self.logger.error(f'Could not retrieve the total count of the accounts')
|
||||||
|
|
||||||
|
raise ApplicationException(AccountErrorCode.GENERIC, AccountErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
self.data_storage = AccountStorage()
|
50
src/piracyshield_service/account/remove.py
Normal file
50
src/piracyshield_service/account/remove.py
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.account.storage import AccountStorageRemoveException
|
||||||
|
|
||||||
|
from piracyshield_service.account.errors import AccountErrorCode, AccountErrorMessage
|
||||||
|
|
||||||
|
class AccountRemoveService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Removes an account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self, data_storage: AccountStorage):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# child data storage class
|
||||||
|
self.data_storage = data_storage()
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def execute(self, account_id: str) -> bool | Exception:
|
||||||
|
try:
|
||||||
|
self.data_storage.remove(account_id)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
except AccountStorageRemoveException as e:
|
||||||
|
self.logger.error(f'Could not remove the account `{account_id}`')
|
||||||
|
|
||||||
|
raise ApplicationException(AccountErrorCode.GENERIC, AccountErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
pass
|
59
src/piracyshield_service/account/set_flag.py
Normal file
59
src/piracyshield_service/account/set_flag.py
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.account.storage import AccountStorageUpdateException
|
||||||
|
|
||||||
|
from piracyshield_service.account.errors import AccountErrorCode, AccountErrorMessage
|
||||||
|
|
||||||
|
class AccountSetFlagService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Sets the flag of an account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self, data_storage: AccountStorage):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# child data storage class
|
||||||
|
self.data_storage = data_storage()
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def execute(self, account_id: str, flag: str, value: any) -> bool | Exception:
|
||||||
|
try:
|
||||||
|
affected_rows = self.data_storage.set_flag(
|
||||||
|
account_id = account_id,
|
||||||
|
flag = flag,
|
||||||
|
value = value
|
||||||
|
)
|
||||||
|
|
||||||
|
if not len(affected_rows):
|
||||||
|
self.logger.debug(f'Could not update the flag for account `{account_id}`')
|
||||||
|
|
||||||
|
raise ApplicationException(AccountErrorCode.GENERIC, AccountErrorMessage.GENERIC)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
except AccountStorageUpdateException as e:
|
||||||
|
self.logger.error(f'Could not update the flag for account `{account_id}`')
|
||||||
|
|
||||||
|
raise ApplicationException(AccountErrorCode.GENERIC, AccountErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
pass
|
58
src/piracyshield_service/account/set_status.py
Normal file
58
src/piracyshield_service/account/set_status.py
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.account.storage import AccountStorageUpdateException
|
||||||
|
|
||||||
|
from piracyshield_service.account.errors import AccountErrorCode, AccountErrorMessage
|
||||||
|
|
||||||
|
class AccountSetStatusService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Sets the status of an account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self, data_storage: AccountStorage):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# child data storage class
|
||||||
|
self.data_storage = data_storage()
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def execute(self, account_id: str, value: bool) -> bool | Exception:
|
||||||
|
try:
|
||||||
|
affected_rows = self.data_storage.update_status(
|
||||||
|
account_id = account_id,
|
||||||
|
value = value
|
||||||
|
)
|
||||||
|
|
||||||
|
if not len(affected_rows):
|
||||||
|
self.logger.debug(f'Could not update the status of account `{account_id}`')
|
||||||
|
|
||||||
|
raise ApplicationException(AccountErrorCode.GENERIC, AccountErrorMessage.GENERIC)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
except AccountStorageUpdateException as e:
|
||||||
|
self.logger.error(f'Could not update the account `{account_id}`')
|
||||||
|
|
||||||
|
raise ApplicationException(AccountErrorCode.GENERIC, AccountErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
pass
|
1
src/piracyshield_service/authentication/__init__.py
Normal file
1
src/piracyshield_service/authentication/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
153
src/piracyshield_service/authentication/authenticate.py
Normal file
153
src/piracyshield_service/authentication/authenticate.py
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.config import Config
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
from piracyshield_component.security.hasher import Hasher, HasherNonValidException
|
||||||
|
|
||||||
|
from piracyshield_data_model.authentication.model import AuthenticationModel, AuthenticationModelEmailException, AuthenticationModelPasswordException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.authentication.storage import AuthenticationStorage, AuthenticationStorageGetException
|
||||||
|
|
||||||
|
from piracyshield_service.authentication.get import AuthenticationGetService
|
||||||
|
|
||||||
|
from piracyshield_service.security.anti_brute_force import SecurityAntiBruteForceService
|
||||||
|
|
||||||
|
from piracyshield_service.authentication.errors import AuthenticationErrorCode, AuthenticationErrorMessage
|
||||||
|
|
||||||
|
class AuthenticationAuthenticateService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Credentials based authentication.
|
||||||
|
"""
|
||||||
|
|
||||||
|
security_anti_brute_force_service = None
|
||||||
|
|
||||||
|
authentication_get_service = None
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
data_model = None
|
||||||
|
|
||||||
|
hasher = None
|
||||||
|
|
||||||
|
hasher_config = None
|
||||||
|
|
||||||
|
login_config = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_configs()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, email: str, password: str, ip_address: str) -> dict | Exception:
|
||||||
|
# ensure the correctness of the data before proceeding
|
||||||
|
model = self._validate_parameters(email, password)
|
||||||
|
|
||||||
|
self.logger.debug(f'Account `{email}` requested an authentication')
|
||||||
|
|
||||||
|
# perform anti brute force controls if active
|
||||||
|
if self.security_anti_brute_force_config.get('active') == True:
|
||||||
|
self.security_anti_brute_force_service.execute(
|
||||||
|
email = email,
|
||||||
|
ip_address = ip_address
|
||||||
|
)
|
||||||
|
|
||||||
|
account = self.authentication_get_service.execute(
|
||||||
|
email = model.get('email')
|
||||||
|
)
|
||||||
|
|
||||||
|
# is the account active?
|
||||||
|
if account.get('is_active') == False:
|
||||||
|
raise ApplicationException(AuthenticationErrorCode.USER_NON_ACTIVE, AuthenticationErrorMessage.USER_NON_ACTIVE)
|
||||||
|
|
||||||
|
# verify password
|
||||||
|
self._verify_password(password, account.get('password'))
|
||||||
|
|
||||||
|
self.logger.debug(f"Account `{account.get('email')}` correctly authenticated.")
|
||||||
|
|
||||||
|
return self._build_payload(account)
|
||||||
|
|
||||||
|
def _verify_password(self, password: str, hashed_password: str) -> bool:
|
||||||
|
"""
|
||||||
|
Checks the hashed password against the plain text password.
|
||||||
|
|
||||||
|
:param hashed_password: Argon2 hash string.
|
||||||
|
:param password: plain text password to verify.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.hasher.verify_hash(password, hashed_password)
|
||||||
|
|
||||||
|
except HasherNonValidException:
|
||||||
|
raise ApplicationException(AuthenticationErrorCode.PASSWORD_MISMATCH, AuthenticationErrorMessage.PASSWORD_MISMATCH)
|
||||||
|
|
||||||
|
def _build_payload(self, account: dict) -> dict:
|
||||||
|
# TODO: we should pass this via the account service.
|
||||||
|
|
||||||
|
return {
|
||||||
|
'account_id': account.get('account_id'),
|
||||||
|
'email': account.get('email'),
|
||||||
|
'name': account.get('name'),
|
||||||
|
'role': account.get('role'),
|
||||||
|
'flags': account.get('flags')
|
||||||
|
}
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self, email: str, password: str) -> dict | Exception:
|
||||||
|
"""
|
||||||
|
Verify passed parameters using the authentication data model.
|
||||||
|
|
||||||
|
:param email: valid string.
|
||||||
|
:param password: valid string.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
model = self.data_model(email, password)
|
||||||
|
|
||||||
|
return model.to_dict()
|
||||||
|
|
||||||
|
except AuthenticationModelEmailException:
|
||||||
|
raise ApplicationException(AuthenticationErrorCode.EMAIL_NON_VALID, AuthenticationErrorMessage.EMAIL_NON_VALID)
|
||||||
|
|
||||||
|
except AuthenticationModelPasswordException:
|
||||||
|
raise ApplicationException(AuthenticationErrorCode.PASSWORD_NON_VALID, AuthenticationErrorMessage.PASSWORD_NON_VALID)
|
||||||
|
|
||||||
|
def _prepare_configs(self) -> None:
|
||||||
|
"""
|
||||||
|
Loads the configs.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.security_anti_brute_force_config = Config('security/anti_brute_force').get('general')
|
||||||
|
|
||||||
|
self.hasher_config = Config('security/token').get('hasher')
|
||||||
|
|
||||||
|
def _prepare_modules(self) -> None:
|
||||||
|
"""
|
||||||
|
Initialize and set the instances.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.data_model = AuthenticationModel
|
||||||
|
|
||||||
|
self.hasher = Hasher(
|
||||||
|
time_cost = self.hasher_config.get('time_cost'),
|
||||||
|
memory_cost = self.hasher_config.get('memory_cost'),
|
||||||
|
parallelism = self.hasher_config.get('parallelism'),
|
||||||
|
hash_length = self.hasher_config.get('hash_length'),
|
||||||
|
salt_length = self.hasher_config.get('salt_length')
|
||||||
|
)
|
||||||
|
|
||||||
|
self.data_storage = AuthenticationStorage()
|
||||||
|
|
||||||
|
self.authentication_get_service = AuthenticationGetService()
|
||||||
|
|
||||||
|
self.security_anti_brute_force_service = SecurityAntiBruteForceService()
|
48
src/piracyshield_service/authentication/errors.py
Normal file
48
src/piracyshield_service/authentication/errors.py
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
|
||||||
|
class AuthenticationErrorCode:
|
||||||
|
|
||||||
|
# NOTE: using duplicate codes to mimic the same issue for the user while keeping track of the exception type internally.
|
||||||
|
|
||||||
|
GENERIC = '2000'
|
||||||
|
|
||||||
|
EMAIL_NON_VALID = '2001'
|
||||||
|
|
||||||
|
EMAIL_NOT_FOUND = '2001'
|
||||||
|
|
||||||
|
USER_NON_ACTIVE = '2001'
|
||||||
|
|
||||||
|
PASSWORD_NON_VALID = '2002'
|
||||||
|
|
||||||
|
PASSWORD_MISMATCH = '2002'
|
||||||
|
|
||||||
|
TOKEN_REFRESH_USER_NON_ACTIVE = '2003'
|
||||||
|
|
||||||
|
TOKEN_MISMATCH = '2003'
|
||||||
|
|
||||||
|
TOKEN_EXPIRED = '2003'
|
||||||
|
|
||||||
|
MAX_LOGIN_ATTEMPTS = '2004'
|
||||||
|
|
||||||
|
class AuthenticationErrorMessage:
|
||||||
|
|
||||||
|
GENERIC = 'Generic authentication error.'
|
||||||
|
|
||||||
|
EMAIL_NON_VALID = 'Unable to authenticate, e-mail address format non valid.'
|
||||||
|
|
||||||
|
EMAIL_NOT_FOUND = 'Unable to authenticate.'
|
||||||
|
|
||||||
|
USER_NON_ACTIVE = 'Unable to authenticate.'
|
||||||
|
|
||||||
|
PASSWORD_NON_VALID = 'Unable to authenticate.'
|
||||||
|
|
||||||
|
PASSWORD_MISMATCH = 'Unable to authenticate.'
|
||||||
|
|
||||||
|
TOKEN_REFRESH_EMAIL_NOT_FOUND = 'Unable to refresh the token.'
|
||||||
|
|
||||||
|
TOKEN_REFRESH_USER_NON_ACTIVE = 'Unable to refresh the token.'
|
||||||
|
|
||||||
|
TOKEN_MISMATCH = 'Unable to authenticate, token non valid.'
|
||||||
|
|
||||||
|
TOKEN_EXPIRED = 'Unable to verify the token, expired.'
|
||||||
|
|
||||||
|
MAX_LOGIN_ATTEMPTS = 'Max login attempts reached. Your IP address is temporary banned for {} seconds'
|
56
src/piracyshield_service/authentication/exists_by_email.py
Normal file
56
src/piracyshield_service/authentication/exists_by_email.py
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.authentication.storage import AuthenticationStorage, AuthenticationStorageGetException
|
||||||
|
|
||||||
|
from piracyshield_service.authentication.errors import AuthenticationErrorCode, AuthenticationErrorMessage
|
||||||
|
|
||||||
|
class AuthenticationExistsByEmailService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Checks wether an e-mail address exists.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, email: str) -> bool | Exception:
|
||||||
|
try:
|
||||||
|
response = self.data_storage.get(
|
||||||
|
email = email
|
||||||
|
)
|
||||||
|
|
||||||
|
batch = response.batch()
|
||||||
|
|
||||||
|
if len(batch):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
except AuthenticationStorageGetException as e:
|
||||||
|
self.logger.error(f'Could not verify if e-mail exists')
|
||||||
|
|
||||||
|
raise ApplicationException(AuthenticationErrorCode.GENERIC, AuthenticationErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
self.data_storage = AuthenticationStorage()
|
|
@ -0,0 +1,65 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.config import Config
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
from piracyshield_component.security.token import JWTToken, JWTTokenGenericException
|
||||||
|
|
||||||
|
from piracyshield_service.authentication.errors import AuthenticationErrorCode, AuthenticationErrorMessage
|
||||||
|
|
||||||
|
class AuthenticationGenerateAccessTokenService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Generates an access token.
|
||||||
|
"""
|
||||||
|
|
||||||
|
token = None
|
||||||
|
|
||||||
|
jwt_config = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_configs()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, payload: dict) -> str | Exception:
|
||||||
|
try:
|
||||||
|
return self.token.generate_access_token(payload)
|
||||||
|
|
||||||
|
except JWTTokenGenericException as e:
|
||||||
|
self.logger.error(f'Cannot generate an access token for payload `{payload}`')
|
||||||
|
|
||||||
|
raise ApplicationException(AuthenticationErrorCode.GENERIC, AuthenticationErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self, email: str, password: str) -> dict | Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self) -> None:
|
||||||
|
"""
|
||||||
|
Loads the configs.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.jwt_config = Config('security/token').get('jwt_token')
|
||||||
|
|
||||||
|
def _prepare_modules(self) -> None:
|
||||||
|
"""
|
||||||
|
Initialize and set the instances.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.token = JWTToken(
|
||||||
|
access_secret_key = self.jwt_config.get('access_secret_key'),
|
||||||
|
refresh_secret_key = self.jwt_config.get('refresh_secret_key'),
|
||||||
|
access_expiration_time = self.jwt_config.get('access_expiration_time'),
|
||||||
|
refresh_expiration_time = self.jwt_config.get('refresh_expiration_time'),
|
||||||
|
algorithm = self.jwt_config.get('algorithm')
|
||||||
|
)
|
|
@ -0,0 +1,65 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.config import Config
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
from piracyshield_component.security.token import JWTToken, JWTTokenGenericException
|
||||||
|
|
||||||
|
from piracyshield_service.authentication.errors import AuthenticationErrorCode, AuthenticationErrorMessage
|
||||||
|
|
||||||
|
class AuthenticationGenerateRefreshTokenService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Generates a refresh token.
|
||||||
|
"""
|
||||||
|
|
||||||
|
token = None
|
||||||
|
|
||||||
|
jwt_config = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_configs()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, payload: dict) -> str | Exception:
|
||||||
|
try:
|
||||||
|
return self.token.generate_refresh_token(payload)
|
||||||
|
|
||||||
|
except JWTTokenGenericException as e:
|
||||||
|
self.logger.error(f'Cannot generate a refresh token for payload `{payload}`')
|
||||||
|
|
||||||
|
raise ApplicationException(AuthenticationErrorCode.GENERIC, AuthenticationErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self, email: str, password: str) -> dict | Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self) -> None:
|
||||||
|
"""
|
||||||
|
Loads the configs.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.jwt_config = Config('security/token').get('jwt_token')
|
||||||
|
|
||||||
|
def _prepare_modules(self) -> None:
|
||||||
|
"""
|
||||||
|
Initialize and set the instances.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.token = JWTToken(
|
||||||
|
access_secret_key = self.jwt_config.get('access_secret_key'),
|
||||||
|
refresh_secret_key = self.jwt_config.get('refresh_secret_key'),
|
||||||
|
access_expiration_time = self.jwt_config.get('access_expiration_time'),
|
||||||
|
refresh_expiration_time = self.jwt_config.get('refresh_expiration_time'),
|
||||||
|
algorithm = self.jwt_config.get('algorithm')
|
||||||
|
)
|
58
src/piracyshield_service/authentication/get.py
Normal file
58
src/piracyshield_service/authentication/get.py
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.authentication.storage import AuthenticationStorage, AuthenticationStorageGetException
|
||||||
|
|
||||||
|
from piracyshield_service.authentication.errors import AuthenticationErrorCode, AuthenticationErrorMessage
|
||||||
|
|
||||||
|
class AuthenticationGetService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Retrieves an account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, email: str) -> dict | Exception:
|
||||||
|
try:
|
||||||
|
response = self.data_storage.get(
|
||||||
|
email = email
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.empty():
|
||||||
|
self.logger.debug(f'Could not find any account for `{email}`')
|
||||||
|
|
||||||
|
raise ApplicationException(AuthenticationErrorCode.EMAIL_NOT_FOUND, AuthenticationErrorMessage.EMAIL_NOT_FOUND)
|
||||||
|
|
||||||
|
document = next(response, None)
|
||||||
|
|
||||||
|
return document
|
||||||
|
|
||||||
|
except AuthenticationStorageGetException as e:
|
||||||
|
self.logger.error(f'Could not verify if e-mail exists')
|
||||||
|
|
||||||
|
raise ApplicationException(AuthenticationErrorCode.GENERIC, AuthenticationErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
self.data_storage = AuthenticationStorage()
|
|
@ -0,0 +1,72 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.config import Config
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
from piracyshield_component.security.token import JWTToken, JWTTokenExpiredException, JWTTokenNonValidException
|
||||||
|
|
||||||
|
from piracyshield_service.authentication.errors import AuthenticationErrorCode, AuthenticationErrorMessage
|
||||||
|
|
||||||
|
class AuthenticationVerifyAccessTokenService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Verifies an access token.
|
||||||
|
"""
|
||||||
|
|
||||||
|
token = None
|
||||||
|
|
||||||
|
jwt_config = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_configs()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, token: bytes) -> dict | Exception:
|
||||||
|
"""
|
||||||
|
Verifies the validity of the JWT token and unrolls the payload.
|
||||||
|
|
||||||
|
:param token: a valid JWT token string.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.token.verify_access_token(token)
|
||||||
|
|
||||||
|
except JWTTokenExpiredException:
|
||||||
|
raise ApplicationException(AuthenticationErrorCode.TOKEN_EXPIRED, AuthenticationErrorMessage.TOKEN_EXPIRED)
|
||||||
|
|
||||||
|
except JWTTokenNonValidException:
|
||||||
|
raise ApplicationException(AuthenticationErrorCode.TOKEN_MISMATCH, AuthenticationErrorMessage.TOKEN_MISMATCH)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self, email: str, password: str) -> dict | Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self) -> None:
|
||||||
|
"""
|
||||||
|
Loads the configs.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.jwt_config = Config('security/token').get('jwt_token')
|
||||||
|
|
||||||
|
def _prepare_modules(self) -> None:
|
||||||
|
"""
|
||||||
|
Initialize and set the instances.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.token = JWTToken(
|
||||||
|
access_secret_key = self.jwt_config.get('access_secret_key'),
|
||||||
|
refresh_secret_key = self.jwt_config.get('refresh_secret_key'),
|
||||||
|
access_expiration_time = self.jwt_config.get('access_expiration_time'),
|
||||||
|
refresh_expiration_time = self.jwt_config.get('refresh_expiration_time'),
|
||||||
|
algorithm = self.jwt_config.get('algorithm')
|
||||||
|
)
|
|
@ -0,0 +1,72 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.config import Config
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
from piracyshield_component.security.token import JWTToken, JWTTokenExpiredException, JWTTokenNonValidException
|
||||||
|
|
||||||
|
from piracyshield_service.authentication.errors import AuthenticationErrorCode, AuthenticationErrorMessage
|
||||||
|
|
||||||
|
class AuthenticationVerifyRefreshTokenService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Verifies a refresh token.
|
||||||
|
"""
|
||||||
|
|
||||||
|
token = None
|
||||||
|
|
||||||
|
jwt_config = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_configs()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, token: bytes) -> dict | Exception:
|
||||||
|
"""
|
||||||
|
Verifies the validity of the JWT token and unrolls the payload.
|
||||||
|
|
||||||
|
:param token: a valid JWT token string.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.token.verify_refresh_token(token)
|
||||||
|
|
||||||
|
except JWTTokenExpiredException:
|
||||||
|
raise ApplicationException(AuthenticationErrorCode.TOKEN_EXPIRED, AuthenticationErrorMessage.TOKEN_EXPIRED)
|
||||||
|
|
||||||
|
except JWTTokenNonValidException:
|
||||||
|
raise ApplicationException(AuthenticationErrorCode.TOKEN_MISMATCH, AuthenticationErrorMessage.TOKEN_MISMATCH)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self, email: str, password: str) -> dict | Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self) -> None:
|
||||||
|
"""
|
||||||
|
Loads the configs.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.jwt_config = Config('security/token').get('jwt_token')
|
||||||
|
|
||||||
|
def _prepare_modules(self) -> None:
|
||||||
|
"""
|
||||||
|
Initialize and set the instances.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.token = JWTToken(
|
||||||
|
access_secret_key = self.jwt_config.get('access_secret_key'),
|
||||||
|
refresh_secret_key = self.jwt_config.get('refresh_secret_key'),
|
||||||
|
access_expiration_time = self.jwt_config.get('access_expiration_time'),
|
||||||
|
refresh_expiration_time = self.jwt_config.get('refresh_expiration_time'),
|
||||||
|
algorithm = self.jwt_config.get('algorithm')
|
||||||
|
)
|
55
src/piracyshield_service/base.py
Normal file
55
src/piracyshield_service/base.py
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
from piracyshield_component.log.logger import Logger
|
||||||
|
|
||||||
|
from piracyshield_service.task.service import TaskService
|
||||||
|
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
class BaseService(ABC):
|
||||||
|
|
||||||
|
task_service = None
|
||||||
|
|
||||||
|
logger = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.logger = Logger('service')
|
||||||
|
|
||||||
|
self.task_service = TaskService()
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def execute(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Executes the service main task.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _schedule_task(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Schedule a specific task.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def _validate_parameters(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Validates parameters in input.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def _prepare_configs(self):
|
||||||
|
"""
|
||||||
|
Prepares the config files.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def _prepare_modules(self):
|
||||||
|
"""
|
||||||
|
Prepare additional components.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
1
src/piracyshield_service/dda/__init__.py
Normal file
1
src/piracyshield_service/dda/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
174
src/piracyshield_service/dda/create.py
Normal file
174
src/piracyshield_service/dda/create.py
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.security.identifier import Identifier
|
||||||
|
from piracyshield_component.utils.time import Time
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_model.dda.model import (
|
||||||
|
DDAModel,
|
||||||
|
DDAModelDDAIdException,
|
||||||
|
DDAModelDescriptionMissingException,
|
||||||
|
DDAModelDescriptionNonValidException,
|
||||||
|
DDAModelInstanceMissingException,
|
||||||
|
DDAModelInstanceNonValidException,
|
||||||
|
DDAModelAccountIdMissingException,
|
||||||
|
DDAModelAccountIdNonValidException
|
||||||
|
)
|
||||||
|
|
||||||
|
from piracyshield_data_model.account.role.model import AccountRoleModel
|
||||||
|
|
||||||
|
from piracyshield_data_storage.dda.storage import DDAStorage, DDAStorageCreateException
|
||||||
|
|
||||||
|
from piracyshield_service.account.general.get import GeneralAccountGetService
|
||||||
|
|
||||||
|
from piracyshield_service.dda.exists_by_instance import DDAExistsByInstanceService
|
||||||
|
|
||||||
|
from piracyshield_service.dda.errors import DDAErrorCode, DDAErrorMessage
|
||||||
|
|
||||||
|
class DDACreateService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
DDA creation class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
general_account_get_service = None
|
||||||
|
|
||||||
|
dda_exists_by_instance_service = None
|
||||||
|
|
||||||
|
identifier = None
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
data_model = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, description: str, instance: str, account_id: str, created_by: str) -> bool | Exception:
|
||||||
|
model = self._validate_parameters(
|
||||||
|
dda_id = self._generate_dda_id(),
|
||||||
|
description = description,
|
||||||
|
instance = instance,
|
||||||
|
account_id = account_id,
|
||||||
|
is_active = True
|
||||||
|
)
|
||||||
|
|
||||||
|
# check for duplicates
|
||||||
|
if self.dda_exists_by_instance_service.execute(
|
||||||
|
instance = model.get('instance')
|
||||||
|
):
|
||||||
|
raise ApplicationException(DDAErrorCode.INSTANCE_EXISTS, DDAErrorMessage.INSTANCE_EXISTS)
|
||||||
|
|
||||||
|
# ensure that the account exists as this will trigger an exception if not
|
||||||
|
account_data = self.general_account_get_service.execute(
|
||||||
|
account_id = account_id
|
||||||
|
)
|
||||||
|
|
||||||
|
# verify it's a reporter account
|
||||||
|
if AccountRoleModel.REPORTER.value != account_data.get('role'):
|
||||||
|
raise ApplicationException(DDAErrorCode.NON_VALID_ACCOUNT_ROLE, DDAErrorMessage.NON_VALID_ACCOUNT_ROLE)
|
||||||
|
|
||||||
|
document = self._build_document(
|
||||||
|
model = model,
|
||||||
|
created_by = created_by,
|
||||||
|
now = Time.now_iso8601()
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# insert the data into the database
|
||||||
|
self.data_storage.insert(document)
|
||||||
|
|
||||||
|
except DDAStorageCreateException as e:
|
||||||
|
self.logger.error(f'Error while creating the DDA instance')
|
||||||
|
|
||||||
|
raise ApplicationException(DDAErrorCode.GENERIC, DDAErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
self.logger.info(f'DDA instance `{document.get("instance")}` created by `{document.get("metadata").get("created_by")}`')
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _generate_dda_id(self) -> str:
|
||||||
|
"""
|
||||||
|
Generates a UUIDv4.
|
||||||
|
|
||||||
|
:return: a randomly generated 32 characters string.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.identifier.generate()
|
||||||
|
|
||||||
|
def _build_document(self, model: dict, created_by: str, now: str) -> dict:
|
||||||
|
return {
|
||||||
|
'dda_id': model.get('dda_id'),
|
||||||
|
'description': model.get('description'),
|
||||||
|
'instance': model.get('instance'),
|
||||||
|
'account_id': model.get('account_id'),
|
||||||
|
'is_active': model.get('is_active'),
|
||||||
|
'metadata': {
|
||||||
|
# creation date
|
||||||
|
'created_at': now,
|
||||||
|
|
||||||
|
# same as creation date
|
||||||
|
'updated_at': now,
|
||||||
|
|
||||||
|
# who created this item
|
||||||
|
'created_by': created_by
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self, dda_id: str, description: str, instance: str, account_id: str, is_active: bool) -> dict | Exception:
|
||||||
|
try:
|
||||||
|
model = self.data_model(
|
||||||
|
dda_id = dda_id,
|
||||||
|
description = description,
|
||||||
|
instance = instance,
|
||||||
|
account_id = account_id,
|
||||||
|
is_active = is_active
|
||||||
|
)
|
||||||
|
|
||||||
|
return model.to_dict()
|
||||||
|
|
||||||
|
except DDAModelDDAIdException:
|
||||||
|
raise ApplicationException(DDAErrorCode.GENERIC, DDAErrorMessage.GENERIC)
|
||||||
|
|
||||||
|
except DDAModelDescriptionMissingException:
|
||||||
|
raise ApplicationException(DDAErrorCode.MISSING_DESCRIPTION, DDAErrorMessage.MISSING_DESCRIPTION)
|
||||||
|
|
||||||
|
except DDAModelDescriptionNonValidException:
|
||||||
|
raise ApplicationException(DDAErrorCode.NON_VALID_DESCRIPTION, DDAErrorMessage.NON_VALID_DESCRIPTION)
|
||||||
|
|
||||||
|
except DDAModelInstanceMissingException:
|
||||||
|
raise ApplicationException(DDAErrorCode.MISSING_INSTANCE, DDAErrorMessage.MISSING_INSTANCE)
|
||||||
|
|
||||||
|
except DDAModelInstanceNonValidException:
|
||||||
|
raise ApplicationException(DDAErrorCode.NON_VALID_INSTANCE, DDAErrorMessage.NON_VALID_INSTANCE)
|
||||||
|
|
||||||
|
except DDAModelAccountIdMissingException:
|
||||||
|
raise ApplicationException(DDAErrorCode.MISSING_ACCOUNT_ID, DDAErrorMessage.MISSING_ACCOUNT_ID)
|
||||||
|
|
||||||
|
except DDAModelAccountIdNonValidException:
|
||||||
|
raise ApplicationException(DDAErrorCode.NON_VALID_ACCOUNT_ID, DDAErrorMessage.NON_VALID_ACCOUNT_ID)
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
self.data_model = DDAModel
|
||||||
|
|
||||||
|
self.data_storage = DDAStorage()
|
||||||
|
|
||||||
|
self.identifier = Identifier()
|
||||||
|
|
||||||
|
self.dda_exists_by_instance_service = DDAExistsByInstanceService()
|
||||||
|
|
||||||
|
self.general_account_get_service = GeneralAccountGetService()
|
52
src/piracyshield_service/dda/errors.py
Normal file
52
src/piracyshield_service/dda/errors.py
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
|
||||||
|
class DDAErrorCode:
|
||||||
|
|
||||||
|
GENERIC = '7000'
|
||||||
|
|
||||||
|
MISSING_DESCRIPTION = '7001'
|
||||||
|
|
||||||
|
NON_VALID_DESCRIPTION = '7002'
|
||||||
|
|
||||||
|
MISSING_INSTANCE = '7003'
|
||||||
|
|
||||||
|
NON_VALID_INSTANCE = '7004'
|
||||||
|
|
||||||
|
MISSING_ACCOUNT_ID = '7005'
|
||||||
|
|
||||||
|
NON_VALID_ACCOUNT_ID = '7006'
|
||||||
|
|
||||||
|
NON_VALID_ACCOUNT_ROLE = '7007'
|
||||||
|
|
||||||
|
INSTANCE_EXISTS = '7008'
|
||||||
|
|
||||||
|
CANNOT_REMOVE = '7009'
|
||||||
|
|
||||||
|
INSTANCE_USED = '7010'
|
||||||
|
|
||||||
|
CANNOT_SET_STATUS = '7011'
|
||||||
|
|
||||||
|
class DDAErrorMessage:
|
||||||
|
|
||||||
|
GENERIC = 'Error during the creation of the DDA instance.'
|
||||||
|
|
||||||
|
MISSING_DESCRIPTION = 'Missing description.'
|
||||||
|
|
||||||
|
NON_VALID_DESCRIPTION = 'Non valid description.'
|
||||||
|
|
||||||
|
MISSING_INSTANCE = 'Missing instance.'
|
||||||
|
|
||||||
|
NON_VALID_INSTANCE = 'Non valid instance.'
|
||||||
|
|
||||||
|
MISSING_ACCOUNT_ID = 'Missing account identifier.'
|
||||||
|
|
||||||
|
NON_VALID_ACCOUNT_ID = 'Non valid account identifier.'
|
||||||
|
|
||||||
|
NON_VALID_ACCOUNT_ROLE = 'Account with wrong role. Only Reporter accounts are allowed to obtain and use a DDA instance.'
|
||||||
|
|
||||||
|
INSTANCE_EXISTS = 'This item has been already created.'
|
||||||
|
|
||||||
|
CANNOT_REMOVE = 'The item could not be removed. Ensure you have proper permissions or to specify a valid item.'
|
||||||
|
|
||||||
|
INSTANCE_USED = 'A ticket is using this instance, therefore it cannot be removed.'
|
||||||
|
|
||||||
|
CANNOT_SET_STATUS = 'Cannot update the status of the DDA instance.'
|
56
src/piracyshield_service/dda/exists_by_instance.py
Normal file
56
src/piracyshield_service/dda/exists_by_instance.py
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.dda.storage import DDAStorage, DDAStorageGetException
|
||||||
|
|
||||||
|
from piracyshield_service.dda.errors import DDAErrorCode, DDAErrorMessage
|
||||||
|
|
||||||
|
class DDAExistsByInstanceService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Check if a DDA instance exists.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, instance: str) -> bool | Exception:
|
||||||
|
try:
|
||||||
|
response = self.data_storage.exists_by_instance(
|
||||||
|
instance = instance
|
||||||
|
)
|
||||||
|
|
||||||
|
batch = response.batch()
|
||||||
|
|
||||||
|
if len(batch):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
except DDAStorageGetException as e:
|
||||||
|
self.logger.error(f'Cannot find DDA instance `{instance}`')
|
||||||
|
|
||||||
|
raise ApplicationException(DDAErrorCode.GENERIC, DDAErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
self.data_storage = DDAStorage()
|
56
src/piracyshield_service/dda/get_all_by_account.py
Normal file
56
src/piracyshield_service/dda/get_all_by_account.py
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.dda.storage import DDAStorage, DDAStorageGetException
|
||||||
|
|
||||||
|
from piracyshield_service.dda.errors import DDAErrorCode, DDAErrorMessage
|
||||||
|
|
||||||
|
class DDAGetAllByAccountService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Get all the DDA identifiers assigned to an account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, account_id: str) -> list | Exception:
|
||||||
|
try:
|
||||||
|
response = self.data_storage.get_all_by_account(
|
||||||
|
account_id = account_id
|
||||||
|
)
|
||||||
|
|
||||||
|
batch = response.batch()
|
||||||
|
|
||||||
|
if not len(batch):
|
||||||
|
self.logger.debug(f'No DDA identifier found')
|
||||||
|
|
||||||
|
return list(batch)
|
||||||
|
|
||||||
|
except DDAStorageGetException as e:
|
||||||
|
self.logger.error(f'Cannot get all the DDA identifiers')
|
||||||
|
|
||||||
|
raise ApplicationException(DDAErrorCode.GENERIC, DDAErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
self.data_storage = DDAStorage()
|
54
src/piracyshield_service/dda/get_global.py
Normal file
54
src/piracyshield_service/dda/get_global.py
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.dda.storage import DDAStorage, DDAStorageGetException
|
||||||
|
|
||||||
|
from piracyshield_service.dda.errors import DDAErrorCode, DDAErrorMessage
|
||||||
|
|
||||||
|
class DDAGetGlobalService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Get all the DDA identifiers.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self) -> list | Exception:
|
||||||
|
try:
|
||||||
|
response = self.data_storage.get_global()
|
||||||
|
|
||||||
|
batch = response.batch()
|
||||||
|
|
||||||
|
if not len(batch):
|
||||||
|
self.logger.debug(f'No DDA identifier found')
|
||||||
|
|
||||||
|
return list(batch)
|
||||||
|
|
||||||
|
except DDAStorageGetException as e:
|
||||||
|
self.logger.error(f'Cannot get all the DDA identifiers')
|
||||||
|
|
||||||
|
raise ApplicationException(DDAErrorCode.GENERIC, DDAErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
self.data_storage = DDAStorage()
|
57
src/piracyshield_service/dda/is_assigned_to_account.py
Normal file
57
src/piracyshield_service/dda/is_assigned_to_account.py
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.dda.storage import DDAStorage, DDAStorageGetException
|
||||||
|
|
||||||
|
from piracyshield_service.dda.errors import DDAErrorCode, DDAErrorMessage
|
||||||
|
|
||||||
|
class DDAIsAssignedToAccountService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Check if a DDA instance is assigned to a specified account identifier.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, dda_id: str, account_id: str) -> bool | Exception:
|
||||||
|
try:
|
||||||
|
response = self.data_storage.is_assigned_to_account(
|
||||||
|
dda_id = dda_id,
|
||||||
|
account_id = account_id
|
||||||
|
)
|
||||||
|
|
||||||
|
batch = response.batch()
|
||||||
|
|
||||||
|
if len(batch):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
except DDAStorageGetException as e:
|
||||||
|
self.logger.error(f'Cannot find DDA instance `{instance}`')
|
||||||
|
|
||||||
|
raise ApplicationException(DDAErrorCode.GENERIC, DDAErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
self.data_storage = DDAStorage()
|
64
src/piracyshield_service/dda/remove.py
Normal file
64
src/piracyshield_service/dda/remove.py
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.dda.storage import DDAStorage, DDAStorageRemoveException
|
||||||
|
|
||||||
|
from piracyshield_service.ticket.has_dda_id import TicketHasDDAIdService
|
||||||
|
|
||||||
|
from piracyshield_service.dda.errors import DDAErrorCode, DDAErrorMessage
|
||||||
|
|
||||||
|
class DDARemoveService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Removes a DDA identifier.
|
||||||
|
"""
|
||||||
|
|
||||||
|
ticket_has_dda_id_service = None
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, dda_id: str) -> None | Exception:
|
||||||
|
try:
|
||||||
|
# check if no tickets uses this DDA instance
|
||||||
|
if self.ticket_has_dda_id_service.execute(
|
||||||
|
dda_id = dda_id
|
||||||
|
):
|
||||||
|
raise ApplicationException(DDAErrorCode.INSTANCE_USED, DDAErrorMessage.INSTANCE_USED)
|
||||||
|
|
||||||
|
affected_rows = self.data_storage.remove(
|
||||||
|
dda_id = dda_id
|
||||||
|
)
|
||||||
|
|
||||||
|
if not affected_rows:
|
||||||
|
raise ApplicationException(DDAErrorCode.CANNOT_REMOVE, DDAErrorMessage.CANNOT_REMOVE)
|
||||||
|
|
||||||
|
except DDAStorageRemoveException as e:
|
||||||
|
self.logger.error(f'Cannot remove DDA `{value}`')
|
||||||
|
|
||||||
|
raise ApplicationException(DDAErrorCode.GENERIC, DDAErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
self.data_storage = DDAStorage()
|
||||||
|
|
||||||
|
self.ticket_has_dda_id_service = TicketHasDDAIdService()
|
57
src/piracyshield_service/dda/set_status.py
Normal file
57
src/piracyshield_service/dda/set_status.py
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.dda.storage import DDAStorage, DDAStorageUpdateException
|
||||||
|
|
||||||
|
from piracyshield_service.dda.errors import DDAErrorCode, DDAErrorMessage
|
||||||
|
|
||||||
|
class DDASetStatusService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Sets the status of a DDA identifier.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, dda_id: str, status: bool) -> bool | Exception:
|
||||||
|
try:
|
||||||
|
affected_rows = self.data_storage.update_status(
|
||||||
|
dda_id = dda_id,
|
||||||
|
status = status
|
||||||
|
)
|
||||||
|
|
||||||
|
if not len(affected_rows):
|
||||||
|
self.logger.debug(f'Could not set the status for the DDA identifier')
|
||||||
|
|
||||||
|
raise ApplicationException(DDAErrorCode.CANNOT_SET_STATUS, DDAErrorMessage.CANNOT_SET_STATUS)
|
||||||
|
|
||||||
|
except DDAStorageUpdateException as e:
|
||||||
|
self.logger.error(f'Could not update the status of the DDA identifier `{dda_id}`')
|
||||||
|
|
||||||
|
raise ApplicationException(DDAErrorCode.GENERIC, DDAErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
self.data_storage = DDAStorage()
|
1
src/piracyshield_service/forensic/__init__.py
Normal file
1
src/piracyshield_service/forensic/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
174
src/piracyshield_service/forensic/create_archive.py
Normal file
174
src/piracyshield_service/forensic/create_archive.py
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.utils.time import Time
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_model.forensic.archive.model import ForensicArchiveModel, ForensicArchiveModelNameException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.forensic.storage import ForensicStorage, ForensicStorageCreateException, ForensicStorageGetException, ForensicStorageUpdateException
|
||||||
|
|
||||||
|
from piracyshield_service.log.ticket.create import LogTicketCreateService
|
||||||
|
|
||||||
|
from piracyshield_service.importer.save_file import ImporterSaveFileService
|
||||||
|
|
||||||
|
from piracyshield_service.forensic.tasks.analyze_forensic_archive import analyze_forensic_archive_task_caller
|
||||||
|
|
||||||
|
from piracyshield_service.forensic.errors import ForensicErrorCode, ForensicErrorMessage
|
||||||
|
|
||||||
|
class ForensicCreateArchiveService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Manages the upload and schedules the analysis for the evidence archive.
|
||||||
|
"""
|
||||||
|
|
||||||
|
importer_save_file_service = None
|
||||||
|
|
||||||
|
log_ticket_create_service = None
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
data_model = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, ticket_id: str, archive_name: str, archive_content: bytes) -> bool | Exception:
|
||||||
|
"""
|
||||||
|
:param ticket_id: the ticket identifier related to the archive.
|
||||||
|
:param archive_name: the name of the archive.
|
||||||
|
:param archive_content: the data content of the file.
|
||||||
|
:return: true if everything is successful.
|
||||||
|
"""
|
||||||
|
|
||||||
|
model = self._validate_parameters(ticket_id, archive_name)
|
||||||
|
|
||||||
|
# before getting to the expensive operations, let's check if the ticket exists
|
||||||
|
if not self._ticket_id_exists(ticket_id):
|
||||||
|
raise ApplicationException(ForensicErrorCode.NO_HASH_FOR_TICKET, ForensicErrorMessage.NO_HASH_FOR_TICKET)
|
||||||
|
|
||||||
|
# put the file in cache
|
||||||
|
cache_filename = self.importer_save_file_service.execute(
|
||||||
|
filename = model.get('name'),
|
||||||
|
content = archive_content
|
||||||
|
)
|
||||||
|
|
||||||
|
self.logger.debug(f'Forensic evidence `{cache_filename}` moved to cache for ticket `{ticket_id}`')
|
||||||
|
|
||||||
|
try:
|
||||||
|
# update ticket with the archive name
|
||||||
|
self.data_storage.update_archive_name(
|
||||||
|
ticket_id = ticket_id,
|
||||||
|
archive_name = cache_filename,
|
||||||
|
status = model.get('status'),
|
||||||
|
updated_at = Time.now_iso8601()
|
||||||
|
)
|
||||||
|
|
||||||
|
except ForensicStorageUpdateException as e:
|
||||||
|
self.logger.error(f"Could not update the ticket's forensic archive name")
|
||||||
|
|
||||||
|
raise ApplicationException(ForensicErrorCode.GENERIC, ForensicErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
self._schedule_task(
|
||||||
|
ticket_id = ticket_id
|
||||||
|
)
|
||||||
|
|
||||||
|
self.logger.info(f'Scheduled analysis for forensic evidence `{archive_name}` for ticket `{ticket_id}`')
|
||||||
|
|
||||||
|
self.log_ticket_create_service.execute(
|
||||||
|
ticket_id = ticket_id,
|
||||||
|
message = f'Scheduled analysis for new forensic evidence package `{archive_name}`.'
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _ticket_id_exists(self, ticket_id: str) -> bool | Exception:
|
||||||
|
"""
|
||||||
|
Check if the ticket exists.
|
||||||
|
|
||||||
|
:param ticket_id: a ticket identifier.
|
||||||
|
:return: true if everything is successful.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = self.data_storage.exists_ticket_id(
|
||||||
|
ticket_id = ticket_id
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.next():
|
||||||
|
self.logger.debug(f'Ticket found for `{ticket_id}`')
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
except ForensicStorageGetException as e:
|
||||||
|
self.logger.error(f'Could not verify `{ticket_id}` existence')
|
||||||
|
|
||||||
|
raise ApplicationException(ForensicErrorCode.GENERIC, ForensicErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _schedule_task(self, ticket_id: str) -> bool | Exception:
|
||||||
|
"""
|
||||||
|
Schedules the archive analysis.
|
||||||
|
|
||||||
|
:param ticket_id: a ticket identifier.
|
||||||
|
:return: true if everything is successful.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
# schedule package analysis and upload to storage.
|
||||||
|
analysis_task_id = self.task_service.create(
|
||||||
|
task_caller = analyze_forensic_archive_task_caller,
|
||||||
|
delay = 1,
|
||||||
|
ticket_id = ticket_id
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f'Could not create the task for `{ticket_id}`')
|
||||||
|
|
||||||
|
raise ApplicationException(ForensicErrorCode.GENERIC, ForensicErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _validate_parameters(self, ticket_id: str, archive_name: str) -> bool | Exception:
|
||||||
|
"""
|
||||||
|
Validates the inputs.
|
||||||
|
|
||||||
|
:param ticket_id: a ticket identifier.
|
||||||
|
:param archive_name: the name of the archive.
|
||||||
|
:return: true if everything is successful.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
model = self.data_model(
|
||||||
|
ticket_id = ticket_id,
|
||||||
|
name = archive_name
|
||||||
|
)
|
||||||
|
|
||||||
|
return model.to_dict()
|
||||||
|
|
||||||
|
except ForensicArchiveModelNameException:
|
||||||
|
raise ApplicationException(ForensicErrorCode.ARCHIVE_NAME, TicketErrorMessage.ARCHIVE_NAME)
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
"""
|
||||||
|
Initialize and set the instances.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.data_model = ForensicArchiveModel
|
||||||
|
|
||||||
|
self.data_storage = ForensicStorage()
|
||||||
|
|
||||||
|
self.importer_save_file_service = ImporterSaveFileService()
|
||||||
|
|
||||||
|
self.log_ticket_create_service = LogTicketCreateService()
|
150
src/piracyshield_service/forensic/create_hash.py
Normal file
150
src/piracyshield_service/forensic/create_hash.py
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.security.identifier import Identifier
|
||||||
|
from piracyshield_component.utils.time import Time
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_model.forensic.archive.model import ForensicArchiveModel, ForensicArchiveModelNameException
|
||||||
|
from piracyshield_data_model.forensic.hash.model import ForensicHashModel, ForensicHashModelNotSupportedException, ForensicHashModelStringMissingException, ForensicHashModelNonValidException
|
||||||
|
from piracyshield_data_model.forensic.hash.rule import ForensicHashRule
|
||||||
|
|
||||||
|
from piracyshield_data_storage.forensic.storage import ForensicStorage, ForensicStorageCreateException, ForensicStorageGetException
|
||||||
|
|
||||||
|
from piracyshield_service.forensic.errors import ForensicErrorCode, ForensicErrorMessage
|
||||||
|
|
||||||
|
class ForensicCreateHashService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Stores the evidence's hash during the ticket creation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
data_model = None
|
||||||
|
|
||||||
|
identifier = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, ticket_id: str, hash_list: list, reporter_id: str) -> bool | Exception:
|
||||||
|
for hash_type, hash_string in hash_list.items():
|
||||||
|
model = self._validate_parameters(
|
||||||
|
hash_type = hash_type,
|
||||||
|
hash_string = hash_string
|
||||||
|
)
|
||||||
|
|
||||||
|
# check for already inserted hashes strings
|
||||||
|
if self._hash_string_exists(hash_string):
|
||||||
|
raise ApplicationException(ForensicErrorCode.HASH_STRING_EXISTS, ForensicErrorMessage.HASH_STRING_EXISTS)
|
||||||
|
|
||||||
|
self.logger.info(f'Created new hash `{hash_string}` for ticket `{ticket_id}`')
|
||||||
|
|
||||||
|
document = self._build_document(
|
||||||
|
model = model,
|
||||||
|
forensic_id = self._generate_forensic_id(),
|
||||||
|
ticket_id = ticket_id,
|
||||||
|
created_by = reporter_id,
|
||||||
|
now = Time.now_iso8601()
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.data_storage.insert(document)
|
||||||
|
|
||||||
|
except ForensicStorageCreateException as e:
|
||||||
|
self.logger.error(f"Could not update the ticket's forensic archive hash")
|
||||||
|
|
||||||
|
raise ApplicationException(ForensicErrorCode.GENERIC, ForensicErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _hash_string_exists(self, hash_string: str) -> bool | Exception:
|
||||||
|
try:
|
||||||
|
response = self.data_storage.exists_hash_string(
|
||||||
|
hash_string = hash_string
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.next():
|
||||||
|
self.logger.debug(f'Hash string found for `{hash_string}`')
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
except ForensicStorageGetException as e:
|
||||||
|
self.logger.error(f'Could not verify `{hash_string}` existence')
|
||||||
|
|
||||||
|
raise ApplicationException(ForensicErrorCode.GENERIC, ForensicErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _build_document(self, model: dict, forensic_id: str, ticket_id: str, created_by: str, now: str):
|
||||||
|
return {
|
||||||
|
'forensic_id': forensic_id,
|
||||||
|
'ticket_id': ticket_id,
|
||||||
|
'hash_type': model.get('hash_type'),
|
||||||
|
'hash_string': model.get('hash_string'),
|
||||||
|
'metadata': {
|
||||||
|
'created_at': now,
|
||||||
|
'updated_at': now,
|
||||||
|
'created_by': created_by
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def _generate_forensic_id(self) -> str:
|
||||||
|
"""
|
||||||
|
Generates a UUIDv4.
|
||||||
|
|
||||||
|
:return: a randomly generated 32 characters string.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.identifier.generate()
|
||||||
|
|
||||||
|
def _validate_parameters(self, hash_type: str, hash_string: str) -> dict | Exception:
|
||||||
|
"""
|
||||||
|
Validates the inputs.
|
||||||
|
|
||||||
|
:param hash_type: a supported hash algorithm.
|
||||||
|
:param hash_string: a valid hash string.
|
||||||
|
:return: the data converted into a usable dictionary.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
model = self.data_model(
|
||||||
|
hash_type = hash_type,
|
||||||
|
hash_string = hash_string
|
||||||
|
)
|
||||||
|
|
||||||
|
return model.to_dict()
|
||||||
|
|
||||||
|
except ForensicHashModelNotSupportedException:
|
||||||
|
raise ApplicationException(ForensicErrorCode.HASH_TYPE_NOT_SUPPORTED, ForensicErrorMessage.HASH_TYPE_NOT_SUPPORTED)
|
||||||
|
|
||||||
|
except ForensicHashModelStringMissingException:
|
||||||
|
raise ApplicationException(ForensicErrorCode.HASH_STRING_EMPTY, ForensicErrorMessage.HASH_STRING_EMPTY)
|
||||||
|
|
||||||
|
except ForensicHashModelNonValidException:
|
||||||
|
raise ApplicationException(ForensicErrorCode.HASH_STRING_NON_VALID, ForensicErrorMessage.HASH_STRING_NON_VALID)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
"""
|
||||||
|
Initialize and set the instances.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.data_model = ForensicHashModel
|
||||||
|
|
||||||
|
self.data_storage = ForensicStorage()
|
||||||
|
|
||||||
|
self.identifier = Identifier()
|
32
src/piracyshield_service/forensic/errors.py
Normal file
32
src/piracyshield_service/forensic/errors.py
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
|
||||||
|
class ForensicErrorCode:
|
||||||
|
|
||||||
|
GENERIC = '9001'
|
||||||
|
|
||||||
|
ARCHIVE_NAME = '9002'
|
||||||
|
|
||||||
|
HASH_TYPE_NOT_SUPPORTED = '9003'
|
||||||
|
|
||||||
|
HASH_STRING_EMPTY = '9004'
|
||||||
|
|
||||||
|
HASH_STRING_NON_VALID = '9005'
|
||||||
|
|
||||||
|
HASH_STRING_EXISTS = '9006'
|
||||||
|
|
||||||
|
NO_HASH_FOR_TICKET = '9007'
|
||||||
|
|
||||||
|
class ForensicErrorMessage:
|
||||||
|
|
||||||
|
GENERIC = 'Error during the handling of the forensic evidence.'
|
||||||
|
|
||||||
|
ARCHIVE_NAME = 'The archive name contains non valid characters.'
|
||||||
|
|
||||||
|
HASH_TYPE_NOT_SUPPORTED = 'Forensic hash type not supported.'
|
||||||
|
|
||||||
|
HASH_STRING_EMPTY = 'Forensic evidence hash not found.'
|
||||||
|
|
||||||
|
HASH_STRING_NON_VALID = 'Forensic evidence hash non valid.'
|
||||||
|
|
||||||
|
HASH_STRING_EXISTS = 'The hash string value is already present, meaning that this forensic evidence archive has already been submitted.'
|
||||||
|
|
||||||
|
NO_HASH_FOR_TICKET = 'This ticket does not have any forensic evidence hash.'
|
58
src/piracyshield_service/forensic/get_by_ticket.py
Normal file
58
src/piracyshield_service/forensic/get_by_ticket.py
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.forensic.storage import ForensicStorage, ForensicStorageGetException
|
||||||
|
|
||||||
|
from piracyshield_service.forensic.errors import ForensicErrorCode, ForensicErrorMessage
|
||||||
|
|
||||||
|
class ForensicGetByTicketService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Returns the forensic data of a ticket by its identifier.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, ticket_id: str) -> bool | Exception:
|
||||||
|
try:
|
||||||
|
response = self.data_storage.get_by_ticket(
|
||||||
|
ticket_id = ticket_id
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.empty():
|
||||||
|
self.logger.debug(f'Could not find any forensic data associated to ticket `{ticket_id}`')
|
||||||
|
|
||||||
|
raise ApplicationException(ForensicErrorCode.NO_HASH_FOR_TICKET, ForensicErrorMessage.NO_HASH_FOR_TICKET)
|
||||||
|
|
||||||
|
document = next(response, None)
|
||||||
|
|
||||||
|
return document
|
||||||
|
|
||||||
|
except ForensicStorageGetException as e:
|
||||||
|
self.logger.error(f'Could not get any forensic data for ticket `{ticket_id}`')
|
||||||
|
|
||||||
|
raise ApplicationException(ForensicErrorCode.GENERIC, ForensicErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
self.data_storage = ForensicStorage()
|
|
@ -0,0 +1,59 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.forensic.storage import ForensicStorage, ForensicStorageGetException
|
||||||
|
|
||||||
|
from piracyshield_service.forensic.errors import ForensicErrorCode, ForensicErrorMessage
|
||||||
|
|
||||||
|
class ForensicGetByTicketForReporterService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Returns the forensic data of a ticket by its identifier and creator account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, ticket_id: str, reporter_id: str) -> bool | Exception:
|
||||||
|
try:
|
||||||
|
response = self.data_storage.get_by_ticket_for_reporter(
|
||||||
|
ticket_id = ticket_id,
|
||||||
|
reporter_id = reporter_id
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.empty():
|
||||||
|
self.logger.debug(f'Could not find any forensic data associated to ticket `{ticket_id}` for account `{reporter_id}`')
|
||||||
|
|
||||||
|
raise ApplicationException(ForensicErrorCode.NO_HASH_FOR_TICKET, ForensicErrorMessage.NO_HASH_FOR_TICKET)
|
||||||
|
|
||||||
|
document = next(response, None)
|
||||||
|
|
||||||
|
return document
|
||||||
|
|
||||||
|
except ForensicStorageGetException as e:
|
||||||
|
self.logger.error(f'Could not get any forensic data for ticket `{ticket_id}`')
|
||||||
|
|
||||||
|
raise ApplicationException(ForensicErrorCode.GENERIC, ForensicErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
self.data_storage = ForensicStorage()
|
37
src/piracyshield_service/forensic/get_supported_formats.py
Normal file
37
src/piracyshield_service/forensic/get_supported_formats.py
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_data_model.forensic.format.model import ForensicFormatsModel
|
||||||
|
|
||||||
|
class ForensicGetSupportedFormatsService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Supported formats for the forensic evidence archive.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def execute(self) -> list | Exception:
|
||||||
|
"""
|
||||||
|
:return: a list of strings representing the supported archives.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return ForensicFormatsModel().get_formats()
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
pass
|
37
src/piracyshield_service/forensic/get_supported_hashes.py
Normal file
37
src/piracyshield_service/forensic/get_supported_hashes.py
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_data_model.forensic.hash.rule import ForensicHashRule
|
||||||
|
|
||||||
|
class ForensicGetSupportedHashesService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Supported hashes algorithms for the forensic evidence archive.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def execute(self) -> list | Exception:
|
||||||
|
"""
|
||||||
|
:return: a list of strings representing the supported hashes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return ForensicHashRule().get_hash_types()
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
pass
|
62
src/piracyshield_service/forensic/remove_by_ticket.py
Normal file
62
src/piracyshield_service/forensic/remove_by_ticket.py
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.forensic.storage import ForensicStorage, ForensicStorageRemoveException
|
||||||
|
|
||||||
|
from piracyshield_service.forensic.errors import ForensicErrorCode, ForensicErrorMessage
|
||||||
|
|
||||||
|
class ForensicRemoveByTicketService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Removes the forensic data for a ticket.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, ticket_id: str) -> bool | Exception:
|
||||||
|
"""
|
||||||
|
:param ticket_id: a ticket identifier.
|
||||||
|
:return: true if everything is successful.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.data_storage.remove_by_ticket(
|
||||||
|
ticket_id = ticket_id
|
||||||
|
)
|
||||||
|
|
||||||
|
self.logger.debug(f'Removed forensic hashes for ticket `{ticket_id}`')
|
||||||
|
|
||||||
|
except ForensicStorageRemoveException as e:
|
||||||
|
self.logger.error(f'Could not remove the forensic evidence for ticket `{ticket_id}`')
|
||||||
|
|
||||||
|
raise ApplicationException(ForensicErrorCode.GENERIC, ForensicErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
"""
|
||||||
|
Initialize and set the instances.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.data_storage = ForensicStorage()
|
1
src/piracyshield_service/forensic/tasks/__init__.py
Normal file
1
src/piracyshield_service/forensic/tasks/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
from piracyshield_service.task.base import BaseTask
|
||||||
|
|
||||||
|
from piracyshield_data_model.forensic.archive.status.model import ForensicArchiveStatusModel
|
||||||
|
|
||||||
|
from piracyshield_forensic.analyze import ForensicAnalysis
|
||||||
|
|
||||||
|
from piracyshield_service.forensic.update_archive_status import ForensicUpdateArchiveStatusService
|
||||||
|
|
||||||
|
from piracyshield_service.log.ticket.create import LogTicketCreateService
|
||||||
|
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
class AnalyzeForensicArchiveTask(BaseTask):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Forensic evidence archive integrity analysis procedure.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage_cache = None
|
||||||
|
|
||||||
|
log_ticket_create_service = None
|
||||||
|
|
||||||
|
update_archive_status_service = None
|
||||||
|
|
||||||
|
ticket_id = None
|
||||||
|
|
||||||
|
def __init__(self, ticket_id: str):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.ticket_id = ticket_id
|
||||||
|
|
||||||
|
def run(self) -> bool:
|
||||||
|
"""
|
||||||
|
Performs the analysis and updates the ticket.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.update_archive_status_service.execute(
|
||||||
|
ticket_id = self.ticket_id,
|
||||||
|
status = ForensicArchiveStatusModel.IN_PROGRESS.value
|
||||||
|
)
|
||||||
|
|
||||||
|
self.log_ticket_create_service.execute(
|
||||||
|
ticket_id = self.ticket_id,
|
||||||
|
message = f'Started a new forensic archive analysis.'
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# perform analysis.
|
||||||
|
self.forensic_analysis.process(self.ticket_id)
|
||||||
|
|
||||||
|
except ApplicationException as e:
|
||||||
|
self.logger.error(f'Could not process the forensic evidence for ticket `{self.ticket_id}`: {e.message}')
|
||||||
|
|
||||||
|
self.update_archive_status_service.execute(
|
||||||
|
ticket_id = self.ticket_id,
|
||||||
|
status = ForensicArchiveStatusModel.REJECTED.value,
|
||||||
|
reason = e.message
|
||||||
|
)
|
||||||
|
|
||||||
|
self.log_ticket_create_service.execute(
|
||||||
|
ticket_id = self.ticket_id,
|
||||||
|
message = f'The forensic evidence has been rejected: {e.message}'
|
||||||
|
)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
# NOTE: we won't clean the package from the cache here as we might want to schedule a cleaning process separately.
|
||||||
|
|
||||||
|
self.update_archive_status_service.execute(
|
||||||
|
ticket_id = self.ticket_id,
|
||||||
|
status = ForensicArchiveStatusModel.APPROVED.value
|
||||||
|
)
|
||||||
|
|
||||||
|
# log success operation
|
||||||
|
self.log_ticket_create_service.execute(
|
||||||
|
ticket_id = self.ticket_id,
|
||||||
|
message = f'The forensic evidence has been successfully approved.'
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def before_run(self):
|
||||||
|
"""
|
||||||
|
Initialize required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.log_ticket_create_service = LogTicketCreateService()
|
||||||
|
|
||||||
|
self.forensic_analysis = ForensicAnalysis()
|
||||||
|
|
||||||
|
self.update_archive_status_service = ForensicUpdateArchiveStatusService()
|
||||||
|
|
||||||
|
def after_run(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_failure(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def analyze_forensic_archive_task_caller(**kwargs):
|
||||||
|
t = AnalyzeForensicArchiveTask(**kwargs)
|
||||||
|
|
||||||
|
return t.execute()
|
68
src/piracyshield_service/forensic/update_archive_status.py
Normal file
68
src/piracyshield_service/forensic/update_archive_status.py
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.utils.time import Time
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.forensic.storage import ForensicStorage, ForensicStorageUpdateException
|
||||||
|
|
||||||
|
from piracyshield_service.forensic.errors import ForensicErrorCode, ForensicErrorMessage
|
||||||
|
|
||||||
|
class ForensicUpdateArchiveStatusService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Updates the status of a previously uploaded archive.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, ticket_id: str, status: str, reason: str = None) -> bool | Exception:
|
||||||
|
"""
|
||||||
|
:param ticket_id: a ticket identifier.
|
||||||
|
:param status: status of the analysis.
|
||||||
|
:param reason: optional reason for non successful statuses.
|
||||||
|
:return: true if everything is successful.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.data_storage.update_archive_status(
|
||||||
|
ticket_id = ticket_id,
|
||||||
|
status = status,
|
||||||
|
updated_at = Time.now_iso8601(),
|
||||||
|
reason = reason
|
||||||
|
)
|
||||||
|
|
||||||
|
self.logger.debug(f'Updated forensic evidence status for ticket `{ticket_id}` with status `{status}`')
|
||||||
|
|
||||||
|
except ForensicStorageUpdateException as e:
|
||||||
|
self.logger.error(f'Could not update the status of the forensic evidence for ticket `{ticket_id}` with status `{status}`')
|
||||||
|
|
||||||
|
raise ApplicationException(ForensicErrorCode.GENERIC, ForensicErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
"""
|
||||||
|
Initialize and set the instances.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.data_storage = ForensicStorage()
|
1
src/piracyshield_service/guest/__init__.py
Normal file
1
src/piracyshield_service/guest/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
35
src/piracyshield_service/guest/create.py
Normal file
35
src/piracyshield_service/guest/create.py
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.account.create import AccountCreateService
|
||||||
|
|
||||||
|
from piracyshield_data_model.guest.model import GuestModel
|
||||||
|
|
||||||
|
from piracyshield_data_storage.guest.storage import GuestStorage
|
||||||
|
|
||||||
|
class GuestCreateService(AccountCreateService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Creates a guest account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Pass the data model and data storage to the parent class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(GuestModel, GuestStorage)
|
||||||
|
|
||||||
|
def execute(self, name: str, email: str, password: str, confirm_password: str, flags: dict, created_by: str) -> str | Exception:
|
||||||
|
"""
|
||||||
|
Account creation.
|
||||||
|
|
||||||
|
:param name: a string that identificates the real name (and, eventually, sourname) of the account.
|
||||||
|
:param email: e-mail address, used in conjunction with a password to authenticate the account.
|
||||||
|
:param password: a string.
|
||||||
|
:param confirm_password: must be the same as `password`.
|
||||||
|
:param flags: flags of the account.
|
||||||
|
:param created_by: account id of the creator.
|
||||||
|
:return account id of the created account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return super().execute(name, email, password, confirm_password, flags, created_by)
|
18
src/piracyshield_service/guest/get.py
Normal file
18
src/piracyshield_service/guest/get.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.account.get import AccountGetService
|
||||||
|
|
||||||
|
from piracyshield_data_storage.guest.storage import GuestStorage
|
||||||
|
|
||||||
|
class GuestGetService(AccountGetService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Retrieves a guest account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Pass the data storage to the parent class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(GuestStorage)
|
18
src/piracyshield_service/guest/get_all.py
Normal file
18
src/piracyshield_service/guest/get_all.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.account.get_all import AccountGetAllService
|
||||||
|
|
||||||
|
from piracyshield_data_storage.guest.storage import GuestStorage
|
||||||
|
|
||||||
|
class GuestGetAllService(AccountGetAllService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Retrieves all the guest accounts.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Pass the data storage to the parent class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(GuestStorage)
|
18
src/piracyshield_service/guest/remove.py
Normal file
18
src/piracyshield_service/guest/remove.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.account.remove import AccountRemoveService
|
||||||
|
|
||||||
|
from piracyshield_data_storage.guest.storage import GuestStorage
|
||||||
|
|
||||||
|
class GuestRemoveService(AccountRemoveService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Removes a guest account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Pass the data storage to the parent class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(GuestStorage)
|
1
src/piracyshield_service/importer/__init__.py
Normal file
1
src/piracyshield_service/importer/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
12
src/piracyshield_service/importer/errors.py
Normal file
12
src/piracyshield_service/importer/errors.py
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
class ImporterErrorCode:
|
||||||
|
|
||||||
|
GENERIC = '9000'
|
||||||
|
|
||||||
|
EXTENSION_NOT_SUPPORTED = '9001'
|
||||||
|
|
||||||
|
class ImporterErrorMessage:
|
||||||
|
|
||||||
|
GENERIC = 'Error during the saving of the file.'
|
||||||
|
|
||||||
|
EXTENSION_NOT_SUPPORTED = 'The file extension is not supported.'
|
141
src/piracyshield_service/importer/save_file.py
Normal file
141
src/piracyshield_service/importer/save_file.py
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.environment import Environment
|
||||||
|
from piracyshield_component.config import Config
|
||||||
|
from piracyshield_component.security.identifier import Identifier
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.cache.storage import CacheStorage
|
||||||
|
|
||||||
|
from piracyshield_service.importer.errors import ImporterErrorCode, ImporterErrorMessage
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
class ImporterSaveFileService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Reads and writes the content into the cache.
|
||||||
|
"""
|
||||||
|
|
||||||
|
identifier = None
|
||||||
|
|
||||||
|
data_storage_cache = None
|
||||||
|
|
||||||
|
application_archive_config = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_configs()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, filename: str, content: bytes) -> str | Exception:
|
||||||
|
"""
|
||||||
|
:param filename: the name of the file.
|
||||||
|
:param content: the file's content.
|
||||||
|
:return: the name of the file with a unique identifier.
|
||||||
|
"""
|
||||||
|
|
||||||
|
extension = self._get_extension(filename)
|
||||||
|
|
||||||
|
if self._has_supported_extension(extension) == False:
|
||||||
|
raise ApplicationException(ImporterErrorCode.EXTENSION_NOT_SUPPORTED, ImporterErrorMessage.EXTENSION_NOT_SUPPORTED)
|
||||||
|
|
||||||
|
# generate a unique name for the file
|
||||||
|
unique_filename = self._generate_unique_name(filename)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.data_storage_cache.write(unique_filename, content)
|
||||||
|
|
||||||
|
return unique_filename
|
||||||
|
|
||||||
|
except OSError as e:
|
||||||
|
self.logger.error(f'Could not save the file: {e}')
|
||||||
|
|
||||||
|
raise ApplicationException(ImporterErrorCode.GENERIC, ImporterErrorMessage.GENERIC)
|
||||||
|
|
||||||
|
def _generate_identifier(self, filename: str) -> str:
|
||||||
|
identifier = self.identifier.generate_short_unsafe()
|
||||||
|
|
||||||
|
return f'{identifier}-{filename}'
|
||||||
|
|
||||||
|
def _get_extension(self, filename: str) -> str:
|
||||||
|
"""
|
||||||
|
Extracts the extension from the filename.
|
||||||
|
|
||||||
|
:param filename: the name of the file.
|
||||||
|
:return: the extension of the file.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_, extension = os.path.splitext(filename)
|
||||||
|
|
||||||
|
return extension
|
||||||
|
|
||||||
|
def _has_supported_extension(self, extension: str) -> bool:
|
||||||
|
"""
|
||||||
|
Checks wether the extension is supported.
|
||||||
|
|
||||||
|
:param extension: the extension of the file.
|
||||||
|
:return: true if supported.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return extension.lower() in self.application_archive_config.get('supported_extensions')
|
||||||
|
|
||||||
|
def _generate_unique_name(self, filename: str) -> str:
|
||||||
|
"""
|
||||||
|
Attempts to generate a unique name string for the file that will be put in the cache.
|
||||||
|
|
||||||
|
:param filename: the name of the file.
|
||||||
|
:return: the newly generated name of the file.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# TODO: just use a timestamp.
|
||||||
|
|
||||||
|
retries = 5
|
||||||
|
current_tries = 0
|
||||||
|
|
||||||
|
while (True):
|
||||||
|
# exceeded maximum retries
|
||||||
|
if current_tries >= retries:
|
||||||
|
raise ApplicationException(ImporterErrorCode.GENERIC, ImporterErrorMessage.GENERIC)
|
||||||
|
|
||||||
|
filename = self._generate_identifier(filename)
|
||||||
|
|
||||||
|
# check if the file already exists
|
||||||
|
if not self.data_storage_cache.exists(filename):
|
||||||
|
break
|
||||||
|
|
||||||
|
current_tries += 1
|
||||||
|
|
||||||
|
return filename
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
# TODO: should at least validate the name.
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self) -> None:
|
||||||
|
"""
|
||||||
|
Loads the configs.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.application_archive_config = Config('application').get('archive')
|
||||||
|
|
||||||
|
def _prepare_modules(self) -> None:
|
||||||
|
"""
|
||||||
|
Initialize and set the instances.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.data_storage_cache = CacheStorage()
|
||||||
|
|
||||||
|
self.identifier = Identifier()
|
1
src/piracyshield_service/internal/__init__.py
Normal file
1
src/piracyshield_service/internal/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
18
src/piracyshield_service/internal/change_password.py
Normal file
18
src/piracyshield_service/internal/change_password.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.account.change_password import AccountChangePasswordService
|
||||||
|
|
||||||
|
from piracyshield_data_storage.internal.storage import InternalStorage
|
||||||
|
|
||||||
|
class InternalChangePasswordService(AccountChangePasswordService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Changes password of an internal account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Pass the data storage to the parent class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(InternalStorage)
|
33
src/piracyshield_service/internal/create.py
Normal file
33
src/piracyshield_service/internal/create.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.account.create import AccountCreateService
|
||||||
|
|
||||||
|
from piracyshield_data_model.internal.model import InternalModel
|
||||||
|
|
||||||
|
from piracyshield_data_storage.internal.storage import InternalStorage
|
||||||
|
|
||||||
|
class InternalCreateService(AccountCreateService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Creates an internal account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Pass the data model and data storage to the parent class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(InternalModel, InternalStorage)
|
||||||
|
|
||||||
|
def execute(self, name: str, email: str, password: str, confirm_password: str, flags: dict, created_by: str) -> str | Exception:
|
||||||
|
"""
|
||||||
|
:param name: a string that identificates the real name (and, eventually, sourname) of the account.
|
||||||
|
:param email: e-mail address, used in conjunction with a password to authenticate the account.
|
||||||
|
:param password: a string.
|
||||||
|
:param confirm_password: must be the same as `password`.
|
||||||
|
:param flags: flags of the account.
|
||||||
|
:param created_by: account id of the creator.
|
||||||
|
:return account id of the created account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return super().execute(name, email, password, confirm_password, flags, created_by)
|
18
src/piracyshield_service/internal/get.py
Normal file
18
src/piracyshield_service/internal/get.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.account.get import AccountGetService
|
||||||
|
|
||||||
|
from piracyshield_data_storage.internal.storage import InternalStorage
|
||||||
|
|
||||||
|
class InternalGetService(AccountGetService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Retrieves an internal account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Pass the data storage to the parent class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(InternalStorage)
|
18
src/piracyshield_service/internal/get_all.py
Normal file
18
src/piracyshield_service/internal/get_all.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.account.get_all import AccountGetAllService
|
||||||
|
|
||||||
|
from piracyshield_data_storage.internal.storage import InternalStorage
|
||||||
|
|
||||||
|
class InternalGetAllService(AccountGetAllService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Retrieves all the internal accounts.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Pass the data storage to the parent class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(InternalStorage)
|
18
src/piracyshield_service/internal/remove.py
Normal file
18
src/piracyshield_service/internal/remove.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.account.remove import AccountRemoveService
|
||||||
|
|
||||||
|
from piracyshield_data_storage.internal.storage import InternalStorage
|
||||||
|
|
||||||
|
class InternalRemoveService(AccountRemoveService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Removes an internal account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Pass the data storage to the parent class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(InternalStorage)
|
18
src/piracyshield_service/internal/set_status.py
Normal file
18
src/piracyshield_service/internal/set_status.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.account.set_status import AccountSetStatusService
|
||||||
|
|
||||||
|
from piracyshield_data_storage.internal.storage import InternalStorage
|
||||||
|
|
||||||
|
class InternalSetStatusService(AccountSetStatusService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Sets the status of an internal account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Pass the data storage to the parent class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(InternalStorage)
|
1
src/piracyshield_service/log/__init__.py
Normal file
1
src/piracyshield_service/log/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
8
src/piracyshield_service/log/errors.py
Normal file
8
src/piracyshield_service/log/errors.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
class LogErrorCode:
|
||||||
|
|
||||||
|
GENERIC = '8000'
|
||||||
|
|
||||||
|
class LogErrorMessage:
|
||||||
|
|
||||||
|
GENERIC = 'Generic error.'
|
1
src/piracyshield_service/log/ticket/__init__.py
Normal file
1
src/piracyshield_service/log/ticket/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
100
src/piracyshield_service/log/ticket/create.py
Normal file
100
src/piracyshield_service/log/ticket/create.py
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.utils.time import Time
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.log.ticket.storage import LogTicketStorage, LogTicketStorageCreateException
|
||||||
|
|
||||||
|
from piracyshield_data_model.log.ticket.model import (
|
||||||
|
LogTicketModel,
|
||||||
|
LogTicketModelTicketIdentifierMissingException,
|
||||||
|
LogTicketModelTicketIdentifierNonValidException,
|
||||||
|
LogTicketModelMessageNonValidException,
|
||||||
|
LogTicketModelMessageMissingException
|
||||||
|
)
|
||||||
|
|
||||||
|
from piracyshield_service.log.ticket.errors import LogTicketErrorCode, LogTicketErrorMessage
|
||||||
|
|
||||||
|
class LogTicketCreateService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Creates a new ticket log record.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
data_model = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, ticket_id: str, message: str) -> bool | Exception:
|
||||||
|
model = self._validate_parameters(
|
||||||
|
ticket_id = ticket_id,
|
||||||
|
message = message
|
||||||
|
)
|
||||||
|
|
||||||
|
document = self._build_document(
|
||||||
|
model = model,
|
||||||
|
now = Time.now_iso8601()
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.data_storage.insert(document)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
except LogTicketStorageCreateException as e:
|
||||||
|
self.logger.error(f'Could not create the log entry')
|
||||||
|
|
||||||
|
raise ApplicationException(LogTicketErrorCode.GENERIC, LogTicketErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _build_document(self, model: dict, now: str) -> dict:
|
||||||
|
return {
|
||||||
|
'ticket_id': model.get('ticket_id'),
|
||||||
|
'message': model.get('message'),
|
||||||
|
'metadata': {
|
||||||
|
# creation date
|
||||||
|
'created_at': now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self, ticket_id: str, message: str):
|
||||||
|
try:
|
||||||
|
model = self.data_model(
|
||||||
|
ticket_id = ticket_id,
|
||||||
|
message = message
|
||||||
|
)
|
||||||
|
|
||||||
|
return model.to_dict()
|
||||||
|
|
||||||
|
except LogTicketModelTicketIdentifierMissingException:
|
||||||
|
raise ApplicationException(LogTicketErrorCode.MISSING_TICKET_ID, LogTicketErrorMessage.MISSING_TICKET_ID)
|
||||||
|
|
||||||
|
except LogTicketModelTicketIdentifierNonValidException:
|
||||||
|
raise ApplicationException(LogTicketErrorCode.NON_VALID_TICKET_ID, LogTicketErrorMessage.NON_VALID_TICKET_ID)
|
||||||
|
|
||||||
|
except LogTicketModelMessageMissingException:
|
||||||
|
raise ApplicationException(LogTicketErrorCode.MISSING_MESSAGE, LogTicketErrorMessage.MISSING_MESSAGE)
|
||||||
|
|
||||||
|
except LogTicketModelMessageNonValidException:
|
||||||
|
raise ApplicationException(LogTicketErrorCode.NON_VALID_MESSAGE, LogTicketErrorMessage.NON_VALID_MESSAGE)
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
self.data_model = LogTicketModel
|
||||||
|
|
||||||
|
self.data_storage = LogTicketStorage()
|
28
src/piracyshield_service/log/ticket/errors.py
Normal file
28
src/piracyshield_service/log/ticket/errors.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
|
||||||
|
class LogTicketErrorCode:
|
||||||
|
|
||||||
|
GENERIC = '5101'
|
||||||
|
|
||||||
|
MISSING_TICKET_ID = '5102'
|
||||||
|
|
||||||
|
NON_VALID_TICKET_ID = '5103'
|
||||||
|
|
||||||
|
MISSING_MESSAGE = '5104'
|
||||||
|
|
||||||
|
NON_VALID_MESSAGE = '5105'
|
||||||
|
|
||||||
|
CANNOT_REMOVE = '5106'
|
||||||
|
|
||||||
|
class LogTicketErrorMessage:
|
||||||
|
|
||||||
|
GENERIC = 'Generic error.'
|
||||||
|
|
||||||
|
MISSING_TICKET_ID = 'Missing ticket identifier.'
|
||||||
|
|
||||||
|
NON_VALID_TICKET_ID = 'Non valid ticket identifier.'
|
||||||
|
|
||||||
|
MISSING_MESSAGE = 'Missing message.'
|
||||||
|
|
||||||
|
NON_VALID_MESSAGE = 'Non valid message.'
|
||||||
|
|
||||||
|
CANNOT_REMOVE = 'The items could not be removed. Ensure you have proper permissions to perform this operation.'
|
56
src/piracyshield_service/log/ticket/get_all.py
Normal file
56
src/piracyshield_service/log/ticket/get_all.py
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.log.ticket.storage import LogTicketStorage, LogTicketStorageGetException
|
||||||
|
|
||||||
|
from piracyshield_service.log.ticket.errors import LogTicketErrorCode, LogTicketErrorMessage
|
||||||
|
|
||||||
|
class LogTicketGetAllService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Gets all the logs of a ticket.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, ticket_id: str) -> list | Exception:
|
||||||
|
try:
|
||||||
|
response = self.data_storage.get_all(
|
||||||
|
ticket_id = ticket_id
|
||||||
|
)
|
||||||
|
|
||||||
|
batch = response.batch()
|
||||||
|
|
||||||
|
if not len(batch):
|
||||||
|
self.logger.debug(f'Could not find any log for ticket `{ticket_id}`')
|
||||||
|
|
||||||
|
return list(batch)
|
||||||
|
|
||||||
|
except LogTicketStorageGetException as e:
|
||||||
|
self.logger.error(f'Could not get all the logs for ticket `{ticket_id}`')
|
||||||
|
|
||||||
|
raise ApplicationException(LogTicketErrorCode.GENERIC, LogTicketErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
self.data_storage = LogTicketStorage()
|
1
src/piracyshield_service/log/ticket/item/__init__.py
Normal file
1
src/piracyshield_service/log/ticket/item/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
100
src/piracyshield_service/log/ticket/item/create.py
Normal file
100
src/piracyshield_service/log/ticket/item/create.py
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.utils.time import Time
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.log.ticket.item.storage import LogTicketItemStorage, LogTicketItemStorageCreateException
|
||||||
|
|
||||||
|
from piracyshield_data_model.log.ticket.item.model import (
|
||||||
|
LogTicketItemModel,
|
||||||
|
LogTicketItemModelTicketItemIdentifierMissingException,
|
||||||
|
LogTicketItemModelTicketItemIdentifierNonValidException,
|
||||||
|
LogTicketItemModelMessageNonValidException,
|
||||||
|
LogTicketItemModelMessageMissingException
|
||||||
|
)
|
||||||
|
|
||||||
|
from piracyshield_service.log.ticket.item.errors import LogTicketItemErrorCode, LogTicketItemErrorMessage
|
||||||
|
|
||||||
|
class LogTicketItemCreateService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Creates a new ticket item log record.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
data_model = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, ticket_item_id: str, message: str) -> bool | Exception:
|
||||||
|
model = self._validate_parameters(
|
||||||
|
ticket_item_id = ticket_item_id,
|
||||||
|
message = message
|
||||||
|
)
|
||||||
|
|
||||||
|
document = self._build_document(
|
||||||
|
model = model,
|
||||||
|
now = Time.now_iso8601()
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.data_storage.insert(document)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
except LogTicketItemStorageCreateException as e:
|
||||||
|
self.logger.error(f'Could not create the ticket item log record')
|
||||||
|
|
||||||
|
raise ApplicationException(LogTicketItemErrorCode.GENERIC, LogTicketItemErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _build_document(self, model: dict, now: str) -> dict:
|
||||||
|
return {
|
||||||
|
'ticket_item_id': model.get('ticket_item_id'),
|
||||||
|
'message': model.get('message'),
|
||||||
|
'metadata': {
|
||||||
|
# creation date
|
||||||
|
'created_at': now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self, ticket_item_id: str, message: str):
|
||||||
|
try:
|
||||||
|
model = self.data_model(
|
||||||
|
ticket_item_id = ticket_item_id,
|
||||||
|
message = message
|
||||||
|
)
|
||||||
|
|
||||||
|
return model.to_dict()
|
||||||
|
|
||||||
|
except LogTicketItemModelTicketItemIdentifierMissingException:
|
||||||
|
raise ApplicationException(LogTicketItemErrorCode.MISSING_TICKET_ITEM_ID, LogTicketItemErrorMessage.MISSING_TICKET_ITEM_ID)
|
||||||
|
|
||||||
|
except LogTicketItemModelTicketItemIdentifierNonValidException:
|
||||||
|
raise ApplicationException(LogTicketItemErrorCode.NON_VALID_TICKET_ITEM_ID, LogTicketItemErrorMessage.NON_VALID_TICKET_ITEM_ID)
|
||||||
|
|
||||||
|
except LogTicketItemModelMessageMissingException:
|
||||||
|
raise ApplicationException(LogTicketItemErrorCode.MISSING_MESSAGE, LogTicketItemErrorMessage.MISSING_MESSAGE)
|
||||||
|
|
||||||
|
except LogTicketItemModelMessageNonValidException:
|
||||||
|
raise ApplicationException(LogTicketItemErrorCode.NON_VALID_MESSAGE, LogTicketItemErrorMessage.NON_VALID_MESSAGE)
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
self.data_model = LogTicketItemModel
|
||||||
|
|
||||||
|
self.data_storage = LogTicketItemStorage()
|
28
src/piracyshield_service/log/ticket/item/errors.py
Normal file
28
src/piracyshield_service/log/ticket/item/errors.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
|
||||||
|
class LogTicketItemErrorCode:
|
||||||
|
|
||||||
|
GENERIC = '5201'
|
||||||
|
|
||||||
|
MISSING_TICKET_ID = '5202'
|
||||||
|
|
||||||
|
NON_VALID_TICKET_ID = '5203'
|
||||||
|
|
||||||
|
MISSING_MESSAGE = '5204'
|
||||||
|
|
||||||
|
NON_VALID_MESSAGE = '5205'
|
||||||
|
|
||||||
|
CANNOT_REMOVE = '5206'
|
||||||
|
|
||||||
|
class LogTicketItemErrorMessage:
|
||||||
|
|
||||||
|
GENERIC = 'Generic error.'
|
||||||
|
|
||||||
|
MISSING_TICKET_ITEM_ID = 'Missing ticket item identifier.'
|
||||||
|
|
||||||
|
NON_VALID_TICKET_ITEM_ID = 'Non valid ticket item identifier.'
|
||||||
|
|
||||||
|
MISSING_MESSAGE = 'Missing message.'
|
||||||
|
|
||||||
|
NON_VALID_MESSAGE = 'Non valid message.'
|
||||||
|
|
||||||
|
CANNOT_REMOVE = 'The items could not be removed. Ensure you have proper permissions to perform this operation.'
|
56
src/piracyshield_service/log/ticket/item/get_all.py
Normal file
56
src/piracyshield_service/log/ticket/item/get_all.py
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.log.ticket.item.storage import LogTicketItemStorage, LogTicketItemStorageGetException
|
||||||
|
|
||||||
|
from piracyshield_service.log.ticket.item.errors import LogTicketItemErrorCode, LogTicketItemErrorMessage
|
||||||
|
|
||||||
|
class LogTicketItemGetAllService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Gets all the logs of a ticket item.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, ticket_item_id: str) -> list | Exception:
|
||||||
|
try:
|
||||||
|
response = self.data_storage.get_all(
|
||||||
|
ticket_item_id = ticket_item_id
|
||||||
|
)
|
||||||
|
|
||||||
|
batch = response.batch()
|
||||||
|
|
||||||
|
if not len(batch):
|
||||||
|
self.logger.debug(f'Could not find any log for ticket item `{ticket_item_id}`')
|
||||||
|
|
||||||
|
return list(batch)
|
||||||
|
|
||||||
|
except LogTicketItemStorageGetException as e:
|
||||||
|
self.logger.error(f'Could not get all the logs for ticket item `{ticket_item_id}`')
|
||||||
|
|
||||||
|
raise ApplicationException(LogTicketItemErrorCode.GENERIC, LogTicketItemErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
self.data_storage = LogTicketItemStorage()
|
54
src/piracyshield_service/log/ticket/item/remove_all.py
Normal file
54
src/piracyshield_service/log/ticket/item/remove_all.py
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.log.ticket.item.storage import LogTicketItemStorage, LogTicketItemStorageRemoveException
|
||||||
|
|
||||||
|
from piracyshield_service.log.ticket.item.errors import LogTicketItemErrorCode, LogTicketItemErrorMessage
|
||||||
|
|
||||||
|
class LogTicketItemRemoveAllService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Removes all the logs of a ticket item.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, ticket_item_id: str) -> bool | Exception:
|
||||||
|
try:
|
||||||
|
affected_rows = self.data_storage.remove_all(
|
||||||
|
ticket_item_id = ticket_item_id
|
||||||
|
)
|
||||||
|
|
||||||
|
if not affected_rows:
|
||||||
|
raise ApplicationException(LogTicketItemErrorCode.CANNOT_REMOVE, LogTicketItemErrorMessage.CANNOT_REMOVE)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
except LogTicketItemStorageRemoveException as e:
|
||||||
|
self.logger.error(f'Could not remove all the logs for ticket item `{ticket_item_id}`')
|
||||||
|
|
||||||
|
raise ApplicationException(LogTicketItemErrorCode.GENERIC, LogTicketItemErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
self.data_storage = LogTicketItemStorage()
|
54
src/piracyshield_service/log/ticket/remove_all.py
Normal file
54
src/piracyshield_service/log/ticket/remove_all.py
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.log.ticket.storage import LogTicketStorage, LogTicketStorageRemoveException
|
||||||
|
|
||||||
|
from piracyshield_service.log.ticket.errors import LogTicketErrorCode, LogTicketErrorMessage
|
||||||
|
|
||||||
|
class LogTicketRemoveAllService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Removes all the logs of a ticket.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, ticket_id: str) -> bool | Exception:
|
||||||
|
try:
|
||||||
|
affected_rows = self.data_storage.remove_all(
|
||||||
|
ticket_id = ticket_id
|
||||||
|
)
|
||||||
|
|
||||||
|
if not affected_rows:
|
||||||
|
raise ApplicationException(LogTicketErrorCode.CANNOT_REMOVE, LogTicketErrorMessage.CANNOT_REMOVE)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
except LogTicketStorageRemoveException as e:
|
||||||
|
self.logger.error(f'Could not remove all the logs for ticket `{ticket_id}`')
|
||||||
|
|
||||||
|
raise ApplicationException(LogTicketErrorCode.GENERIC, LogTicketErrorMessage.GENERIC, e)
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
self.data_storage = LogTicketStorage()
|
1
src/piracyshield_service/permission/__init__.py
Normal file
1
src/piracyshield_service/permission/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
76
src/piracyshield_service/permission/errors.py
Normal file
76
src/piracyshield_service/permission/errors.py
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
|
||||||
|
class PermissionErrorCode:
|
||||||
|
|
||||||
|
GENERIC = '1100'
|
||||||
|
|
||||||
|
ACCOUNT_CREATE_ERROR = '1101'
|
||||||
|
|
||||||
|
ACCOUNT_VIEW_ERROR = '1102'
|
||||||
|
|
||||||
|
ACCOUNT_EDIT_ERROR = '1103'
|
||||||
|
|
||||||
|
ACCOUNT_DELETE_ERROR = '1104'
|
||||||
|
|
||||||
|
TICKET_CREATE_ERROR = '1105'
|
||||||
|
|
||||||
|
TICKET_VIEW_ERROR = '1106'
|
||||||
|
|
||||||
|
TICKET_EDIT_ERROR = '1107'
|
||||||
|
|
||||||
|
TICKET_DELETE_ERROR = '1108'
|
||||||
|
|
||||||
|
TICKET_UPLOAD_ERROR = '1109'
|
||||||
|
|
||||||
|
WHITELIST_ITEM_CREATE_ERROR = '1110'
|
||||||
|
|
||||||
|
WHITELIST_ITEM_VIEW_ERROR = '1111'
|
||||||
|
|
||||||
|
WHITELIST_ITEM_EDIT_ERROR = '1112'
|
||||||
|
|
||||||
|
WHITELIST_ITEM_DELETE_ERROR = '1113'
|
||||||
|
|
||||||
|
DDA_CREATE_ERROR = '1114'
|
||||||
|
|
||||||
|
DDA_VIEW_ERROR = '1115'
|
||||||
|
|
||||||
|
DDA_EDIT_ERROR = '1116'
|
||||||
|
|
||||||
|
DDA_DELETE_ERROR = '1117'
|
||||||
|
|
||||||
|
class PermissionErrorMessage:
|
||||||
|
|
||||||
|
GENERIC = 'Cannot validate the account permissions.'
|
||||||
|
|
||||||
|
ACCOUNT_CREATE_ERROR = 'Not enough permissions to create an account.'
|
||||||
|
|
||||||
|
ACCOUNT_VIEW_ERROR = 'Not enough permissions to view an account.'
|
||||||
|
|
||||||
|
ACCOUNT_EDIT_ERROR = 'Not enough permissions to edit an account.'
|
||||||
|
|
||||||
|
ACCOUNT_DELETE_ERROR = 'Not enough permissions to delete an account.'
|
||||||
|
|
||||||
|
TICKET_CREATE_ERROR = 'Not enough permissions to create a ticket.'
|
||||||
|
|
||||||
|
TICKET_VIEW_ERROR = 'Not enough permissions to view a ticket.'
|
||||||
|
|
||||||
|
TICKET_EDIT_ERROR = 'Not enough permissions to edit a ticket.'
|
||||||
|
|
||||||
|
TICKET_DELETE_ERROR = 'Not enough permissions to delete a ticket.'
|
||||||
|
|
||||||
|
TICKET_UPLOAD_ERROR = 'Not enough permissions to upload add an archive to ticket.'
|
||||||
|
|
||||||
|
WHITELIST_ITEM_CREATE_ERROR = 'Not enough permissions to create a whitelist item.'
|
||||||
|
|
||||||
|
WHITELIST_ITEM_VIEW_ERROR = 'Not enough permissions to view a whitelist item.'
|
||||||
|
|
||||||
|
WHITELIST_ITEM_EDIT_ERROR = 'Not enough permissions to edit a whitelist item.'
|
||||||
|
|
||||||
|
WHITELIST_ITEM_DELETE_ERROR = 'Not enough permissions to delete a whitelist item.'
|
||||||
|
|
||||||
|
DDA_CREATE_ERROR = 'Not enough permissions to create a DDA identifier.'
|
||||||
|
|
||||||
|
DDA_VIEW_ERROR = 'Not enough permissions to view a DDA identifier.'
|
||||||
|
|
||||||
|
DDA_EDIT_ERROR = 'Not enough permissions to edit a DDA identifier.'
|
||||||
|
|
||||||
|
DDA_DELETE_ERROR = 'Not enough permissions to delete a DDA identifier.'
|
54
src/piracyshield_service/permission/schema.py
Normal file
54
src/piracyshield_service/permission/schema.py
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
from piracyshield_data_model.account.role.model import AccountRoleModel
|
||||||
|
from piracyshield_data_model.permission.model import PermissionModel
|
||||||
|
|
||||||
|
class PermissionSchema:
|
||||||
|
|
||||||
|
SCHEMA = {
|
||||||
|
AccountRoleModel.GUEST: {
|
||||||
|
PermissionModel.VIEW_TICKET
|
||||||
|
},
|
||||||
|
|
||||||
|
AccountRoleModel.INTERNAL: {
|
||||||
|
PermissionModel.CREATE_TICKET,
|
||||||
|
PermissionModel.VIEW_TICKET,
|
||||||
|
PermissionModel.EDIT_TICKET,
|
||||||
|
PermissionModel.DELETE_TICKET,
|
||||||
|
PermissionModel.UPLOAD_TICKET,
|
||||||
|
PermissionModel.CREATE_ACCOUNT,
|
||||||
|
PermissionModel.VIEW_ACCOUNT,
|
||||||
|
PermissionModel.EDIT_ACCOUNT,
|
||||||
|
PermissionModel.DELETE_ACCOUNT,
|
||||||
|
PermissionModel.CREATE_WHITELIST_ITEM,
|
||||||
|
PermissionModel.VIEW_WHITELIST_ITEM,
|
||||||
|
PermissionModel.EDIT_WHITELIST_ITEM,
|
||||||
|
PermissionModel.DELETE_WHITELIST_ITEM,
|
||||||
|
PermissionModel.CREATE_DDA,
|
||||||
|
PermissionModel.VIEW_DDA,
|
||||||
|
PermissionModel.EDIT_DDA,
|
||||||
|
PermissionModel.DELETE_DDA
|
||||||
|
},
|
||||||
|
|
||||||
|
AccountRoleModel.REPORTER: {
|
||||||
|
PermissionModel.CREATE_TICKET,
|
||||||
|
PermissionModel.VIEW_TICKET,
|
||||||
|
PermissionModel.UPLOAD_TICKET,
|
||||||
|
PermissionModel.DELETE_TICKET,
|
||||||
|
PermissionModel.CREATE_WHITELIST_ITEM,
|
||||||
|
PermissionModel.VIEW_WHITELIST_ITEM,
|
||||||
|
PermissionModel.EDIT_WHITELIST_ITEM,
|
||||||
|
PermissionModel.DELETE_WHITELIST_ITEM,
|
||||||
|
# this is required as we need the list of reporter accounts to be assigned to the ticket
|
||||||
|
# to allow a future selection of the reporters instead of using all of them.
|
||||||
|
PermissionModel.VIEW_ACCOUNT,
|
||||||
|
PermissionModel.VIEW_DDA
|
||||||
|
},
|
||||||
|
|
||||||
|
AccountRoleModel.PROVIDER: {
|
||||||
|
PermissionModel.VIEW_TICKET,
|
||||||
|
PermissionModel.EDIT_TICKET,
|
||||||
|
PermissionModel.CREATE_WHITELIST_ITEM,
|
||||||
|
PermissionModel.VIEW_WHITELIST_ITEM,
|
||||||
|
PermissionModel.EDIT_WHITELIST_ITEM,
|
||||||
|
PermissionModel.DELETE_WHITELIST_ITEM
|
||||||
|
}
|
||||||
|
}
|
168
src/piracyshield_service/permission/service.py
Normal file
168
src/piracyshield_service/permission/service.py
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_service.permission.schema import PermissionSchema
|
||||||
|
|
||||||
|
from piracyshield_data_model.account.role.model import AccountRoleModel
|
||||||
|
from piracyshield_data_model.permission.model import PermissionModel
|
||||||
|
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_service.permission.errors import PermissionErrorCode, PermissionErrorMessage
|
||||||
|
|
||||||
|
class PermissionService(BaseService):
|
||||||
|
|
||||||
|
permission_schema = None
|
||||||
|
|
||||||
|
role_model = None
|
||||||
|
|
||||||
|
role = None
|
||||||
|
|
||||||
|
def __init__(self, role: int):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.role = role
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def can_create_account(self) -> bool | Exception:
|
||||||
|
if self.has_permission(PermissionModel.CREATE_ACCOUNT) == False:
|
||||||
|
raise ApplicationException(PermissionErrorCode.ACCOUNT_CREATE_ERROR, PermissionErrorMessage.ACCOUNT_CREATE_ERROR)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def can_view_account(self) -> bool | Exception:
|
||||||
|
if self.has_permission(PermissionModel.VIEW_ACCOUNT) == False:
|
||||||
|
raise ApplicationException(PermissionErrorCode.ACCOUNT_VIEW_ERROR, PermissionErrorMessage.ACCOUNT_VIEW_ERROR)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def can_edit_account(self) -> bool | Exception:
|
||||||
|
if self.has_permission(PermissionModel.EDIT_ACCOUNT) == False:
|
||||||
|
raise ApplicationException(PermissionErrorCode.ACCOUNT_EDIT_ERROR, PermissionErrorMessage.ACCOUNT_EDIT_ERROR)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def can_delete_account(self) -> bool | Exception:
|
||||||
|
if self.has_permission(PermissionModel.DELETE_ACCOUNT) == False:
|
||||||
|
raise ApplicationException(PermissionErrorCode.ACCOUNT_DELETE_ERROR, PermissionErrorMessage.ACCOUNT_DELETE_ERROR)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def can_create_ticket(self) -> bool | Exception:
|
||||||
|
if self.has_permission(PermissionModel.CREATE_TICKET) == False:
|
||||||
|
raise ApplicationException(PermissionErrorCode.TICKET_CREATE_ERROR, PermissionErrorMessage.TICKET_CREATE_ERROR)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def can_view_ticket(self) -> bool | Exception:
|
||||||
|
if self.has_permission(PermissionModel.VIEW_TICKET) == False:
|
||||||
|
raise ApplicationException(PermissionErrorCode.TICKET_VIEW_ERROR, PermissionErrorMessage.TICKET_VIEW_ERROR)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def can_edit_ticket(self) -> bool | Exception:
|
||||||
|
if self.has_permission(PermissionModel.EDIT_TICKET) == False:
|
||||||
|
raise ApplicationException(PermissionErrorCode.TICKET_EDIT_ERROR, PermissionErrorMessage.TICKET_EDIT_ERROR)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def can_delete_ticket(self) -> bool | Exception:
|
||||||
|
if self.has_permission(PermissionModel.DELETE_TICKET) == False:
|
||||||
|
raise ApplicationException(PermissionErrorCode.TICKET_DELETE_ERROR, PermissionErrorMessage.TICKET_DELETE_ERROR)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def can_upload_ticket(self) -> bool | Exception:
|
||||||
|
if self.has_permission(PermissionModel.UPLOAD_TICKET) == False:
|
||||||
|
raise ApplicationException(PermissionErrorCode.TICKET_UPLOAD_ERROR, PermissionErrorMessage.TICKET_UPLOAD_ERROR)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def can_create_whitelist_item(self) -> bool | Exception:
|
||||||
|
if self.has_permission(PermissionModel.CREATE_WHITELIST_ITEM) == False:
|
||||||
|
raise ApplicationException(PermissionErrorCode.WHITELIST_ITEM_CREATE_ERROR, PermissionErrorMessage.WHITELIST_ITEM_CREATE_ERROR)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def can_view_whitelist_item(self) -> bool | Exception:
|
||||||
|
if self.has_permission(PermissionModel.VIEW_WHITELIST_ITEM) == False:
|
||||||
|
raise ApplicationException(PermissionErrorCode.WHITELIST_ITEM_VIEW_ERROR, PermissionErrorMessage.WHITELIST_ITEM_VIEW_ERROR)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def can_edit_whitelist_item(self) -> bool | Exception:
|
||||||
|
if self.has_permission(PermissionModel.EDIT_WHITELIST_ITEM) == False:
|
||||||
|
raise ApplicationException(PermissionErrorCode.WHITELIST_ITEM_EDIT_ERROR, PermissionErrorMessage.WHITELIST_ITEM_EDIT_ERROR)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def can_delete_whitelist_item(self) -> bool | Exception:
|
||||||
|
if self.has_permission(PermissionModel.DELETE_WHITELIST_ITEM) == False:
|
||||||
|
raise ApplicationException(PermissionErrorCode.WHITELIST_ITEM_DELETE_ERROR, PermissionErrorMessage.WHITELIST_ITEM_DELETE_ERROR)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def can_create_dda(self) -> bool | Exception:
|
||||||
|
if self.has_permission(PermissionModel.CREATE_DDA) == False:
|
||||||
|
raise ApplicationException(PermissionErrorCode.DDA_CREATE_ERROR, PermissionErrorMessage.DDA_CREATE_ERROR)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def can_view_dda(self) -> bool | Exception:
|
||||||
|
if self.has_permission(PermissionModel.VIEW_DDA) == False:
|
||||||
|
raise ApplicationException(PermissionErrorCode.DDA_VIEW_ERROR, PermissionErrorMessage.DDA_VIEW_ERROR)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def can_edit_dda(self) -> bool | Exception:
|
||||||
|
if self.has_permission(PermissionModel.EDIT_DDA) == False:
|
||||||
|
raise ApplicationException(PermissionErrorCode.DDA_EDIT_ERROR, PermissionErrorMessage.DDA_EDIT_ERROR)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def can_delete_dda(self) -> bool | Exception:
|
||||||
|
if self.has_permission(PermissionModel.DELETE_DDA) == False:
|
||||||
|
raise ApplicationException(PermissionErrorCode.DDA_DELETE_ERROR, PermissionErrorMessage.DDA_DELETE_ERROR)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def has_permission(self, permission: int) -> bool:
|
||||||
|
"""
|
||||||
|
Checks if the permission is available based on the provided role.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return permission in self.get_permissions(self.role_model)
|
||||||
|
|
||||||
|
def get_permissions(self, role_model: int) -> set:
|
||||||
|
"""
|
||||||
|
Returns the set of permissions associated with the given role model.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if role_model in self.permission_schema.SCHEMA:
|
||||||
|
return self.permission_schema.SCHEMA[role_model]
|
||||||
|
|
||||||
|
else:
|
||||||
|
return set()
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_modules(self):
|
||||||
|
self.role_model = AccountRoleModel(self.role)
|
||||||
|
|
||||||
|
self.permission_schema = PermissionSchema()
|
1
src/piracyshield_service/provider/__init__.py
Normal file
1
src/piracyshield_service/provider/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
18
src/piracyshield_service/provider/change_password.py
Normal file
18
src/piracyshield_service/provider/change_password.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.account.change_password import AccountChangePasswordService
|
||||||
|
|
||||||
|
from piracyshield_data_storage.provider.storage import ProviderStorage
|
||||||
|
|
||||||
|
class ProviderChangePasswordService(AccountChangePasswordService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Changes password of a provider account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Pass the data storage to the parent class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(ProviderStorage)
|
33
src/piracyshield_service/provider/create.py
Normal file
33
src/piracyshield_service/provider/create.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.account.create import AccountCreateService
|
||||||
|
|
||||||
|
from piracyshield_data_model.provider.model import ProviderModel
|
||||||
|
|
||||||
|
from piracyshield_data_storage.provider.storage import ProviderStorage
|
||||||
|
|
||||||
|
class ProviderCreateService(AccountCreateService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Creates a provider account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Pass the data model and data storage to the parent class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(ProviderModel, ProviderStorage)
|
||||||
|
|
||||||
|
def execute(self, name: str, email: str, password: str, confirm_password: str, flags: dict, created_by: str) -> str | Exception:
|
||||||
|
"""
|
||||||
|
:param name: a string that identificates the real name (and, eventually, sourname) of the account.
|
||||||
|
:param email: e-mail address, used in conjunction with a password to authenticate the account.
|
||||||
|
:param password: a string.
|
||||||
|
:param confirm_password: must be the same as `password`.
|
||||||
|
:param flags: flags of the account.
|
||||||
|
:param created_by: account id of the creator.
|
||||||
|
:return account id of the created account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return super().execute(name, email, password, confirm_password, flags, created_by)
|
18
src/piracyshield_service/provider/exists_by_identifier.py
Normal file
18
src/piracyshield_service/provider/exists_by_identifier.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.account.exists_by_identifier import AccountExistsByIdentifierService
|
||||||
|
|
||||||
|
from piracyshield_data_storage.provider.storage import ProviderStorage
|
||||||
|
|
||||||
|
class ProviderExistsByIdentifierService(AccountExistsByIdentifierService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Checks if an account with this identifier exists.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Pass the data storage to the parent class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(ProviderStorage)
|
18
src/piracyshield_service/provider/get.py
Normal file
18
src/piracyshield_service/provider/get.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.account.get import AccountGetService
|
||||||
|
|
||||||
|
from piracyshield_data_storage.provider.storage import ProviderStorage
|
||||||
|
|
||||||
|
class ProviderGetService(AccountGetService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Retrieves a provider account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Pass the data storage to the parent class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(ProviderStorage)
|
18
src/piracyshield_service/provider/get_all.py
Normal file
18
src/piracyshield_service/provider/get_all.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.account.get_all import AccountGetAllService
|
||||||
|
|
||||||
|
from piracyshield_data_storage.provider.storage import ProviderStorage
|
||||||
|
|
||||||
|
class ProviderGetAllService(AccountGetAllService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Retrieves all the provider accounts.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Pass the data storage to the parent class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(ProviderStorage)
|
18
src/piracyshield_service/provider/remove.py
Normal file
18
src/piracyshield_service/provider/remove.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.account.remove import AccountRemoveService
|
||||||
|
|
||||||
|
from piracyshield_data_storage.provider.storage import ProviderStorage
|
||||||
|
|
||||||
|
class ProviderRemoveService(AccountRemoveService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Removes a provider account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Pass the data storage to the parent class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(ProviderStorage)
|
18
src/piracyshield_service/provider/set_status.py
Normal file
18
src/piracyshield_service/provider/set_status.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.account.set_status import AccountSetStatusService
|
||||||
|
|
||||||
|
from piracyshield_data_storage.provider.storage import ProviderStorage
|
||||||
|
|
||||||
|
class ProviderSetStatusService(AccountSetStatusService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Sets the status of a provider account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Pass the data storage to the parent class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(ProviderStorage)
|
1
src/piracyshield_service/reporter/__init__.py
Normal file
1
src/piracyshield_service/reporter/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
18
src/piracyshield_service/reporter/change_password.py
Normal file
18
src/piracyshield_service/reporter/change_password.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.account.change_password import AccountChangePasswordService
|
||||||
|
|
||||||
|
from piracyshield_data_storage.reporter.storage import ReporterStorage
|
||||||
|
|
||||||
|
class ReporterChangePasswordService(AccountChangePasswordService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Changes password of a reporter account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Pass the data storage to the parent class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(ReporterStorage)
|
33
src/piracyshield_service/reporter/create.py
Normal file
33
src/piracyshield_service/reporter/create.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.account.create import AccountCreateService
|
||||||
|
|
||||||
|
from piracyshield_data_model.reporter.model import ReporterModel
|
||||||
|
|
||||||
|
from piracyshield_data_storage.reporter.storage import ReporterStorage
|
||||||
|
|
||||||
|
class ReporterCreateService(AccountCreateService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Creates a reporter account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Pass the data model and data storage to the parent class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(ReporterModel, ReporterStorage)
|
||||||
|
|
||||||
|
def execute(self, name: str, email: str, password: str, confirm_password: str, flags: dict, created_by: str) -> str | Exception:
|
||||||
|
"""
|
||||||
|
:param name: a string that identificates the real name (and, eventually, sourname) of the account.
|
||||||
|
:param email: e-mail address, used in conjunction with a password to authenticate the account.
|
||||||
|
:param password: a string.
|
||||||
|
:param confirm_password: must be the same as `password`.
|
||||||
|
:param flags: flags of the account.
|
||||||
|
:param created_by: account id of the creator.
|
||||||
|
:return account id of the created account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return super().execute(name, email, password, confirm_password, flags, created_by)
|
18
src/piracyshield_service/reporter/get.py
Normal file
18
src/piracyshield_service/reporter/get.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.account.get import AccountGetService
|
||||||
|
|
||||||
|
from piracyshield_data_storage.reporter.storage import ReporterStorage
|
||||||
|
|
||||||
|
class ReporterGetService(AccountGetService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Retrieves a reporter account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Pass the data storage to the parent class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(ReporterStorage)
|
18
src/piracyshield_service/reporter/get_all.py
Normal file
18
src/piracyshield_service/reporter/get_all.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.account.get_all import AccountGetAllService
|
||||||
|
|
||||||
|
from piracyshield_data_storage.reporter.storage import ReporterStorage
|
||||||
|
|
||||||
|
class ReporterGetAllService(AccountGetAllService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Retrieves all the reporter accounts.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Pass the data storage to the parent class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(ReporterStorage)
|
18
src/piracyshield_service/reporter/remove.py
Normal file
18
src/piracyshield_service/reporter/remove.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.account.remove import AccountRemoveService
|
||||||
|
|
||||||
|
from piracyshield_data_storage.reporter.storage import ReporterStorage
|
||||||
|
|
||||||
|
class ReporterRemoveService(AccountRemoveService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Removes a reporter account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Pass the data storage to the parent class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(ReporterStorage)
|
18
src/piracyshield_service/reporter/set_status.py
Normal file
18
src/piracyshield_service/reporter/set_status.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.account.set_status import AccountSetStatusService
|
||||||
|
|
||||||
|
from piracyshield_data_storage.reporter.storage import ReporterStorage
|
||||||
|
|
||||||
|
class ReporterSetStatusService(AccountSetStatusService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Sets the status of a reporter account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Pass the data storage to the parent class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(ReporterStorage)
|
1
src/piracyshield_service/security/__init__.py
Normal file
1
src/piracyshield_service/security/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
94
src/piracyshield_service/security/anti_brute_force.py
Normal file
94
src/piracyshield_service/security/anti_brute_force.py
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.config import Config
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.security.anti_brute_force.memory import SecurityAntiBruteForceMemory, SecurityAntiBruteForceMemorySetException, SecurityAntiBruteForceMemoryGetException
|
||||||
|
|
||||||
|
from piracyshield_service.security.blacklist.add_ip_address import SecurityBlacklistAddIPAddressService
|
||||||
|
|
||||||
|
from piracyshield_service.security.errors import SecurityErrorCode, SecurityErrorMessage
|
||||||
|
|
||||||
|
class SecurityAntiBruteForceService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Protection against authentication flooding.
|
||||||
|
"""
|
||||||
|
|
||||||
|
security_blacklist_add_ip_address_service = None
|
||||||
|
|
||||||
|
anti_brute_force_config = None
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_configs()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, email: str, ip_address: str) -> bool | Exception:
|
||||||
|
login_attempts = self.data_memory.get_login_attempts(email = email)
|
||||||
|
|
||||||
|
# we don't have any record, let's start monitoring
|
||||||
|
if not isinstance(login_attempts, int) or login_attempts == 0:
|
||||||
|
self.data_memory.set_login_attempts(
|
||||||
|
email = email,
|
||||||
|
timeframe = self.anti_brute_force_config.get('general').get('timeframe')
|
||||||
|
)
|
||||||
|
|
||||||
|
# we already have a count ongoing
|
||||||
|
else:
|
||||||
|
# the account has exceeded the maximum authentication attempts
|
||||||
|
if login_attempts > self.anti_brute_force_config.get('general').get('max_attempts'):
|
||||||
|
# remove any data as we're going to blacklist the IP
|
||||||
|
self.data_memory.reset_login_attempts(email)
|
||||||
|
|
||||||
|
self.logger.warning(f"Authentication limit reached by `{email}`")
|
||||||
|
|
||||||
|
# create a blacklisting the IP address
|
||||||
|
self.security_blacklist_add_ip_address_service.execute(
|
||||||
|
ip_address = ip_address,
|
||||||
|
duration = self.anti_brute_force_config.get('general').get('blacklist_duration')
|
||||||
|
)
|
||||||
|
|
||||||
|
raise ApplicationException(SecurityErrorCode.MAX_LOGIN_ATTEMPTS, SecurityErrorMessage.MAX_LOGIN_ATTEMPTS.format(self.anti_brute_force_config.get('general').get('blacklist_duration')))
|
||||||
|
|
||||||
|
# increase the requests count until the maximum attempts limit is triggered
|
||||||
|
else:
|
||||||
|
self.data_memory.increment_login_attempts(
|
||||||
|
email = email
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self) -> None:
|
||||||
|
"""
|
||||||
|
Loads the configs.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.anti_brute_force_config = Config('security/anti_brute_force')
|
||||||
|
|
||||||
|
def _prepare_modules(self) -> None:
|
||||||
|
"""
|
||||||
|
Initialize and set the instances.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.data_memory = SecurityAntiBruteForceMemory(
|
||||||
|
database = self.anti_brute_force_config.get('database').get('memory_database')
|
||||||
|
)
|
||||||
|
|
||||||
|
self.security_blacklist_add_ip_address_service = SecurityBlacklistAddIPAddressService()
|
1
src/piracyshield_service/security/blacklist/__init__.py
Normal file
1
src/piracyshield_service/security/blacklist/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from piracyshield_service.base import BaseService
|
||||||
|
|
||||||
|
from piracyshield_component.config import Config
|
||||||
|
from piracyshield_component.exception import ApplicationException
|
||||||
|
|
||||||
|
from piracyshield_data_storage.security.blacklist.memory import SecurityBlacklistMemory, SecurityBlacklistMemorySetException, SecurityBlacklistMemoryGetException
|
||||||
|
|
||||||
|
from piracyshield_service.security.errors import SecurityErrorCode, SecurityErrorMessage
|
||||||
|
|
||||||
|
class SecurityBlacklistAddIPAddressService(BaseService):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Blacklists an IP address.
|
||||||
|
"""
|
||||||
|
|
||||||
|
blacklist_config = None
|
||||||
|
|
||||||
|
data_storage = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inizialize logger and required modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._prepare_configs()
|
||||||
|
|
||||||
|
self._prepare_modules()
|
||||||
|
|
||||||
|
def execute(self, ip_address: str, duration: int) -> bool | Exception:
|
||||||
|
response = self.data_memory.add_ip_address(
|
||||||
|
ip_address = ip_address,
|
||||||
|
duration = duration
|
||||||
|
)
|
||||||
|
|
||||||
|
if response == True:
|
||||||
|
self.logger.warning(f"Blacklist item `{ip_address}` has been added for {duration} seconds")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
def _schedule_task(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _validate_parameters(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _prepare_configs(self) -> None:
|
||||||
|
"""
|
||||||
|
Loads the configs.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.blacklist_config = Config('security/blacklist')
|
||||||
|
|
||||||
|
def _prepare_modules(self) -> None:
|
||||||
|
"""
|
||||||
|
Initialize and set the instances.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.data_memory = SecurityBlacklistMemory(
|
||||||
|
database = self.blacklist_config.get('database').get('memory_database')
|
||||||
|
)
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue