diff --git a/api/chalicelib/core/scope.py b/api/chalicelib/core/scope.py new file mode 100644 index 000000000..8e9046602 --- /dev/null +++ b/api/chalicelib/core/scope.py @@ -0,0 +1,27 @@ +from cachetools import cached, TTLCache + +import schemas +from chalicelib.utils import helper +from chalicelib.utils import pg_client + +cache = TTLCache(maxsize=1, ttl=24 * 60 * 60) + + +@cached(cache) +def get_scope(tenant_id) -> schemas.ScopeType: + with pg_client.PostgresClient() as cur: + query = cur.mogrify(f"""SELECT scope + FROM public.tenants;""") + cur.execute(query) + return helper.dict_to_camel_case(cur.fetchone())["scope"] + + +def update_scope(tenant_id, scope: schemas.ScopeType): + with pg_client.PostgresClient() as cur: + query = cur.mogrify(f"""UPDATE public.tenants + SET scope = %(scope)s;""", + {"scope": scope}) + cur.execute(query) + if tenant_id in cache: + cache.pop(tenant_id) + return scope diff --git a/api/chalicelib/core/tenants.py b/api/chalicelib/core/tenants.py index 4a6ab95c6..35cd350bc 100644 --- a/api/chalicelib/core/tenants.py +++ b/api/chalicelib/core/tenants.py @@ -11,7 +11,8 @@ def get_by_tenant_id(tenant_id): tenants.created_at, '{license.EDITION}' AS edition, openreplay_version() AS version_number, - tenants.opt_out + tenants.opt_out, + scope FROM public.tenants LIMIT 1;""", {"tenantId": tenant_id}) diff --git a/api/routers/core.py b/api/routers/core.py index 6f5d830b8..9a5cee24e 100644 --- a/api/routers/core.py +++ b/api/routers/core.py @@ -879,8 +879,6 @@ def health_check(): return {} -# tags - @app.post('/{projectId}/tags', tags=["tags"]) def tags_create(projectId: int, data: schemas.TagCreate = Body(), context: schemas.CurrentContext = Depends(OR_context)): diff --git a/api/routers/core_dynamic.py b/api/routers/core_dynamic.py index 312ac46c4..b0ee025ce 100644 --- a/api/routers/core_dynamic.py +++ b/api/routers/core_dynamic.py @@ -11,6 +11,7 @@ from chalicelib.core import sessions, errors, errors_viewed, errors_favorite, se from chalicelib.core import sessions_viewed from chalicelib.core import tenants, users, projects, license from chalicelib.core import webhook +from chalicelib.core import scope from chalicelib.core.collaboration_slack import Slack from chalicelib.utils import captcha, smtp from chalicelib.utils import helper @@ -72,7 +73,8 @@ def login_user(response: JSONResponse, spot: Optional[bool] = False, data: schem content = { 'jwt': r.pop('jwt'), 'data': { - "user": r + "user": r, + "scope": scope.get_scope(-1) } } response.set_cookie(key="refreshToken", value=refresh_token, path=COOKIE_PATH, @@ -131,6 +133,13 @@ def edit_account(data: schemas.EditAccountSchema = Body(...), return users.edit_account(tenant_id=context.tenant_id, user_id=context.user_id, changes=data) +@app.post('/account/scope', tags=["account"]) +def change_scope(data: schemas.ScopeSchema = Body(), + context: schemas.CurrentContext = Depends(OR_context)): + data = scope.update_scope(tenant_id=-1, scope=data.scope) + return {'data': data} + + @app.post('/integrations/slack', tags=['integrations']) @app.put('/integrations/slack', tags=['integrations']) def add_slack_integration(data: schemas.AddCollaborationSchema, diff --git a/api/schemas/schemas.py b/api/schemas/schemas.py index 83cff57bc..71951512a 100644 --- a/api/schemas/schemas.py +++ b/api/schemas/schemas.py @@ -1651,3 +1651,12 @@ class TagCreate(TagUpdate): selector: str = Field(..., min_length=1, max_length=255) ignoreClickRage: bool = Field(default=False) ignoreDeadClick: bool = Field(default=False) + + +class ScopeType(str, Enum): + FULL_OR = "full" + SPOT_ONLY = "spot" + + +class ScopeSchema(BaseModel): + scope: ScopeType = Field(default=ScopeType.FULL_OR) diff --git a/ee/scripts/schema/db/init_dbs/postgresql/1.20.0/1.20.0.sql b/ee/scripts/schema/db/init_dbs/postgresql/1.20.0/1.20.0.sql index d5f79fb3a..de6dfb0aa 100644 --- a/ee/scripts/schema/db/init_dbs/postgresql/1.20.0/1.20.0.sql +++ b/ee/scripts/schema/db/init_dbs/postgresql/1.20.0/1.20.0.sql @@ -30,7 +30,8 @@ WHERE NOT permissions @> '{SPOT}' UPDATE public.roles SET permissions = (SELECT array_agg(distinct e) FROM unnest(permissions || '{SPOT_PUBLIC}') AS e) WHERE NOT permissions @> '{SPOT_PUBLIC}' - AND name ILIKE 'owner'; + AND NOT service_role; +-- AND name ILIKE 'owner'; ALTER TABLE IF EXISTS public.users ADD COLUMN IF NOT EXISTS spot_jwt_iat timestamp without time zone NULL DEFAULT NULL, @@ -46,9 +47,12 @@ CREATE TABLE IF NOT EXISTS or_cache.autocomplete_top_values result jsonb NULL, execution_time integer NULL, created_at timestamp DEFAULT timezone('utc'::text, now()) NOT NULL, - UNIQUE (project_id, event_type, event_key) + UNIQUE NULLS NOT DISTINCT (project_id, event_type, event_key) ); +ALTER TABLE IF EXISTS public.tenants + ADD COLUMN IF NOT EXISTS scope text NOT NULL DEFAULT 'full'; + COMMIT; \elif :is_next diff --git a/ee/scripts/schema/db/init_dbs/postgresql/init_schema.sql b/ee/scripts/schema/db/init_dbs/postgresql/init_schema.sql index 75d0dcc0c..3fb78f6c2 100644 --- a/ee/scripts/schema/db/init_dbs/postgresql/init_schema.sql +++ b/ee/scripts/schema/db/init_dbs/postgresql/init_schema.sql @@ -103,7 +103,8 @@ CREATE TABLE public.tenants t_sessions bigint NOT NULL DEFAULT 0, t_users integer NOT NULL DEFAULT 1, t_integrations integer NOT NULL DEFAULT 0, - last_telemetry bigint NOT NULL DEFAULT CAST(EXTRACT(epoch FROM date_trunc('day', now())) * 1000 AS BIGINT) + last_telemetry bigint NOT NULL DEFAULT CAST(EXTRACT(epoch FROM date_trunc('day', now())) * 1000 AS BIGINT), + scope text NOT NULL DEFAULT 'full' ); @@ -1313,9 +1314,7 @@ CREATE TABLE or_cache.autocomplete_top_values result jsonb NULL, execution_time integer NULL, created_at timestamp DEFAULT timezone('utc'::text, now()) NOT NULL, - UNIQUE (project_id, event_type, event_key) --- TODO: use `UNIQUE NULLS NOT DISTINCT (project_id, event_type, event_key)` --- when PG upgrade is validated by devops team + UNIQUE NULLS NOT DISTINCT (project_id, event_type, event_key) ); COMMIT; \ No newline at end of file diff --git a/scripts/helmcharts/openreplay/charts/chalice/values.yaml b/scripts/helmcharts/openreplay/charts/chalice/values.yaml index 36aaed3e4..9d81776ec 100644 --- a/scripts/helmcharts/openreplay/charts/chalice/values.yaml +++ b/scripts/helmcharts/openreplay/charts/chalice/values.yaml @@ -97,7 +97,6 @@ env: announcement_url: '' jwt_secret: "SetARandomStringHere" jwt_algorithm: HS512 - jwt_exp_delta_seconds: '2592000' # Enable logging for python app # Ref: https://stackoverflow.com/questions/43969743/logs-in-kubernetes-pod-not-showing-up PYTHONUNBUFFERED: '0' diff --git a/scripts/helmcharts/openreplay/charts/utilities/values.yaml b/scripts/helmcharts/openreplay/charts/utilities/values.yaml index 9f5fe2d73..25da66396 100644 --- a/scripts/helmcharts/openreplay/charts/utilities/values.yaml +++ b/scripts/helmcharts/openreplay/charts/utilities/values.yaml @@ -95,7 +95,6 @@ chalice: announcement_url: '' jwt_secret: "SetARandomStringHere" jwt_algorithm: HS512 - jwt_exp_delta_seconds: '2592000' # Enable logging for python app # Ref: https://stackoverflow.com/questions/43969743/logs-in-kubernetes-pod-not-showing-up PYTHONUNBUFFERED: '0' diff --git a/scripts/schema/db/init_dbs/postgresql/1.20.0/1.20.0.sql b/scripts/schema/db/init_dbs/postgresql/1.20.0/1.20.0.sql index 357f52ca8..5b62e277b 100644 --- a/scripts/schema/db/init_dbs/postgresql/1.20.0/1.20.0.sql +++ b/scripts/schema/db/init_dbs/postgresql/1.20.0/1.20.0.sql @@ -36,9 +36,12 @@ CREATE TABLE IF NOT EXISTS or_cache.autocomplete_top_values result jsonb NULL, execution_time integer NULL, created_at timestamp DEFAULT timezone('utc'::text, now()) NOT NULL, - UNIQUE (project_id, event_type, event_key) + UNIQUE NULLS NOT DISTINCT (project_id, event_type, event_key) ); +ALTER TABLE IF EXISTS public.tenants + ADD COLUMN IF NOT EXISTS scope text NOT NULL DEFAULT 'full'; + COMMIT; \elif :is_next diff --git a/scripts/schema/db/init_dbs/postgresql/init_schema.sql b/scripts/schema/db/init_dbs/postgresql/init_schema.sql index 38d6962e1..15cc114de 100644 --- a/scripts/schema/db/init_dbs/postgresql/init_schema.sql +++ b/scripts/schema/db/init_dbs/postgresql/init_schema.sql @@ -103,8 +103,9 @@ CREATE TABLE public.tenants t_sessions bigint NOT NULL DEFAULT 0, t_users integer NOT NULL DEFAULT 1, t_integrations integer NOT NULL DEFAULT 0, - last_telemetry bigint NOT NULL DEFAULT CAST(EXTRACT(epoch FROM date_trunc('day', now())) * 1000 AS BIGINT) - CONSTRAINT onerow_uni CHECK (tenant_id = 1) + last_telemetry bigint NOT NULL DEFAULT CAST(EXTRACT(epoch FROM date_trunc('day', now())) * 1000 AS BIGINT), + scope text NOT NULL DEFAULT 'full', + CONSTRAINT onerow_uni CHECK (tenant_id = 1) ); CREATE TYPE user_role AS ENUM ('owner', 'admin', 'member'); @@ -1199,9 +1200,7 @@ CREATE TABLE or_cache.autocomplete_top_values result jsonb NULL, execution_time integer NULL, created_at timestamp DEFAULT timezone('utc'::text, now()) NOT NULL, - UNIQUE (project_id, event_type, event_key) --- TODO: use `UNIQUE NULLS NOT DISTINCT (project_id, event_type, event_key)` --- when PG upgrade is validated by devops team + UNIQUE NULLS NOT DISTINCT (project_id, event_type, event_key) );