Dev (#2449)
* refactor(chalice): upgraded dependencies * refactor(chalice): upgraded dependencies feat(chalice): support 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 * refactor(chalice): upgraded dependencies * refactor(chalice): upgraded dependencies feat(chalice): support 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 * refactor(chalice): upgraded dependencies refactor(crons): upgraded dependencies refactor(alerts): upgraded dependencies * feat(chalice): get top 10 values for autocomplete CH * refactor(chalice): cleaned code refactor(chalice): upgraded dependencies refactor(alerts): upgraded dependencies refactor(crons): upgraded dependencies * feat(chalice): autocomplete return top 10 with stats * fix(chalice): fixed autocomplete top 10 meta-filters * refactor(chalice): upgraded dependencies refactor(chalice): merged spot-login logic with regular-login
This commit is contained in:
parent
01af0ccfda
commit
8136d83025
12 changed files with 119 additions and 49 deletions
11
api/app.py
11
api/app.py
|
|
@ -14,7 +14,7 @@ from starlette.responses import StreamingResponse
|
|||
from chalicelib.utils import helper
|
||||
from chalicelib.utils import pg_client
|
||||
from crons import core_crons, core_dynamic_crons
|
||||
from routers import core, core_dynamic, additional_routes
|
||||
from routers import core, core_dynamic
|
||||
from routers.subs import insights, metrics, v1_api, health, usability_tests, spot
|
||||
|
||||
loglevel = config("LOGLEVEL", default=logging.WARNING)
|
||||
|
|
@ -127,12 +127,3 @@ app.include_router(usability_tests.app_apikey)
|
|||
app.include_router(spot.public_app)
|
||||
app.include_router(spot.app)
|
||||
app.include_router(spot.app_apikey)
|
||||
|
||||
app.include_router(additional_routes.app)
|
||||
|
||||
# @app.get('/private/shutdown', tags=["private"])
|
||||
# async def stop_server():
|
||||
# logging.info("Requested shutdown")
|
||||
# await shutdown()
|
||||
# import os, signal
|
||||
# os.kill(1, signal.SIGTERM)
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@ import secrets
|
|||
|
||||
from decouple import config
|
||||
from fastapi import BackgroundTasks
|
||||
from pydantic import BaseModel
|
||||
|
||||
import schemas
|
||||
from chalicelib.core import authorizers, metadata, projects
|
||||
from chalicelib.core import tenants, assist
|
||||
from chalicelib.core import tenants, assist, spot
|
||||
from chalicelib.utils import email_helper, smtp
|
||||
from chalicelib.utils import helper
|
||||
from chalicelib.utils import pg_client
|
||||
|
|
@ -555,20 +556,40 @@ def refresh_auth_exists(user_id, jwt_jti=None):
|
|||
return r is not None
|
||||
|
||||
|
||||
def change_jwt_iat_jti(user_id):
|
||||
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
|
||||
|
||||
|
||||
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"""
|
||||
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')
|
||||
jwt_refresh_iat = timezone('utc'::text, now()-INTERVAL '10s')
|
||||
{sub_query}
|
||||
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;""",
|
||||
EXTRACT (epoch FROM jwt_refresh_iat)::BIGINT AS jwt_refresh_iat
|
||||
{sub_result};""",
|
||||
{"user_id": user_id})
|
||||
cur.execute(query)
|
||||
row = cur.fetchone()
|
||||
return row.get("jwt_iat"), row.get("jwt_refresh_jti"), row.get("jwt_refresh_iat")
|
||||
return ChangeJwt(**row)
|
||||
|
||||
|
||||
def refresh_jwt_iat_jti(user_id):
|
||||
|
|
@ -586,7 +607,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) -> dict | bool | None:
|
||||
def authenticate(email, password, for_change_password=False, include_spot=False) -> dict | bool | None:
|
||||
with pg_client.PostgresClient() as cur:
|
||||
query = cur.mogrify(
|
||||
f"""SELECT
|
||||
|
|
@ -611,17 +632,29 @@ def authenticate(email, password, for_change_password=False) -> dict | bool | No
|
|||
if for_change_password:
|
||||
return True
|
||||
r = helper.dict_to_camel_case(r)
|
||||
jwt_iat, jwt_r_jti, jwt_r_iat = change_jwt_iat_jti(user_id=r['userId'])
|
||||
return {
|
||||
"jwt": authorizers.generate_jwt(user_id=r['userId'], tenant_id=r['tenantId'], iat=jwt_iat,
|
||||
j_r = change_jwt_iat_jti(user_id=r['userId'], include_spot=include_spot)
|
||||
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=jwt_r_iat, aud=AUDIENCE,
|
||||
jwt_jti=jwt_r_jti),
|
||||
iat=j_r.jwt_refresh_iat, aud=AUDIENCE,
|
||||
jwt_jti=j_r.jwt_refresh_jti),
|
||||
"refreshTokenMaxAge": config("JWT_REFRESH_EXPIRATION", cast=int),
|
||||
"email": email,
|
||||
**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),
|
||||
"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_REFRESH_EXPIRATION", cast=int),
|
||||
}
|
||||
return response
|
||||
return None
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Keep this version to not have conflicts between requests and boto3
|
||||
urllib3==1.26.16
|
||||
requests==2.32.3
|
||||
boto3==1.34.147
|
||||
boto3==1.34.151
|
||||
pyjwt==2.8.0
|
||||
psycopg2-binary==2.9.9
|
||||
psycopg[pool,binary]==3.2.1
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Keep this version to not have conflicts between requests and boto3
|
||||
urllib3==1.26.16
|
||||
requests==2.32.3
|
||||
boto3==1.34.147
|
||||
boto3==1.34.151
|
||||
pyjwt==2.8.0
|
||||
psycopg2-binary==2.9.9
|
||||
psycopg[pool,binary]==3.2.1
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
# this file will be overwritten by the managed saas
|
||||
from routers.base import get_routers
|
||||
|
||||
public_app, app, app_apikey = get_routers()
|
||||
|
|
@ -46,14 +46,14 @@ if not tenants.tenants_exists_sync(use_pool=False):
|
|||
|
||||
|
||||
@public_app.post('/login', tags=["authentication"])
|
||||
def login_user(response: JSONResponse, data: schemas.UserLoginSchema = Body(...)):
|
||||
def login_user(response: JSONResponse, spot: Optional[bool] = False, 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(data.email, data.password.get_secret_value())
|
||||
r = users.authenticate(email=data.email, password=data.password.get_secret_value(), include_spot=spot)
|
||||
if r is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
|
|
@ -74,9 +74,17 @@ def login_user(response: JSONResponse, data: schemas.UserLoginSchema = Body(...)
|
|||
"user": r
|
||||
}
|
||||
}
|
||||
if spot:
|
||||
content["spotJwt"] = r.pop("spotJwt")
|
||||
spot_refresh_token = r.pop("spotRefreshToken")
|
||||
spot_refresh_token_max_age = r.pop("spotRefreshTokenMaxAge")
|
||||
|
||||
response = JSONResponse(content=content)
|
||||
response.set_cookie(key="refreshToken", value=refresh_token, path="/api/refresh",
|
||||
max_age=refresh_token_max_age, secure=True, httponly=True)
|
||||
if spot:
|
||||
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 response
|
||||
|
||||
|
||||
|
|
@ -84,6 +92,7 @@ def login_user(response: JSONResponse, data: schemas.UserLoginSchema = Body(...)
|
|||
def logout_user(response: Response, context: schemas.CurrentContext = Depends(OR_context)):
|
||||
users.logout(user_id=context.user_id)
|
||||
response.delete_cookie(key="refreshToken", path="/api/refresh")
|
||||
response.delete_cookie(key="spotRefreshToken", path="/api/spot/refresh")
|
||||
return {"data": "success"}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ def login_spot(response: JSONResponse, data: schemas.UserLoginSchema = Body(...)
|
|||
}
|
||||
}
|
||||
response = JSONResponse(content=content)
|
||||
response.set_cookie(key="refreshToken", value=refresh_token, path=COOKIE_PATH,
|
||||
response.set_cookie(key="spotRefreshToken", value=refresh_token, path=COOKIE_PATH,
|
||||
max_age=refresh_token_max_age, secure=True, httponly=True)
|
||||
return response
|
||||
|
||||
|
|
@ -51,7 +51,7 @@ def login_spot(response: JSONResponse, data: schemas.UserLoginSchema = Body(...)
|
|||
@app.get('/logout')
|
||||
def logout_spot(response: Response, context: schemas.CurrentContext = Depends(OR_context)):
|
||||
spot.logout(user_id=context.user_id)
|
||||
response.delete_cookie(key="refreshToken", path="/api/refresh")
|
||||
response.delete_cookie(key="spotRefreshToken", path="/api/refresh")
|
||||
return {"data": "success"}
|
||||
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ def refresh_spot_login(context: schemas.CurrentContext = Depends(OR_context)):
|
|||
r = spot.refresh(user_id=context.user_id)
|
||||
content = {"jwt": r.get("jwt")}
|
||||
response = JSONResponse(content=content)
|
||||
response.set_cookie(key="refreshToken", value=r.get("refreshToken"), path=COOKIE_PATH,
|
||||
response.set_cookie(key="spotRefreshToken", value=r.get("refreshToken"), path=COOKIE_PATH,
|
||||
max_age=r.pop("refreshTokenMaxAge"), secure=True, httponly=True)
|
||||
return response
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,12 @@ import secrets
|
|||
|
||||
from decouple import config
|
||||
from fastapi import BackgroundTasks, HTTPException
|
||||
from pydantic import BaseModel
|
||||
from starlette import status
|
||||
|
||||
import schemas
|
||||
from chalicelib.core import authorizers, metadata, projects
|
||||
from chalicelib.core import roles
|
||||
from chalicelib.core import roles, spot
|
||||
from chalicelib.core import tenants, assist
|
||||
from chalicelib.utils import email_helper, smtp
|
||||
from chalicelib.utils import helper
|
||||
|
|
@ -643,20 +644,40 @@ def refresh_auth_exists(user_id, tenant_id, jwt_jti=None):
|
|||
return r is not None
|
||||
|
||||
|
||||
def change_jwt_iat_jti(user_id):
|
||||
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
|
||||
|
||||
|
||||
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"""
|
||||
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')
|
||||
jwt_refresh_iat = timezone('utc'::text, now()-INTERVAL '10s')
|
||||
{sub_query}
|
||||
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;""",
|
||||
EXTRACT (epoch FROM jwt_refresh_iat)::BIGINT AS jwt_refresh_iat
|
||||
{sub_result};""",
|
||||
{"user_id": user_id})
|
||||
cur.execute(query)
|
||||
row = cur.fetchone()
|
||||
return row.get("jwt_iat"), row.get("jwt_refresh_jti"), row.get("jwt_refresh_iat")
|
||||
return ChangeJwt(**row)
|
||||
|
||||
|
||||
def refresh_jwt_iat_jti(user_id):
|
||||
|
|
@ -674,7 +695,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) -> dict | bool | None:
|
||||
def authenticate(email, password, for_change_password=False, include_spot=False) -> dict | bool | None:
|
||||
with pg_client.PostgresClient() as cur:
|
||||
query = cur.mogrify(
|
||||
f"""SELECT
|
||||
|
|
@ -724,18 +745,29 @@ def authenticate(email, password, for_change_password=False) -> dict | bool | No
|
|||
elif config("enforce_SSO", cast=bool, default=False) and helper.is_saml2_available():
|
||||
return {"errors": ["must sign-in with SSO, enforced by admin"]}
|
||||
|
||||
jwt_iat, jwt_r_jti, jwt_r_iat = change_jwt_iat_jti(user_id=r['userId'])
|
||||
|
||||
return {
|
||||
"jwt": authorizers.generate_jwt(user_id=r['userId'], tenant_id=r['tenantId'], iat=jwt_iat,
|
||||
j_r = change_jwt_iat_jti(user_id=r['userId'], include_spot=include_spot)
|
||||
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=jwt_r_iat, aud=AUDIENCE,
|
||||
jwt_jti=jwt_r_jti),
|
||||
iat=j_r.jwt_refresh_iat, aud=AUDIENCE,
|
||||
jwt_jti=j_r.jwt_refresh_jti),
|
||||
"refreshTokenMaxAge": config("JWT_REFRESH_EXPIRATION", cast=int),
|
||||
"email": email,
|
||||
**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),
|
||||
"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_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
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Keep this version to not have conflicts between requests and boto3
|
||||
urllib3==1.26.16
|
||||
requests==2.32.3
|
||||
boto3==1.34.147
|
||||
boto3==1.34.151
|
||||
pyjwt==2.8.0
|
||||
psycopg2-binary==2.9.9
|
||||
psycopg[pool,binary]==3.2.1
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Keep this version to not have conflicts between requests and boto3
|
||||
urllib3==1.26.16
|
||||
requests==2.32.3
|
||||
boto3==1.34.147
|
||||
boto3==1.34.151
|
||||
pyjwt==2.8.0
|
||||
psycopg2-binary==2.9.9
|
||||
psycopg[pool,binary]==3.2.1
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Keep this version to not have conflicts between requests and boto3
|
||||
urllib3==1.26.16
|
||||
requests==2.32.3
|
||||
boto3==1.34.147
|
||||
boto3==1.34.151
|
||||
pyjwt==2.8.0
|
||||
psycopg2-binary==2.9.9
|
||||
psycopg[pool,binary]==3.2.1
|
||||
|
|
|
|||
|
|
@ -52,14 +52,14 @@ if config("MULTI_TENANTS", cast=bool, default=False) or not tenants.tenants_exis
|
|||
|
||||
|
||||
@public_app.post('/login', tags=["authentication"])
|
||||
def login_user(response: JSONResponse, data: schemas.UserLoginSchema = Body(...)):
|
||||
def login_user(response: JSONResponse, spot: Optional[bool] = False, 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(data.email, data.password.get_secret_value())
|
||||
r = users.authenticate(email=data.email, password=data.password.get_secret_value(), include_spot=spot)
|
||||
if r is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
|
|
@ -80,9 +80,17 @@ def login_user(response: JSONResponse, data: schemas.UserLoginSchema = Body(...)
|
|||
"user": r
|
||||
}
|
||||
}
|
||||
if spot:
|
||||
content["spotJwt"] = r.pop("spotJwt")
|
||||
spot_refresh_token = r.pop("spotRefreshToken")
|
||||
spot_refresh_token_max_age = r.pop("spotRefreshTokenMaxAge")
|
||||
|
||||
response = JSONResponse(content=content)
|
||||
response.set_cookie(key="refreshToken", value=refresh_token, path="/api/refresh",
|
||||
max_age=refresh_token_max_age, secure=True, httponly=True)
|
||||
if spot:
|
||||
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 response
|
||||
|
||||
|
||||
|
|
@ -90,6 +98,7 @@ def login_user(response: JSONResponse, data: schemas.UserLoginSchema = Body(...)
|
|||
def logout_user(response: Response, context: schemas.CurrentContext = Depends(OR_context)):
|
||||
users.logout(user_id=context.user_id)
|
||||
response.delete_cookie(key="refreshToken", path="/api/refresh")
|
||||
response.delete_cookie(key="spotRefreshToken", path="/api/spot/refresh")
|
||||
return {"data": "success"}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue