This repository has been archived on 2024-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
devicehub-teal/ereuse_devicehub/resources/user/models.py

298 lines
9.3 KiB
Python
Raw Normal View History

2022-11-22 18:12:11 +00:00
import json
2023-11-06 12:23:47 +00:00
import requests
2018-04-27 17:16:43 +00:00
from uuid import uuid4
2022-11-22 18:12:11 +00:00
from citext import CIText
2023-06-02 07:54:14 +00:00
from ereuseapi.methods import API
from flask import current_app as app
2023-03-15 14:49:35 +00:00
from flask import g, session
from flask_login import UserMixin
2022-04-06 10:35:08 +00:00
from sqlalchemy import BigInteger, Boolean, Column, Sequence
2018-04-27 17:16:43 +00:00
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy_utils import EmailType, PasswordType
2018-04-27 17:16:43 +00:00
2019-01-21 15:08:55 +00:00
from ereuse_devicehub.db import db
2022-04-06 10:35:08 +00:00
from ereuse_devicehub.resources.enums import SessionType
2019-01-21 15:08:55 +00:00
from ereuse_devicehub.resources.inventory.model import Inventory
from ereuse_devicehub.resources.models import STR_SIZE, Thing
2023-03-21 11:08:13 +00:00
from ereuse_devicehub.teal.db import CASCADE_OWN, URL, IntEnum
2018-04-27 17:16:43 +00:00
class User(UserMixin, Thing):
2018-04-27 17:16:43 +00:00
__table_args__ = {'schema': 'common'}
id = Column(UUID(as_uuid=True), default=uuid4, primary_key=True)
email = Column(EmailType, nullable=False, unique=True)
2022-04-06 10:35:08 +00:00
password = Column(
PasswordType(
max_length=STR_SIZE,
onload=lambda **kwargs: dict(
schemes=app.config['PASSWORD_SCHEMES'], **kwargs
),
)
)
2019-01-23 15:55:04 +00:00
token = Column(UUID(as_uuid=True), default=uuid4, unique=True, nullable=False)
active = Column(Boolean, default=True, nullable=False)
phantom = Column(Boolean, default=False, nullable=False)
2022-11-22 18:12:11 +00:00
api_keys_dlt = Column(CIText(), nullable=True)
2023-06-14 14:06:10 +00:00
rols_dlt = Column(CIText(), nullable=True)
2022-04-06 10:35:08 +00:00
inventories = db.relationship(
Inventory,
backref=db.backref('users', lazy=True, collection_class=set),
secondary=lambda: UserInventory.__table__,
collection_class=set,
)
2019-01-23 15:55:04 +00:00
# todo set restriction that user has, at least, one active db
2023-05-12 10:05:41 +00:00
def get_user_id(self):
return self.id
2022-04-06 10:35:08 +00:00
def __init__(
self, email, password=None, inventories=None, active=True, phantom=False
) -> None:
"""Creates an user.
2019-01-23 15:55:04 +00:00
:param email:
:param password:
:param inventories: A set of Inventory where the user has
access to. If none, the user is granted access to the current
inventory.
:param active: allow active and deactive one account without delete the account
:param phantom: it's util for identify the phantom accounts
create during the trade actions
2019-01-23 15:55:04 +00:00
"""
inventories = inventories or {Inventory.current}
2022-04-06 10:35:08 +00:00
super().__init__(
email=email,
password=password,
inventories=inventories,
active=active,
phantom=phantom,
)
def __repr__(self) -> str:
return '<User {0.email}>'.format(self)
@property
def type(self) -> str:
return self.__class__.__name__
@property
def individual(self):
"""The individual associated for this database, or None."""
return next(iter(self.individuals), None)
2019-01-21 15:08:55 +00:00
2021-07-01 10:08:18 +00:00
@property
def code(self):
"""Code of phantoms accounts"""
if not self.phantom:
return
return self.email.split('@')[0].split('_')[1]
@property
def is_active(self):
"""Alias because flask-login expects `is_active` attribute"""
return self.active
@property
def get_full_name(self):
# TODO(@slamora) create first_name & last_name fields???
# needs to be discussed related to Agent <--> User concepts
return self.email
def check_password(self, password):
# take advantage of SQL Alchemy PasswordType to verify password
return self.password == password
2022-11-22 18:12:11 +00:00
def set_new_dlt_keys(self, password):
2023-06-02 09:42:38 +00:00
if 'dpp' not in app.blueprints.keys():
2022-11-22 18:12:11 +00:00
return
from ereuseapi.methods import register_user
2023-06-02 09:42:38 +00:00
from ereuse_devicehub.modules.dpp.utils import encrypt
2022-11-22 18:12:11 +00:00
api_dlt = app.config.get('API_DLT')
data = register_user(api_dlt)
2022-12-01 12:03:24 +00:00
api_token = data.get('data', {}).get('api_token')
2022-11-22 18:12:11 +00:00
data = json.dumps(data)
self.api_keys_dlt = encrypt(password, data)
2022-12-01 12:03:24 +00:00
return api_token
2022-11-22 18:12:11 +00:00
def get_dlt_keys(self, password):
2023-06-02 09:42:38 +00:00
if 'dpp' not in app.blueprints.keys():
2022-11-22 18:12:11 +00:00
return {}
2023-06-02 09:42:38 +00:00
from ereuse_devicehub.modules.dpp.utils import decrypt
2022-11-22 18:12:11 +00:00
2022-11-25 12:27:14 +00:00
if not self.api_keys_dlt:
return {}
2022-11-22 18:12:11 +00:00
data = decrypt(password, self.api_keys_dlt)
return json.loads(data)
def reset_dlt_keys(self, password, data):
2023-06-02 09:42:38 +00:00
if 'dpp' not in app.blueprints.keys():
2022-11-22 18:12:11 +00:00
return
2023-06-02 09:42:38 +00:00
from ereuse_devicehub.modules.dpp.utils import encrypt
2022-11-22 18:12:11 +00:00
data = json.dumps(data)
self.api_keys_dlt = encrypt(password, data)
2023-06-15 08:43:43 +00:00
def allow_permitions(self, api_token=None, rols="Operator"):
2023-06-02 09:42:38 +00:00
if 'dpp' not in app.blueprints.keys():
2022-11-24 16:54:25 +00:00
return
2022-11-24 17:00:16 +00:00
if not api_token:
api_token = session.get('token_dlt', '.')
target_user = api_token.split(".")[0]
keyUser1 = app.config.get('API_DLT_TOKEN')
2022-11-24 16:54:25 +00:00
api_dlt = app.config.get('API_DLT')
if not keyUser1 or not api_dlt:
2022-11-24 16:54:25 +00:00
return
apiUser1 = API(api_dlt, keyUser1, "ethereum")
2023-06-15 08:43:43 +00:00
for rol in rols.split(","):
result = apiUser1.issue_credential(rol.strip(), target_user)
return result
2022-11-24 16:54:25 +00:00
2023-06-15 08:43:43 +00:00
def get_rols_dlt(self):
return json.loads(self.rols_dlt)
2023-06-23 08:22:46 +00:00
def set_rols_dlt(self, token_dlt=None):
rols = self.get_rols(self, token_dlt=token_dlt)
if rols:
self.rols_dlt = json.dumps(rols)
return rols
2023-06-15 08:43:43 +00:00
def get_rols(self, token_dlt=None):
2023-06-02 07:54:14 +00:00
2023-06-02 09:42:38 +00:00
if 'dpp' not in app.blueprints.keys():
2023-06-02 07:54:14 +00:00
return []
2023-06-15 08:43:43 +00:00
if not token_dlt:
token_dlt = session.get('token_dlt')
if not token_dlt:
return []
2023-06-02 07:54:14 +00:00
api_dlt = app.config.get('API_DLT')
2023-06-15 08:43:43 +00:00
if not api_dlt:
2023-06-02 07:54:14 +00:00
return []
2024-01-29 17:49:02 +00:00
self.get_abac_did()
role = session.get('iota_abac_attributes', {}).get('role', [])
return [(x.strip(), x.strip()) for x in role.split(",")]
2023-06-02 07:54:14 +00:00
2023-11-06 12:23:47 +00:00
def _call_abac(self, path):
abac_tk = app.config.get('ABAC_TOKEN')
2024-01-29 17:49:02 +00:00
# abac_coockie = app.config.get('ABAC_COOKIE')
2024-01-23 17:08:16 +00:00
domain = app.config.get('ABAC_URL')
2023-11-06 15:48:03 +00:00
eth_pub_key = session.get('eth_pub_key')
2024-01-23 17:08:16 +00:00
2023-11-06 12:23:47 +00:00
abac_path = path
2024-01-23 17:08:16 +00:00
if not (abac_tk and eth_pub_key and abac_path and domain):
2023-11-06 12:23:47 +00:00
return ''
header = {
'Authorization': f'Bearer {abac_tk}',
2024-01-29 17:49:02 +00:00
# 'Cookie': abac_coockie
2023-11-06 12:23:47 +00:00
}
url = f'{domain}{eth_pub_key}/{abac_path}'
return requests.get(url, headers=header)
def get_abac_did(self):
try:
2023-11-06 15:48:03 +00:00
if session.get('iota_abac_did'):
return session.get('iota_abac_did')
2023-11-06 12:23:47 +00:00
r = self._call_abac('did')
if not r or not r.status_code == 200:
return ''
2023-11-06 15:48:03 +00:00
did = r.json().get('did', '').strip()
if not did:
return ''
session['iota_abac_did'] = did
return did
2023-11-06 12:23:47 +00:00
except Exception:
return ''
def get_abac_attributes(self):
try:
2023-11-06 15:48:03 +00:00
if session.get('iota_abac_attributes'):
return session.get('iota_abac_attributes')
2023-11-06 12:23:47 +00:00
r = self._call_abac('attributes')
if not r or not r.status_code == 200:
return {}
data = r.json()
if not data:
return {}
result = {}
for j in data:
k = j.get('attributeURI', '').split('/')[-1].split("#")[-1]
2023-11-06 15:48:03 +00:00
v = j.get('attributeValue', '').strip()
2023-11-06 12:23:47 +00:00
if not (k and v):
continue
result[k] = v
2023-11-06 15:48:03 +00:00
session['iota_abac_attributes'] = result
2023-11-06 12:23:47 +00:00
return result
except Exception:
return {}
2019-01-21 15:08:55 +00:00
class UserInventory(db.Model):
"""Relationship between users and their inventories."""
2022-04-06 10:35:08 +00:00
2019-01-21 15:08:55 +00:00
__table_args__ = {'schema': 'common'}
user_id = db.Column(db.UUID(as_uuid=True), db.ForeignKey(User.id), primary_key=True)
2022-04-06 10:35:08 +00:00
inventory_id = db.Column(
db.Unicode(), db.ForeignKey(Inventory.id), primary_key=True
)
2021-04-13 16:33:15 +00:00
class Session(Thing):
2021-05-24 11:00:03 +00:00
__table_args__ = {'schema': 'common'}
2021-04-13 16:33:15 +00:00
id = Column(BigInteger, Sequence('device_seq'), primary_key=True)
expired = Column(BigInteger, default=0)
token = Column(UUID(as_uuid=True), default=uuid4, unique=True, nullable=False)
type = Column(IntEnum(SessionType), default=SessionType.Internal, nullable=False)
user_id = db.Column(db.UUID(as_uuid=True), db.ForeignKey(User.id))
2022-04-06 10:35:08 +00:00
user = db.relationship(
User,
backref=db.backref('sessions', lazy=True, collection_class=set),
collection_class=set,
)
2021-04-15 19:18:15 +00:00
def __str__(self) -> str:
return '{0.token}'.format(self)
class SanitizationEntity(Thing):
id = db.Column(BigInteger, primary_key=True)
company_name = db.Column(db.String, nullable=True)
location = db.Column(db.String, nullable=True)
# logo = db.Column(db.String, nullable=True)
logo = db.Column(URL(), nullable=True)
responsable_person = db.Column(db.String, nullable=True)
supervisor_person = db.Column(db.String, nullable=True)
user_id = db.Column(
db.UUID(as_uuid=True),
db.ForeignKey(User.id),
default=lambda: g.user.id,
)
user = db.relationship(
User,
backref=db.backref(
'sanitization_entity', lazy=True, uselist=False, cascade=CASCADE_OWN
),
primaryjoin=user_id == User.id,
)
def __str__(self) -> str:
return '{0.company_name}'.format(self)