Initial commit.

This commit is contained in:
Daniele Maglie 2024-01-19 15:32:27 +01:00
commit b43282e1a7
461 changed files with 11679 additions and 0 deletions

6
.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
__pycache__/
build/
eggs/
.eggs/
*.egg
*.egg-info/

3
README.md Normal file
View file

@ -0,0 +1,3 @@
### Service
Main logic of the application.

12
pyproject.toml Normal file
View 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
View 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

View file

@ -0,0 +1 @@

View file

@ -0,0 +1 @@

View 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')
)

View 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()

View 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.'

View 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

View file

@ -0,0 +1 @@

View 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()

View 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()

View 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

View 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

View 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()

View 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

View 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

View 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

View file

@ -0,0 +1 @@

View 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()

View 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'

View 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()

View file

@ -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')
)

View file

@ -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')
)

View 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()

View file

@ -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')
)

View file

@ -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')
)

View 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

View file

@ -0,0 +1 @@

View 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()

View 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.'

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

View file

@ -0,0 +1 @@

View 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()

View 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()

View 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.'

View 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()

View 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.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()

View 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

View 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

View 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()

View file

@ -0,0 +1 @@

View file

@ -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()

View 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()

View file

@ -0,0 +1 @@

View 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)

View 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)

View 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)

View 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)

View file

@ -0,0 +1 @@

View 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.'

View 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()

View file

@ -0,0 +1 @@

View 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)

View 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)

View 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)

View 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)

View 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)

View 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)

View file

@ -0,0 +1 @@

View file

@ -0,0 +1,8 @@
class LogErrorCode:
GENERIC = '8000'
class LogErrorMessage:
GENERIC = 'Generic error.'

View file

@ -0,0 +1 @@

View 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()

View 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.'

View 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()

View file

@ -0,0 +1 @@

View 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()

View 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.'

View 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()

View 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()

View 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()

View file

@ -0,0 +1 @@

View 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.'

View 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
}
}

View 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()

View file

@ -0,0 +1 @@

View 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)

View 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)

View 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)

View 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)

View 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)

View 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)

View 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)

View file

@ -0,0 +1 @@

View 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)

View 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)

View 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)

View 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)

View 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)

View 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)

View file

@ -0,0 +1 @@

View 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()

View file

@ -0,0 +1 @@

View file

@ -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