Reworked user spaces #15 #27
@ -9,18 +9,28 @@ class Keycloak:
|
||||
client: Optional[KeycloakOpenID] = None
|
||||
admin: Optional[KeycloakAdmin] = None
|
||||
|
||||
url: Optional[str] = None
|
||||
client_id: Optional[str] = None
|
||||
realm: Optional[str] = None
|
||||
__client_secret: Optional[str] = None
|
||||
|
||||
@classmethod
|
||||
def init(cls):
|
||||
cls.url = Environment.get("KEYCLOAK_URL", str)
|
||||
cls.client_id = Environment.get("KEYCLOAK_CLIENT_ID", str)
|
||||
cls.realm = Environment.get("KEYCLOAK_REALM", str)
|
||||
cls.__client_secret = Environment.get("KEYCLOAK_CLIENT_SECRET", str)
|
||||
|
||||
cls.client = KeycloakOpenID(
|
||||
server_url=Environment.get("KEYCLOAK_URL", str),
|
||||
client_id=Environment.get("KEYCLOAK_CLIENT_ID", str),
|
||||
realm_name=Environment.get("KEYCLOAK_REALM", str),
|
||||
client_secret_key=Environment.get("KEYCLOAK_CLIENT_SECRET", str),
|
||||
server_url=cls.url,
|
||||
client_id=cls.client_id,
|
||||
realm_name=cls.realm,
|
||||
client_secret_key=cls.__client_secret,
|
||||
)
|
||||
connection = KeycloakOpenIDConnection(
|
||||
server_url=Environment.get("KEYCLOAK_URL", str),
|
||||
client_id=Environment.get("KEYCLOAK_CLIENT_ID", str),
|
||||
realm_name=Environment.get("KEYCLOAK_REALM", str),
|
||||
client_secret_key=Environment.get("KEYCLOAK_CLIENT_SECRET", str),
|
||||
server_url=cls.url,
|
||||
client_id=cls.client_id,
|
||||
realm_name=cls.realm,
|
||||
client_secret_key=cls.__client_secret,
|
||||
)
|
||||
cls.admin = KeycloakAdmin(connection=connection)
|
||||
|
@ -1,33 +1,72 @@
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import RedirectResponse, JSONResponse
|
||||
|
||||
from api.auth.keycloak_client import Keycloak
|
||||
from api.route import Route
|
||||
from core.environment import Environment
|
||||
from core.logger import Logger
|
||||
from data.schemas.administration.user import User
|
||||
from data.schemas.administration.user_dao import userDao
|
||||
from data.schemas.public.user_invitation import UserInvitation
|
||||
from data.schemas.public.user_invitation_dao import userInvitationDao
|
||||
from data.schemas.public.user_space_user import UserSpaceUser
|
||||
from data.schemas.public.user_space_user_dao import userSpaceUserDao
|
||||
|
||||
BasePath = f"/invitation"
|
||||
BasePath = f"/api/invitation"
|
||||
logger = Logger(__name__)
|
||||
|
||||
|
||||
def _redirect_to_client():
|
||||
client_urls = Environment.get("CLIENT_URLS", str)
|
||||
if client_urls is None:
|
||||
return JSONResponse({"message": "Invitation accepted."})
|
||||
|
||||
return RedirectResponse(client_urls.split(",")[0], status_code=303)
|
||||
|
||||
|
||||
@Route.get(f"{BasePath}/accept/{{invitation_id:path}}")
|
||||
async def accept_invitation(request: Request):
|
||||
invitation_id = request.path_params["invitation_id"]
|
||||
|
||||
user_invitation = await userInvitationDao.find_single_by(
|
||||
[{UserInvitation.invitation: invitation_id}]
|
||||
)
|
||||
if user_invitation is None:
|
||||
return JSONResponse({"error": "Invitation not found."}, 404)
|
||||
|
||||
if user_invitation.deleted:
|
||||
return _redirect_to_client()
|
||||
|
||||
keycloak_user = Keycloak.admin.get_users({"email": user_invitation.email})
|
||||
if len(keycloak_user) == 0:
|
||||
redirect_uri = f"{str(request.base_url)}api/invitation/accept/{invitation_id}"
|
||||
registration_url = f"{Keycloak.url}/realms/{Keycloak.realm}/protocol/openid-connect/auth?prompt=create&client_id={Keycloak.client_id}&response_type=code&scope=openid&redirect_uri={redirect_uri}"
|
||||
return RedirectResponse(registration_url, status_code=303)
|
||||
|
||||
if user_invitation.email not in [
|
||||
x.email for x in await userDao.find_by([{User.deleted: False}])
|
||||
]:
|
||||
user_id = await userDao.create(User(0, keycloak_user[0]["id"]))
|
||||
await userSpaceUserDao.create(
|
||||
UserSpaceUser(0, user_invitation.user_space_id, user_id)
|
||||
)
|
||||
|
||||
await userInvitationDao.delete(user_invitation)
|
||||
|
||||
return _redirect_to_client()
|
||||
|
||||
|
||||
@Route.get(f"{BasePath}/user-space/accept/{{invitation_id:path}}")
|
||||
async def accept_user_space_invitation(request: Request):
|
||||
invitation_id = request.path_params["invitation_id"]
|
||||
|
||||
user_space_user = await userSpaceUserDao.find_single_by(
|
||||
[{UserSpaceUser.invitation: invitation_id}, {UserSpaceUser.deleted: False}]
|
||||
)
|
||||
if user_space_user is None:
|
||||
return JSONResponse({"error": "Invitation not found or already accepted."})
|
||||
return JSONResponse({"error": "Invitation not found or already accepted."}, 404)
|
||||
|
||||
user_space_user.invitation = None
|
||||
await userSpaceUserDao.update(user_space_user)
|
||||
|
||||
client_urls = Environment.get("CLIENT_URLS", str)
|
||||
if client_urls is None:
|
||||
return JSONResponse(
|
||||
{"message": "Invitation accepted, but no client URLs configured."}
|
||||
)
|
||||
|
||||
return RedirectResponse(client_urls.split(",")[0], status_code=303)
|
||||
return _redirect_to_client()
|
||||
|
@ -22,12 +22,12 @@ class UserSettingMutation(MutationABC):
|
||||
f"{first_to_lower(self.name.replace("Mutation", ""))}Change"
|
||||
)
|
||||
.with_input(UserSettingInput, "input")
|
||||
.with_require_any([], [self._x])
|
||||
.with_require_any([], [self._check_user])
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
async def _x(ctx):
|
||||
return ctx.data.user_id == (await Route.get_user()).id
|
||||
async def _check_user(ctx):
|
||||
return ctx.user.id == (await Route.get_user()).id
|
||||
|
||||
@staticmethod
|
||||
async def resolve_change(obj: UserSettingInput, *_):
|
||||
|
@ -0,0 +1,10 @@
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS user_invitations_unique_email
|
||||
ON public.user_invitations (email)
|
||||
WHERE email != 'ANONYMOUS';
|
||||
|
||||
ALTER TABLE public.user_invitations
|
||||
ADD CONSTRAINT user_invitations_unique_invitation UNIQUE (invitation);
|
||||
|
||||
ALTER TABLE public.user_spaces_users
|
||||
ADD CONSTRAINT user_spaces_users_unique_invitation UNIQUE (invitation),
|
||||
ADD CONSTRAINT user_spaces_users_unique_entry UNIQUE (userspaceid, userid);
|
@ -9,6 +9,7 @@ from core.database.abc.db_model_dao_abc import DbModelDaoABC
|
||||
from core.logger import Logger
|
||||
from core.string import first_to_lower
|
||||
from data.schemas.administration.user_dao import userDao
|
||||
from data.schemas.public.user_invitation_dao import userInvitationDao
|
||||
|
||||
logger = Logger("DataPrivacy")
|
||||
|
||||
@ -77,6 +78,14 @@ class DataPrivacyService:
|
||||
keycloak_id = user.keycloak_id
|
||||
|
||||
# Anonymize internal data
|
||||
user_invitations = await userInvitationDao.find_by(
|
||||
[{"email": {"equal": user.email}}]
|
||||
)
|
||||
for user_invitation in user_invitations:
|
||||
user_invitation.email = "ANONYMOUS"
|
||||
user_invitation.deleted = True
|
||||
await userInvitationDao.update(user_invitation)
|
||||
|
||||
await user.anonymize()
|
||||
await userDao.delete(user)
|
||||
|
||||
|
@ -45,6 +45,7 @@ export class SidebarService {
|
||||
|
||||
this.setSelectedUserSpaceIdToLS(value ? value.id : 0);
|
||||
await this.setElements();
|
||||
await this.router.navigate(['/admin/urls']);
|
||||
});
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user