Improve handling of fixed users

This commit is contained in:
allegroai 2024-03-18 15:49:42 +02:00
parent 92a4e56c1f
commit 5189adf4f1
5 changed files with 44 additions and 8 deletions

View File

@ -58,6 +58,9 @@
# verify user tokens # verify user tokens
verify_user_tokens: false verify_user_tokens: false
# If set then users that were created from secure credentials or fixed user settings and are no longer in these settings will be deleted on startup
delete_missing_autocreated_users: true
# max token expiration timeout in seconds (1 year) # max token expiration timeout in seconds (1 year)
max_expiration_sec: 31536000 max_expiration_sec: 31536000

View File

@ -4,6 +4,7 @@ from mongoengine import (
EmbeddedDocumentListField, EmbeddedDocumentListField,
EmailField, EmailField,
DateTimeField, DateTimeField,
BooleanField,
) )
from apiserver.database import Database, strict from apiserver.database import Database, strict
@ -76,3 +77,6 @@ class User(DbModelMixin, AuthDocument):
email = EmailField(unique=True, sparse=True) email = EmailField(unique=True, sparse=True)
""" Email uniquely identifying the user """ """ Email uniquely identifying the user """
autocreated = BooleanField(default=False)
""" Set to true if the user was auto created based on config settings"""

View File

@ -3,7 +3,7 @@ from typing import Sequence, Union
from apiserver.config_repo import config from apiserver.config_repo import config
from apiserver.config.info import get_default_company from apiserver.config.info import get_default_company
from apiserver.database.model.auth import Role from apiserver.database.model.auth import Role, User as AuthUser
from apiserver.service_repo.auth.fixed_user import FixedUser from apiserver.service_repo.auth.fixed_user import FixedUser
from .migration import _apply_migrations, check_mongo_empty, get_last_server_version from .migration import _apply_migrations, check_mongo_empty, get_last_server_version
from .pre_populate import PrePopulate from .pre_populate import PrePopulate
@ -60,14 +60,18 @@ def init_mongo_data():
fixed_mode = FixedUser.enabled() fixed_mode = FixedUser.enabled()
internal_user_emails = set()
for user, credentials in config.get("secure.credentials", {}).items(): for user, credentials in config.get("secure.credentials", {}).items():
email = f"{user}@example.com"
user_data = { user_data = {
"name": user, "name": user,
"role": credentials.role, "role": credentials.role,
"email": f"{user}@example.com", "email": email,
"key": credentials.user_key, "key": credentials.user_key,
"secret": credentials.user_secret, "secret": credentials.user_secret,
"autocreated": True,
} }
internal_user_emails.add(email.lower())
revoke = fixed_mode and credentials.get("revoke_in_fixed_mode", False) revoke = fixed_mode and credentials.get("revoke_in_fixed_mode", False)
user_id = _ensure_auth_user(user_data, company_id, log=log, revoke=revoke) user_id = _ensure_auth_user(user_data, company_id, log=log, revoke=revoke)
if credentials.role == Role.user: if credentials.role == Role.user:
@ -82,8 +86,20 @@ def init_mongo_data():
for user in FixedUser.from_config(): for user in FixedUser.from_config():
try: try:
ensure_fixed_user(user, log=log) ensure_fixed_user(user, log=log, emails=internal_user_emails)
except Exception as ex: except Exception as ex:
log.error(f"Failed creating fixed user {user.name}: {ex}") log.error(f"Failed creating fixed user {user.name}: {ex}")
if internal_user_emails and config.get(
f"apiserver.auth.delete_missing_autocreated_users", True
):
for user in AuthUser.objects(
company=company_id, autocreated=True, email__nin=internal_user_emails
):
log.info(
f"Removing user that is no longer in configuration: {user['id']}\t{user['email']}\t{user['name']}"
)
user.delete()
except Exception as ex: except Exception as ex:
log.exception("Failed initializing mongodb") log.exception(f"Failed initializing mongodb: {str(ex)}")

View File

@ -26,6 +26,7 @@ def _ensure_auth_user(user_data: dict, company_id: str, log: Logger, revoke: boo
credentials = [] if revoke else [creds] credentials = [] if revoke else [creds]
user_id = user_data.get("id", f"__{user_data['name']}__") user_id = user_data.get("id", f"__{user_data['name']}__")
autocreated = user_data.get("autocreated", False)
log.info(f"Creating user: {user_data['name']}") log.info(f"Creating user: {user_data['name']}")
@ -37,6 +38,7 @@ def _ensure_auth_user(user_data: dict, company_id: str, log: Logger, revoke: boo
email=user_data["email"], email=user_data["email"],
created=datetime.utcnow(), created=datetime.utcnow(),
credentials=credentials, credentials=credentials,
autocreated=autocreated,
) )
user.save() user.save()
@ -59,7 +61,7 @@ def _ensure_backend_user(user_id: str, company_id: str, user_name: str):
return user_id return user_id
def ensure_fixed_user(user: FixedUser, log: Logger): def ensure_fixed_user(user: FixedUser, log: Logger, emails: set):
db_user = User.objects(company=user.company, id=user.user_id).first() db_user = User.objects(company=user.company, id=user.user_id).first()
if db_user: if db_user:
# noinspection PyBroadException # noinspection PyBroadException
@ -73,9 +75,12 @@ def ensure_fixed_user(user: FixedUser, log: Logger):
data = attr.asdict(user) data = attr.asdict(user)
data["id"] = user.user_id data["id"] = user.user_id
data["email"] = f"{user.user_id}@example.com" email = f"{user.user_id}@example.com"
data["email"] = email
data["role"] = Role.guest if user.is_guest else Role.user data["role"] = Role.guest if user.is_guest else Role.user
data["autocreated"] = True
_ensure_auth_user(user_data=data, company_id=user.company, log=log) _ensure_auth_user(user_data=data, company_id=user.company, log=log)
emails.add(email)
return _ensure_backend_user(user.user_id, user.company, user.name) return _ensure_backend_user(user.user_id, user.company, user.name)

View File

@ -16,7 +16,7 @@ from apiserver.bll.project import ProjectBLL
from apiserver.bll.user import UserBLL from apiserver.bll.user import UserBLL
from apiserver.config_repo import config from apiserver.config_repo import config
from apiserver.database.errors import translate_errors_context from apiserver.database.errors import translate_errors_context
from apiserver.database.model.auth import Role from apiserver.database.model.auth import Role, User as AuthUser
from apiserver.database.model.company import Company from apiserver.database.model.company import Company
from apiserver.database.model.user import User from apiserver.database.model.user import User
from apiserver.database.utils import parse_from_call from apiserver.database.utils import parse_from_call
@ -158,9 +158,17 @@ def update_user(user_id, company_id, data: dict) -> Tuple[int, dict]:
update_fields = { update_fields = {
k: v for k, v in create_fields.items() if k in User.user_set_allowed() k: v for k, v in create_fields.items() if k in User.user_set_allowed()
} }
auth_user_update_fields = ("name",)
partial_update_dict = parse_from_call(data, update_fields, User.get_fields()) partial_update_dict = parse_from_call(data, update_fields, User.get_fields())
with translate_errors_context("updating user"): with translate_errors_context("updating user"):
return User.safe_update(company_id, user_id, partial_update_dict) ret = User.safe_update(company_id, user_id, partial_update_dict)
auth_update = {
k: v for k, v in partial_update_dict.items() if k in auth_user_update_fields
}
if auth_update:
AuthUser.objects(id=user_id).update(**auth_update)
return ret
@endpoint("users.update", response_data_model=UpdateResponse) @endpoint("users.update", response_data_model=UpdateResponse)