Ejemplo n.º 1
0
class Recommendation(db.Model):  # type: ignore
    __bind_key__ = 'experiment_service'
    id = db.Column(UUIDType(binary=False),
                   primary_key=True,
                   default=uuid.uuid4)
    recommendation_type = db.Column(db.String(), index=True)
    tags = db.relationship('RecommendationTag',
                           secondary="recommendation_tags_assoc",
                           backref=db.backref('recommendations', lazy=True))
    checksum = db.Column(db.String(), nullable=False)
    last_updated = db.Column(db.DateTime())
    rating = db.Column(db.Float, default=3.0)
    meta = db.Column(JSONB())
    data = db.Column(JSONB(), nullable=False)

    def to_dict(self):
        data = self.data
        if self.recommendation_type == "experiment":
            data = data["hashed"]
        return {
            "id": str(self.id),
            "type": self.recommendation_type,
            "tags": [tag.value for tag in self.tags],
            "checksum": self.checksum,
            "last_updated": "{}Z".format(self.last_updated.isoformat()),
            "meta": self.meta,
            "data": data
        }
Ejemplo n.º 2
0
class AccessToken(db.Model, OAuth2TokenMixin):  # type: ignore
    __bind_key__ = 'auth_service'
    id = db.Column(UUIDType(binary=False),
                   primary_key=True,
                   unique=True,
                   default=uuid.uuid4)
    name = db.Column(db.String(), nullable=False)
    account_id = db.Column(UUIDType(binary=False),
                           db.ForeignKey('account.id', ondelete='CASCADE'),
                           nullable=False)
    last_used_on = db.Column(db.DateTime())

    def to_dict(self):
        last_used = None
        if self.last_used_on:
            last_used = "{}Z".format(self.last_used_on.isoformat())

        return {
            "id": shortuuid.encode(self.id),
            "name": self.name,
            "account_id": shortuuid.encode(self.account_id),
            "access_token": self.access_token,
            "refresh_token": self.refresh_token,
            "client_id": self.client_id,
            "scope": self.scope,
            "token_type": self.token_type,
            "issued_at": self.issued_at,
            "expires_in": self.expires_in,
            "last_used": last_used,
            "revoked": self.revoked
        }
Ejemplo n.º 3
0
class ProviderToken(db.Model, OAuth2TokenMixin):  # type: ignore
    __bind_key__ = 'auth_service'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(20), nullable=False)
    account_id = db.Column(UUIDType(binary=False),
                           db.ForeignKey('account.id', ondelete='CASCADE'))
    account = db.relationship('Account')

    def to_dict(self):
        return {
            "id": self.id,
            "access_token": self.access_token,
            "token_type": self.token_type,
            "refresh_token": self.refresh_token,
            "expires_at": self.expires_in,
            "revoked": self.revoked
        }
Ejemplo n.º 4
0
class APIAccessToken(db.Model):  # type: ignore
    __bind_key__ = 'api_service'
    __tablename__ = 'api_access_token'
    __table_args__ = (db.UniqueConstraint('name',
                                          'account_id',
                                          name='name_account_uniq'), )

    id = db.Column(UUIDType(binary=False),
                   primary_key=True,
                   default=uuid.uuid4)
    name = db.Column(db.String, nullable=False)
    account_id = db.Column(UUIDType(binary=False), nullable=False)
    last_used_on = db.Column(db.DateTime())
    client_id = db.Column(db.String(48))
    token_type = db.Column(db.String(40))
    access_token = db.Column(db.String(255), unique=True, nullable=False)
    refresh_token = db.Column(db.String(255), index=True)
    scope = db.Column(db.Text, default='')
    # be conservative
    revoked = db.Column(db.Boolean, nullable=False, default=True)
    issued_at = db.Column(db.Integer, nullable=False)
    expires_in = db.Column(db.Integer, nullable=False, default=0)

    def get_scope(self):
        return self.scope

    def get_expires_in(self):
        return self.expires_in

    def get_expires_at(self):
        return self.issued_at + self.expires_in

    def is_expired(self):
        now = datetime.utcnow().timestamp()
        return self.get_expires_at() < now

    def is_active(self):
        now = datetime.utcnow().timestamp()
        return self.get_expires_at() >= now

    def revoke(self):
        self.revoked = True

    def to_dict(self):
        last_used = None
        if self.last_used_on:
            last_used = self.last_used_on.replace(
                tzinfo=timezone.utc).timestamp()

        return {
            "id": str(self.id),
            "account_id": str(self.account_id),
            "access_token": self.access_token,
            "refresh_token": self.refresh_token,
            "client_id": self.client_id,
            "scope": self.scope,
            "token_type": self.token_type,
            "issued_at": self.issued_at,
            "expires_in": self.expires_in,
            "last_used": last_used,
            "revoked": self.revoked
        }

    @staticmethod
    def from_dict(token: Dict[str, Any]) -> 'APIAccessToken':
        """
        Create or update a token from the source access token.

        On update, only the scope, revoked and dates properties are changed.
        Others are left as they are.
        """
        access_token = APIAccessToken.get_by_token(token["access_token"])
        if not access_token:
            access_token = APIAccessToken()
            access_token.id = shortuuid.decode(token["id"])
            access_token.account_id = shortuuid.decode(token["account_id"])
            access_token.access_token = token["access_token"]
            access_token.client_id = token["client_id"]

        access_token.name = token["name"]
        access_token.refresh_token = token["refresh_token"]
        access_token.scope = token["scope"]
        access_token.revoked = token["revoked"]
        access_token.issued_at = token["issued_at"]
        access_token.expires_in = token["expires_in"]

        return access_token

    @staticmethod
    def get_by_token(access_token: str) -> Optional['APIAccessToken']:
        return APIAccessToken.query.filter(
            APIAccessToken.access_token == access_token).first()

    @staticmethod
    def get_by_id_for_account(account_id: str,
                              token_id: str) -> Optional['APIAccessToken']:
        return APIAccessToken.query.filter(
            APIAccessToken.account_id == account_id,
            APIAccessToken.id == token_id).first()

    @staticmethod
    def get_all_for_account(account_id: str) -> List['APIAccessToken']:
        return APIAccessToken.query.filter(
            APIAccessToken.account_id == account_id).all()

    @staticmethod
    def get_active_for_account(account_id: str) -> List['APIAccessToken']:
        non_revoked_tokens = APIAccessToken.query.filter(
            APIAccessToken.revoked == False,
            APIAccessToken.account_id == account_id).all()

        return [token for token in non_revoked_tokens if token.is_active()]
Ejemplo n.º 5
0
class RecommendationTag(db.Model):  # type: ignore
    __bind_key__ = 'experiment_service'
    __tablename__ = "recommendation_tag"
    id = db.Column(db.Integer, primary_key=True)
    value = db.Column(db.String(), nullable=False, unique=True)
Ejemplo n.º 6
0
class Org(db.Model):  # type: ignore
    __bind_key__ = 'dashboard_service'

    id = db.Column(UUIDType(binary=False),
                   primary_key=True,
                   default=uuid.uuid4)
    # only set when this is a personal org linked to a single account,
    # otherwise it's not set
    account_id = db.Column(UUIDType(binary=False),
                           db.ForeignKey('user_account.id'),
                           nullable=True)
    name = db.Column(db.String(), nullable=False, unique=True)
    name_lower = db.Column(db.String(), nullable=False, unique=True)
    kind = db.Column(db.Enum(OrgType),
                     nullable=False,
                     default=OrgType.personal)
    created_on = db.Column(db.DateTime(), server_default=func.now())
    workspaces = db.relationship('Workspace',
                                 backref='org',
                                 cascade="all, delete-orphan")
    settings = db.Column(JSONB(), nullable=False, default=DEFAULT_ORG_SETTINGS)

    def to_dict(self, public_workspaces_only: bool = False):
        workspaces = []
        for w in self.workspaces:
            if public_workspaces_only and w.kind != WorkspaceType.public:
                continue

            workspaces.append({"id": shortuuid.encode(w.id), "name": w.name})

        return {
            "id": shortuuid.encode(self.id),
            "name": self.name,
            "settings": self.settings,
            "type": self.kind.value,
            "created_on": "{}Z".format(self.created_on.isoformat()),
            "workspaces": workspaces
        }

    def to_short_dict(self):
        return {
            "id": shortuuid.encode(self.id),
            "name": self.name,
            "created_on": "{}Z".format(self.created_on.isoformat()),
            "settings": self.settings,
            "type": self.kind.value
        }

    @staticmethod
    def get_next_available_name(suggested_name: str) -> str:
        """
        Return the next available name prefixed by the given suggested name and
        suffixed by a number between 0 and 1000.

        If `suggested_name` is not used yet, return it as the available name
        """
        while True:
            has_org = Org.query.filter(Org.name == suggested_name).first()
            if not has_org:
                return suggested_name
            suggested_name = "{}{}".format(suggested_name,
                                           secrets.randbelow(1000))

    @staticmethod
    def get_by_id(org_id: Union[str, uuid.UUID]) -> 'Org':
        """
        Lookup an organization by its identifier
        """
        return Org.query.filter(Org.id == org_id).first()

    @staticmethod
    def find_by_name(org_name: str) -> 'Org':
        """
        Lookup an organization by its name
        """
        return Org.query.filter(Org.name_lower == org_name.lower()).first()

    def find_workspace_by_name(self,
                               workspace_name: str) -> Optional[Workspace]:
        """
        Lookup a workspace in the organization by its name
        """
        w_name = workspace_name.lower()
        for workspace in self.workspaces:
            if workspace.name_lower == w_name:
                return workspace

        return None

    def is_member(self, account_id: Union[str, uuid.UUID]) -> bool:
        """
        Return `True` when the given account is a member of the organization
        """
        return OrgsMembers.query.filter(
            OrgsMembers.org_id == self.id,
            OrgsMembers.account_id == account_id).first() is not None

    def is_owner(self, account_id: Union[str, uuid.UUID]) -> bool:
        """
        Return `True` when the given account is an owner of the organization
        """
        return OrgsMembers.query.filter(
            OrgsMembers.org_id == self.id, OrgsMembers.is_owner == True,
            OrgsMembers.account_id == account_id).first() is not None

    def has_single_owner(self) -> bool:
        """
        Return `True` if only one owner exists for this organization
        """
        return OrgsMembers.query.filter(
            OrgsMembers.org_id == self.id,
            OrgsMembers.is_owner == True).count() == 1

    def make_member(self, account_id: Union[str, uuid.UUID]):
        """
        Turn an user as a member only of this organization.

        The user must already be part of this organization, this is mostly
        therefore useful when moving an owner down to simple member.
        """
        membership = OrgsMembers.query.filter(
            OrgsMembers.org_id == self.id,
            OrgsMembers.account_id == account_id).first()
        if membership:
            membership.is_owner = False

    def make_owner(self, account_id: Union[str, uuid.UUID]):
        """
        Turn an user as an owner of this organization.
        """
        membership = OrgsMembers.query.filter(
            OrgsMembers.org_id == self.id,
            OrgsMembers.account_id == account_id).first()
        if membership:
            membership.is_owner = True

    def add_member(self, account_id: Union[str, uuid.UUID]) -> OrgsMembers:
        """
        Add this user to the organization as a member
        """
        membership = OrgsMembers(org_id=self.id, account_id=account_id)
        db.session.add(membership)
        return membership

    def remove_member(self, account_id: Union[str, uuid.UUID]):
        """
        Remove this member from the organization
        """
        OrgsMembers.query.filter(
            OrgsMembers.org_id == self.id,
            OrgsMembers.account_id == account_id).delete()

    def get_public_workspace_ids(self) -> List[uuid.UUID]:
        """
        List all public workspaces in this organization and return their
        identifiers
        """
        result = db.session.query(Workspace.id).filter(
            Workspace.org_id == self.id,
            Workspace.kind == WorkspaceType.public).all()
        if not result:
            return []

        return result[0]
Ejemplo n.º 7
0
class Workspace(db.Model):  # type: ignore
    __bind_key__ = 'dashboard_service'
    __table_args__ = (db.UniqueConstraint('name',
                                          'org_id',
                                          name='workspace_org_uniq'), )

    id = db.Column(UUIDType(binary=False),
                   primary_key=True,
                   default=uuid.uuid4)
    name = db.Column(db.String(), nullable=False)
    name_lower = db.Column(db.String(), nullable=False)
    kind = db.Column(db.Enum(WorkspaceType),
                     nullable=False,
                     default=WorkspaceType.personal)
    org_id = db.Column(UUIDType(binary=False),
                       db.ForeignKey('org.id'),
                       nullable=False)
    settings = db.Column(JSONB(),
                         nullable=False,
                         default=DEFAULT_WORKSPACE_SETTINGS)

    def to_dict(self):
        return {
            "id": shortuuid.encode(self.id),
            "name": self.name,
            "type": self.kind.value,
            "org": {
                "id": shortuuid.encode(self.org_id),
                "name": self.org.name,
                "type": self.org.kind.value
            },
            "settings": self.settings
        }

    def to_short_dict(self):
        return {
            "id": shortuuid.encode(self.id),
            "name": self.name,
            "type": self.kind.value,
            "settings": self.settings
        }

    @staticmethod
    def find_by_name(workspace_name: str) -> 'Workspace':
        """
        Get a workspace by its name
        """
        return Workspace.query.filter(
            Workspace.name_lower == workspace_name.lower()).first()

    @staticmethod
    def get_by_id(workspace_id: Union[str, uuid.UUID]) -> 'Workspace':
        """
        Get a workspace by its identifier
        """
        return Workspace.query.filter(Workspace.id == workspace_id).first()

    def is_collaborator(self, account_id: Union[str, uuid.UUID]) -> bool:
        """
        Return `True` when the given account is a collaborator to this
        workspace
        """
        return WorkpacesMembers.query.filter(
            WorkpacesMembers.workspace_id == self.id,
            WorkpacesMembers.account_id == account_id).first() is not None

    def is_owner(self, account_id: Union[str, uuid.UUID]) -> bool:
        """
        Return `True` when the given account is an owner of the workspace
        """
        return WorkpacesMembers.query.filter(
            WorkpacesMembers.workspace_id == self.id,
            WorkpacesMembers.is_owner == True,
            WorkpacesMembers.account_id == account_id).first() is not None

    def has_single_owner(self) -> bool:
        """
        Return `True` if only one owner exists for this workspace
        """
        return WorkpacesMembers.query.filter(
            WorkpacesMembers.workspace_id == self.id,
            WorkpacesMembers.is_owner == True).count() == 1

    def make_collaborator(self, account_id: Union[str, uuid.UUID]):
        """
        Turn an user as a collaborator only of this workspace.
        """
        membership = WorkpacesMembers.query.filter(
            WorkpacesMembers.workspace_id == self.id,
            WorkpacesMembers.account_id == account_id).first()
        if membership:
            membership.is_owner = False

    def make_owner(self, account_id: Union[str, uuid.UUID]):
        """
        Turn an user as an owner of this workspace.
        """
        membership = WorkpacesMembers.query.filter(
            WorkpacesMembers.workspace_id == self.id,
            WorkpacesMembers.account_id == account_id).first()
        if membership:
            membership.is_owner = True

    def add_collaborator(self, account_id: Union[str, uuid.UUID]) \
            -> WorkpacesMembers:
        """
        Add this user to the organization as a collaborator
        """
        membership = WorkpacesMembers(workspace_id=self.id,
                                      account_id=account_id)
        db.session.add(membership)
        return membership

    def remove_collaborator(self, account_id: Union[str, uuid.UUID]):
        """
        Remove this collaborator from the organization
        """
        WorkpacesMembers.query.filter(
            WorkpacesMembers.workspace_id == self.id,
            WorkpacesMembers.account_id == account_id).delete()