Пример #1
0
class Client(db.Model, OAuth2ClientMixin):
    id = db.Column(db.Integer, primary_key=True)
    client_id = db.Column(db.String(48), index=True, unique=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id',
                                                  ondelete='CASCADE'))
    user = db.relationship(
        'User',
        backref=db.backref(
            "clients",
            cascade="all, delete-orphan",
        ),
    )

    __tablename__ = "oauth2_client"

    @property
    def redirect_uris(self):
        return self.client_metadata.get('redirect_uris', [])

    @redirect_uris.setter
    def redirect_uris(self, value):
        if isinstance(value, str):
            value = value.split(',')
        metadata = self.client_metadata
        metadata['redirect_uris'] = value
        self.set_client_metadata(metadata)

    @property
    def auth_method(self):
        return self.client_metadata.get('token_endpoint_auth_method')

    @auth_method.setter
    def auth_method(self, value):
        metadata = self.client_metadata
        metadata['token_endpoint_auth_method'] = value
        self.set_client_metadata(metadata)

    @classmethod
    def find_by_id(cls, client_id) -> Client:
        return cls.query.get(client_id)

    @classmethod
    def all(cls) -> List[Client]:
        return cls.query.all()
Пример #2
0
class User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(256), unique=True, nullable=False)
    password_hash = db.Column(db.LargeBinary, nullable=True)
    picture = db.Column(db.String(), nullable=True)

    permissions = db.relationship("Permission",
                                  back_populates="user",
                                  cascade="all, delete-orphan")
    authorizations = db.relationship("ExternalServiceAccessAuthorization",
                                     cascade="all, delete-orphan")

    def __init__(self, username=None) -> None:
        super().__init__()
        self.username = username

    def get_user_id(self):
        return self.id

    def get_authorization(self, resource: Resource):
        auths = ExternalServiceAccessAuthorization.find_by_user_and_resource(
            self, resource)
        # check for sub-resource authorizations
        for subresource in ["api"]:
            if hasattr(resource, subresource):
                auths.extend(
                    ExternalServiceAccessAuthorization.
                    find_by_user_and_resource(self,
                                              getattr(resource, subresource)))
        return auths

    @property
    def current_identity(self):
        from .services import current_registry, current_user
        if not current_user.is_anonymous:
            return self.oauth_identity
        if current_registry:
            for p, i in self.oauth_identity.items():
                if i.provider == current_registry.server_credentials:
                    return {p: i}
        return None

    @property
    def password(self):
        raise AttributeError("password is not a readable attribute")

    @password.setter
    def password(self, password):
        self.password_hash = generate_password_hash(password)

    @password.deleter
    def password(self):
        self.password_hash = None

    @property
    def has_password(self):
        return bool(self.password_hash)

    def has_permission(self, resource: Resource) -> bool:
        return self.get_permission(resource) is not None

    def get_permission(self, resource: Resource) -> Permission:
        return next((p for p in self.permissions if p.resource == resource),
                    None)

    def verify_password(self, password):
        return check_password_hash(self.password_hash, password)

    def save(self):
        db.session.add(self)
        db.session.commit()

    def to_dict(self):
        return {
            "id": self.id,
            "username": self.username,
            "identities":
            {n: i.user_info
             for n, i in self.oauth_identity.items()}
        }

    @classmethod
    def find_by_username(cls, username):
        return cls.query.filter(cls.username == username).first()

    @classmethod
    def all(cls):
        return cls.query.all()
Пример #3
0
class OAuthIdentity(models.ExternalServiceAccessAuthorization, ModelMixin):
    id = db.Column(db.Integer,
                   db.ForeignKey('external_service_access_authorization.id'),
                   primary_key=True)
    provider_user_id = db.Column(db.String(256), nullable=False)
    provider_id = db.Column(db.Integer,
                            db.ForeignKey("oauth2_identity_provider.id"),
                            nullable=False)
    created_at = db.Column(DateTime, default=datetime.utcnow, nullable=False)
    token = db.Column(JSON, nullable=True)
    _user_info = None
    provider = db.relationship("OAuth2IdentityProvider",
                               uselist=False,
                               back_populates="identities")
    user = db.relationship(
        models.User,
        # This `backref` thing sets up an `oauth` property on the User model,
        # which is a dictionary of OAuth models associated with that user,
        # where the dictionary key is the OAuth provider name.
        backref=db.backref(
            "oauth_identity",
            collection_class=attribute_mapped_collection("provider.name"),
            cascade="all, delete-orphan",
        ),
    )

    __table_args__ = (db.UniqueConstraint("provider_id", "provider_user_id"), )
    __tablename__ = "oauth2_identity"
    __mapper_args__ = {'polymorphic_identity': 'oauth2_identity'}

    def __init__(self, provider, user_info, provider_user_id, token):
        super().__init__(self.user)
        self.provider = provider
        self.provider_user_id = provider_user_id
        self._user_info = user_info
        self.token = token
        self.resources.append(provider.api_resource)

    def as_http_header(self):
        return f"{self.provider.token_type} {self.token['access_token']}"

    @property
    def username(self):
        return f"{self.provider.name}_{self.provider_user_id}"

    @property
    def user_info(self):
        if not self._user_info:
            self._user_info = self.provider.get_user_info(
                self.provider_user_id, self.token)
        return self._user_info

    @user_info.setter
    def user_info(self, value):
        self._user_info = value

    def set_token(self, token):
        self.token = token

    def __repr__(self):
        parts = []
        parts.append(self.__class__.__name__)
        if self.id:
            parts.append("id={}".format(self.id))
        if self.provider:
            parts.append('provider="{}"'.format(self.provider))
        return "<{}>".format(" ".join(parts))

    @staticmethod
    def find_by_user_id(user_id, provider_name) -> OAuthIdentity:
        try:
            return OAuthIdentity.query\
                .filter(OAuthIdentity.provider.has(name=provider_name))\
                .filter_by(user_id=user_id).one()
        except NoResultFound:
            raise OAuthIdentityNotFoundException(f"{user_id}_{provider_name}")

    @staticmethod
    def find_by_provider_user_id(provider_user_id,
                                 provider_name) -> OAuthIdentity:
        try:
            return OAuthIdentity.query\
                .filter(OAuthIdentity.provider.has(name=provider_name))\
                .filter_by(provider_user_id=provider_user_id).one()
        except NoResultFound:
            raise OAuthIdentityNotFoundException(
                f"{provider_name}_{provider_user_id}")

    @classmethod
    def all(cls) -> List[OAuthIdentity]:
        return cls.query.all()
Пример #4
0
class OAuthIdentity(models.ExternalServiceAccessAuthorization, ModelMixin):
    id = db.Column(db.Integer,
                   db.ForeignKey('external_service_access_authorization.id'),
                   primary_key=True)
    provider_user_id = db.Column(db.String(256), nullable=False)
    provider_id = db.Column(db.Integer,
                            db.ForeignKey("oauth2_identity_provider.id"),
                            nullable=False)
    created_at = db.Column(DateTime, default=datetime.utcnow, nullable=False)
    _token = db.Column("token", JSON, nullable=True)
    _user_info = None
    provider = db.relationship("OAuth2IdentityProvider",
                               uselist=False,
                               back_populates="identities")
    user = db.relationship(
        models.User,
        # This `backref` thing sets up an `oauth` property on the User model,
        # which is a dictionary of OAuth models associated with that user,
        # where the dictionary key is the OAuth provider name.
        backref=db.backref(
            "oauth_identity",
            collection_class=attribute_mapped_collection("provider.name"),
            cascade="all, delete-orphan",
        ),
    )

    __table_args__ = (db.UniqueConstraint("provider_id", "provider_user_id"), )
    __tablename__ = "oauth2_identity"
    __mapper_args__ = {'polymorphic_identity': 'oauth2_identity'}

    def __init__(self, provider, user_info, provider_user_id, token):
        super().__init__(self.user)
        self.provider = provider
        self.provider_user_id = provider_user_id
        self._user_info = user_info
        self.token = token
        self.resources.append(provider.api_resource)

    def as_http_header(self):
        return f"{self.provider.token_type} {self.fetch_token()['access_token']}"

    @property
    def username(self):
        return f"{self.provider.name}_{self.provider_user_id}"

    @property
    def token(self) -> OAuth2Token:
        return OAuth2Token(self._token)

    @token.setter
    def token(self, token: dict):
        self._token = token

    def fetch_token(self):
        # enable dynamic refresh only if the identity
        # has been already stored in the database
        if inspect(self).persistent:
            # fetch up to date identity data
            self.refresh()
            # reference to the token associated with the identity instance
            token = self.token
            # the token should be refreshed
            # if it is expired or close to expire (i.e., n secs before expiration)
            if token.to_be_refreshed():
                if 'refresh_token' not in token:
                    logger.debug(
                        "The token should be refreshed but no refresh token is associated with the token"
                    )
                else:
                    logger.debug("Trying to refresh the token...")
                    oauth2session = OAuth2Session(self.provider.client_id,
                                                  self.provider.client_secret,
                                                  token=self.token)
                    new_token = oauth2session.refresh_token(
                        self.provider.access_token_url,
                        refresh_token=token['refresh_token'])
                    self.token = new_token
                    self.save()
                    logger.debug("User token updated")
                    logger.debug("Using token %r", self.token)
        return self.token

    @property
    def user_info(self):
        if not self._user_info:
            logger.debug(
                "[Identity %r], Trying to read profile of user %r from provider %r...",
                self.id, self.user_id, self.provider.name)
            self._user_info = self.provider.get_user_info(
                self.provider_user_id, self.fetch_token())
        return self._user_info

    @user_info.setter
    def user_info(self, value):
        self._user_info = value

    def __repr__(self):
        parts = []
        parts.append(self.__class__.__name__)
        if self.id:
            parts.append("id={}".format(self.id))
        if self.provider:
            parts.append('provider="{}"'.format(self.provider))
        return "<{}>".format(" ".join(parts))

    @staticmethod
    def find_by_user_id(user_id, provider_name) -> OAuthIdentity:
        try:
            return OAuthIdentity.query\
                .filter(OAuthIdentity.provider.has(name=provider_name))\
                .filter_by(user_id=user_id).one()
        except NoResultFound:
            raise OAuthIdentityNotFoundException(f"{user_id}_{provider_name}")

    @staticmethod
    def find_by_provider_user_id(provider_user_id,
                                 provider_name) -> OAuthIdentity:
        try:
            return OAuthIdentity.query\
                .filter(OAuthIdentity.provider.has(name=provider_name))\
                .filter_by(provider_user_id=provider_user_id).one()
        except NoResultFound:
            raise OAuthIdentityNotFoundException(
                f"{provider_name}_{provider_user_id}")

    @classmethod
    def all(cls) -> List[OAuthIdentity]:
        return cls.query.all()
Пример #5
0
class Client(db.Model, OAuth2ClientMixin):
    id = db.Column(db.Integer, primary_key=True)
    client_id = db.Column(db.String(48), index=True, unique=True)
    user_id = db.Column(
        db.Integer, db.ForeignKey('user.id', ondelete='CASCADE')
    )
    user = db.relationship(
        'User',
        backref=db.backref(
            "clients",
            cascade="all, delete-orphan",
        ),
    )

    __tablename__ = "oauth2_client"

    def is_confidential(self):
        return self.has_client_secret()

    def set_client_metadata(self, value):
        if not isinstance(value, dict):
            return
        data = copy.deepcopy(value)
        data['scope'] = values_as_string(value['scope'], out_separator=" ")
        for p in ('redirect_uris', 'grant_types', 'response_types', 'contacts'):
            data[p] = values_as_list(value.get(p, []))
        return super().set_client_metadata(data)

    @property
    def redirect_uris(self):
        return super().redirect_uris

    @redirect_uris.setter
    def redirect_uris(self, value):
        metadata = self.client_metadata
        metadata['redirect_uris'] = value
        self.set_client_metadata(metadata)

    @property
    def scopes(self):
        return self.scope.split(" ") if self.scope else []

    @scopes.setter
    def scopes(self, scopes):
        metadata = self.client_metadata
        metadata['scope'] = scopes
        self.set_client_metadata(metadata)

    @property
    def auth_method(self):
        return self.client_metadata.get('token_endpoint_auth_method')

    @auth_method.setter
    def auth_method(self, value):
        metadata = self.client_metadata
        metadata['token_endpoint_auth_method'] = value
        self.set_client_metadata(metadata)

    @classmethod
    def find_by_id(cls, client_id) -> Client:
        return cls.query.get(client_id)

    @classmethod
    def all(cls) -> List[Client]:
        return cls.query.all()