mirror of
https://github.com/fuckpiracyshield/service.git
synced 2024-11-21 04:39:47 +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