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
This commit is contained in:
parent
2d8ff056c4
commit
25e6862ce6
5 changed files with 158 additions and 169 deletions
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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")])
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")])
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue