diff --git a/.github/workflows/license-check.yml b/.github/workflows/license-check.yml index 8b35f33b..94260d98 100644 --- a/.github/workflows/license-check.yml +++ b/.github/workflows/license-check.yml @@ -50,7 +50,7 @@ jobs: with: requirements: "backend/requirements-all.txt" fail: "Copyleft,Other,Error" - exclude: '(psycopg2.*2\.9\.3|fqdn.*1\.5\.1|pyzmq.*25\.1\.1|debugpy.*1\.6\.7|certifi.*2023\.11\.17|tqdm.*4\.66\.1|webencodings.*0\.5\.1|torch.*1\.10\.2.*|torch.*1\.11\.0.*|pytorch-ignite.*0\.4\.10.*|torchaudio.*0\.11\.0.*|torchvision.*0\.12\.0.*|terminado.*0\.15\.0|qudida.*0\.0\.4|expiringdict.*1\.2\.2|botocore.*1\.29\.80|orderedmultidict.*1\.0\.1|deepchecks.*)' + exclude: '(psycopg2.*2\.9\.3|fqdn.*1\.5\.1|pyzmq.*25\.1\.2|debugpy.*1\.6\.7|certifi.*2023\.11\.17|tqdm.*4\.66\.1|webencodings.*0\.5\.1|torch.*1\.10\.2.*|torch.*1\.11\.0.*|pytorch-ignite.*0\.4\.10.*|torchaudio.*0\.11\.0.*|torchvision.*0\.12\.0.*|terminado.*0\.15\.0|qudida.*0\.0\.4|expiringdict.*1\.2\.2|botocore.*1\.29\.80|orderedmultidict.*1\.0\.1|deepchecks.*)' # psycopg2 is LGPL 2 # pyzmq is Revised BSD https://github.com/zeromq/pyzmq/blob/main/examples/LICENSE # debugpy is MIT https://github.com/microsoft/debugpy/blob/main/LICENSE diff --git a/backend/deepchecks_monitoring/ee/features_control_cloud.py b/backend/deepchecks_monitoring/ee/features_control_cloud.py index 313828f1..16d02ad6 100644 --- a/backend/deepchecks_monitoring/ee/features_control_cloud.py +++ b/backend/deepchecks_monitoring/ee/features_control_cloud.py @@ -125,7 +125,10 @@ def _load_tier(self): ld_user = context.build() tier_conf = self.ld_client.variation("paid-features", ld_user, default={}) - self._signup_enabled = self.ld_client.variation("signUpEnabled", ld_user, default=True) + if getattr(self.user, "email_verified", False): + self._signup_enabled = self.ld_client.variation("signUpEnabled", ld_user, default=True) + else: + self._signup_enabled = False tier_conf = TierConfSchema(**tier_conf) self._custom_checks_enabled = tier_conf.custom_checks self._data_retention_months = tier_conf.data_retention_months diff --git a/backend/deepchecks_monitoring/ee/middlewares.py b/backend/deepchecks_monitoring/ee/middlewares.py index 27821706..2cd13288 100644 --- a/backend/deepchecks_monitoring/ee/middlewares.py +++ b/backend/deepchecks_monitoring/ee/middlewares.py @@ -140,6 +140,7 @@ async def wrapped_send(message: Message): "email": access_token.email, "is_admin": access_token.is_admin, "exp": access_token.exp, + "email_verified": access_token.email_verified, } if state and (user := state.get("user")): diff --git a/backend/deepchecks_monitoring/middlewares.py b/backend/deepchecks_monitoring/middlewares.py index af5bc708..022b1f90 100644 --- a/backend/deepchecks_monitoring/middlewares.py +++ b/backend/deepchecks_monitoring/middlewares.py @@ -21,6 +21,7 @@ def _fill_user_and_token_from_state(info, state): "email": access_token.email, "is_admin": access_token.is_admin, "exp": access_token.exp, + "email_verified": access_token.email_verified, } # TODO: this creates DetachedInstanceError for some reason diff --git a/backend/deepchecks_monitoring/public_models/user.py b/backend/deepchecks_monitoring/public_models/user.py index 65b7f88e..9e7ad9b3 100644 --- a/backend/deepchecks_monitoring/public_models/user.py +++ b/backend/deepchecks_monitoring/public_models/user.py @@ -36,6 +36,7 @@ class UserOAuthDTO(PydanticModel): name: str email: EmailStr + email_verified: t.Optional[bool] = False picture: t.Optional[AnyHttpUrl] = None @@ -98,7 +99,7 @@ async def from_oauth_info( organization_id: int | None = None ) -> Self: """Create or get user instance from ouath info.""" - token_data = auth.UserAccessToken(email=info.email, is_admin=True) + token_data = auth.UserAccessToken(email=info.email, email_verified=info.email_verified, is_admin=True) access_token = auth.create_access_token(token_data, auth_jwt_secret, expires_delta=pdl.duration(days=7)) # Checking if the user is already in the database diff --git a/backend/deepchecks_monitoring/utils/auth.py b/backend/deepchecks_monitoring/utils/auth.py index 382dcc1a..7b75ce52 100644 --- a/backend/deepchecks_monitoring/utils/auth.py +++ b/backend/deepchecks_monitoring/utils/auth.py @@ -32,6 +32,7 @@ class UserAccessToken(BaseModel): email: EmailStr + email_verified: bool is_admin: bool exp: t.Optional[float] = None @@ -54,7 +55,7 @@ async def get_user( return if isinstance(token, UserAccessToken): - return (await session.scalar( + user = (await session.scalar( select(models.User) .where(models.User.email == token.email) .options( @@ -62,6 +63,8 @@ async def get_user( joinedload(models.User.roles) ) )) + user.email_verified = token.email_verified + return user if isinstance(token, APIAccessToken): # If we have api token query the user and validate the secret @@ -274,8 +277,7 @@ async def __call__( if self.enforce: raise Unauthorized("expired or invalid access token") return - else: - return request.state.user + return request.state.user class CurrentActiveUser(CurrentUser): diff --git a/backend/tests/common.py b/backend/tests/common.py index 8bbff45b..f79878ac 100644 --- a/backend/tests/common.py +++ b/backend/tests/common.py @@ -41,7 +41,7 @@ async def generate_user( f = faker.Faker() email = email or f.email() u = await User.from_oauth_info( - info=UserOAuthDTO(email=email, name=f.name()), + info=UserOAuthDTO(email=email, name=f.name(), email_verified=True), session=session, auth_jwt_secret=auth_jwt_secret, eula=eula