diff --git a/api/chalicelib/core/users.py b/api/chalicelib/core/users.py index 82648cdd8..45b034587 100644 --- a/api/chalicelib/core/users.py +++ b/api/chalicelib/core/users.py @@ -259,37 +259,41 @@ def generate_new_api_key(user_id): return helper.dict_to_camel_case(r) -def edit(user_id_to_update, tenant_id, changes: schemas.EditUserSchema, editor_id): - user = get(user_id=user_id_to_update, tenant_id=tenant_id) - if editor_id != user_id_to_update or changes.admin is not None and changes.admin != user["admin"]: - admin = get(tenant_id=tenant_id, user_id=editor_id) - if not admin["superAdmin"] and not admin["admin"]: - return {"errors": ["unauthorized"]} - _changes = {} - if editor_id == user_id_to_update: - if changes.admin is not None: - if user["superAdmin"]: - changes.admin = None - elif changes.admin != user["admin"]: - return {"errors": ["cannot change your own role"]} +def __get_account_info(tenant_id, user_id): + with pg_client.PostgresClient() as cur: + cur.execute( + cur.mogrify( + f"""SELECT users.name, + tenants.name AS tenant_name, + tenants.opt_out + FROM public.users INNER JOIN public.tenants ON(TRUE) + WHERE users.user_id = %(userId)s + AND users.deleted_at IS NULL;""", + {"tenantId": tenant_id, "userId": user_id}) + ) + r = cur.fetchone() + return helper.dict_to_camel_case(r) - if changes.email is not None and changes.email != user["email"]: - if email_exists(changes.email): - return {"errors": ["email already exists."]} - if get_deleted_user_by_email(changes.email) is not None: - return {"errors": ["email previously deleted."]} - _changes["email"] = changes.email + +def edit_account(user_id, tenant_id, changes: schemas.EditAccountSchema): + if changes.opt_out is not None or changes.tenantName is not None and len(changes.tenantName) > 0: + user = get(user_id=user_id, tenant_id=tenant_id) + if not user["superAdmin"] and not user["admin"]: + return {"errors": ["unauthorized"]} if changes.name is not None and len(changes.name) > 0: - _changes["name"] = changes.name + update(tenant_id=tenant_id, user_id=user_id, changes={"name": changes.name}) - if changes.admin is not None: - _changes["role"] = "admin" if changes.admin else "member" + _tenant_changes = {} + if changes.tenantName is not None and len(changes.tenantName) > 0: + _tenant_changes["name"] = changes.tenantName - if len(_changes.keys()) > 0: - updated_user = update(tenant_id=tenant_id, user_id=user_id_to_update, changes=_changes) - return {"data": updated_user} - return {"data": user} + if changes.opt_out is not None: + _tenant_changes["opt_out"] = changes.opt_out + if len(_tenant_changes.keys()) > 0: + tenants.edit_client(tenant_id=tenant_id, changes=_tenant_changes) + + return {"data": __get_account_info(tenant_id=tenant_id, user_id=user_id)} def edit_member(user_id_to_update, tenant_id, changes: schemas.EditMemberSchema, editor_id): diff --git a/api/routers/core_dynamic.py b/api/routers/core_dynamic.py index 92788da3f..2300e59f8 100644 --- a/api/routers/core_dynamic.py +++ b/api/routers/core_dynamic.py @@ -92,10 +92,9 @@ async def get_account(context: schemas.CurrentContext = Depends(OR_context)): @app.post('/account', tags=["account"]) -async def edit_account(data: schemas.EditUserSchema = Body(...), +async def edit_account(data: schemas.EditAccountSchema = Body(...), context: schemas.CurrentContext = Depends(OR_context)): - return users.edit(tenant_id=context.tenant_id, user_id_to_update=context.user_id, changes=data, - editor_id=context.user_id) + return users.edit_account(tenant_id=context.tenant_id, user_id=context.user_id, changes=data) @app.post('/integrations/slack', tags=['integrations']) diff --git a/api/schemas.py b/api/schemas.py index ddd1686a1..b13ee6547 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -37,13 +37,13 @@ class UserSignupSchema(UserLoginSchema): alias_generator = attribute_to_camel_case -class EditUserSchema(BaseModel): +class EditAccountSchema(BaseModel): name: Optional[str] = Field(None) - email: Optional[EmailStr] = Field(None) - admin: Optional[bool] = Field(None) + tenantName: Optional[str] = Field(None) + opt_out: Optional[bool] = Field(None) _transform_name = validator('name', pre=True, allow_reuse=True)(remove_whitespace) - _transform_email = validator('email', pre=True, allow_reuse=True)(transform_email) + _transform_tenantName = validator('tenantName', pre=True, allow_reuse=True)(remove_whitespace) class ForgetPasswordPayloadSchema(_Grecaptcha): @@ -63,6 +63,7 @@ class EditUserPasswordSchema(BaseModel): class UpdateTenantSchema(BaseModel): name: Optional[str] = Field(None) opt_out: Optional[bool] = Field(None) + tenant_name: Optional[str] = Field(None) class Config: alias_generator = attribute_to_camel_case @@ -149,7 +150,7 @@ class CreateMemberSchema(BaseModel): _transform_name = validator('name', pre=True, allow_reuse=True)(remove_whitespace) -class EditMemberSchema(EditUserSchema): +class EditMemberSchema(BaseModel): name: str = Field(...) email: EmailStr = Field(...) admin: bool = Field(False) @@ -1062,8 +1063,8 @@ class CardSchema(__CardSchema, CardChartSchema): MetricTableViewType, MetricOtherViewType] = Field(...) metric_type: MetricType = Field(...) metric_of: Union[MetricOfTimeseries, MetricOfTable, MetricOfErrors, \ - MetricOfPerformance, MetricOfResources, MetricOfWebVitals, \ - MetricOfClickMap] = Field(default=MetricOfTable.user_id) + MetricOfPerformance, MetricOfResources, MetricOfWebVitals, \ + MetricOfClickMap] = Field(default=MetricOfTable.user_id) metric_value: List[IssueType] = Field(default=[]) is_template: bool = Field(default=False) @@ -1235,7 +1236,7 @@ class LiveSessionSearchFilterSchema(BaseModel): type: LiveFilterType = Field(...) source: Optional[str] = Field(default=None) operator: Literal[SearchEventOperator._is, \ - SearchEventOperator._contains] = Field(default=SearchEventOperator._contains) + SearchEventOperator._contains] = Field(default=SearchEventOperator._contains) transform = root_validator(pre=True, allow_reuse=True)(transform_old_FilterType) diff --git a/ee/api/chalicelib/core/users.py b/ee/api/chalicelib/core/users.py index 7075c42eb..b6141a351 100644 --- a/ee/api/chalicelib/core/users.py +++ b/ee/api/chalicelib/core/users.py @@ -4,6 +4,7 @@ import secrets from decouple import config from fastapi import BackgroundTasks +import schemas import schemas_ee from chalicelib.core import authorizers, metadata, projects, roles from chalicelib.core import tenants, assist @@ -304,45 +305,43 @@ def generate_new_api_key(user_id): return helper.dict_to_camel_case(r) -def edit(user_id_to_update, tenant_id, changes: schemas_ee.EditUserSchema, editor_id): - user = get(user_id=user_id_to_update, tenant_id=tenant_id) - if editor_id != user_id_to_update or changes.admin is not None and changes.admin != user["admin"]: - admin = get(tenant_id=tenant_id, user_id=editor_id) - if not admin["superAdmin"] and not admin["admin"]: - return {"errors": ["unauthorized"]} - _changes = {} - if editor_id == user_id_to_update: - if changes.admin is not None: - if user["superAdmin"]: - changes.admin = None - elif changes.admin != user["admin"]: - return {"errors": ["cannot change your own role"]} - if changes.roleId is not None: - if user["superAdmin"]: - changes.roleId = None - elif changes.roleId != user["roleId"]: - return {"errors": ["cannot change your own role"]} +def __get_account_info(tenant_id, user_id): + with pg_client.PostgresClient() as cur: + cur.execute( + cur.mogrify( + f"""SELECT users.name, + tenants.name AS tenant_name, + tenants.opt_out + FROM public.users INNER JOIN public.tenants USING (tenant_id) + WHERE users.user_id = %(userId)s + AND tenants.tenant_id= %(tenantId)s + AND tenants.deleted_at IS NULL + AND users.deleted_at IS NULL;""", + {"tenantId": tenant_id, "userId": user_id}) + ) + r = cur.fetchone() + return helper.dict_to_camel_case(r) - if changes.email is not None and changes.email != user["email"]: - if email_exists(changes.email): - return {"errors": ["email already exists."]} - if get_deleted_user_by_email(changes.email) is not None: - return {"errors": ["email previously deleted."]} - _changes["email"] = changes.email + +def edit_account(user_id, tenant_id, changes: schemas.EditAccountSchema): + if changes.opt_out is not None or changes.tenantName is not None and len(changes.tenantName) > 0: + user = get(user_id=user_id, tenant_id=tenant_id) + if not user["superAdmin"] and not user["admin"]: + return {"errors": ["unauthorized"]} if changes.name is not None and len(changes.name) > 0: - _changes["name"] = changes.name + update(tenant_id=tenant_id, user_id=user_id, changes={"name": changes.name}) - if changes.admin is not None: - _changes["role"] = "admin" if changes.admin else "member" + _tenant_changes = {} + if changes.tenantName is not None and len(changes.tenantName) > 0: + _tenant_changes["name"] = changes.tenantName - if changes.roleId is not None: - _changes["roleId"] = changes.roleId + if changes.opt_out is not None: + _tenant_changes["opt_out"] = changes.opt_out + if len(_tenant_changes.keys()) > 0: + tenants.edit_client(tenant_id=tenant_id, changes=_tenant_changes) - if len(_changes.keys()) > 0: - updated_user = update(tenant_id=tenant_id, user_id=user_id_to_update, changes=_changes) - return {"data": updated_user} - return {"data": user} + return {"data": __get_account_info(tenant_id=tenant_id, user_id=user_id)} def edit_member(user_id_to_update, tenant_id, changes: schemas_ee.EditMemberSchema, editor_id): diff --git a/ee/api/routers/core_dynamic.py b/ee/api/routers/core_dynamic.py index 6bacfa3be..a42c7d69d 100644 --- a/ee/api/routers/core_dynamic.py +++ b/ee/api/routers/core_dynamic.py @@ -97,10 +97,9 @@ async def get_account(context: schemas.CurrentContext = Depends(OR_context)): @app.post('/account', tags=["account"]) -async def edit_account(data: schemas_ee.EditUserSchema = Body(...), +async def edit_account(data: schemas.EditAccountSchema = Body(...), context: schemas.CurrentContext = Depends(OR_context)): - return users.edit(tenant_id=context.tenant_id, user_id_to_update=context.user_id, changes=data, - editor_id=context.user_id) + return users.edit_account(tenant_id=context.tenant_id, user_id=context.user_id, changes=data) @app.post('/integrations/slack', tags=['integrations']) diff --git a/ee/api/schemas_ee.py b/ee/api/schemas_ee.py index 8674f207e..48ab5bbeb 100644 --- a/ee/api/schemas_ee.py +++ b/ee/api/schemas_ee.py @@ -63,11 +63,7 @@ class CreateMemberSchema(schemas.CreateMemberSchema): roleId: Optional[int] = Field(None) -class EditUserSchema(schemas.EditUserSchema): - roleId: Optional[int] = Field(None) - - -class EditMemberSchema(EditUserSchema): +class EditMemberSchema(BaseModel): name: str = Field(...) email: EmailStr = Field(...) admin: bool = Field(False) diff --git a/ee/connectors/deploy/requirements_bigquery.txt b/ee/connectors/deploy/requirements_bigquery.txt index 7ce437323..b0589ce25 100644 --- a/ee/connectors/deploy/requirements_bigquery.txt +++ b/ee/connectors/deploy/requirements_bigquery.txt @@ -1,7 +1,7 @@ -confluent-kafka -psycopg2-binary==2.9.3 +confluent-kafka==2.1.1 +psycopg2-binary==2.9.6 SQLAlchemy==1.4.43 google-cloud-bigquery==3.4.2 pandas==1.5.1 -PyYAML -pandas-gbq +PyYAML==6.0 +pandas-gbq==0.19.2 \ No newline at end of file diff --git a/ee/connectors/deploy/requirements_clickhouse.txt b/ee/connectors/deploy/requirements_clickhouse.txt index 8853a865f..453873680 100644 --- a/ee/connectors/deploy/requirements_clickhouse.txt +++ b/ee/connectors/deploy/requirements_clickhouse.txt @@ -1,14 +1,13 @@ certifi==2022.09.24 -chardet==5.0.0 -clickhouse-driver==0.2.4 -clickhouse-sqlalchemy==0.2.2 +chardet==5.1.0 +clickhouse-driver==0.2.6 +clickhouse-sqlalchemy==0.2.4 idna==3.4 -confluent-kafka +confluent-kafka==2.1.1 pandas==1.5.1 pytz==2022.6 requests==2.28.1 SQLAlchemy==1.4.43 -tzlocal +tzlocal==5.0.1 urllib3==1.26.12 -PyYAML - +PyYAML==6.0 \ No newline at end of file diff --git a/ee/connectors/deploy/requirements_pg.txt b/ee/connectors/deploy/requirements_pg.txt index 8354e61a9..634e50eca 100644 --- a/ee/connectors/deploy/requirements_pg.txt +++ b/ee/connectors/deploy/requirements_pg.txt @@ -1,12 +1,12 @@ certifi==2022.09.24 -chardet==5.0.0 +chardet==5.1.0 idna==3.4 -confluent-kafka +confluent-kafka==2.1.1 pandas==1.5.1 -psycopg2-binary==2.9.3 -pytz==2022.6 +psycopg2-binary==2.9.6 +pytz==2023.3 requests==2.28.1 SQLAlchemy==1.4.43 -tzlocal +tzlocal==5.0.1 urllib3==1.26.12 -PyYAML +PyYAML==6.0 diff --git a/ee/connectors/deploy/requirements_redshift.txt b/ee/connectors/deploy/requirements_redshift.txt index b32f4efcb..e7151e9da 100644 --- a/ee/connectors/deploy/requirements_redshift.txt +++ b/ee/connectors/deploy/requirements_redshift.txt @@ -1,14 +1,14 @@ chardet==5.1.0 idna==3.4 -confluent-kafka==2.0.2 +confluent-kafka==2.1.1 psycopg2-binary==2.9.6 python-decouple==3.8 pytz==2022.6 requests==2.28.1 -SQLAlchemy==2.0.8 -tzlocal +SQLAlchemy==1.4.48 +tzlocal==5.0.1 urllib3==1.26.15 -sqlalchemy-redshift -redshift-connector -pandas-redshift -PyYAML +sqlalchemy-redshift==0.8.14 +redshift-connector==2.0.911 +pandas-redshift==2.0.5 +PyYAML==6.0 diff --git a/ee/connectors/deploy/requirements_snowflake.txt b/ee/connectors/deploy/requirements_snowflake.txt index 5a3aaca99..bb147b010 100644 --- a/ee/connectors/deploy/requirements_snowflake.txt +++ b/ee/connectors/deploy/requirements_snowflake.txt @@ -1,33 +1,33 @@ pandas==1.5.1 -confluent-kafka -SQLAlchemy==1.4.43 -snowflake-connector-python==2.8.2 -snowflake-sqlalchemy==1.4.4 -PyYAML +confluent-kafka==2.1.1 +SQLAlchemy==1.4.48 +snowflake-connector-python==3.0.4 +snowflake-sqlalchemy==1.4.7 +PyYAML==6.0 asn1crypto==1.5.1 azure-common==1.1.28 -azure-core==1.26.1 -azure-storage-blob==12.14.1 -boto3==1.26.3 -botocore==1.29.3 +azure-core==1.27.0 +azure-storage-blob==12.16.0 +boto3==1.26.151 +botocore==1.29.151 certifi==2022.09.24 cffi==1.15.1 -chardet==5.0.0 +chardet==5.1.0 idna==3.4 -isodate +isodate==0.6.1 jmespath==1.0.1 msrest==0.7.1 oauthlib==3.2.2 oscrypto==1.3.0 pycparser==2.21 -pycryptodomex==3.15.0 -PyJWT==2.5.0 +pycryptodomex==3.18.0 +PyJWT==2.7.0 pyOpenSSL==22.1.0 python-dateutil==2.8.2 -pytz==2022.6 -requests==2.28.1 +pytz==2023.3 +requests==2.31.0 requests-oauthlib==1.3.1 -s3transfer==0.6.0 +s3transfer==0.6.1 six==1.16.0 urllib3==1.26.12