From 25e6862ce6b2ec985d10eed7ab68889505646182 Mon Sep 17 00:00:00 2001 From: Kraiem Taha Yassine Date: Fri, 6 Sep 2024 16:02:07 +0200 Subject: [PATCH] Dev (#2544) * fix(chalice): fixed Math-operators validation refactor(chalice): search for sessions that have events for heatmaps * refactor(chalice): search for sessions that have at least 1 location event for heatmaps * fix(chalice): fixed Math-operators validation refactor(chalice): search for sessions that have events for heatmaps * refactor(chalice): search for sessions that have at least 1 location event for heatmaps * feat(chalice): autocomplete return top 10 with stats * fix(chalice): fixed autocomplete top 10 meta-filters * refactor(chalice): always generate spot-jwt refactor(chalice): generate spot-jwt on password change refactor(chalice): generate spot-jwt on accept invitation --- api/chalicelib/core/users.py | 80 +++++++++++------------ api/routers/core_dynamic.py | 55 +++++++++------- ee/api/chalicelib/core/users.py | 108 ++++++++++++++------------------ ee/api/routers/core_dynamic.py | 64 ++++++++++--------- ee/api/routers/saml.py | 20 +++--- 5 files changed, 158 insertions(+), 169 deletions(-) diff --git a/api/chalicelib/core/users.py b/api/chalicelib/core/users.py index 4ad7c56e3..7803afa4b 100644 --- a/api/chalicelib/core/users.py +++ b/api/chalicelib/core/users.py @@ -6,9 +6,9 @@ from fastapi import BackgroundTasks from pydantic import BaseModel import schemas -from chalicelib.core import authorizers, metadata, projects -from chalicelib.core import tenants, assist, spot -from chalicelib.utils import email_helper, smtp +from chalicelib.core import authorizers, metadata +from chalicelib.core import tenants, spot, scope +from chalicelib.utils import email_helper from chalicelib.utils import helper from chalicelib.utils import pg_client from chalicelib.utils.TimeUTC import TimeUTC @@ -209,7 +209,7 @@ def get(user_id, tenant_id): users.user_id, email, role, - name, + users.name, (CASE WHEN role = 'owner' THEN TRUE ELSE FALSE END) AS super_admin, (CASE WHEN role = 'admin' THEN TRUE ELSE FALSE END) AS admin, (CASE WHEN role = 'member' THEN TRUE ELSE FALSE END) AS member, @@ -458,16 +458,16 @@ def set_password_invitation(user_id, new_password): "projects": -1, "metadata": metadata.get_remaining_metadata_with_count(tenant_id)} - c = tenants.get_by_tenant_id(tenant_id) - c.pop("createdAt") - c["projects"] = projects.get_projects(tenant_id=tenant_id, recorded=True) - c["smtp"] = smtp.has_smtp() - c["iceServers"] = assist.get_ice_servers() return { - 'jwt': r.pop('jwt'), + "jwt": r.pop("jwt"), + "refreshToken": r.pop("refreshToken"), + "refreshTokenMaxAge": r.pop("refreshTokenMaxAge"), + "spotJwt": r.pop("spotJwt"), + "spotRefreshToken": r.pop("spotRefreshToken"), + "spotRefreshTokenMaxAge": r.pop("spotRefreshTokenMaxAge"), 'data': { - "user": r, - "client": c + "scopeState": scope.get_scope(-1), + "user": r } } @@ -560,32 +560,27 @@ class ChangeJwt(BaseModel): jwt_iat: int jwt_refresh_jti: int jwt_refresh_iat: int - spot_jwt_iat: int | None = None - spot_jwt_refresh_jti: int | None = None - spot_jwt_refresh_iat: int | None = None + spot_jwt_iat: int + spot_jwt_refresh_jti: int + spot_jwt_refresh_iat: int -def change_jwt_iat_jti(user_id, include_spot: bool = False): - sub_query = "" - sub_result = "" - if include_spot: - sub_query = """,spot_jwt_iat = timezone('utc'::text, now()-INTERVAL '10s'), - spot_jwt_refresh_jti = 0, - spot_jwt_refresh_iat = timezone('utc'::text, now()-INTERVAL '10s')""" - sub_result = """,EXTRACT (epoch FROM spot_jwt_iat)::BIGINT AS spot_jwt_iat, - spot_jwt_refresh_jti, - EXTRACT (epoch FROM spot_jwt_refresh_iat)::BIGINT AS spot_jwt_refresh_iat""" +def change_jwt_iat_jti(user_id): with pg_client.PostgresClient() as cur: query = cur.mogrify(f"""UPDATE public.users SET jwt_iat = timezone('utc'::text, now()-INTERVAL '10s'), jwt_refresh_jti = 0, - jwt_refresh_iat = timezone('utc'::text, now()-INTERVAL '10s') - {sub_query} + jwt_refresh_iat = timezone('utc'::text, now()-INTERVAL '10s'), + spot_jwt_iat = timezone('utc'::text, now()-INTERVAL '10s'), + spot_jwt_refresh_jti = 0, + spot_jwt_refresh_iat = timezone('utc'::text, now()-INTERVAL '10s') WHERE user_id = %(user_id)s RETURNING EXTRACT (epoch FROM jwt_iat)::BIGINT AS jwt_iat, jwt_refresh_jti, - EXTRACT (epoch FROM jwt_refresh_iat)::BIGINT AS jwt_refresh_iat - {sub_result};""", + EXTRACT (epoch FROM jwt_refresh_iat)::BIGINT AS jwt_refresh_iat, + EXTRACT (epoch FROM spot_jwt_iat)::BIGINT AS spot_jwt_iat, + spot_jwt_refresh_jti, + EXTRACT (epoch FROM spot_jwt_refresh_iat)::BIGINT AS spot_jwt_refresh_iat;""", {"user_id": user_id}) cur.execute(query) row = cur.fetchone() @@ -607,7 +602,7 @@ def refresh_jwt_iat_jti(user_id): return row.get("jwt_iat"), row.get("jwt_refresh_jti"), row.get("jwt_refresh_iat") -def authenticate(email, password, for_change_password=False, include_spot=False) -> dict | bool | None: +def authenticate(email, password, for_change_password=False) -> dict | bool | None: with pg_client.PostgresClient() as cur: query = cur.mogrify( f"""SELECT @@ -632,7 +627,7 @@ def authenticate(email, password, for_change_password=False, include_spot=False) if for_change_password: return True r = helper.dict_to_camel_case(r) - j_r = change_jwt_iat_jti(user_id=r['userId'], include_spot=include_spot) + j_r = change_jwt_iat_jti(user_id=r['userId']) response = { "jwt": authorizers.generate_jwt(user_id=r['userId'], tenant_id=r['tenantId'], iat=j_r.jwt_iat, aud=AUDIENCE), @@ -641,22 +636,19 @@ def authenticate(email, password, for_change_password=False, include_spot=False) jwt_jti=j_r.jwt_refresh_jti), "refreshTokenMaxAge": config("JWT_REFRESH_EXPIRATION", cast=int), "email": email, + "spotJwt": authorizers.generate_jwt(user_id=r['userId'], tenant_id=r['tenantId'], + iat=j_r.spot_jwt_iat, aud=spot.AUDIENCE, for_spot=True), + "spotRefreshToken": authorizers.generate_jwt_refresh(user_id=r['userId'], + tenant_id=r['tenantId'], + iat=j_r.spot_jwt_refresh_iat, + aud=spot.AUDIENCE, + jwt_jti=j_r.spot_jwt_refresh_jti, + for_spot=True), + "spotRefreshTokenMaxAge": config("JWT_SPOT_REFRESH_EXPIRATION", cast=int), **r } - if include_spot: - response = { - **response, - "spotJwt": authorizers.generate_jwt(user_id=r['userId'], tenant_id=r['tenantId'], - iat=j_r.spot_jwt_iat, aud=spot.AUDIENCE, for_spot=True), - "spotRefreshToken": authorizers.generate_jwt_refresh(user_id=r['userId'], - tenant_id=r['tenantId'], - iat=j_r.spot_jwt_refresh_iat, - aud=spot.AUDIENCE, - jwt_jti=j_r.spot_jwt_refresh_jti, - for_spot=True), - "spotRefreshTokenMaxAge": config("JWT_SPOT_REFRESH_EXPIRATION", cast=int) - } return response + return None diff --git a/api/routers/core_dynamic.py b/api/routers/core_dynamic.py index 78218643e..fc48c72e8 100644 --- a/api/routers/core_dynamic.py +++ b/api/routers/core_dynamic.py @@ -20,6 +20,7 @@ from chalicelib.utils import helper from chalicelib.utils.TimeUTC import TimeUTC from or_dependencies import OR_context, OR_role from routers.base import get_routers +from routers.subs import spot logger = logging.getLogger(__name__) public_app, app, app_apikey = get_routers() @@ -50,15 +51,36 @@ if not tenants.tenants_exists_sync(use_pool=False): return content +def __process_authentication_response(response: JSONResponse, data: dict) -> dict: + data["smtp"] = smtp.has_smtp() + refresh_token = data.pop("refreshToken") + refresh_token_max_age = data.pop("refreshTokenMaxAge") + spot_refresh_token = data.pop("spotRefreshToken") + spot_refresh_token_max_age = data.pop("spotRefreshTokenMaxAge") + data = { + 'jwt': data.pop('jwt'), + "spotJwt": data.pop("spotJwt"), + 'data': { + "scopeState": scope.get_scope(-1), + "user": data + } + } + response.set_cookie(key="refreshToken", value=refresh_token, path=COOKIE_PATH, + max_age=refresh_token_max_age, secure=True, httponly=True) + response.set_cookie(key="spotRefreshToken", value=spot_refresh_token, path=spot.COOKIE_PATH, + max_age=spot_refresh_token_max_age, secure=True, httponly=True) + return data + + @public_app.post('/login', tags=["authentication"]) -def login_user(response: JSONResponse, spot: Optional[bool] = False, data: schemas.UserLoginSchema = Body(...)): +def login_user(response: JSONResponse, data: schemas.UserLoginSchema = Body(...)): if helper.allow_captcha() and not captcha.is_valid(data.g_recaptcha_response): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid captcha." ) - r = users.authenticate(email=data.email, password=data.password.get_secret_value(), include_spot=spot) + r = users.authenticate(email=data.email, password=data.password.get_secret_value()) if r is None: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, @@ -69,26 +91,8 @@ def login_user(response: JSONResponse, spot: Optional[bool] = False, data: schem status_code=status.HTTP_401_UNAUTHORIZED, detail=r["errors"][0] ) - - r["smtp"] = smtp.has_smtp() - refresh_token = r.pop("refreshToken") - refresh_token_max_age = r.pop("refreshTokenMaxAge") - content = { - 'jwt': r.pop('jwt'), - 'data': { - "user": r, - "scopeState": scope.get_scope(-1) - } - } - response.set_cookie(key="refreshToken", value=refresh_token, path=COOKIE_PATH, - max_age=refresh_token_max_age, secure=True, httponly=True) - if spot: - content["spotJwt"] = r.pop("spotJwt") - spot_refresh_token = r.pop("spotRefreshToken") - spot_refresh_token_max_age = r.pop("spotRefreshTokenMaxAge") - response.set_cookie(key="spotRefreshToken", value=spot_refresh_token, path="/api/spot/refresh", - max_age=spot_refresh_token_max_age, secure=True, httponly=True) - return content + r = __process_authentication_response(response=response, data=r) + return r @app.get('/logout', tags=["login"]) @@ -125,6 +129,7 @@ def get_account(context: schemas.CurrentContext = Depends(OR_context)): **r, **t, **license.get_status(context.tenant_id), + "settings": users.get_user_settings(context.user_id)["settings"], "smtp": smtp.has_smtp() } } @@ -197,7 +202,7 @@ def process_invitation_link(token: str): @public_app.post('/password/reset', tags=["users"]) -def change_password_by_invitation(data: schemas.EditPasswordByInvitationSchema = Body(...)): +def change_password_by_invitation(response: JSONResponse, data: schemas.EditPasswordByInvitationSchema = Body(...)): if data is None or len(data.invitation) < 64 or len(data.passphrase) < 8: return {"errors": ["please provide a valid invitation & pass"]} user = users.get_by_invitation_token(token=data.invitation, pass_token=data.passphrase) @@ -206,7 +211,9 @@ def change_password_by_invitation(data: schemas.EditPasswordByInvitationSchema = if user["expiredChange"]: return {"errors": ["expired change, please re-use the invitation link"]} - return users.set_password_invitation(new_password=data.password.get_secret_value(), user_id=user["userId"]) + r = users.set_password_invitation(new_password=data.password.get_secret_value(), user_id=user["userId"]) + r = __process_authentication_response(response=response, data=r) + return r @app.put('/client/members/{memberId}', tags=["client"], dependencies=[OR_role("owner", "admin")]) diff --git a/ee/api/chalicelib/core/users.py b/ee/api/chalicelib/core/users.py index 730d65c4f..14d4e65bb 100644 --- a/ee/api/chalicelib/core/users.py +++ b/ee/api/chalicelib/core/users.py @@ -8,10 +8,9 @@ from pydantic import BaseModel from starlette import status import schemas -from chalicelib.core import authorizers, metadata, projects -from chalicelib.core import roles, spot -from chalicelib.core import tenants, assist -from chalicelib.utils import email_helper, smtp +from chalicelib.core import authorizers, metadata +from chalicelib.core import tenants, roles, spot, scope +from chalicelib.utils import email_helper from chalicelib.utils import helper from chalicelib.utils import pg_client from chalicelib.utils.TimeUTC import TimeUTC @@ -541,16 +540,16 @@ def set_password_invitation(tenant_id, user_id, new_password): "projects": -1, "metadata": metadata.get_remaining_metadata_with_count(tenant_id)} - c = tenants.get_by_tenant_id(tenant_id) - c.pop("createdAt") - c["projects"] = projects.get_projects(tenant_id=tenant_id, recorded=True, user_id=user_id) - c["smtp"] = smtp.has_smtp() - c["iceServers"] = assist.get_ice_servers() return { - 'jwt': r.pop('jwt'), + "jwt": r.pop("jwt"), + "refreshToken": r.pop("refreshToken"), + "refreshTokenMaxAge": r.pop("refreshTokenMaxAge"), + "spotJwt": r.pop("spotJwt"), + "spotRefreshToken": r.pop("spotRefreshToken"), + "spotRefreshTokenMaxAge": r.pop("spotRefreshTokenMaxAge"), 'data': { - "user": r, - "client": c + "scopeState": scope.get_scope(r["tenantId"]), + "user": r } } @@ -652,32 +651,27 @@ class ChangeJwt(BaseModel): jwt_iat: int jwt_refresh_jti: int jwt_refresh_iat: int - spot_jwt_iat: int | None = None - spot_jwt_refresh_jti: int | None = None - spot_jwt_refresh_iat: int | None = None + spot_jwt_iat: int + spot_jwt_refresh_jti: int + spot_jwt_refresh_iat: int -def change_jwt_iat_jti(user_id, include_spot: bool = False): - sub_query = "" - sub_result = "" - if include_spot: - sub_query = """,spot_jwt_iat = timezone('utc'::text, now()-INTERVAL '10s'), - spot_jwt_refresh_jti = 0, - spot_jwt_refresh_iat = timezone('utc'::text, now()-INTERVAL '10s')""" - sub_result = """,EXTRACT (epoch FROM spot_jwt_iat)::BIGINT AS spot_jwt_iat, - spot_jwt_refresh_jti, - EXTRACT (epoch FROM spot_jwt_refresh_iat)::BIGINT AS spot_jwt_refresh_iat""" +def change_jwt_iat_jti(user_id): with pg_client.PostgresClient() as cur: query = cur.mogrify(f"""UPDATE public.users SET jwt_iat = timezone('utc'::text, now()-INTERVAL '10s'), jwt_refresh_jti = 0, - jwt_refresh_iat = timezone('utc'::text, now()-INTERVAL '10s') - {sub_query} + jwt_refresh_iat = timezone('utc'::text, now()-INTERVAL '10s'), + spot_jwt_iat = timezone('utc'::text, now()-INTERVAL '10s'), + spot_jwt_refresh_jti = 0, + spot_jwt_refresh_iat = timezone('utc'::text, now()-INTERVAL '10s') WHERE user_id = %(user_id)s RETURNING EXTRACT (epoch FROM jwt_iat)::BIGINT AS jwt_iat, jwt_refresh_jti, - EXTRACT (epoch FROM jwt_refresh_iat)::BIGINT AS jwt_refresh_iat - {sub_result};""", + EXTRACT (epoch FROM jwt_refresh_iat)::BIGINT AS jwt_refresh_iat, + EXTRACT (epoch FROM spot_jwt_iat)::BIGINT AS spot_jwt_iat, + spot_jwt_refresh_jti, + EXTRACT (epoch FROM spot_jwt_refresh_iat)::BIGINT AS spot_jwt_refresh_iat;""", {"user_id": user_id}) cur.execute(query) row = cur.fetchone() @@ -699,7 +693,9 @@ def refresh_jwt_iat_jti(user_id): return row.get("jwt_iat"), row.get("jwt_refresh_jti"), row.get("jwt_refresh_iat") -def authenticate(email, password, for_change_password=False, include_spot=False) -> dict | bool | None: +def authenticate(email, password, for_change_password=False) -> dict | bool | None: + if config("enforce_SSO", cast=bool, default=False) and helper.is_saml2_available(): + return {"errors": ["must sign-in with SSO, enforced by admin"]} with pg_client.PostgresClient() as cur: query = cur.mogrify( f"""SELECT @@ -749,7 +745,7 @@ def authenticate(email, password, for_change_password=False, include_spot=False) elif config("enforce_SSO", cast=bool, default=False) and helper.is_saml2_available(): return {"errors": ["must sign-in with SSO, enforced by admin"]} - j_r = change_jwt_iat_jti(user_id=r['userId'], include_spot=include_spot) + j_r = change_jwt_iat_jti(user_id=r['userId']) response = { "jwt": authorizers.generate_jwt(user_id=r['userId'], tenant_id=r['tenantId'], iat=j_r.jwt_iat, aud=AUDIENCE), @@ -758,23 +754,19 @@ def authenticate(email, password, for_change_password=False, include_spot=False) jwt_jti=j_r.jwt_refresh_jti), "refreshTokenMaxAge": config("JWT_REFRESH_EXPIRATION", cast=int), "email": email, + "spotJwt": authorizers.generate_jwt(user_id=r['userId'], tenant_id=r['tenantId'], + iat=j_r.spot_jwt_iat, aud=spot.AUDIENCE, for_spot=True), + "spotRefreshToken": authorizers.generate_jwt_refresh(user_id=r['userId'], + tenant_id=r['tenantId'], + iat=j_r.spot_jwt_refresh_iat, + aud=spot.AUDIENCE, + jwt_jti=j_r.spot_jwt_refresh_jti, + for_spot=True), + "spotRefreshTokenMaxAge": config("JWT_SPOT_REFRESH_EXPIRATION", cast=int), **r } - if include_spot: - response = {**response, - "spotJwt": authorizers.generate_jwt(user_id=r['userId'], tenant_id=r['tenantId'], - iat=j_r.spot_jwt_iat, aud=spot.AUDIENCE, for_spot=True), - "spotRefreshToken": authorizers.generate_jwt_refresh(user_id=r['userId'], - tenant_id=r['tenantId'], - iat=j_r.spot_jwt_refresh_iat, - aud=spot.AUDIENCE, - jwt_jti=j_r.spot_jwt_refresh_jti, - for_spot=True), - "spotRefreshTokenMaxAge": config("JWT_SPOT_REFRESH_EXPIRATION", cast=int), - } return response - if config("enforce_SSO", cast=bool, default=False) and helper.is_saml2_available(): - return {"errors": ["must sign-in with SSO, enforced by admin"]} + return None @@ -865,7 +857,7 @@ def refresh(user_id: int, tenant_id: int) -> dict: } -def authenticate_sso(email: str, internal_id: str, include_spot: bool = False): +def authenticate_sso(email: str, internal_id: str): with pg_client.PostgresClient() as cur: query = cur.mogrify( f"""SELECT @@ -891,27 +883,23 @@ def authenticate_sso(email: str, internal_id: str, include_spot: bool = False): if r["serviceAccount"]: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="service account is not authorized to login") - j_r = change_jwt_iat_jti(user_id=r['userId'], include_spot=include_spot) + j_r = change_jwt_iat_jti(user_id=r['userId']) response = { "jwt": authorizers.generate_jwt(user_id=r['userId'], tenant_id=r['tenantId'], iat=j_r.jwt_iat, aud=AUDIENCE), "refreshToken": authorizers.generate_jwt_refresh(user_id=r['userId'], tenant_id=r['tenantId'], iat=j_r.jwt_refresh_iat, aud=AUDIENCE, jwt_jti=j_r.jwt_refresh_jti), - "refreshTokenMaxAge": config("JWT_REFRESH_EXPIRATION", cast=int) + "refreshTokenMaxAge": config("JWT_REFRESH_EXPIRATION", cast=int), + "spotJwt": authorizers.generate_jwt(user_id=r['userId'], tenant_id=r['tenantId'], + iat=j_r.spot_jwt_iat, aud=spot.AUDIENCE), + "spotRefreshToken": authorizers.generate_jwt_refresh(user_id=r['userId'], + tenant_id=r['tenantId'], + iat=j_r.spot_jwt_refresh_iat, + aud=spot.AUDIENCE, + jwt_jti=j_r.spot_jwt_refresh_jti), + "spotRefreshTokenMaxAge": config("JWT_SPOT_REFRESH_EXPIRATION", cast=int) } - if include_spot: - response = { - **response, - "spotJwt": authorizers.generate_jwt(user_id=r['userId'], tenant_id=r['tenantId'], - iat=j_r.spot_jwt_iat, aud=spot.AUDIENCE), - "spotRefreshToken": authorizers.generate_jwt_refresh(user_id=r['userId'], - tenant_id=r['tenantId'], - iat=j_r.spot_jwt_refresh_iat, - aud=spot.AUDIENCE, - jwt_jti=j_r.spot_jwt_refresh_jti), - "spotRefreshTokenMaxAge": config("JWT_SPOT_REFRESH_EXPIRATION", cast=int) - } return response logger.warning(f"SSO user not found with email: {email} and internal_id: {internal_id}") return None diff --git a/ee/api/routers/core_dynamic.py b/ee/api/routers/core_dynamic.py index 1272f4950..e0387fdd9 100644 --- a/ee/api/routers/core_dynamic.py +++ b/ee/api/routers/core_dynamic.py @@ -15,13 +15,13 @@ from chalicelib.core import tenants, users, projects, license from chalicelib.core import unprocessed_sessions from chalicelib.core import webhook from chalicelib.core.collaboration_slack import Slack -from chalicelib.core.users import get_user_settings from chalicelib.utils import SAML2_helper, smtp from chalicelib.utils import captcha from chalicelib.utils import helper from chalicelib.utils.TimeUTC import TimeUTC from or_dependencies import OR_context, OR_scope, OR_role from routers.base import get_routers +from routers.subs import spot from schemas import Permissions, ServicePermissions if config("ENABLE_SSO", cast=bool, default=True): @@ -55,15 +55,36 @@ if config("MULTI_TENANTS", cast=bool, default=False) or not tenants.tenants_exis return content +def __process_authentication_response(response: JSONResponse, data: dict) -> dict: + data["smtp"] = smtp.has_smtp() + refresh_token = data.pop("refreshToken") + refresh_token_max_age = data.pop("refreshTokenMaxAge") + spot_refresh_token = data.pop("spotRefreshToken") + spot_refresh_token_max_age = data.pop("spotRefreshTokenMaxAge") + data = { + 'jwt': data.pop('jwt'), + "spotJwt": data.pop("spotJwt"), + 'data': { + "scopeState": scope.get_scope(data["tenantId"]), + "user": data + } + } + response.set_cookie(key="refreshToken", value=refresh_token, path=COOKIE_PATH, + max_age=refresh_token_max_age, secure=True, httponly=True) + response.set_cookie(key="spotRefreshToken", value=spot_refresh_token, path=spot.COOKIE_PATH, + max_age=spot_refresh_token_max_age, secure=True, httponly=True) + return data + + @public_app.post('/login', tags=["authentication"]) -def login_user(response: JSONResponse, spot: Optional[bool] = False, data: schemas.UserLoginSchema = Body(...)): +def login_user(response: JSONResponse, data: schemas.UserLoginSchema = Body(...)): if helper.allow_captcha() and not captcha.is_valid(data.g_recaptcha_response): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid captcha." ) - r = users.authenticate(email=data.email, password=data.password.get_secret_value(), include_spot=spot) + r = users.authenticate(email=data.email, password=data.password.get_secret_value()) if r is None: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, @@ -74,26 +95,8 @@ def login_user(response: JSONResponse, spot: Optional[bool] = False, data: schem status_code=status.HTTP_401_UNAUTHORIZED, detail=r["errors"][0] ) - - r["smtp"] = smtp.has_smtp() - refresh_token = r.pop("refreshToken") - refresh_token_max_age = r.pop("refreshTokenMaxAge") - content = { - 'jwt': r.pop('jwt'), - 'data': { - "scopeState": scope.get_scope(r["tenantId"]), - "user": r - } - } - response.set_cookie(key="refreshToken", value=refresh_token, path=COOKIE_PATH, - max_age=refresh_token_max_age, secure=True, httponly=True) - if spot: - content["spotJwt"] = r.pop("spotJwt") - spot_refresh_token = r.pop("spotRefreshToken") - spot_refresh_token_max_age = r.pop("spotRefreshTokenMaxAge") - response.set_cookie(key="spotRefreshToken", value=spot_refresh_token, path="/api/spot/refresh", - max_age=spot_refresh_token_max_age, secure=True, httponly=True) - return content + r = __process_authentication_response(response=response, data=r) + return r @app.get('/logout', tags=["login"]) @@ -105,13 +108,12 @@ def logout_user(response: Response, context: schemas.CurrentContext = Depends(OR @app.get('/refresh', tags=["login"]) -def refresh_login(context: schemas.CurrentContext = Depends(OR_context)): +def refresh_login(response: JSONResponse, context: schemas.CurrentContext = Depends(OR_context)): r = users.refresh(user_id=context.user_id, tenant_id=context.tenant_id) content = {"jwt": r.get("jwt")} - response = JSONResponse(content=content) response.set_cookie(key="refreshToken", value=r.get("refreshToken"), path=COOKIE_PATH, max_age=r.pop("refreshTokenMaxAge"), secure=True, httponly=True) - return response + return content @app.get('/account', tags=['accounts']) @@ -131,7 +133,7 @@ def get_account(context: schemas.CurrentContext = Depends(OR_context)): **r, **t, **license.get_status(context.tenant_id), - "settings": get_user_settings(context.user_id)["settings"], + "settings": users.get_user_settings(context.user_id)["settings"], "smtp": smtp.has_smtp(), "saml2": SAML2_helper.is_saml2_available() } @@ -207,7 +209,7 @@ async def process_invitation_link(token: str, request: Request): @public_app.post('/password/reset', tags=["users"]) -def change_password_by_invitation(data: schemas.EditPasswordByInvitationSchema = Body(...)): +def change_password_by_invitation(response: JSONResponse, data: schemas.EditPasswordByInvitationSchema = Body(...)): if data is None or len(data.invitation) < 64 or len(data.passphrase) < 8: return {"errors": ["please provide a valid invitation & pass"]} user = users.get_by_invitation_token(token=data.invitation, pass_token=data.passphrase) @@ -216,8 +218,10 @@ def change_password_by_invitation(data: schemas.EditPasswordByInvitationSchema = if user["expiredChange"]: return {"errors": ["expired change, please re-use the invitation link"]} - return users.set_password_invitation(new_password=data.password.get_secret_value(), user_id=user["userId"], - tenant_id=user["tenantId"]) + r = users.set_password_invitation(new_password=data.password.get_secret_value(), user_id=user["userId"], + tenant_id=user["tenantId"]) + r = __process_authentication_response(response=response, data=r) + return r @app.put('/client/members/{memberId}', tags=["client"], dependencies=[OR_role("owner", "admin")]) diff --git a/ee/api/routers/saml.py b/ee/api/routers/saml.py index a06d669ad..42c6f529b 100644 --- a/ee/api/routers/saml.py +++ b/ee/api/routers/saml.py @@ -8,7 +8,9 @@ from starlette.responses import RedirectResponse from chalicelib.core import users, tenants, roles from chalicelib.utils import SAML2_helper +from routers import core_dynamic from routers.base import get_routers +from routers.subs import spot logger = logging.getLogger(__name__) @@ -17,11 +19,11 @@ public_app, app, app_apikey = get_routers(prefix="/sso/saml2") @public_app.get("", tags=["saml2"]) @public_app.get("/", tags=["saml2"]) -async def start_sso(request: Request, iFrame: bool = False, spot: bool = False): +async def start_sso(request: Request, iFrame: bool = False): request.path = '' req = await SAML2_helper.prepare_request(request=request) auth = SAML2_helper.init_saml_auth(req) - sso_built_url = auth.login(return_to=json.dumps({'iFrame': iFrame, 'spot': spot})) + sso_built_url = auth.login(return_to=json.dumps({'iFrame': iFrame})) return RedirectResponse(url=sso_built_url) @@ -42,7 +44,6 @@ async def __process_assertion(request: Request, tenant_key=None) -> Response | d post_data = {} redirect_to_link2 = None - spot = False relay_state = post_data.get('RelayState') if relay_state: if isinstance(relay_state, str): @@ -53,7 +54,6 @@ async def __process_assertion(request: Request, tenant_key=None) -> Response | d logger.error(relay_state) relay_state = {} redirect_to_link2 = relay_state.get("iFrame") - spot = relay_state.get("spot") request_id = None if 'AuthNRequestID' in session: @@ -153,17 +153,15 @@ async def __process_assertion(request: Request, tenant_key=None) -> Response | d logger.info(f"== Updating user:{existing['userId']}: {to_update} ==") users.update(tenant_id=t['tenantId'], user_id=existing["userId"], changes=to_update) - jwt = users.authenticate_sso(email=email, internal_id=internal_id, include_spot=spot) + jwt = users.authenticate_sso(email=email, internal_id=internal_id) if jwt is None: return {"errors": ["null JWT"]} response = Response(status_code=status.HTTP_302_FOUND) - response.set_cookie(key="refreshToken", value=jwt["refreshToken"], path="/api/refresh", + response.set_cookie(key="refreshToken", value=jwt["refreshToken"], path=core_dynamic.COOKIE_PATH, max_age=jwt["refreshTokenMaxAge"], secure=True, httponly=True) - query_params = {"jwt": jwt["jwt"]} - if spot: - response.set_cookie(key="spotRefreshToken", value=jwt["spotRefreshToken"], path="/api/spot/refresh", - max_age=jwt["spotRefreshTokenMaxAge"], secure=True, httponly=True) - query_params["spotJwt"] = jwt["spotJwt"] + query_params = {"jwt": jwt["jwt"], "spotJwt": jwt["spotJwt"]} + response.set_cookie(key="spotRefreshToken", value=jwt["spotRefreshToken"], path=spot.COOKIE_PATH, + max_age=jwt["spotRefreshTokenMaxAge"], secure=True, httponly=True) headers = {'Location': SAML2_helper.get_landing_URL(query_params=query_params, redirect_to_link2=redirect_to_link2)} response.init_headers(headers)