mirror of
https://github.com/fuckpiracyshield/data-storage.git
synced 2024-11-23 05:39:47 +01:00
Initial commit.
This commit is contained in:
commit
4acfb7e91c
57 changed files with 3664 additions and 0 deletions
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
__pycache__/
|
||||||
|
build/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
*.egg
|
||||||
|
*.egg-info/
|
3
README.md
Normal file
3
README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
### Data Storage
|
||||||
|
|
||||||
|
Storage and filesystem management.
|
5
pyproject.toml
Normal file
5
pyproject.toml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[build-system]
|
||||||
|
requires = [
|
||||||
|
"setuptools>=54",
|
||||||
|
]
|
||||||
|
build-backend = "setuptools.build_meta"
|
16
setup.cfg
Normal file
16
setup.cfg
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
[metadata]
|
||||||
|
name = piracyshield_data_storage
|
||||||
|
version = 1.0.0
|
||||||
|
description = Data Storage
|
||||||
|
|
||||||
|
[options]
|
||||||
|
package_dir=
|
||||||
|
=src
|
||||||
|
packages = find:
|
||||||
|
python_requires = >= 3.10
|
||||||
|
install_requires =
|
||||||
|
python-arango
|
||||||
|
redis
|
||||||
|
|
||||||
|
[options.packages.find]
|
||||||
|
where = src
|
1
src/piracyshield_data_storage/__init__.py
Normal file
1
src/piracyshield_data_storage/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
1
src/piracyshield_data_storage/account/__init__.py
Normal file
1
src/piracyshield_data_storage/account/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
|
86
src/piracyshield_data_storage/account/general/storage.py
Normal file
86
src/piracyshield_data_storage/account/general/storage.py
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
from piracyshield_data_storage.database.arangodb.document import DatabaseArangodbDocument
|
||||||
|
|
||||||
|
from arango.cursor import Cursor
|
||||||
|
|
||||||
|
class GeneralAccountStorage(DatabaseArangodbDocument):
|
||||||
|
|
||||||
|
view_name = 'accounts_view'
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def get(self, account_id: str) -> Cursor | Exception:
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.view_name}
|
||||||
|
|
||||||
|
FILTER document.account_id == @account_id
|
||||||
|
|
||||||
|
// resolve account identifier
|
||||||
|
LET created_by_name = (
|
||||||
|
FOR a IN accounts_view
|
||||||
|
FILTER a.account_id == document.metadata.created_by
|
||||||
|
RETURN a['name']
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
RETURN {{
|
||||||
|
'account_id': document.account_id,
|
||||||
|
'name': document.name,
|
||||||
|
'email': document.email,
|
||||||
|
'role': document.role,
|
||||||
|
'is_active': document.is_active,
|
||||||
|
'metadata': {{
|
||||||
|
'created_at': document.metadata.created_at,
|
||||||
|
'updated_at': document.metadata.updated_at,
|
||||||
|
'created_by': document.metadata.created_by,
|
||||||
|
'created_by_name': created_by_name
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'account_id': account_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise GeneralAccountStorageGetException()
|
||||||
|
|
||||||
|
def get_all(self) -> Cursor | Exception:
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.view_name}
|
||||||
|
|
||||||
|
// resolve account identifier
|
||||||
|
LET created_by_name = (
|
||||||
|
FOR a IN accounts_view
|
||||||
|
FILTER a.account_id == document.metadata.created_by
|
||||||
|
RETURN a['name']
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
RETURN {{
|
||||||
|
'account_id': document.account_id,
|
||||||
|
'name': document.name,
|
||||||
|
'email': document.email,
|
||||||
|
'role': document.role,
|
||||||
|
'is_active': document.is_active,
|
||||||
|
'metadata': {{
|
||||||
|
'created_at': document.metadata.created_at,
|
||||||
|
'updated_at': document.metadata.updated_at,
|
||||||
|
'created_by': document.metadata.created_by,
|
||||||
|
'created_by_name': created_by_name
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql)
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise GeneralAccountStorageGetException()
|
||||||
|
|
||||||
|
class GeneralAccountStorageGetException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot get the account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
284
src/piracyshield_data_storage/account/storage.py
Normal file
284
src/piracyshield_data_storage/account/storage.py
Normal file
|
@ -0,0 +1,284 @@
|
||||||
|
from piracyshield_data_storage.database.arangodb.document import DatabaseArangodbDocument
|
||||||
|
|
||||||
|
from arango.cursor import Cursor
|
||||||
|
|
||||||
|
class AccountStorage(DatabaseArangodbDocument):
|
||||||
|
|
||||||
|
collection_name = None
|
||||||
|
|
||||||
|
collection_instance = None
|
||||||
|
|
||||||
|
def __init__(self, collection_name: str):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.collection_name = collection_name
|
||||||
|
|
||||||
|
self.collection_instance = self.collection(self.collection_name)
|
||||||
|
|
||||||
|
def insert(self, document: dict) -> dict | Exception:
|
||||||
|
try:
|
||||||
|
return self.collection_instance.insert(document)
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise AccountStorageCreateException()
|
||||||
|
|
||||||
|
def get(self, account_id: str) -> Cursor | Exception:
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
FILTER document.account_id == @account_id
|
||||||
|
RETURN {{
|
||||||
|
'account_id': document.account_id,
|
||||||
|
'name': document.name,
|
||||||
|
'email': document.email,
|
||||||
|
'role': document.role,
|
||||||
|
'is_active': document.is_active,
|
||||||
|
'created_by': document.created_by
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'account_id': account_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise AccountStorageGetException()
|
||||||
|
|
||||||
|
def get_complete(self, account_id: str) -> Cursor | Exception:
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.account_id == @account_id
|
||||||
|
|
||||||
|
RETURN document
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'account_id': account_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise AccountStorageGetException()
|
||||||
|
|
||||||
|
def get_all(self) -> Cursor | Exception:
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
RETURN {{
|
||||||
|
'account_id': document.account_id,
|
||||||
|
'name': document.name,
|
||||||
|
'email': document.email,
|
||||||
|
'is_active': document.is_active,
|
||||||
|
'role': document.role
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql)
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise AccountStorageGetException()
|
||||||
|
|
||||||
|
def get_total(self) -> int | Exception:
|
||||||
|
try:
|
||||||
|
return self.collection_instance.count()
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise AccountStorageGetException()
|
||||||
|
|
||||||
|
def exists_by_identifier(self, identifier: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Checks if an account with this identifier is in the collection.
|
||||||
|
|
||||||
|
:param value: a valid account identifier.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.account_id == @identifier
|
||||||
|
|
||||||
|
LIMIT 1
|
||||||
|
|
||||||
|
RETURN document
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'identifier': identifier
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise AccountStorageGetException()
|
||||||
|
|
||||||
|
def set_flag(self, account_id: str, flag: str, value: any) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Sets a flag with a status.
|
||||||
|
|
||||||
|
:param account_id: account identifier.
|
||||||
|
:param flag: the flag to update.
|
||||||
|
:param value: any value.
|
||||||
|
:return: the number of updated rows.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.account_id == @account_id
|
||||||
|
|
||||||
|
UPDATE {{
|
||||||
|
'_key': document._key,
|
||||||
|
'flags': {{
|
||||||
|
@flag: @value
|
||||||
|
}}
|
||||||
|
}} IN {self.collection_name}
|
||||||
|
|
||||||
|
RETURN NEW
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
affected_rows = self.query(
|
||||||
|
aql,
|
||||||
|
bind_vars = {
|
||||||
|
'account_id': account_id,
|
||||||
|
'flag': flag,
|
||||||
|
'value': value
|
||||||
|
},
|
||||||
|
count = True
|
||||||
|
)
|
||||||
|
|
||||||
|
return affected_rows
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise AccountStorageUpdateException()
|
||||||
|
|
||||||
|
def change_password(self, account_id: str, password: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Changes the accounts' password.
|
||||||
|
|
||||||
|
:param account_id: account identifier.
|
||||||
|
:param password: the encoded password.
|
||||||
|
:return: the number of updated rows.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.account_id == @account_id
|
||||||
|
|
||||||
|
UPDATE {{
|
||||||
|
'_key': document._key,
|
||||||
|
'password': @password
|
||||||
|
}} IN {self.collection_name}
|
||||||
|
|
||||||
|
RETURN NEW
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
affected_rows = self.query(
|
||||||
|
aql,
|
||||||
|
bind_vars = {
|
||||||
|
'account_id': account_id,
|
||||||
|
'password': password
|
||||||
|
},
|
||||||
|
count = True
|
||||||
|
)
|
||||||
|
|
||||||
|
return affected_rows
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise AccountStorageUpdateException()
|
||||||
|
|
||||||
|
def update_status(self, account_id: str, value: bool) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Updates the account status.
|
||||||
|
|
||||||
|
:param account_id: account identifier.
|
||||||
|
:param is_active: account status.
|
||||||
|
:return: the number of updated rows.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.account_id == @account_id
|
||||||
|
|
||||||
|
UPDATE {{
|
||||||
|
'_key': document._key,
|
||||||
|
'is_active': @value
|
||||||
|
}} IN {self.collection_name}
|
||||||
|
|
||||||
|
RETURN NEW
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
affected_rows = self.query(
|
||||||
|
aql,
|
||||||
|
bind_vars = {
|
||||||
|
'account_id': account_id,
|
||||||
|
'value': value
|
||||||
|
},
|
||||||
|
count = True
|
||||||
|
)
|
||||||
|
|
||||||
|
return affected_rows
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise AccountStorageUpdateException()
|
||||||
|
|
||||||
|
def remove(self, account_id: str) -> Cursor | Exception:
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.account_id == @account_id
|
||||||
|
REMOVE document IN {self.collection_name}
|
||||||
|
|
||||||
|
RETURN OLD
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
affected_rows = self.query(
|
||||||
|
aql,
|
||||||
|
bind_vars = {
|
||||||
|
'account_id': account_id
|
||||||
|
},
|
||||||
|
count = True
|
||||||
|
)
|
||||||
|
|
||||||
|
return affected_rows
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise AccountStorageRemoveException()
|
||||||
|
|
||||||
|
class AccountStorageCreateException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot create the account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class AccountStorageGetException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot get the account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class AccountStorageUpdateException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot update the account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class AccountStorageRemoveException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot remove the account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
1
src/piracyshield_data_storage/authentication/__init__.py
Normal file
1
src/piracyshield_data_storage/authentication/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
42
src/piracyshield_data_storage/authentication/storage.py
Normal file
42
src/piracyshield_data_storage/authentication/storage.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
from piracyshield_data_storage.database.arangodb.document import DatabaseArangodbDocument
|
||||||
|
|
||||||
|
from arango.cursor import Cursor
|
||||||
|
|
||||||
|
class AuthenticationStorage(DatabaseArangodbDocument):
|
||||||
|
|
||||||
|
view_name = 'accounts_view'
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def get(self, email: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Returns the account via email.
|
||||||
|
|
||||||
|
:param email: a valid e-mail address.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.view_name}
|
||||||
|
|
||||||
|
FILTER document.email == @email
|
||||||
|
|
||||||
|
RETURN document
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'email': email
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise AuthenticationStorageGetException()
|
||||||
|
|
||||||
|
class AuthenticationStorageGetException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot get the account email.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
16
src/piracyshield_data_storage/base.py
Normal file
16
src/piracyshield_data_storage/base.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import time
|
||||||
|
|
||||||
|
class BaseStorage(DatabaseArangodbDocument):
|
||||||
|
|
||||||
|
start_counter = 0
|
||||||
|
|
||||||
|
stop_counter = 0
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def _start_counter(self):
|
||||||
|
self.start_counter = time.time()
|
||||||
|
|
||||||
|
def _stop_counter(self):
|
||||||
|
self.stop_counter = time.time()
|
1
src/piracyshield_data_storage/blob/__init__.py
Normal file
1
src/piracyshield_data_storage/blob/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
7
src/piracyshield_data_storage/blob/driver.py
Normal file
7
src/piracyshield_data_storage/blob/driver.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
class BlobStorageDriver(ABC):
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def upload(self, file_path: str):
|
||||||
|
pass
|
1
src/piracyshield_data_storage/blob/drivers/__init__.py
Normal file
1
src/piracyshield_data_storage/blob/drivers/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
124
src/piracyshield_data_storage/blob/drivers/azure.py
Normal file
124
src/piracyshield_data_storage/blob/drivers/azure.py
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
from azure.storage.blob import BlobServiceClient, BlobClient, ContainerClient, BlobType
|
||||||
|
|
||||||
|
from azure.core.exceptions import ResourceNotFoundError, ResourceExistsError, AzureError
|
||||||
|
|
||||||
|
from piracyshield_data_storage.blob.driver import BlobStorageDriver
|
||||||
|
|
||||||
|
class AzureBlobStorage(BlobStorageDriver):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Azure blob storage manage.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, connection_string: str, container_name: str):
|
||||||
|
"""
|
||||||
|
Initializes the client expecting a connection string.
|
||||||
|
The connection string exploded looks something like:
|
||||||
|
DefaultEndpointsProtocol=http;
|
||||||
|
AccountName=ACCOUNT_NAME;
|
||||||
|
AccountKey=ACCOUNT_KEY;
|
||||||
|
BlobEndpoint=http://ACCOUNT_NAME.blob.localhost:10000;
|
||||||
|
QueueEndpoint=http://ACCOUNT_NAME.queue.localhost:10001;
|
||||||
|
TableEndpoint=http://ACCOUNT_NAME.table.localhost:10002;
|
||||||
|
|
||||||
|
:param connection_string: Azure connection string (https://learn.microsoft.com/en-us/azure/storage/common/storage-configure-connection-string).
|
||||||
|
:param container_name: container name in use.
|
||||||
|
"""
|
||||||
|
self.blob_service_client = BlobServiceClient.from_connection_string(connection_string)
|
||||||
|
|
||||||
|
self.container_client = self.blob_service_client.get_container_client(container_name)
|
||||||
|
|
||||||
|
def create_container(self):
|
||||||
|
"""
|
||||||
|
Creates the container.
|
||||||
|
Currently used to mock a blob storage.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.container_client.create_container()
|
||||||
|
|
||||||
|
def upload(self, blob_name: str, file_path: str) -> bool | Exception:
|
||||||
|
"""
|
||||||
|
Uploads a new file in chunks to handle big files safely.
|
||||||
|
|
||||||
|
:param blob_name: name of the file to store.
|
||||||
|
:param data: file content.
|
||||||
|
:return bool if correct.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
blob_client = self.container_client.get_blob_client(blob_name)
|
||||||
|
|
||||||
|
blob_client.upload_blob("", blob_type = BlobType.BlockBlob)
|
||||||
|
|
||||||
|
# upload in small chunks
|
||||||
|
with open(file_path, "rb") as file:
|
||||||
|
block_size = 4 * 1024 * 1024 # 4 MB chunks
|
||||||
|
|
||||||
|
block_ids = []
|
||||||
|
|
||||||
|
block_id_prefix = "block"
|
||||||
|
|
||||||
|
block_number = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
data = file.read(block_size)
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
break
|
||||||
|
|
||||||
|
block_id = block_id_prefix + str(block_number).zfill(6)
|
||||||
|
|
||||||
|
blob_client.stage_block(block_id, data)
|
||||||
|
|
||||||
|
block_ids.append(block_id)
|
||||||
|
|
||||||
|
block_number += 1
|
||||||
|
|
||||||
|
# commit the blocks and create the blob
|
||||||
|
blob_client.commit_block_list(block_ids)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
except (ResourceNotFoundError, ResourceExistsError, AzureError):
|
||||||
|
raise AzureBlobStorageUploadException()
|
||||||
|
|
||||||
|
def get_list(self):
|
||||||
|
"""
|
||||||
|
Returns a list of blobs in the container.
|
||||||
|
|
||||||
|
:return a paginated list of the files.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.container_client.list_blobs()
|
||||||
|
|
||||||
|
def remove(self, blob_name: str) -> bool | Exception:
|
||||||
|
"""
|
||||||
|
Removes the blob from the container.
|
||||||
|
|
||||||
|
:param blob_name: the file name to be removed.
|
||||||
|
:return bool if correct.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
blob_client = self.container_client.get_blob_client(blob_name)
|
||||||
|
|
||||||
|
blob_client.delete_blob()
|
||||||
|
|
||||||
|
except (ResourceNotFoundError, AzureError):
|
||||||
|
raise AzureBlobStorageRemoveException()
|
||||||
|
|
||||||
|
class AzureBlobStorageUploadException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
The blob cannot be uploaded.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class AzureBlobStorageRemoveException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
The blob cannot be removed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
30
src/piracyshield_data_storage/blob/storage.py
Normal file
30
src/piracyshield_data_storage/blob/storage.py
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
from piracyshield_data_storage.blob.driver import BlobStorageDriver
|
||||||
|
|
||||||
|
class BlobStorage:
|
||||||
|
|
||||||
|
"""
|
||||||
|
Blob storage manager.
|
||||||
|
"""
|
||||||
|
|
||||||
|
driver = None
|
||||||
|
|
||||||
|
def __init__(self, driver: BlobStorageDriver):
|
||||||
|
self.driver = driver
|
||||||
|
|
||||||
|
def upload(self, blob_name: str, file_path: str):
|
||||||
|
try:
|
||||||
|
return self.driver.upload(
|
||||||
|
blob_name = blob_name,
|
||||||
|
file_path = file_path
|
||||||
|
)
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise BlobStorageUploadException()
|
||||||
|
|
||||||
|
class BlobStorageUploadException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot upload the file.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
1
src/piracyshield_data_storage/cache/__init__.py
vendored
Normal file
1
src/piracyshield_data_storage/cache/__init__.py
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
77
src/piracyshield_data_storage/cache/storage.py
vendored
Normal file
77
src/piracyshield_data_storage/cache/storage.py
vendored
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
from piracyshield_component.environment import Environment
|
||||||
|
from piracyshield_component.log.logger import Logger
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
# TODO: use custom exceptions.
|
||||||
|
|
||||||
|
class CacheStorage:
|
||||||
|
|
||||||
|
logger = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.logger = Logger('storage')
|
||||||
|
|
||||||
|
if not os.path.exists(Environment.CACHE_PATH):
|
||||||
|
raise FileNotFoundError(f'The specified folder `{Environment.CACHE_PATH}` does not exist')
|
||||||
|
|
||||||
|
if not os.path.isdir(Environment.CACHE_PATH):
|
||||||
|
raise NotADirectoryError(f'The specified path `{Environment.CACHE_PATH}` is not a directory')
|
||||||
|
|
||||||
|
def write(self, filename: str, content: bytes) -> str | Exception:
|
||||||
|
path = self._get_absolute_path(filename)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(path, 'wb') as handle:
|
||||||
|
handle.write(content)
|
||||||
|
|
||||||
|
except IOError:
|
||||||
|
raise IOError(f'Failed to write content to file `{filename}`')
|
||||||
|
|
||||||
|
# return the absolute path
|
||||||
|
return path
|
||||||
|
|
||||||
|
def get(self, filename: str) -> str | Exception:
|
||||||
|
return self._get_absolute_path(filename)
|
||||||
|
|
||||||
|
def read(self, filename: str) -> bytes | Exception:
|
||||||
|
path = self._get_absolute_path(filename)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(path, 'r') as handle:
|
||||||
|
return handle.read()
|
||||||
|
|
||||||
|
except FileNotFoundError:
|
||||||
|
raise FileNotFoundError(f'The specified file `{filename}` does not exist')
|
||||||
|
|
||||||
|
except IOError:
|
||||||
|
raise IOError(f'Failed to read content from file `{filename}`')
|
||||||
|
|
||||||
|
def exists(self, filename: str) -> bool:
|
||||||
|
path = self._get_absolute_path(filename)
|
||||||
|
|
||||||
|
return os.path.exists(path)
|
||||||
|
|
||||||
|
def get_all(self) -> list | Exception:
|
||||||
|
try:
|
||||||
|
return os.listdir(Environment.CACHE_PATH)
|
||||||
|
|
||||||
|
except IOError:
|
||||||
|
raise IOError(f'Failed to get files from folder `{Environment.CACHE_PATH}`')
|
||||||
|
|
||||||
|
def remove(self, filename: str) -> bool | Exception:
|
||||||
|
path = self._get_absolute_path(filename)
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.remove(path)
|
||||||
|
|
||||||
|
except FileNotFoundError:
|
||||||
|
raise FileNotFoundError(f'The specified file `{filename}` does not exist')
|
||||||
|
|
||||||
|
except IOError:
|
||||||
|
raise IOError(f'Failed to remove file `{filename}`.')
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _get_absolute_path(self, filename: str) -> str:
|
||||||
|
return os.path.join(Environment.CACHE_PATH, filename)
|
1
src/piracyshield_data_storage/database/__init__.py
Normal file
1
src/piracyshield_data_storage/database/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
from piracyshield_component.config import Config
|
||||||
|
|
||||||
|
from arango import ArangoClient
|
||||||
|
|
||||||
|
class DatabaseArangodbConnection:
|
||||||
|
|
||||||
|
instance = None
|
||||||
|
|
||||||
|
database_config = None
|
||||||
|
|
||||||
|
def __init__(self, as_root = False):
|
||||||
|
self._prepare_configs()
|
||||||
|
|
||||||
|
self._prepare_settings()
|
||||||
|
|
||||||
|
self._prepare_credentials(as_root)
|
||||||
|
|
||||||
|
self.establish()
|
||||||
|
|
||||||
|
def establish(self):
|
||||||
|
client = ArangoClient(hosts = f'{self.protocol}://{self.host}:{self.port}')
|
||||||
|
|
||||||
|
self.instance = client.db(self.database, self.username, self.password, self.verify)
|
||||||
|
|
||||||
|
def _prepare_settings(self):
|
||||||
|
connection = self.database_config.get('connection')
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.protocol = connection['protocol']
|
||||||
|
|
||||||
|
self.host = connection['host']
|
||||||
|
|
||||||
|
self.port = connection['port']
|
||||||
|
|
||||||
|
self.verify = connection['verify']
|
||||||
|
|
||||||
|
except KeyError:
|
||||||
|
DatabaseArangodbConnectionException('Cannot find the database settings')
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.database = connection['database']
|
||||||
|
|
||||||
|
except KeyError:
|
||||||
|
DatabaseArangodbConnectionException('Cannot find the database name')
|
||||||
|
|
||||||
|
def _prepare_credentials(self, as_root):
|
||||||
|
credentials = self.database_config.get('root_credentials') if as_root else self.database_config.get('user_credentials')
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.username = credentials['username']
|
||||||
|
|
||||||
|
self.password = credentials['password']
|
||||||
|
|
||||||
|
except KeyError:
|
||||||
|
DatabaseArangodbConnectionException('Cannot find the database credentials')
|
||||||
|
|
||||||
|
def _prepare_configs(self):
|
||||||
|
self.database_config = Config('database/arangodb')
|
||||||
|
|
||||||
|
class DatabaseArangodbConnectionException(Exception):
|
||||||
|
|
||||||
|
pass
|
27
src/piracyshield_data_storage/database/arangodb/document.py
Normal file
27
src/piracyshield_data_storage/database/arangodb/document.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
from piracyshield_data_storage.database.arangodb.connection import DatabaseArangodbConnection
|
||||||
|
|
||||||
|
from arango.exceptions import AQLQueryExecuteError
|
||||||
|
|
||||||
|
class DatabaseArangodbDocument(DatabaseArangodbConnection):
|
||||||
|
|
||||||
|
def collection(self, collection):
|
||||||
|
try:
|
||||||
|
return self.instance.collection(collection)
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise DatabaseArangodbCollectionNotFoundException()
|
||||||
|
|
||||||
|
def query(self, aql, **kwargs):
|
||||||
|
try:
|
||||||
|
return self.instance.aql.execute(aql, **kwargs)
|
||||||
|
|
||||||
|
except AQLQueryExecuteError:
|
||||||
|
raise DatabaseArangodbQueryException()
|
||||||
|
|
||||||
|
class DatabaseArangodbCollectionNotFoundException(Exception):
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class DatabaseArangodbQueryException(Exception):
|
||||||
|
|
||||||
|
pass
|
1
src/piracyshield_data_storage/database/redis/__init__.py
Normal file
1
src/piracyshield_data_storage/database/redis/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
31
src/piracyshield_data_storage/database/redis/connection.py
Normal file
31
src/piracyshield_data_storage/database/redis/connection.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
from piracyshield_component.config import Config
|
||||||
|
|
||||||
|
from redis import Redis
|
||||||
|
|
||||||
|
class DatabaseRedisConnection:
|
||||||
|
|
||||||
|
instance = None
|
||||||
|
|
||||||
|
database_config = None
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self._prepare_configs()
|
||||||
|
|
||||||
|
def establish(self, database: str) -> None:
|
||||||
|
try:
|
||||||
|
self.instance = Redis(
|
||||||
|
host = self.database_config['host'],
|
||||||
|
port = self.database_config['port'],
|
||||||
|
db = database,
|
||||||
|
decode_responses = True
|
||||||
|
)
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise DatabaseRedisConnectionException()
|
||||||
|
|
||||||
|
def _prepare_configs(self) -> None:
|
||||||
|
self.database_config = Config('database/redis').get('connection')
|
||||||
|
|
||||||
|
class DatabaseRedisConnectionException(Exception):
|
||||||
|
|
||||||
|
pass
|
34
src/piracyshield_data_storage/database/redis/document.py
Normal file
34
src/piracyshield_data_storage/database/redis/document.py
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
from piracyshield_data_storage.database.redis.connection import DatabaseRedisConnection
|
||||||
|
|
||||||
|
class DatabaseRedisDocument(DatabaseRedisConnection):
|
||||||
|
|
||||||
|
def set_with_expiry(self, key: str, value: any, expiry: int) -> bool | Exception:
|
||||||
|
if self.instance.set(key, value, ex = expiry) == True:
|
||||||
|
return True
|
||||||
|
|
||||||
|
raise DatabaseRedisSetException()
|
||||||
|
|
||||||
|
def incr(self, key: str, amount: int = 1) -> bool | Exception:
|
||||||
|
return self.instance.incr(name = key, amount = amount)
|
||||||
|
|
||||||
|
def get(self, key: str) -> any:
|
||||||
|
return self.instance.get(key)
|
||||||
|
|
||||||
|
def delete(self, key: str) -> any:
|
||||||
|
return self.instance.delete(key)
|
||||||
|
|
||||||
|
class DatabaseRedisSetException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot set the data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class DatabaseRedisGetException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot get the data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
1
src/piracyshield_data_storage/dda/__init__.py
Normal file
1
src/piracyshield_data_storage/dda/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
253
src/piracyshield_data_storage/dda/storage.py
Normal file
253
src/piracyshield_data_storage/dda/storage.py
Normal file
|
@ -0,0 +1,253 @@
|
||||||
|
from piracyshield_data_storage.database.arangodb.document import DatabaseArangodbDocument
|
||||||
|
|
||||||
|
from arango.cursor import Cursor
|
||||||
|
|
||||||
|
class DDAStorage(DatabaseArangodbDocument):
|
||||||
|
|
||||||
|
collection_name = 'dda_instances'
|
||||||
|
|
||||||
|
collection_instance = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.collection_instance = self.collection(self.collection_name)
|
||||||
|
|
||||||
|
def insert(self, document: dict) -> dict | Exception:
|
||||||
|
"""
|
||||||
|
Adds a new DDA identifier.
|
||||||
|
|
||||||
|
:param document: dictionary with the expected data model values.
|
||||||
|
:return: cursor with the inserted data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.collection_instance.insert(document)
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise DDAStorageCreateException()
|
||||||
|
|
||||||
|
def get_global(self) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Fetches all the DDA instances.
|
||||||
|
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
// resolve account identifier
|
||||||
|
LET account_name = (
|
||||||
|
FOR a IN accounts_view
|
||||||
|
FILTER a.account_id == document.account_id
|
||||||
|
RETURN a['name']
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
SORT document.instance ASC
|
||||||
|
|
||||||
|
RETURN {{
|
||||||
|
'dda_id': document.dda_id,
|
||||||
|
'description': document.description,
|
||||||
|
'instance': document.instance,
|
||||||
|
'account_id': document.account_id,
|
||||||
|
'account_name': account_name,
|
||||||
|
'is_active': document.is_active,
|
||||||
|
'metadata': {{
|
||||||
|
'created_at': document.metadata.created_at
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql)
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise DDAStorageGetException()
|
||||||
|
|
||||||
|
def get_all_by_account(self, account_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Fetches all the DDA instances assigned to an account.
|
||||||
|
|
||||||
|
:param account_id: a valid account identifier.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.account_id == @account_id
|
||||||
|
|
||||||
|
SORT document.instance ASC
|
||||||
|
|
||||||
|
RETURN {{
|
||||||
|
'dda_id': document.dda_id,
|
||||||
|
'description': document.description,
|
||||||
|
'instance': document.instance,
|
||||||
|
'is_active': document.is_active,
|
||||||
|
'metadata': {{
|
||||||
|
'created_at': document.metadata.created_at
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'account_id': account_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise DDAStorageGetException()
|
||||||
|
|
||||||
|
def exists_by_instance(self, instance: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Searches for an instance.
|
||||||
|
|
||||||
|
:param instance: a valid DDA instance.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.instance == @instance
|
||||||
|
|
||||||
|
RETURN document
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'instance': instance
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise DDAStorageGetException()
|
||||||
|
|
||||||
|
def is_assigned_to_account(self, dda_id: str, account_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Searches for a DDA identifier assigned to a specified account identifier.
|
||||||
|
|
||||||
|
:param dda_id: a valid DDA identifier.
|
||||||
|
:param account_id: a valid account identifier.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER
|
||||||
|
document.dda_id == @dda_id AND
|
||||||
|
document.account_id == @account_id AND
|
||||||
|
document.is_active == True
|
||||||
|
|
||||||
|
RETURN document
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'dda_id': dda_id,
|
||||||
|
'account_id': account_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise DDAStorageGetException()
|
||||||
|
|
||||||
|
def update_status(self, dda_id: str, status: bool) -> list | Exception:
|
||||||
|
"""
|
||||||
|
Sets the DDA identifier status.
|
||||||
|
|
||||||
|
:param dda_id: a valid DDA identifier.
|
||||||
|
:param status: true/false if active/non active.
|
||||||
|
:return: true if the query has been processed successfully.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.dda_id == @dda_id
|
||||||
|
|
||||||
|
UPDATE document WITH {{
|
||||||
|
is_active: @status
|
||||||
|
}} IN {self.collection_name}
|
||||||
|
|
||||||
|
RETURN NEW
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
affected_rows = self.query(
|
||||||
|
aql,
|
||||||
|
bind_vars = {
|
||||||
|
'dda_id': dda_id,
|
||||||
|
'status': status
|
||||||
|
},
|
||||||
|
count = True
|
||||||
|
)
|
||||||
|
|
||||||
|
return affected_rows
|
||||||
|
|
||||||
|
except:
|
||||||
|
return DDAStorageUpdateException()
|
||||||
|
|
||||||
|
def remove(self, dda_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Removes a DDA identifier.
|
||||||
|
|
||||||
|
:param dda_id: DDA identifier.
|
||||||
|
:return: true if the query has been processed successfully.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.dda_id == @dda_id
|
||||||
|
|
||||||
|
REMOVE document IN {self.collection_name}
|
||||||
|
|
||||||
|
RETURN OLD
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
affected_rows = self.query(
|
||||||
|
aql,
|
||||||
|
bind_vars = {
|
||||||
|
'dda_id': dda_id
|
||||||
|
},
|
||||||
|
count = True
|
||||||
|
)
|
||||||
|
|
||||||
|
return affected_rows
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise DDAStorageRemoveException()
|
||||||
|
|
||||||
|
class DDAStorageCreateException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot create the DDA item.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class DDAStorageGetException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot get the DDA item.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class DDAStorageUpdateException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot update the DDA item.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class DDAStorageRemoveException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot remove the DDA item.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
1
src/piracyshield_data_storage/forensic/__init__.py
Normal file
1
src/piracyshield_data_storage/forensic/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
268
src/piracyshield_data_storage/forensic/storage.py
Normal file
268
src/piracyshield_data_storage/forensic/storage.py
Normal file
|
@ -0,0 +1,268 @@
|
||||||
|
from piracyshield_data_storage.database.arangodb.document import DatabaseArangodbDocument
|
||||||
|
|
||||||
|
from arango.cursor import Cursor
|
||||||
|
|
||||||
|
class ForensicStorage(DatabaseArangodbDocument):
|
||||||
|
|
||||||
|
collection_name = 'forensics'
|
||||||
|
|
||||||
|
collection_instance = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.collection_instance = self.collection(self.collection_name)
|
||||||
|
|
||||||
|
def insert(self, document: dict) -> dict | Exception:
|
||||||
|
"""
|
||||||
|
Adds a new ticket.
|
||||||
|
|
||||||
|
:param document: dictionary with the expected ticket data model values.
|
||||||
|
:return: cursor with the inserted data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.collection_instance.insert(document)
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise ForensicStorageCreateException()
|
||||||
|
|
||||||
|
def exists_ticket_id(self, ticket_id: str) -> Cursor | Exception:
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.ticket_id == @ticket_id
|
||||||
|
|
||||||
|
COLLECT WITH COUNT INTO length
|
||||||
|
|
||||||
|
RETURN length
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'ticket_id': ticket_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise ForensicStorageGetException()
|
||||||
|
|
||||||
|
def exists_hash_string(self, hash_string: str) -> Cursor | Exception:
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.hash_string == @hash_string
|
||||||
|
|
||||||
|
COLLECT WITH COUNT INTO length
|
||||||
|
|
||||||
|
RETURN length
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'hash_string': hash_string
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise ForensicStorageGetException()
|
||||||
|
|
||||||
|
def get_by_ticket(self, ticket_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Returns forensic document by ticket identifier.
|
||||||
|
|
||||||
|
:param ticket_id: a ticket identifier.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.ticket_id == @ticket_id
|
||||||
|
|
||||||
|
RETURN document
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'ticket_id': ticket_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise ForensicStorageGetException()
|
||||||
|
|
||||||
|
def get_by_ticket_for_reporter(self, ticket_id: str, reporter_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Returns forensic document by ticket identifier.
|
||||||
|
|
||||||
|
:param ticket_id: a ticket identifier.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER
|
||||||
|
document.ticket_id == @ticket_id AND
|
||||||
|
document.metadata.created_by == @reporter_id
|
||||||
|
|
||||||
|
RETURN document
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'ticket_id': ticket_id,
|
||||||
|
'reporter_id': reporter_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise ForensicStorageGetException()
|
||||||
|
|
||||||
|
def update_archive_name(self, ticket_id: str, archive_name: str, status: str, updated_at: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Insert the forensic evidence archive name.
|
||||||
|
|
||||||
|
:param ticket_id: ticket identifier.
|
||||||
|
:param archive_name: name of the file archive.
|
||||||
|
:param status: status of the analysis.
|
||||||
|
:param updated_at: date of this update.
|
||||||
|
:return: list of updated rows.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.ticket_id == @ticket_id
|
||||||
|
|
||||||
|
UPDATE document WITH {{
|
||||||
|
archive_name: @archive_name,
|
||||||
|
status: @status,
|
||||||
|
metadata: {{
|
||||||
|
updated_at: @updated_at
|
||||||
|
}}
|
||||||
|
}} IN {self.collection_name}
|
||||||
|
|
||||||
|
RETURN NEW
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
affected_rows = self.query(
|
||||||
|
aql,
|
||||||
|
bind_vars = {
|
||||||
|
'ticket_id': ticket_id,
|
||||||
|
'archive_name': archive_name,
|
||||||
|
'status': status,
|
||||||
|
'updated_at': updated_at
|
||||||
|
},
|
||||||
|
count = True
|
||||||
|
)
|
||||||
|
|
||||||
|
return affected_rows
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise ForensicStorageUpdateException()
|
||||||
|
|
||||||
|
def update_archive_status(self, ticket_id: str, status: str, updated_at: str, reason: str = None) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Updates the status of a previously created record.
|
||||||
|
|
||||||
|
:param ticket_id: ticket identifier.
|
||||||
|
:param status: status of the analysis.
|
||||||
|
:param reason: additional string message.
|
||||||
|
:param updated_at: date of this update.
|
||||||
|
:return: list of updated rows.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.ticket_id == @ticket_id
|
||||||
|
|
||||||
|
UPDATE document WITH {{
|
||||||
|
status: @status,
|
||||||
|
reason: @reason,
|
||||||
|
metadata: {{
|
||||||
|
updated_at: @updated_at
|
||||||
|
}}
|
||||||
|
}} IN {self.collection_name}
|
||||||
|
|
||||||
|
RETURN NEW
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
affected_rows = self.query(
|
||||||
|
aql,
|
||||||
|
bind_vars = {
|
||||||
|
'ticket_id': ticket_id,
|
||||||
|
'status': status,
|
||||||
|
'reason': reason,
|
||||||
|
'updated_at': updated_at
|
||||||
|
},
|
||||||
|
count = True
|
||||||
|
)
|
||||||
|
|
||||||
|
return affected_rows
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise ForensicStorageUpdateException()
|
||||||
|
|
||||||
|
def remove_by_ticket(self, ticket_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Removes a forensic archive.
|
||||||
|
|
||||||
|
:param ticket_id: ticket identifier.
|
||||||
|
:return: list of removed rows.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.ticket_id == @ticket_id
|
||||||
|
REMOVE document IN {self.collection_name}
|
||||||
|
|
||||||
|
RETURN OLD
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
affected_rows = self.query(
|
||||||
|
aql,
|
||||||
|
bind_vars = {
|
||||||
|
'ticket_id': ticket_id
|
||||||
|
},
|
||||||
|
count = True
|
||||||
|
)
|
||||||
|
|
||||||
|
return affected_rows
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketStorageRemoveException()
|
||||||
|
|
||||||
|
class ForensicStorageCreateException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot create the forensic archive.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ForensicStorageGetException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot get the forensic archive.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ForensicStorageUpdateException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot update the forensic archive.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ForensicStorageRemoveException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot remove the forensic archive.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
1
src/piracyshield_data_storage/guest/__init__.py
Normal file
1
src/piracyshield_data_storage/guest/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
10
src/piracyshield_data_storage/guest/storage.py
Normal file
10
src/piracyshield_data_storage/guest/storage.py
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
from piracyshield_data_storage.account.storage import AccountStorage
|
||||||
|
|
||||||
|
class GuestStorage(AccountStorage):
|
||||||
|
|
||||||
|
COLLECTION = 'guests'
|
||||||
|
|
||||||
|
collection_instance = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(collection_name = self.COLLECTION)
|
1
src/piracyshield_data_storage/internal/__init__.py
Normal file
1
src/piracyshield_data_storage/internal/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
10
src/piracyshield_data_storage/internal/storage.py
Normal file
10
src/piracyshield_data_storage/internal/storage.py
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
from piracyshield_data_storage.account.storage import AccountStorage
|
||||||
|
|
||||||
|
class InternalStorage(AccountStorage):
|
||||||
|
|
||||||
|
COLLECTION = 'internals'
|
||||||
|
|
||||||
|
collection_instance = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(collection_name = self.COLLECTION)
|
1
src/piracyshield_data_storage/log/__init__.py
Normal file
1
src/piracyshield_data_storage/log/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
114
src/piracyshield_data_storage/log/storage.py
Normal file
114
src/piracyshield_data_storage/log/storage.py
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
from piracyshield_data_storage.database.arangodb.document import DatabaseArangodbDocument
|
||||||
|
|
||||||
|
from arango.cursor import Cursor
|
||||||
|
|
||||||
|
class LogStorage(DatabaseArangodbDocument):
|
||||||
|
|
||||||
|
collection_name = 'logs'
|
||||||
|
|
||||||
|
collection_instance = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.collection_instance = self.collection(self.collection_name)
|
||||||
|
|
||||||
|
def insert(self, document: dict) -> dict:
|
||||||
|
"""
|
||||||
|
Adds a new log record.
|
||||||
|
|
||||||
|
:param document: dictionary with the values to insert.
|
||||||
|
:return: cursor with the inserted data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.collection_instance.insert(document)
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise LogStorageCreateException()
|
||||||
|
|
||||||
|
def get(self, identifier: str) -> Cursor:
|
||||||
|
"""
|
||||||
|
Gets log records for a specific identifier.
|
||||||
|
|
||||||
|
:param identifier: generic identifier.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.identifier == @identifier
|
||||||
|
|
||||||
|
RETURN {{
|
||||||
|
'log_id': document.log_id,
|
||||||
|
'time': document.identifier,
|
||||||
|
'message': document.message,
|
||||||
|
'metadata': {{
|
||||||
|
'created_at': document.metadata.created_at
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'identifier': identifier
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise LogStorageGetException()
|
||||||
|
|
||||||
|
def remove_by_ticket_id(self, ticket_id: str) -> dict | bool:
|
||||||
|
"""
|
||||||
|
Removes all the logs related to a ticket.
|
||||||
|
|
||||||
|
:param ticket_id: ticket identifier.
|
||||||
|
:return: true if the query has been processed successfully.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.identifier == @ticket_id
|
||||||
|
REMOVE document IN {self.collection_name}
|
||||||
|
|
||||||
|
RETURN OLD
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
affected_rows = self.query(
|
||||||
|
aql,
|
||||||
|
bind_vars = {
|
||||||
|
'ticket_id': ticket_id
|
||||||
|
},
|
||||||
|
count = True
|
||||||
|
)
|
||||||
|
|
||||||
|
return affected_rows
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise LogStorageRemoveException()
|
||||||
|
|
||||||
|
class LogStorageCreateException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot create the log.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class LogStorageGetException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot get the log.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class LogStorageRemoveException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot remove the log.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
1
src/piracyshield_data_storage/log/ticket/__init__.py
Normal file
1
src/piracyshield_data_storage/log/ticket/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
|
114
src/piracyshield_data_storage/log/ticket/item/storage.py
Normal file
114
src/piracyshield_data_storage/log/ticket/item/storage.py
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
from piracyshield_data_storage.database.arangodb.document import DatabaseArangodbDocument
|
||||||
|
|
||||||
|
from arango.cursor import Cursor
|
||||||
|
|
||||||
|
class LogTicketItemStorage(DatabaseArangodbDocument):
|
||||||
|
|
||||||
|
collection_name = 'log_ticket_blocking_items'
|
||||||
|
|
||||||
|
collection_instance = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.collection_instance = self.collection(self.collection_name)
|
||||||
|
|
||||||
|
def insert(self, document: dict) -> dict | Exception:
|
||||||
|
"""
|
||||||
|
Adds a new log record.
|
||||||
|
|
||||||
|
:param document: dictionary with the values to insert.
|
||||||
|
:return: cursor with the inserted data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.collection_instance.insert(document)
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise LogTicketItemStorageCreateException()
|
||||||
|
|
||||||
|
def get_all(self, ticket_item_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Gets all records for a specific ticket item identifier.
|
||||||
|
|
||||||
|
:param ticket_item_id: a valid ticket item identifier.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.ticket_item_id == @ticket_item_id
|
||||||
|
|
||||||
|
RETURN {{
|
||||||
|
'ticket_item_id': document.ticket_item_id,
|
||||||
|
'message': document.message,
|
||||||
|
'metadata': {{
|
||||||
|
'created_at': document.metadata.created_at
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'ticket_item_id': ticket_item_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise LogTicketItemStorageGetException()
|
||||||
|
|
||||||
|
def remove_all(self, ticket_item_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Removes all the records related to a ticket item identifier.
|
||||||
|
|
||||||
|
:param ticket_item_id: a valid ticket item identifier.
|
||||||
|
:return: number of affected records.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.ticket_item_id == @ticket_item_id
|
||||||
|
|
||||||
|
REMOVE document IN {self.collection_name}
|
||||||
|
|
||||||
|
RETURN OLD
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
affected_rows = self.query(
|
||||||
|
aql,
|
||||||
|
bind_vars = {
|
||||||
|
'ticket_item_id': ticket_item_id
|
||||||
|
},
|
||||||
|
count = True
|
||||||
|
)
|
||||||
|
|
||||||
|
return affected_rows
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise LogTicketItemStorageRemoveException()
|
||||||
|
|
||||||
|
class LogTicketItemStorageCreateException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot create the log.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class LogTicketItemStorageGetException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot get the log.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class LogTicketItemStorageRemoveException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot remove the log.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
114
src/piracyshield_data_storage/log/ticket/storage.py
Normal file
114
src/piracyshield_data_storage/log/ticket/storage.py
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
from piracyshield_data_storage.database.arangodb.document import DatabaseArangodbDocument
|
||||||
|
|
||||||
|
from arango.cursor import Cursor
|
||||||
|
|
||||||
|
class LogTicketStorage(DatabaseArangodbDocument):
|
||||||
|
|
||||||
|
collection_name = 'log_ticket_blockings'
|
||||||
|
|
||||||
|
collection_instance = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.collection_instance = self.collection(self.collection_name)
|
||||||
|
|
||||||
|
def insert(self, document: dict) -> dict | Exception:
|
||||||
|
"""
|
||||||
|
Adds a new log record.
|
||||||
|
|
||||||
|
:param document: dictionary with the values to insert.
|
||||||
|
:return: cursor with the inserted data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.collection_instance.insert(document)
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise LogTicketStorageCreateException()
|
||||||
|
|
||||||
|
def get_all(self, ticket_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Gets all records for a specific ticket identifier.
|
||||||
|
|
||||||
|
:param ticket_id: a valid ticket identifier.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.ticket_id == @ticket_id
|
||||||
|
|
||||||
|
RETURN {{
|
||||||
|
'ticket_id': document.ticket_id,
|
||||||
|
'message': document.message,
|
||||||
|
'metadata': {{
|
||||||
|
'created_at': document.metadata.created_at
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'ticket_id': ticket_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise LogTicketStorageGetException()
|
||||||
|
|
||||||
|
def remove_all(self, ticket_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Removes all the records related to a ticket identifier.
|
||||||
|
|
||||||
|
:param ticket_id: a valid ticket identifier.
|
||||||
|
:return: number of affected records.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.ticket_id == @ticket_id
|
||||||
|
|
||||||
|
REMOVE document IN {self.collection_name}
|
||||||
|
|
||||||
|
RETURN OLD
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
affected_rows = self.query(
|
||||||
|
aql,
|
||||||
|
bind_vars = {
|
||||||
|
'ticket_id': ticket_id
|
||||||
|
},
|
||||||
|
count = True
|
||||||
|
)
|
||||||
|
|
||||||
|
return affected_rows
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise LogTicketStorageRemoveException()
|
||||||
|
|
||||||
|
class LogTicketStorageCreateException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot create the log.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class LogTicketStorageGetException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot get the log.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class LogTicketStorageRemoveException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot remove the log.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
1
src/piracyshield_data_storage/provider/__init__.py
Normal file
1
src/piracyshield_data_storage/provider/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
10
src/piracyshield_data_storage/provider/storage.py
Normal file
10
src/piracyshield_data_storage/provider/storage.py
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
from piracyshield_data_storage.account.storage import AccountStorage
|
||||||
|
|
||||||
|
class ProviderStorage(AccountStorage):
|
||||||
|
|
||||||
|
COLLECTION = 'providers'
|
||||||
|
|
||||||
|
collection_instance = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(collection_name = self.COLLECTION)
|
1
src/piracyshield_data_storage/reporter/__init__.py
Normal file
1
src/piracyshield_data_storage/reporter/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
10
src/piracyshield_data_storage/reporter/storage.py
Normal file
10
src/piracyshield_data_storage/reporter/storage.py
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
from piracyshield_data_storage.account.storage import AccountStorage
|
||||||
|
|
||||||
|
class ReporterStorage(AccountStorage):
|
||||||
|
|
||||||
|
COLLECTION = 'reporters'
|
||||||
|
|
||||||
|
collection_instance = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(collection_name = self.COLLECTION)
|
1
src/piracyshield_data_storage/security/__init__.py
Normal file
1
src/piracyshield_data_storage/security/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
from piracyshield_data_storage.database.redis.document import DatabaseRedisDocument, DatabaseRedisSetException, DatabaseRedisGetException
|
||||||
|
|
||||||
|
class SecurityAntiBruteForceMemory(DatabaseRedisDocument):
|
||||||
|
|
||||||
|
def __init__(self, database: int):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.establish(database)
|
||||||
|
|
||||||
|
def set_login_attempts(self, email: str, timeframe: int, attempts: int = 1) -> bool | Exception:
|
||||||
|
"""
|
||||||
|
Sets the current login attempts in a given timeframe.
|
||||||
|
|
||||||
|
:param email: a valid e-mail address.
|
||||||
|
:param timeframe: a timeframe in seconds.
|
||||||
|
:param attempts: sets number of attempts, default: 1.
|
||||||
|
:return: true if the value has been stored.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.set_with_expiry(
|
||||||
|
key = email,
|
||||||
|
value = attempts, # store the correct types
|
||||||
|
expiry = timeframe
|
||||||
|
)
|
||||||
|
|
||||||
|
except DatabaseRedisSetException:
|
||||||
|
raise SecurityMemorySetException()
|
||||||
|
|
||||||
|
def increment_login_attempts(self, email: str, amount: int = 1) -> bool | Exception:
|
||||||
|
"""
|
||||||
|
Increments the attempts by preserving expiry time.
|
||||||
|
|
||||||
|
:param email: a valid e-mail address.
|
||||||
|
:param amount: increment by a number, default: 1.
|
||||||
|
:return: true if successfully executed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.incr(
|
||||||
|
key = email,
|
||||||
|
amount = amount
|
||||||
|
)
|
||||||
|
|
||||||
|
except DatabaseRedisSetException:
|
||||||
|
raise SecurityMemorySetException()
|
||||||
|
|
||||||
|
def get_login_attempts(self, email: str) -> int | Exception:
|
||||||
|
"""
|
||||||
|
Gets the current account login attempts.
|
||||||
|
|
||||||
|
:param email: a valid e-mail address.
|
||||||
|
:return: the number of attempts.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = self.get(key = email)
|
||||||
|
|
||||||
|
if response:
|
||||||
|
# make sure we're dealing with a true int and not a string
|
||||||
|
return int(response)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
except DatabaseRedisGetException:
|
||||||
|
raise SecurityMemoryGetException()
|
||||||
|
|
||||||
|
def reset_login_attempts(self, email: str) -> bool | Exception:
|
||||||
|
"""
|
||||||
|
Unsets the login attempts count.
|
||||||
|
|
||||||
|
:param email: a valid e-mail address.
|
||||||
|
:return: true if successfully executed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.delete(
|
||||||
|
key = email
|
||||||
|
)
|
||||||
|
|
||||||
|
except DatabaseRedisSetException:
|
||||||
|
raise SecurityMemorySetException()
|
||||||
|
|
||||||
|
class SecurityAntiBruteForceMemorySetException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot set the value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class SecurityAntiBruteForceMemoryGetException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot get the value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
|
@ -0,0 +1 @@
|
||||||
|
|
82
src/piracyshield_data_storage/security/blacklist/memory.py
Normal file
82
src/piracyshield_data_storage/security/blacklist/memory.py
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
from piracyshield_data_storage.database.redis.document import DatabaseRedisDocument, DatabaseRedisSetException, DatabaseRedisGetException
|
||||||
|
|
||||||
|
class SecurityBlacklistMemory(DatabaseRedisDocument):
|
||||||
|
|
||||||
|
ip_address_prefix = 'ip_address'
|
||||||
|
|
||||||
|
token_prefix = 'token'
|
||||||
|
|
||||||
|
def __init__(self, database: int):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.establish(database)
|
||||||
|
|
||||||
|
def add_ip_address(self, ip_address: str, duration: int = 60) -> bool | Exception:
|
||||||
|
"""
|
||||||
|
Blacklists an IP address.
|
||||||
|
|
||||||
|
:param ip_address: a valid IP address.
|
||||||
|
:param duration: duration of the blacklist in seconds.
|
||||||
|
:return: true if the item has been stored.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.set_with_expiry(
|
||||||
|
key = f'{self.ip_address_prefix}:{ip_address}',
|
||||||
|
value = '1',
|
||||||
|
expiry = duration
|
||||||
|
)
|
||||||
|
|
||||||
|
except DatabaseRedisSetException:
|
||||||
|
raise SecurityBlacklistMemorySetException()
|
||||||
|
|
||||||
|
def exists_by_ip_address(self, ip_address: str) -> bool | Exception:
|
||||||
|
"""
|
||||||
|
Verifies if an IP address is in the blacklist.
|
||||||
|
|
||||||
|
:param item: a valid IP address.
|
||||||
|
:return: returns the TTL of the item.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = self.get(key = f'{self.ip_address_prefix}:{ip_address}')
|
||||||
|
|
||||||
|
if response:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
except DatabaseRedisGetException:
|
||||||
|
raise SecurityBlacklistMemoryGetException()
|
||||||
|
|
||||||
|
def remove_ip_address(self, ip_address: str) -> bool | Exception:
|
||||||
|
"""
|
||||||
|
Removes an IP address from the blacklist.
|
||||||
|
|
||||||
|
:param item: a valid IP address.
|
||||||
|
:return: true if the item has been removed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.delete(
|
||||||
|
key = f'{self.ip_address_prefix}:{ip_address}'
|
||||||
|
)
|
||||||
|
|
||||||
|
except DatabaseRedisSetException:
|
||||||
|
raise SecurityBlacklistMemorySetException()
|
||||||
|
|
||||||
|
class SecurityBlacklistMemorySetException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot set the value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class SecurityBlacklistMemoryGetException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot get the value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
1
src/piracyshield_data_storage/ticket/__init__.py
Normal file
1
src/piracyshield_data_storage/ticket/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
1
src/piracyshield_data_storage/ticket/error/__init__.py
Normal file
1
src/piracyshield_data_storage/ticket/error/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
154
src/piracyshield_data_storage/ticket/error/storage.py
Normal file
154
src/piracyshield_data_storage/ticket/error/storage.py
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
from piracyshield_data_storage.database.arangodb.document import DatabaseArangodbDocument
|
||||||
|
|
||||||
|
from arango.cursor import Cursor
|
||||||
|
|
||||||
|
class TicketErrorStorage(DatabaseArangodbDocument):
|
||||||
|
|
||||||
|
collection_name = 'ticket_errors'
|
||||||
|
|
||||||
|
collection_instance = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.collection_instance = self.collection(self.collection_name)
|
||||||
|
|
||||||
|
def insert(self, document: dict) -> dict | Exception:
|
||||||
|
"""
|
||||||
|
Adds a new error ticket.
|
||||||
|
|
||||||
|
:param document: dictionary with the expected ticket data model values.
|
||||||
|
:return: cursor with the inserted data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.collection_instance.insert(document)
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketErrorStorageCreateException()
|
||||||
|
|
||||||
|
def get(self, ticket_error_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Gets error ticket by its identifier.
|
||||||
|
|
||||||
|
:param ticket_error_id: a valid ticket error identifier.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.ticket_error_id == @ticket_error_id
|
||||||
|
|
||||||
|
LET created_by_name = (
|
||||||
|
FOR a IN accounts_view
|
||||||
|
FILTER a.account_id == document.metadata.created_by
|
||||||
|
RETURN a.name
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
RETURN {{
|
||||||
|
'ticket_error_id': document.ticket_error_id,
|
||||||
|
'ticket_id': document.ticket_id,
|
||||||
|
'fqdn': document.fqdn,
|
||||||
|
'ipv4': document.ipv4,
|
||||||
|
'ipv6': document.ipv6,
|
||||||
|
'metadata': {{
|
||||||
|
'created_at': document.metadata.created_at,
|
||||||
|
'created_by': document.metadata.created_by,
|
||||||
|
'created_by_name': created_by_name
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'ticket_error_id': ticket_error_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketErrorStorageGetException()
|
||||||
|
|
||||||
|
def get_by_reporter(self, ticket_error_id: str, reporter_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Gets error ticket by its identifier and creator account.
|
||||||
|
|
||||||
|
:param ticket_error_id: a valid ticket error identifier.
|
||||||
|
:param reporter_id: a valid reporter account identifier.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER
|
||||||
|
document.ticket_error_id == @ticket_error_id AND
|
||||||
|
document.metadata.created_by == @reporter_id
|
||||||
|
|
||||||
|
RETURN {{
|
||||||
|
'ticket_error_id': document.ticket_error_id,
|
||||||
|
'ticket_id': document.ticket_id,
|
||||||
|
'fqdn': document.fqdn,
|
||||||
|
'ipv4': document.ipv4,
|
||||||
|
'ipv6': document.ipv6,
|
||||||
|
'metadata': {{
|
||||||
|
'created_at': document.metadata.created_at
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'ticket_error_id': ticket_error_id,
|
||||||
|
'reporter_id': reporter_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketErrorStorageGetException()
|
||||||
|
|
||||||
|
def get_by_ticket(self, ticket_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Gets error tickets by ticket identifier.
|
||||||
|
|
||||||
|
:param ticket_id: a valid ticket identifier.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.ticket_id == @ticket_id
|
||||||
|
|
||||||
|
RETURN {{
|
||||||
|
'ticket_error_id': document.ticket_error_id,
|
||||||
|
'fqdns': document.fqdn or [],
|
||||||
|
'ipv4': document.ipv4 or [],
|
||||||
|
'ipv6': document.ipv6 or [],
|
||||||
|
'metadata': {{
|
||||||
|
'created_at': document.metadata.created_at
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'ticket_id': ticket_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketErrorStorageGetException()
|
||||||
|
|
||||||
|
class TicketErrorStorageCreateException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot create the error ticket.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TicketErrorStorageGetException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot get the error ticket.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
1
src/piracyshield_data_storage/ticket/item/__init__.py
Normal file
1
src/piracyshield_data_storage/ticket/item/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
795
src/piracyshield_data_storage/ticket/item/storage.py
Normal file
795
src/piracyshield_data_storage/ticket/item/storage.py
Normal file
|
@ -0,0 +1,795 @@
|
||||||
|
from piracyshield_data_storage.database.arangodb.document import DatabaseArangodbDocument
|
||||||
|
|
||||||
|
from arango.cursor import Cursor
|
||||||
|
|
||||||
|
class TicketItemStorage(DatabaseArangodbDocument):
|
||||||
|
|
||||||
|
ticket_collection_name = 'ticket_blockings'
|
||||||
|
|
||||||
|
collection_name = 'ticket_blocking_items'
|
||||||
|
|
||||||
|
collection_instance = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.collection_instance = self.collection(self.collection_name)
|
||||||
|
|
||||||
|
def insert(self, document: dict) -> dict | Exception:
|
||||||
|
"""
|
||||||
|
Adds a new ticket item.
|
||||||
|
|
||||||
|
:param document: dictionary ticket item data model structure.
|
||||||
|
:return: cursor with the inserted data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.collection_instance.insert(document)
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketItemStorageCreateException()
|
||||||
|
|
||||||
|
def get_all_items_with_genre(self, genre: str) -> Cursor:
|
||||||
|
"""
|
||||||
|
Gets all ticket items. Values only.
|
||||||
|
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER
|
||||||
|
document.genre == @genre AND
|
||||||
|
document.is_active == true
|
||||||
|
|
||||||
|
RETURN DISTINCT document.value
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'genre': genre
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketItemStorageGetException()
|
||||||
|
|
||||||
|
def get_all_items_with_genre_by_provider(self, genre: str, provider_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Gets all ticket items assigned to this provider.
|
||||||
|
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER
|
||||||
|
document.genre == @genre AND
|
||||||
|
document.is_active == true AND
|
||||||
|
document.is_duplicate == false AND
|
||||||
|
document.is_whitelisted == false AND
|
||||||
|
document.is_error == false AND
|
||||||
|
document.provider_id == @provider_id
|
||||||
|
|
||||||
|
// ensure only available tickets are considered
|
||||||
|
FOR parent_ticket IN {self.ticket_collection_name}
|
||||||
|
FILTER
|
||||||
|
parent_ticket.ticket_id == document.ticket_id AND
|
||||||
|
parent_ticket.status.ticket != 'created'
|
||||||
|
|
||||||
|
RETURN DISTINCT document.value
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'genre': genre,
|
||||||
|
'provider_id': provider_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketItemStorageGetException()
|
||||||
|
|
||||||
|
def get_all_items_with_genre_by_ticket(self, ticket_id: str, genre: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Gets all ticket items. Values only, by ticket_id.
|
||||||
|
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER
|
||||||
|
document.ticket_id == @ticket_id AND
|
||||||
|
document.genre == @genre AND
|
||||||
|
document.is_active == true
|
||||||
|
|
||||||
|
RETURN DISTINCT document.value
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'ticket_id': ticket_id,
|
||||||
|
'genre': genre
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketItemStorageGetException()
|
||||||
|
|
||||||
|
def get_all_items_with_genre_by_ticket_for_reporter(self, ticket_id: str, genre: str, reporter_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Gets items of a ticket created by a reporter account.
|
||||||
|
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER
|
||||||
|
document.ticket_id == @ticket_id AND
|
||||||
|
document.genre == @genre
|
||||||
|
|
||||||
|
FOR ticket IN {self.ticket_collection_name}
|
||||||
|
|
||||||
|
FILTER
|
||||||
|
ticket.ticket_id == @ticket_id AND
|
||||||
|
ticket.metadata.created_by == @reporter_id
|
||||||
|
|
||||||
|
RETURN DISTINCT {{
|
||||||
|
'value': document.value,
|
||||||
|
'genre': document.genre,
|
||||||
|
'is_duplicate': document.is_duplicate,
|
||||||
|
'is_whitelisted': document.is_whitelisted,
|
||||||
|
'is_error': document.is_error
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'ticket_id': ticket_id,
|
||||||
|
'genre': genre,
|
||||||
|
'reporter_id': reporter_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketItemStorageGetException()
|
||||||
|
|
||||||
|
def get_all_items_with_genre_by_ticket_for_provider(self, ticket_id: str, genre: str, provider_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Gets all ticket items. Values only, by ticket_id.
|
||||||
|
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER
|
||||||
|
document.ticket_id == @ticket_id AND
|
||||||
|
document.genre == @genre AND
|
||||||
|
document.is_active == true AND
|
||||||
|
document.is_duplicate == false AND
|
||||||
|
document.is_whitelisted == false AND
|
||||||
|
document.is_error == false AND
|
||||||
|
document.provider_id == @provider_id
|
||||||
|
|
||||||
|
// ensure only available tickets are considered
|
||||||
|
FOR parent_ticket IN {self.ticket_collection_name}
|
||||||
|
FILTER
|
||||||
|
parent_ticket.ticket_id == document.ticket_id AND
|
||||||
|
parent_ticket.status.ticket != 'created'
|
||||||
|
|
||||||
|
RETURN DISTINCT document.value
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'ticket_id': ticket_id,
|
||||||
|
'genre': genre,
|
||||||
|
'provider_id': provider_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketItemStorageGetException()
|
||||||
|
|
||||||
|
def get_all_items_available_by_ticket(self, ticket_id: str, account_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Gets all the ticket items that aren't a duplicate, whitelisted or errors.
|
||||||
|
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER
|
||||||
|
document.ticket_id == @ticket_id AND
|
||||||
|
document.is_active == true AND
|
||||||
|
document.is_duplicate == false AND
|
||||||
|
document.is_whitelisted == false AND
|
||||||
|
document.is_error == false
|
||||||
|
|
||||||
|
FOR ticket IN {self.ticket_collection_name}
|
||||||
|
|
||||||
|
FILTER
|
||||||
|
ticket.ticket_id == @ticket_id AND
|
||||||
|
ticket.metadata.created_by == @account_id
|
||||||
|
|
||||||
|
RETURN DISTINCT document.value
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'ticket_id': ticket_id,
|
||||||
|
'account_id': account_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketItemStorageGetException()
|
||||||
|
|
||||||
|
def get(self, ticket_id: str, value: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Gets a single item for a ticket.
|
||||||
|
|
||||||
|
:param ticket_id: ticket identifier.
|
||||||
|
:param value: item value.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER
|
||||||
|
document.ticket_id == @ticket_id AND
|
||||||
|
document.value == @value
|
||||||
|
|
||||||
|
RETURN {{
|
||||||
|
'ticket_id': document.ticket_id,
|
||||||
|
'value': document.value,
|
||||||
|
'genre': document.genre,
|
||||||
|
'status': document.status,
|
||||||
|
'reason': document.reason,
|
||||||
|
'provider_id': document.provider_id,
|
||||||
|
'created_at': document.created_at,
|
||||||
|
'updated_at': document.updated_at
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'ticket_id': ticket_id,
|
||||||
|
'value': value
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketItemStorageGetException()
|
||||||
|
|
||||||
|
def get_by_value(self, provider_id: str, value: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Gets a single item by its value.
|
||||||
|
|
||||||
|
:param ticket_id: ticket identifier.
|
||||||
|
:param value: item value.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER
|
||||||
|
document.provider_id == @provider_id AND
|
||||||
|
document.value == @value AND
|
||||||
|
document.is_active == true AND
|
||||||
|
document.is_duplicate == false AND
|
||||||
|
document.is_whitelisted == false AND
|
||||||
|
document.is_error == false
|
||||||
|
|
||||||
|
RETURN {{
|
||||||
|
'value': document.value,
|
||||||
|
'genre': document.genre,
|
||||||
|
'metadata': {{
|
||||||
|
'created_at': document.metadata.created_at
|
||||||
|
}},
|
||||||
|
'settings': document.settings
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'provider_id': provider_id,
|
||||||
|
'value': value
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketItemStorageGetException()
|
||||||
|
|
||||||
|
def get_all_ticket_items_by(self, ticket_id: str, account_id: str, genre: str, status: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Gets all the items filtered by genre and associated account identifier.
|
||||||
|
|
||||||
|
:param ticket_id: ticket identifier.
|
||||||
|
:param account_id: provider identifier.
|
||||||
|
:param genre: genre of the ticket item.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER
|
||||||
|
document.ticket_id == @ticket_id AND
|
||||||
|
account_id == @account_id AND
|
||||||
|
genre == @genre AND
|
||||||
|
status == @status
|
||||||
|
|
||||||
|
RETURN {{
|
||||||
|
'ticket_id': document.ticket_id,
|
||||||
|
'value': document.value,
|
||||||
|
'genre': document.genre,
|
||||||
|
'status': document.status,
|
||||||
|
'reason': document.reason,
|
||||||
|
'provider_id': document.provider_id,
|
||||||
|
'metadata': {{
|
||||||
|
'created_at': document.metadata.created_at
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'ticket_id': ticket_id,
|
||||||
|
'account_id': account_id,
|
||||||
|
'genre': genre,
|
||||||
|
'status': status
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketItemStorageGetException()
|
||||||
|
|
||||||
|
def get_all(self, ticket_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Gets all the items for a ticket.
|
||||||
|
|
||||||
|
:param ticket_id: ticket identifier.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.ticket_id == @ticket_id
|
||||||
|
|
||||||
|
RETURN {{
|
||||||
|
'ticket_id': document.ticket_id,
|
||||||
|
'ticket_item_id': document.ticket_item_id,
|
||||||
|
'value': document.value,
|
||||||
|
'genre': document.genre,
|
||||||
|
'status': document.status,
|
||||||
|
'reason': document.reason,
|
||||||
|
'provider_id': document.provider_id,
|
||||||
|
'metadata': {{
|
||||||
|
'created_at': document.metadata.created_at,
|
||||||
|
'updated_at': document.metadata.updated_at
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'ticket_id': ticket_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketItemStorageGetException()
|
||||||
|
|
||||||
|
def get_all_by_provider(self, provider_id: str, ticket_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Gets all the items for a ticket by a specific provider_id.
|
||||||
|
|
||||||
|
:param provider_id: the provider_id.
|
||||||
|
:param ticket_id: ticket identifier.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER
|
||||||
|
document.ticket_id == @ticket_id AND
|
||||||
|
document.provider_id == @provider_id AND
|
||||||
|
document.is_active == true AND
|
||||||
|
document.is_duplicate == false AND
|
||||||
|
document.is_whitelisted == false AND
|
||||||
|
document.is_error == false
|
||||||
|
|
||||||
|
RETURN {{
|
||||||
|
'value': document.value,
|
||||||
|
'status': document.status,
|
||||||
|
'timestamp': document.timestamp,
|
||||||
|
'note': document.note,
|
||||||
|
'reason': document.reason
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'provider_id': provider_id,
|
||||||
|
'ticket_id': ticket_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketItemStorageGetException()
|
||||||
|
|
||||||
|
def get_details(self, ticket_id: str, ticket_item_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Gets all the items for a ticket.
|
||||||
|
|
||||||
|
:param ticket_id: ticket identifier.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER
|
||||||
|
document.ticket_id == @ticket_id AND
|
||||||
|
document.ticket_item_id == @ticket_item_id
|
||||||
|
|
||||||
|
LET provider_details = (
|
||||||
|
FOR a IN accounts_view
|
||||||
|
|
||||||
|
FILTER a.account_id == document.provider_id
|
||||||
|
|
||||||
|
RETURN {{
|
||||||
|
'account_id': a.account_id,
|
||||||
|
'name': a.name
|
||||||
|
}}
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
RETURN {{
|
||||||
|
'ticket_id': document.ticket_id,
|
||||||
|
'ticket_item_id': document.ticket_item_id,
|
||||||
|
'value': document.value,
|
||||||
|
'genre': document.genre,
|
||||||
|
'status': document.status,
|
||||||
|
'reason': document.reason,
|
||||||
|
'is_active': document.is_active,
|
||||||
|
'is_duplicate': document.is_duplicate,
|
||||||
|
'is_whitelisted': document.is_whitelisted,
|
||||||
|
'is_error': document.is_error,
|
||||||
|
'provider': {{
|
||||||
|
'account_id': provider_details.account_id,
|
||||||
|
'name': provider_details.name
|
||||||
|
}},
|
||||||
|
'metadata': {{
|
||||||
|
'created_at': document.metadata.created_at,
|
||||||
|
'updated_at': document.metadata.updated_at
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'ticket_id': ticket_id,
|
||||||
|
'ticket_item_id': ticket_item_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketItemStorageGetException()
|
||||||
|
|
||||||
|
def get_all_status(self, ticket_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Gets all ticket items statuses.
|
||||||
|
|
||||||
|
:param ticket_id: ticket identifier.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.ticket_id == @ticket_id
|
||||||
|
|
||||||
|
RETURN DISTINCT {{
|
||||||
|
'ticket_item_id': document.ticket_item_id,
|
||||||
|
'value': document.value,
|
||||||
|
'genre': document.genre,
|
||||||
|
'is_active': document.is_active,
|
||||||
|
'is_whitelisted': document.is_whitelisted,
|
||||||
|
'is_error': document.is_error
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'ticket_id': ticket_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketItemStorageGetException()
|
||||||
|
|
||||||
|
def exists_by_value(self, genre: str, value: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Searches for a duplicate.
|
||||||
|
|
||||||
|
:param genre: the genre of the ticket item.
|
||||||
|
:param genre: the value of the ticket item.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER
|
||||||
|
document.genre == @genre AND
|
||||||
|
document.value == @value AND
|
||||||
|
document.is_active == true AND
|
||||||
|
document.is_duplicate == false AND
|
||||||
|
document.is_whitelisted == false
|
||||||
|
|
||||||
|
RETURN DISTINCT {{
|
||||||
|
'ticket_id': document.ticket_id,
|
||||||
|
'genre': document.genre,
|
||||||
|
'value': document.value
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'genre': genre,
|
||||||
|
'value': value
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketItemStorageGetException()
|
||||||
|
|
||||||
|
def update_status_by_value(self,
|
||||||
|
provider_id: str,
|
||||||
|
value: str,
|
||||||
|
status: str,
|
||||||
|
updated_at: str,
|
||||||
|
status_reason: str = None,
|
||||||
|
timestamp: str = None,
|
||||||
|
note: str = None
|
||||||
|
) -> list | Exception:
|
||||||
|
"""
|
||||||
|
Sets the item status by its value.
|
||||||
|
|
||||||
|
:param provider_id: the id of the provider account.
|
||||||
|
:param value: item value.
|
||||||
|
:param status: item status value.
|
||||||
|
:param updated_at: a timestamp of the update date.
|
||||||
|
:param status_reason: a valid predefined reason for unprocessed items.
|
||||||
|
:param timestamp: a timestamp of the update date set by the provider account.
|
||||||
|
:param note: a generic text set by the provider account.
|
||||||
|
:return: true if the query has been processed successfully.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER
|
||||||
|
document.value == @value AND
|
||||||
|
document.provider_id == @provider_id AND
|
||||||
|
document.is_active == true AND
|
||||||
|
document.is_duplicate == false AND
|
||||||
|
document.is_whitelisted == false AND
|
||||||
|
document.is_error == false
|
||||||
|
|
||||||
|
// prevent editing a ticket item with a non workable ticket
|
||||||
|
FOR parent_ticket IN {self.ticket_collection_name}
|
||||||
|
FILTER
|
||||||
|
parent_ticket.ticket_id == document.ticket_id AND
|
||||||
|
parent_ticket.status.ticket != 'created'
|
||||||
|
|
||||||
|
UPDATE document WITH {{
|
||||||
|
status: @status,
|
||||||
|
reason: @status_reason,
|
||||||
|
timestamp: @timestamp,
|
||||||
|
note: @note,
|
||||||
|
metadata: {{
|
||||||
|
updated_at: @updated_at
|
||||||
|
}}
|
||||||
|
}} IN {self.collection_name}
|
||||||
|
|
||||||
|
RETURN NEW
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
affected_rows = self.query(
|
||||||
|
aql,
|
||||||
|
bind_vars = {
|
||||||
|
'provider_id': provider_id,
|
||||||
|
'value': value,
|
||||||
|
'status': status,
|
||||||
|
'updated_at': updated_at,
|
||||||
|
'status_reason': status_reason,
|
||||||
|
'timestamp': timestamp,
|
||||||
|
'note': note
|
||||||
|
},
|
||||||
|
count = True
|
||||||
|
)
|
||||||
|
|
||||||
|
return affected_rows
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketItemStorageUpdateException()
|
||||||
|
|
||||||
|
def set_flag_active(self, value: str, status: str) -> list | Exception:
|
||||||
|
"""
|
||||||
|
Sets the item activity status.
|
||||||
|
|
||||||
|
:param value: item value.
|
||||||
|
:param status: item status value.
|
||||||
|
:return: true if the query has been processed successfully.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.value == @value
|
||||||
|
|
||||||
|
UPDATE document WITH {{
|
||||||
|
is_active: @status
|
||||||
|
}} IN {self.collection_name}
|
||||||
|
|
||||||
|
RETURN NEW
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
affected_rows = self.query(
|
||||||
|
aql,
|
||||||
|
bind_vars = {
|
||||||
|
'value': value,
|
||||||
|
'status': status
|
||||||
|
},
|
||||||
|
count = True
|
||||||
|
)
|
||||||
|
|
||||||
|
return affected_rows
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketItemStorageUpdateException()
|
||||||
|
|
||||||
|
def set_flag_error(self, ticket_id: str, value: str, status: bool) -> list | Exception:
|
||||||
|
"""
|
||||||
|
Sets the error flag.
|
||||||
|
|
||||||
|
:param ticket_id: a valid ticket identifier.
|
||||||
|
:param value: item value.
|
||||||
|
:param status: true or false if status is error or not.
|
||||||
|
:return: a list of affected rows.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER
|
||||||
|
document.ticket_id == @ticket_id AND
|
||||||
|
document.value == @value
|
||||||
|
|
||||||
|
UPDATE document WITH {{
|
||||||
|
is_error: @status
|
||||||
|
}} IN {self.collection_name}
|
||||||
|
|
||||||
|
RETURN NEW
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
affected_rows = self.query(
|
||||||
|
aql,
|
||||||
|
bind_vars = {
|
||||||
|
'ticket_id': ticket_id,
|
||||||
|
'value': value,
|
||||||
|
'status': status
|
||||||
|
},
|
||||||
|
count = True
|
||||||
|
)
|
||||||
|
|
||||||
|
return affected_rows
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketItemStorageUpdateException()
|
||||||
|
|
||||||
|
def remove(self, ticket_id: str, value: str) -> list | Exception:
|
||||||
|
"""
|
||||||
|
Removes a ticket item.
|
||||||
|
|
||||||
|
:param ticket_id: ticket identifier.
|
||||||
|
:param value: item to remove.
|
||||||
|
:return: true if the query has been processed successfully.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER
|
||||||
|
document.ticket_id == @ticket_id AND
|
||||||
|
document.value == @value
|
||||||
|
|
||||||
|
REMOVE document IN {self.collection_name}
|
||||||
|
|
||||||
|
RETURN OLD
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
affected_rows = self.query(
|
||||||
|
aql,
|
||||||
|
bind_vars = {
|
||||||
|
'ticket_id': ticket_id,
|
||||||
|
'value': value
|
||||||
|
},
|
||||||
|
count = True
|
||||||
|
)
|
||||||
|
|
||||||
|
return affected_rows
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketItemStorageRemoveException()
|
||||||
|
|
||||||
|
def remove_all(self, ticket_id: str) -> list | Exception:
|
||||||
|
"""
|
||||||
|
Removes all the ticket items.
|
||||||
|
|
||||||
|
:param ticket_id: ticket identifier.
|
||||||
|
:param value: item to remove.
|
||||||
|
:return: true if the query has been processed successfully.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.ticket_id == @ticket_id
|
||||||
|
|
||||||
|
REMOVE document IN {self.collection_name}
|
||||||
|
|
||||||
|
RETURN OLD
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
affected_rows = self.query(
|
||||||
|
aql,
|
||||||
|
bind_vars = {
|
||||||
|
'ticket_id': ticket_id
|
||||||
|
},
|
||||||
|
count = True
|
||||||
|
)
|
||||||
|
|
||||||
|
return affected_rows
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketItemStorageRemoveException()
|
||||||
|
|
||||||
|
class TicketItemStorageCreateException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot create the ticket item.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TicketItemStorageGetException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot get the ticket item.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TicketItemStorageUpdateException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot update the ticket item.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TicketItemStorageRemoveException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot remove the ticket item.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
529
src/piracyshield_data_storage/ticket/storage.py
Normal file
529
src/piracyshield_data_storage/ticket/storage.py
Normal file
|
@ -0,0 +1,529 @@
|
||||||
|
from piracyshield_data_storage.database.arangodb.document import DatabaseArangodbDocument
|
||||||
|
|
||||||
|
from arango.cursor import Cursor
|
||||||
|
|
||||||
|
class TicketStorage(DatabaseArangodbDocument):
|
||||||
|
|
||||||
|
ticket_item_collection_name = 'ticket_blocking_items'
|
||||||
|
|
||||||
|
collection_name = 'ticket_blockings'
|
||||||
|
|
||||||
|
collection_instance = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.collection_instance = self.collection(self.collection_name)
|
||||||
|
|
||||||
|
def insert(self, document: dict) -> dict | Exception:
|
||||||
|
"""
|
||||||
|
Adds a new ticket.
|
||||||
|
|
||||||
|
:param document: dictionary with the expected ticket data model values.
|
||||||
|
:return: cursor with the inserted data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.collection_instance.insert(document)
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketStorageCreateException()
|
||||||
|
|
||||||
|
def get(self, ticket_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Gets a single ticket document.
|
||||||
|
|
||||||
|
:param ticket_id: ticket identifier.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.ticket_id == @ticket_id
|
||||||
|
|
||||||
|
LET created_by_name = (
|
||||||
|
FOR a IN accounts_view
|
||||||
|
FILTER a.account_id == document.metadata.created_by
|
||||||
|
RETURN a.name
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
LET assigned_to_details = (
|
||||||
|
FOR i IN document.assigned_to
|
||||||
|
|
||||||
|
FOR a IN accounts_view
|
||||||
|
|
||||||
|
FILTER a.account_id == i
|
||||||
|
|
||||||
|
RETURN {{
|
||||||
|
'account_id': a.account_id,
|
||||||
|
'name': a.name
|
||||||
|
}}
|
||||||
|
)
|
||||||
|
|
||||||
|
RETURN {{
|
||||||
|
'ticket_id': document.ticket_id,
|
||||||
|
'description': document.description,
|
||||||
|
'fqdn': document.fqdn,
|
||||||
|
'ipv4': document.ipv4,
|
||||||
|
'ipv6': document.ipv6,
|
||||||
|
'assigned_to': assigned_to_details,
|
||||||
|
'status': document.status,
|
||||||
|
'metadata': {{
|
||||||
|
'created_at': document.metadata.created_at,
|
||||||
|
'updated_at': document.metadata.updated_at,
|
||||||
|
'created_by': document.metadata.created_by,
|
||||||
|
'created_by_name': created_by_name
|
||||||
|
}},
|
||||||
|
'settings': document.settings,
|
||||||
|
'tasks': document.tasks
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'ticket_id': ticket_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketStorageGetException()
|
||||||
|
|
||||||
|
def has_dda_id(self, dda_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Finds a ticket with the specified DDA identifier.
|
||||||
|
|
||||||
|
:param dda_id: a valid DDA identifier.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.dda_id == @dda_id
|
||||||
|
|
||||||
|
LIMIT 1
|
||||||
|
|
||||||
|
RETURN document
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'dda_id': dda_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketStorageGetException()
|
||||||
|
|
||||||
|
def get_reporter(self, ticket_id: str, account_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Gets a single ticket document with limitations.
|
||||||
|
|
||||||
|
:param ticket_id: ticket identifier.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER
|
||||||
|
document.ticket_id == @ticket_id AND
|
||||||
|
document.metadata.created_by == @account_id
|
||||||
|
|
||||||
|
RETURN {{
|
||||||
|
'ticket_id': document.ticket_id,
|
||||||
|
'description': document.description,
|
||||||
|
'fqdn': document.fqdn,
|
||||||
|
'ipv4': document.ipv4,
|
||||||
|
'ipv6': document.ipv6,
|
||||||
|
'status': document.status,
|
||||||
|
'metadata': {{
|
||||||
|
'created_at': document.metadata.created_at
|
||||||
|
}},
|
||||||
|
'settings': {{
|
||||||
|
'revoke_time': document.settings.revoke_time,
|
||||||
|
'autoclose_time': document.settings.autoclose_time
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'ticket_id': ticket_id,
|
||||||
|
'account_id': account_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketStorageGetException()
|
||||||
|
|
||||||
|
def get_provider(self, ticket_id: str, account_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Gets a single ticket document with limitations.
|
||||||
|
|
||||||
|
:param ticket_id: ticket identifier.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER
|
||||||
|
document.ticket_id == @ticket_id AND
|
||||||
|
document.status != 'created' AND
|
||||||
|
POSITION(document.assigned_to, @account_id) == true
|
||||||
|
|
||||||
|
LET ticket_items = (
|
||||||
|
FOR ticket_item in {self.ticket_item_collection_name}
|
||||||
|
|
||||||
|
FILTER
|
||||||
|
ticket_item.ticket_id == document.ticket_id AND
|
||||||
|
ticket_item.is_active == true AND
|
||||||
|
ticket_item.is_duplicate == false AND
|
||||||
|
ticket_item.is_whitelisted == false AND
|
||||||
|
ticket_item.is_error == false
|
||||||
|
|
||||||
|
COLLECT genre = ticket_item.genre INTO groupedItems
|
||||||
|
FILTER genre IN ["fqdn", "ipv4", "ipv6"]
|
||||||
|
RETURN {{
|
||||||
|
"genre": genre,
|
||||||
|
"items": UNIQUE(groupedItems[*].ticket_item.value)
|
||||||
|
}}
|
||||||
|
)
|
||||||
|
|
||||||
|
FILTER LENGTH(ticket_items) != 0
|
||||||
|
|
||||||
|
LET fqdn_items = (FOR item IN ticket_items FILTER item.genre == 'fqdn' RETURN item.items)
|
||||||
|
LET ipv4_items = (FOR item IN ticket_items FILTER item.genre == 'ipv4' RETURN item.items)
|
||||||
|
LET ipv6_items = (FOR item IN ticket_items FILTER item.genre == 'ipv6' RETURN item.items)
|
||||||
|
|
||||||
|
RETURN {{
|
||||||
|
'ticket_id': document.ticket_id,
|
||||||
|
'description': document.description,
|
||||||
|
'fqdn': fqdn_items[0] or [],
|
||||||
|
'ipv4': ipv4_items[0] or [],
|
||||||
|
'ipv6': ipv6_items[0] or [],
|
||||||
|
'status': document.status,
|
||||||
|
'metadata': {{
|
||||||
|
'created_at': document.metadata.created_at
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'ticket_id': ticket_id,
|
||||||
|
'account_id': account_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketStorageGetException()
|
||||||
|
|
||||||
|
def get_all(self) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Fetches all the tickets.
|
||||||
|
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
// resolve account identifier
|
||||||
|
LET created_by_name = (
|
||||||
|
FOR a IN accounts_view
|
||||||
|
FILTER a.account_id == document.metadata.created_by
|
||||||
|
RETURN a['name']
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
RETURN {{
|
||||||
|
'ticket_id': document.ticket_id,
|
||||||
|
'description': document.description,
|
||||||
|
'fqdn': document.fqdn,
|
||||||
|
'ipv4': document.ipv4,
|
||||||
|
'ipv6': document.ipv6,
|
||||||
|
'assigned_to': document.assigned_to,
|
||||||
|
'status': document.status,
|
||||||
|
'metadata': {{
|
||||||
|
'created_at': document.metadata.created_at,
|
||||||
|
'updated_at': document.metadata.updated_at,
|
||||||
|
'created_by': document.metadata.created_by,
|
||||||
|
'created_by_name': created_by_name
|
||||||
|
}},
|
||||||
|
'settings': document.settings,
|
||||||
|
'tasks': document.tasks
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql)
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketStorageGetException()
|
||||||
|
|
||||||
|
def get_all_reporter(self, account_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Fetches all the tickets for a single account.
|
||||||
|
|
||||||
|
:param account_id: the identifier of the ticket's creator.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.metadata.created_by == @account_id
|
||||||
|
|
||||||
|
RETURN {{
|
||||||
|
'ticket_id': document.ticket_id,
|
||||||
|
'description': document.description,
|
||||||
|
'fqdn': document.fqdn,
|
||||||
|
'ipv4': document.ipv4,
|
||||||
|
'ipv6': document.ipv6,
|
||||||
|
'status': document.status,
|
||||||
|
'metadata': {{
|
||||||
|
'created_at': document.metadata.created_at
|
||||||
|
}},
|
||||||
|
'settings': {{
|
||||||
|
'revoke_time': document.settings.revoke_time
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'account_id': account_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketStorageGetException()
|
||||||
|
|
||||||
|
def get_all_provider(self, account_id) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Fetches all the tickets with limited parameters.
|
||||||
|
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER
|
||||||
|
document.status != 'created' AND
|
||||||
|
POSITION(document.assigned_to, @account_id) == true
|
||||||
|
|
||||||
|
LET ticket_items = (
|
||||||
|
FOR ticket_item in {self.ticket_item_collection_name}
|
||||||
|
|
||||||
|
FILTER
|
||||||
|
ticket_item.ticket_id == document.ticket_id AND
|
||||||
|
ticket_item.is_active == true AND
|
||||||
|
ticket_item.is_duplicate == false AND
|
||||||
|
ticket_item.is_whitelisted == false AND
|
||||||
|
ticket_item.is_error == false
|
||||||
|
|
||||||
|
COLLECT genre = ticket_item.genre INTO groupedItems
|
||||||
|
FILTER genre IN ["fqdn", "ipv4", "ipv6"]
|
||||||
|
RETURN {{
|
||||||
|
"genre": genre,
|
||||||
|
"items": UNIQUE(groupedItems[*].ticket_item.value)
|
||||||
|
}}
|
||||||
|
)
|
||||||
|
|
||||||
|
FILTER LENGTH(ticket_items) != 0
|
||||||
|
|
||||||
|
LET fqdn_items = (FOR item IN ticket_items FILTER item.genre == 'fqdn' RETURN item.items)
|
||||||
|
LET ipv4_items = (FOR item IN ticket_items FILTER item.genre == 'ipv4' RETURN item.items)
|
||||||
|
LET ipv6_items = (FOR item IN ticket_items FILTER item.genre == 'ipv6' RETURN item.items)
|
||||||
|
|
||||||
|
RETURN {{
|
||||||
|
'ticket_id': document.ticket_id,
|
||||||
|
'description': document.description,
|
||||||
|
'fqdn': fqdn_items[0] or [],
|
||||||
|
'ipv4': ipv4_items[0] or [],
|
||||||
|
'ipv6': ipv6_items[0] or [],
|
||||||
|
'status': document.status,
|
||||||
|
'metadata': {{
|
||||||
|
'created_at': document.metadata.created_at
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'account_id': account_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketStorageGetException()
|
||||||
|
|
||||||
|
def get_total(self) -> int | Exception:
|
||||||
|
"""
|
||||||
|
Total documents in the tickets collection.
|
||||||
|
|
||||||
|
:return: total number of documents.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.collection_instance.count()
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketStorageGetException()
|
||||||
|
|
||||||
|
def exists_by_identifier(self, ticket_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Checks if a ticket with this identifier is in the collection.
|
||||||
|
|
||||||
|
:param value: a valid ticket identifier.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.ticket_id == @ticket_id
|
||||||
|
|
||||||
|
LIMIT 1
|
||||||
|
|
||||||
|
RETURN document
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'ticket_id': ticket_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketStorageGetException()
|
||||||
|
|
||||||
|
def update_task_list(self, ticket_id: str, task_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Appends a new task.
|
||||||
|
|
||||||
|
:param ticket_id: ticket identifier.
|
||||||
|
:param task_id: task identifier.
|
||||||
|
:return: list of updated rows.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
FILTER document.ticket_id == @ticket_id
|
||||||
|
|
||||||
|
UPDATE document WITH {{ "tasks": PUSH(document.tasks, @task_id) }} IN {self.collection_name}
|
||||||
|
|
||||||
|
RETURN NEW
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
affected_rows = self.query(
|
||||||
|
aql,
|
||||||
|
bind_vars = {
|
||||||
|
'ticket_id': ticket_id,
|
||||||
|
'task_id': task_id
|
||||||
|
},
|
||||||
|
count = True
|
||||||
|
)
|
||||||
|
|
||||||
|
return affected_rows
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketStorageUpdateException()
|
||||||
|
|
||||||
|
def update_status(self, ticket_id: str, ticket_status: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Sets the ticket status.
|
||||||
|
Used by tasks to update from `CREATED` to `OPEN` and from `OPEN` to `CLOSED` once each time expires.
|
||||||
|
|
||||||
|
:param ticket_id: ticket identifier.
|
||||||
|
:param ticket_status: ticket status value.
|
||||||
|
:return: list of updated rows.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.ticket_id == @ticket_id
|
||||||
|
|
||||||
|
UPDATE document WITH {{
|
||||||
|
'status': @ticket_status
|
||||||
|
}} IN {self.collection_name}
|
||||||
|
|
||||||
|
RETURN NEW
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
affected_rows = self.query(
|
||||||
|
aql,
|
||||||
|
bind_vars = {
|
||||||
|
'ticket_id': ticket_id,
|
||||||
|
'ticket_status': ticket_status
|
||||||
|
},
|
||||||
|
count = True
|
||||||
|
)
|
||||||
|
|
||||||
|
return affected_rows
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketStorageUpdateException()
|
||||||
|
|
||||||
|
def remove(self, ticket_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Removes a ticket.
|
||||||
|
|
||||||
|
:param ticket_id: ticket identifier.
|
||||||
|
:return: list of removed rows.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.ticket_id == @ticket_id
|
||||||
|
|
||||||
|
REMOVE document IN {self.collection_name}
|
||||||
|
|
||||||
|
RETURN OLD
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
affected_rows = self.query(
|
||||||
|
aql,
|
||||||
|
bind_vars = {
|
||||||
|
'ticket_id': ticket_id
|
||||||
|
},
|
||||||
|
count = True
|
||||||
|
)
|
||||||
|
|
||||||
|
return affected_rows
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise TicketStorageRemoveException()
|
||||||
|
|
||||||
|
class TicketStorageCreateException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot create the ticket.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TicketStorageGetException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot get the ticket.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TicketStorageUpdateException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot update the ticket.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TicketStorageRemoveException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot remove the ticket.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
1
src/piracyshield_data_storage/whitelist/__init__.py
Normal file
1
src/piracyshield_data_storage/whitelist/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
227
src/piracyshield_data_storage/whitelist/storage.py
Normal file
227
src/piracyshield_data_storage/whitelist/storage.py
Normal file
|
@ -0,0 +1,227 @@
|
||||||
|
from piracyshield_data_storage.database.arangodb.document import DatabaseArangodbDocument
|
||||||
|
|
||||||
|
from arango.cursor import Cursor
|
||||||
|
|
||||||
|
class WhitelistStorage(DatabaseArangodbDocument):
|
||||||
|
|
||||||
|
collection_name = 'whitelist'
|
||||||
|
|
||||||
|
collection_instance = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.collection_instance = self.collection(self.collection_name)
|
||||||
|
|
||||||
|
def insert(self, document: dict) -> dict | Exception:
|
||||||
|
"""
|
||||||
|
Adds a new whitelist item.
|
||||||
|
|
||||||
|
:param document: dictionary with the expected data model values.
|
||||||
|
:return: cursor with the inserted data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.collection_instance.insert(document)
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise WhitelistStorageCreateException()
|
||||||
|
|
||||||
|
def get_all(self, account_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Fetches all the whitelist items created by an account.
|
||||||
|
|
||||||
|
:param account_id: a valid account identifier.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.metadata.created_by == @account_id
|
||||||
|
|
||||||
|
SORT document.value ASC
|
||||||
|
|
||||||
|
RETURN {{
|
||||||
|
'genre': document.genre,
|
||||||
|
'value': document.value,
|
||||||
|
'is_active': document.is_active,
|
||||||
|
'metadata': {{
|
||||||
|
'created_at': document.metadata.created_at
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'account_id': account_id
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise WhitelistStorageGetException()
|
||||||
|
|
||||||
|
def get_global(self) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Fetches the whitelist items of all the accounts.
|
||||||
|
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
// resolve account identifier
|
||||||
|
LET created_by_name = (
|
||||||
|
FOR a IN accounts_view
|
||||||
|
FILTER a.account_id == document.metadata.created_by
|
||||||
|
RETURN a['name']
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
SORT document.value ASC
|
||||||
|
|
||||||
|
RETURN {{
|
||||||
|
'genre': document.genre,
|
||||||
|
'value': document.value,
|
||||||
|
'is_active': document.is_active,
|
||||||
|
'metadata': {{
|
||||||
|
'created_at': document.metadata.created_at,
|
||||||
|
'updated_at': document.metadata.updated_at,
|
||||||
|
'created_by': document.metadata.created_by,
|
||||||
|
'created_by_name': created_by_name
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql)
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise WhitelistStorageGetException()
|
||||||
|
|
||||||
|
def exists_by_value(self, value: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Searches for an item.
|
||||||
|
|
||||||
|
:param value: item value.
|
||||||
|
:return: cursor with the requested data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.value == @value
|
||||||
|
|
||||||
|
RETURN document
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.query(aql, bind_vars = {
|
||||||
|
'value': value
|
||||||
|
})
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise WhitelistStorageGetException()
|
||||||
|
|
||||||
|
def update_status(self, value: str, status: bool) -> list | Exception:
|
||||||
|
"""
|
||||||
|
Sets the item status.
|
||||||
|
|
||||||
|
:param value: item value.
|
||||||
|
:param status: true/false if active/non active.
|
||||||
|
:return: true if the query has been processed successfully.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER document.value == @value
|
||||||
|
|
||||||
|
UPDATE document WITH {{
|
||||||
|
is_active: @status
|
||||||
|
}} IN {self.collection_name}
|
||||||
|
|
||||||
|
RETURN NEW
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
affected_rows = self.query(
|
||||||
|
aql,
|
||||||
|
bind_vars = {
|
||||||
|
'value': value,
|
||||||
|
'status': status
|
||||||
|
},
|
||||||
|
count = True
|
||||||
|
)
|
||||||
|
|
||||||
|
return affected_rows
|
||||||
|
|
||||||
|
except:
|
||||||
|
return WhitelistStorageUpdateException()
|
||||||
|
|
||||||
|
def remove(self, value: str, account_id: str) -> Cursor | Exception:
|
||||||
|
"""
|
||||||
|
Removes a whitelist item created by an account.
|
||||||
|
|
||||||
|
:param value: item value.
|
||||||
|
:param account_id: a valid account identifier.
|
||||||
|
:return: true if the query has been processed successfully.
|
||||||
|
"""
|
||||||
|
|
||||||
|
aql = f"""
|
||||||
|
FOR document IN {self.collection_name}
|
||||||
|
|
||||||
|
FILTER
|
||||||
|
document.value == @value AND
|
||||||
|
document.metadata.created_by == @account_id
|
||||||
|
|
||||||
|
REMOVE document IN {self.collection_name}
|
||||||
|
|
||||||
|
RETURN OLD
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
affected_rows = self.query(
|
||||||
|
aql,
|
||||||
|
bind_vars = {
|
||||||
|
'value': value,
|
||||||
|
'account_id': account_id
|
||||||
|
},
|
||||||
|
count = True
|
||||||
|
)
|
||||||
|
|
||||||
|
return affected_rows
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise WhitelistStorageRemoveException()
|
||||||
|
|
||||||
|
class WhitelistStorageCreateException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot create the whitelist item.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class WhitelistStorageGetException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot get the whitelist item.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class WhitelistStorageUpdateException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot update the whitelist item.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class WhitelistStorageRemoveException(Exception):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Cannot remove the whitelist item.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
Loading…
Reference in a new issue