clearml-server/apiserver/bll/auth/__init__.py
2021-01-05 16:28:49 +02:00

163 lines
5.7 KiB
Python

from datetime import datetime
from apiserver import database
from apiserver.apierrors import errors
from apiserver.apimodels.auth import GetTokenResponse, CreateUserRequest, Credentials as CredModel
from apiserver.apimodels.users import CreateRequest as Users_CreateRequest
from apiserver.bll.user import UserBLL
from apiserver.config import config
from apiserver.config.info import get_version, get_build_number
from apiserver.database.errors import translate_errors_context
from apiserver.database.model.auth import User, Role, Credentials
from apiserver.database.model.company import Company
from apiserver.service_repo import APICall, ServiceRepo
from apiserver.service_repo.auth import Identity, Token, get_client_id, get_secret_key
log = config.logger("AuthBLL")
class AuthBLL:
@staticmethod
def get_token_for_user(
user_id: str,
company_id: str = None,
expiration_sec: int = None,
entities: dict = None,
) -> GetTokenResponse:
with translate_errors_context():
query = dict(id=user_id)
if company_id:
query.update(company=company_id)
user = User.objects(**query).first()
if not user:
raise errors.bad_request.InvalidUserId(**query)
company_id = company_id or user.company
company = Company.objects(id=company_id).only("id", "name").first()
if not company:
raise errors.bad_request.InvalidId(
"invalid company associated with user", company=company
)
identity = Identity(
user=user_id,
company=company_id,
role=user.role,
user_name=user.name,
company_name=company.name,
)
token = Token.create_encoded_token(
identity=identity,
entities=entities,
expiration_sec=expiration_sec,
api_version=str(ServiceRepo.max_endpoint_version()),
server_version=str(get_version()),
server_build=str(get_build_number()),
)
return GetTokenResponse(token=token.decode("ascii"))
@staticmethod
def create_user(request: CreateUserRequest, call: APICall = None) -> str:
"""
Create a new user in both the auth database and the backend database
:param request: New user details
:param call: API call that triggered this call. If not None, new backend user creation
will be performed using a new call in the same transaction.
:return: The new user's ID
"""
with translate_errors_context():
if not Company.objects(id=request.company).only("id"):
raise errors.bad_request.InvalidId(company=request.company)
user = User(
id=database.utils.id(),
name=request.name,
company=request.company,
role=request.role or Role.user,
email=request.email,
created=datetime.utcnow(),
)
user.save()
users_create_request = Users_CreateRequest(
id=user.id,
name=request.name,
company=request.company,
family_name=request.family_name,
given_name=request.given_name,
avatar=request.avatar,
)
try:
UserBLL.create(users_create_request)
except Exception as ex:
user.delete()
raise errors.server_error.GeneralError(
"failed adding new user", ex=str(ex)
)
return user.id
@staticmethod
def delete_user(
identity: Identity, user_id: str, company_id: str = None, call: APICall = None
):
"""
Delete an existing user from both the auth database and the backend database
:param identity: Calling user identity
:param user_id: ID of user to delete
:param company_id: Company of user to delete
:param call: API call that triggered this call. If not None, backend user deletion
will be performed using a new call in the same transaction.
"""
if user_id == identity.user:
raise errors.bad_request.FieldsValueError(
"cannot delete yourself", user=user_id
)
if not company_id:
company_id = identity.company
if (
identity.role not in Role.get_system_roles()
and company_id != identity.company
):
raise errors.bad_request.FieldsNotAllowedForRole(
"must be empty or your own company", role=identity.role, field="company"
)
with translate_errors_context():
query = dict(id=user_id, company=company_id)
res = User.objects(**query).delete()
if not res:
raise errors.bad_request.InvalidUserId(**query)
try:
UserBLL.delete(user_id)
except Exception as ex:
log.error(f"Exception calling users.delete: {str(ex)}")
@classmethod
def create_credentials(
cls, user_id: str, company_id: str, role: str = None
) -> CredModel:
with translate_errors_context():
query = dict(id=user_id, company=company_id)
user = User.objects(**query).first()
if not user:
raise errors.bad_request.InvalidUserId(**query)
cred = CredModel(access_key=get_client_id(), secret_key=get_secret_key())
user.credentials.append(
Credentials(key=cred.access_key, secret=cred.secret_key)
)
user.save()
return cred