Example #1
0
    def __init__(
        self,
        *args,
        client_name,
        instance_host_backend=None,
        instance_credentials_backend=None,
        session_class=None,
        **kwargs
    ):
        if session_class is None:
            session_class = MastodonSession

        OAuth2ConsumerBlueprint.__init__(self, *args, session_class=session_class, **kwargs)
        self.client_name = client_name

        if instance_host_backend is None:
            self.instance_host_backend = SessionBackend(key="{bp.name}_instance_host")
        elif callable(instance_host_backend):
            self.instance_host_backend = instance_host_backend()
        else:
            self.instance_host_backend = instance_host_backend

        if instance_credentials_backend is None:
            self.instance_credentials_backend = InMemoryBackend()
        elif callable(instance_credentials_backend):
            self.instance_credentials_backend = instance_credentials_backend()
        else:
            self.instance_credentials_backend = instance_credentials_backend
Example #2
0
    def __init__(self,
                 name,
                 import_name,
                 static_folder=None,
                 static_url_path=None,
                 template_folder=None,
                 url_prefix=None,
                 subdomain=None,
                 url_defaults=None,
                 root_path=None,
                 login_url=None,
                 authorized_url=None,
                 backend=None):

        bp_kwargs = dict(
            name=name,
            import_name=import_name,
            static_folder=static_folder,
            static_url_path=static_url_path,
            template_folder=template_folder,
            url_prefix=url_prefix,
            subdomain=subdomain,
            url_defaults=url_defaults,
            root_path=root_path,
        )
        # `root_path` didn't exist in 0.10, and will cause an error if it's
        # passed in that version. Only pass `root_path` if it's set.
        if bp_kwargs["root_path"] is None:
            del bp_kwargs["root_path"]
        flask.Blueprint.__init__(self, **bp_kwargs)

        login_url = login_url or "/{bp.name}"
        authorized_url = authorized_url or "/{bp.name}/authorized"

        self.add_url_rule(
            rule=login_url.format(bp=self),
            endpoint="login",
            view_func=self.login,
        )
        self.add_url_rule(
            rule=authorized_url.format(bp=self),
            endpoint="authorized",
            view_func=self.authorized,
        )

        if backend is None:
            self.backend = SessionBackend()
        elif callable(backend):
            self.backend = backend()
        else:
            self.backend = backend

        self.logged_in_funcs = []
        self.from_config = {}
        invalidate_token = lambda d: lazy.invalidate(self.session, "token")
        self.config = CallbackDict(on_update=invalidate_token)
        self.before_app_request(self.load_config)
Example #3
0
    def __init__(self,
                 name,
                 import_name,
                 static_folder=None,
                 static_url_path=None,
                 template_folder=None,
                 url_prefix=None,
                 subdomain=None,
                 url_defaults=None,
                 root_path=None,
                 login_url=None,
                 authorized_url=None,
                 backend=None):

        bp_kwargs = dict(
            name=name,
            import_name=import_name,
            static_folder=static_folder,
            static_url_path=static_url_path,
            template_folder=template_folder,
            url_prefix=url_prefix,
            subdomain=subdomain,
            url_defaults=url_defaults,
            root_path=root_path,
        )
        # `root_path` didn't exist until 1.0
        if StrictVersion(flask.__version__) < StrictVersion('1.0'):
            del bp_kwargs["root_path"]
        flask.Blueprint.__init__(self, **bp_kwargs)

        login_url = login_url or "/{bp.name}"
        authorized_url = authorized_url or "/{bp.name}/authorized"

        self.add_url_rule(
            rule=login_url.format(bp=self),
            endpoint="login",
            view_func=self.login,
        )
        self.add_url_rule(
            rule=authorized_url.format(bp=self),
            endpoint="authorized",
            view_func=self.authorized,
        )

        if backend is None:
            self.backend = SessionBackend()
        elif callable(backend):
            self.backend = backend()
        else:
            self.backend = backend

        self.logged_in_funcs = []
        self.from_config = {}
        self.config = Dictective(
            lambda d: lazy.invalidate(self.session, "token"))
        self.before_app_request(self.load_config)
Example #4
0
    def __init__(self, name, import_name,
            static_folder=None, static_url_path=None, template_folder=None,
            url_prefix=None, subdomain=None, url_defaults=None, root_path=None,
            login_url=None, authorized_url=None, backend=None):

        bp_kwargs = dict(
            name=name,
            import_name=import_name,
            static_folder=static_folder,
            static_url_path=static_url_path,
            template_folder=template_folder,
            url_prefix=url_prefix,
            subdomain=subdomain,
            url_defaults=url_defaults,
            root_path=root_path,
        )
        # `root_path` didn't exist in 0.10, and will cause an error if it's
        # passed in that version. Only pass `root_path` if it's set.
        if bp_kwargs["root_path"] is None:
            del bp_kwargs["root_path"]
        flask.Blueprint.__init__(self, **bp_kwargs)

        login_url = login_url or "/{bp.name}"
        authorized_url = authorized_url or "/{bp.name}/authorized"

        self.add_url_rule(
            rule=login_url.format(bp=self),
            endpoint="login",
            view_func=self.login,
        )
        self.add_url_rule(
            rule=authorized_url.format(bp=self),
            endpoint="authorized",
            view_func=self.authorized,
        )

        if backend is None:
            self.backend = SessionBackend()
        elif callable(backend):
            self.backend = backend()
        else:
            self.backend = backend

        self.logged_in_funcs = []
        self.from_config = {}
        invalidate_token = lambda d: lazy.invalidate(self.session, "token")
        self.config = CallbackDict(on_update=invalidate_token)
        self.before_app_request(self.load_config)
Example #5
0
    def __init__(self, name, import_name,
            static_folder=None, static_url_path=None, template_folder=None,
            url_prefix=None, subdomain=None, url_defaults=None, root_path=None,
            login_url=None, authorized_url=None, backend=None):

        bp_kwargs = dict(
            name=name,
            import_name=import_name,
            static_folder=static_folder,
            static_url_path=static_url_path,
            template_folder=template_folder,
            url_prefix=url_prefix,
            subdomain=subdomain,
            url_defaults=url_defaults,
            root_path=root_path,
        )
        # `root_path` didn't exist until 1.0
        if StrictVersion(flask.__version__) < StrictVersion('1.0'):
            del bp_kwargs["root_path"]
        flask.Blueprint.__init__(self, **bp_kwargs)

        login_url = login_url or "/{bp.name}"
        authorized_url = authorized_url or "/{bp.name}/authorized"

        self.add_url_rule(
            rule=login_url.format(bp=self),
            endpoint="login",
            view_func=self.login,
        )
        self.add_url_rule(
            rule=authorized_url.format(bp=self),
            endpoint="authorized",
            view_func=self.authorized,
        )

        if backend is None:
            self.backend = SessionBackend()
        elif callable(backend):
            self.backend = backend()
        else:
            self.backend = backend

        self.logged_in_funcs = []
        self.from_config = {}
        self.config = Dictective(lambda d: lazy.invalidate(self.session, "token"))
        self.before_app_request(self.load_config)
Example #6
0
def make_mastodon_blueprint(
    client_name,
    backend=None,
    instance_host_backend=None,
    instance_credentials_backend=None,
    scope="read",
):
    if backend is None:
        backend = SessionBackend(key="{bp.name}_{bp.instance_host}_oauth_token")

    mastodon_bp = MastodonConsumerBlueprint(
        "mastodon", __name__, client_name=client_name, scope=scope, backend=backend
    )

    @mastodon_bp.before_app_request
    def set_applocal_session():
        ctx = stack.top
        ctx.mastodon_oauth = mastodon_bp.session

    return mastodon_bp
Example #7
0
class BaseOAuthConsumerBlueprint(six.with_metaclass(ABCMeta, flask.Blueprint)):
    def __init__(self, name, import_name,
            static_folder=None, static_url_path=None, template_folder=None,
            url_prefix=None, subdomain=None, url_defaults=None, root_path=None,
            login_url=None, authorized_url=None, backend=None):

        bp_kwargs = dict(
            name=name,
            import_name=import_name,
            static_folder=static_folder,
            static_url_path=static_url_path,
            template_folder=template_folder,
            url_prefix=url_prefix,
            subdomain=subdomain,
            url_defaults=url_defaults,
            root_path=root_path,
        )
        # `root_path` didn't exist until 1.0
        if StrictVersion(flask.__version__) < StrictVersion('1.0'):
            del bp_kwargs["root_path"]
        flask.Blueprint.__init__(self, **bp_kwargs)

        login_url = login_url or "/{bp.name}"
        authorized_url = authorized_url or "/{bp.name}/authorized"

        self.add_url_rule(
            rule=login_url.format(bp=self),
            endpoint="login",
            view_func=self.login,
        )
        self.add_url_rule(
            rule=authorized_url.format(bp=self),
            endpoint="authorized",
            view_func=self.authorized,
        )

        if backend is None:
            self.backend = SessionBackend()
        elif callable(backend):
            self.backend = backend()
        else:
            self.backend = backend

        self.logged_in_funcs = []
        self.from_config = {}
        self.config = Dictective(lambda d: lazy.invalidate(self.session, "token"))
        self.before_app_request(self.load_config)

    def load_config(self):
        """
        Used to dynamically load variables from the Flask application config
        into the blueprint. To tell this blueprint to pull configuration from
        the app, just set key-value pairs in the ``from_config`` dict. Keys
        are the name of the local variable to set on the blueprint object,
        and values are the variable name in the Flask application config.
        For example:

            blueprint.from_config["session.client_id"] = "GITHUB_OAUTH_CLIENT_ID"

        """
        for local_var, config_var in self.from_config.items():
            value = flask.current_app.config.get(config_var)
            if value:
                if "." in local_var:
                    # this is a dotpath -- needs special handling
                    body, tail = local_var.rsplit(".", 1)
                    obj = getattrd(self, body)
                    setattr(obj, tail, value)
                else:
                    # just use a normal setattr call
                    setattr(self, local_var, value)

    @property
    def token(self):
        return self.backend.get(self)

    @token.setter
    def token(self, value):
        self.backend.set(self, value)
        lazy.invalidate(self.session, "token")

    @token.deleter
    def token(self):
        self.backend.delete(self)
        lazy.invalidate(self.session, "token")

    @abstractproperty
    def session(self):
        raise NotImplementedError()

    @abstractmethod
    def login(self):
        raise NotImplementedError()

    @abstractmethod
    def authorized(self):
        raise NotImplementedError()
Example #8
0
class BaseOAuthConsumerBlueprint(six.with_metaclass(ABCMeta, flask.Blueprint)):
    def __init__(self,
                 name,
                 import_name,
                 static_folder=None,
                 static_url_path=None,
                 template_folder=None,
                 url_prefix=None,
                 subdomain=None,
                 url_defaults=None,
                 root_path=None,
                 login_url=None,
                 authorized_url=None,
                 backend=None):

        bp_kwargs = dict(
            name=name,
            import_name=import_name,
            static_folder=static_folder,
            static_url_path=static_url_path,
            template_folder=template_folder,
            url_prefix=url_prefix,
            subdomain=subdomain,
            url_defaults=url_defaults,
            root_path=root_path,
        )
        # `root_path` didn't exist in 0.10, and will cause an error if it's
        # passed in that version. Only pass `root_path` if it's set.
        if bp_kwargs["root_path"] is None:
            del bp_kwargs["root_path"]
        flask.Blueprint.__init__(self, **bp_kwargs)

        login_url = login_url or "/{bp.name}"
        authorized_url = authorized_url or "/{bp.name}/authorized"

        self.add_url_rule(
            rule=login_url.format(bp=self),
            endpoint="login",
            view_func=self.login,
        )
        self.add_url_rule(
            rule=authorized_url.format(bp=self),
            endpoint="authorized",
            view_func=self.authorized,
        )

        if backend is None:
            self.backend = SessionBackend()
        elif callable(backend):
            self.backend = backend()
        else:
            self.backend = backend

        self.logged_in_funcs = []
        self.from_config = {}
        invalidate_token = lambda d: lazy.invalidate(self.session, "token")
        self.config = CallbackDict(on_update=invalidate_token)
        self.before_app_request(self.load_config)

    def load_config(self):
        """
        Used to dynamically load variables from the Flask application config
        into the blueprint. To tell this blueprint to pull configuration from
        the app, just set key-value pairs in the ``from_config`` dict. Keys
        are the name of the local variable to set on the blueprint object,
        and values are the variable name in the Flask application config.
        For example:

            blueprint.from_config["session.client_id"] = "GITHUB_OAUTH_CLIENT_ID"

        """
        for local_var, config_var in self.from_config.items():
            value = flask.current_app.config.get(config_var)
            if value:
                if "." in local_var:
                    # this is a dotpath -- needs special handling
                    body, tail = local_var.rsplit(".", 1)
                    obj = getattrd(self, body)
                    setattr(obj, tail, value)
                else:
                    # just use a normal setattr call
                    setattr(self, local_var, value)

    @property
    def token(self):
        _token = self.backend.get(self)
        if _token and _token.get("expires_in") and _token.get("expires_at"):
            # Update the `expires_in` value, so that requests-oauthlib
            # can handle automatic token refreshing. Assume that
            # `expires_at` is a valid Unix timestamp.
            expires_at = datetime.utcfromtimestamp(_token["expires_at"])
            expires_in = expires_at - datetime.utcnow()
            _token["expires_in"] = expires_in.total_seconds()
        return _token

    @token.setter
    def token(self, value):
        _token = value
        if _token and _token.get("expires_in"):
            # Set the `expires_at` value, overwriting any value
            # that may already be there.
            delta = timedelta(seconds=_token["expires_in"])
            expires_at = datetime.utcnow() + delta
            _token["expires_at"] = timestamp_from_datetime(expires_at)
        self.backend.set(self, _token)
        lazy.invalidate(self.session, "token")

    @token.deleter
    def token(self):
        self.backend.delete(self)
        lazy.invalidate(self.session, "token")

    @abstractproperty
    def session(self):
        """
        This is a session between the consumer (your website) and the provider
        (e.g. Twitter). It is *not* a session between a user of your website
        and your website.
        """
        raise NotImplementedError()

    @abstractmethod
    def login(self):
        raise NotImplementedError()

    @abstractmethod
    def authorized(self):
        """
        This is the route/function that the user will be redirected to by
        the provider (e.g. Twitter) after the user has logged into the
        provider's website and authorized your app to access their account.
        """
        raise NotImplementedError()
Example #9
0
class MastodonConsumerBlueprint(OAuth2ConsumerBlueprint):
    def __init__(
        self,
        *args,
        client_name,
        instance_host_backend=None,
        instance_credentials_backend=None,
        session_class=None,
        **kwargs
    ):
        if session_class is None:
            session_class = MastodonSession

        OAuth2ConsumerBlueprint.__init__(self, *args, session_class=session_class, **kwargs)
        self.client_name = client_name

        if instance_host_backend is None:
            self.instance_host_backend = SessionBackend(key="{bp.name}_instance_host")
        elif callable(instance_host_backend):
            self.instance_host_backend = instance_host_backend()
        else:
            self.instance_host_backend = instance_host_backend

        if instance_credentials_backend is None:
            self.instance_credentials_backend = InMemoryBackend()
        elif callable(instance_credentials_backend):
            self.instance_credentials_backend = instance_credentials_backend()
        else:
            self.instance_credentials_backend = instance_credentials_backend

    def _get_instance_credentials(self, host):
        url = "https://{instance}/api/v1/apps".format(instance=host)
        try:
            response = requests.post(
                url,
                data=dict(
                    client_name=self.client_name,
                    redirect_uris=url_for(".authorized", _external=True),
                    scopes=self.scope,
                ),
            ).json()

            if "error" in response:
                return None

            return dict(client_id=response["client_id"], client_secret=response["client_secret"])

        except Exception:
            return None

    @property
    def credentials(self):
        if not self.instance_host:
            return {}
        cached = self.instance_credentials_backend.get(self)
        if cached:
            return cached

        retrieved = self._get_instance_credentials(self.instance_host)
        if not retrieved:
            return {}

        self.instance_credentials_backend.set(self, retrieved)
        return retrieved

    @property
    def instance_host(self):
        return self.instance_host_backend.get(self)

    @instance_host.setter
    def instance_host(self, value):
        self.instance_host_backend.set(self, value)

        instance_config = self.credentials
        self.client_id = instance_config.get("client_id")

        lazy.invalidate(self.session, "token")

    @instance_host.deleter
    def instance_host(self):
        self.instance_host_backend.delete(self)
        lazy.invalidate(self.session, "token")

    @property
    def client_secret(self):
        return self.credentials.get("client_secret")

    @client_secret.setter
    def client_secret(self, value):
        pass

    @property
    def authorization_url(self):
        instance = self.instance_host
        return "https://{instance}/oauth/authorize".format(instance=instance)

    @authorization_url.setter
    def authorization_url(self, value):
        pass

    @property
    def token_url(self):
        instance = self.instance_host
        return "https://{instance}/oauth/token".format(instance=instance)

    @token_url.setter
    def token_url(self, value):
        pass

    @lazy
    def session(self):
        """
        This is a session between the consumer (your website) and the provider
        (e.g. Twitter). It is *not* a session between a user of your website
        and your website.
        :return:
        """

        instance = self.instance_host

        ret = self.session_class(
            client_id=self.credentials.get("client_id"),
            client=self.client,
            auto_refresh_url=self.auto_refresh_url,
            auto_refresh_kwargs=self.auto_refresh_kwargs,
            scope=self.scope,
            state=self.state,
            blueprint=self,
            base_url="https://{instance}".format(instance=instance),
            **self.kwargs
        )

        def token_updater(token):
            self.token = token

        ret.token_updater = token_updater
        return self.session_created(ret)

    def login(self):
        if "instance_host" in request.args:
            self.instance_host = request.args["instance_host"]

        return super().login()
Example #10
0
class BaseOAuthConsumerBlueprint(six.with_metaclass(ABCMeta, flask.Blueprint)):
    def __init__(self, name, import_name,
            static_folder=None, static_url_path=None, template_folder=None,
            url_prefix=None, subdomain=None, url_defaults=None, root_path=None,
            login_url=None, authorized_url=None, backend=None):

        bp_kwargs = dict(
            name=name,
            import_name=import_name,
            static_folder=static_folder,
            static_url_path=static_url_path,
            template_folder=template_folder,
            url_prefix=url_prefix,
            subdomain=subdomain,
            url_defaults=url_defaults,
            root_path=root_path,
        )
        # `root_path` didn't exist in 0.10, and will cause an error if it's
        # passed in that version. Only pass `root_path` if it's set.
        if bp_kwargs["root_path"] is None:
            del bp_kwargs["root_path"]
        flask.Blueprint.__init__(self, **bp_kwargs)

        login_url = login_url or "/{bp.name}"
        authorized_url = authorized_url or "/{bp.name}/authorized"

        self.add_url_rule(
            rule=login_url.format(bp=self),
            endpoint="login",
            view_func=self.login,
        )
        self.add_url_rule(
            rule=authorized_url.format(bp=self),
            endpoint="authorized",
            view_func=self.authorized,
        )

        if backend is None:
            self.backend = SessionBackend()
        elif callable(backend):
            self.backend = backend()
        else:
            self.backend = backend

        self.logged_in_funcs = []
        self.from_config = {}
        invalidate_token = lambda d: lazy.invalidate(self.session, "token")
        self.config = CallbackDict(on_update=invalidate_token)
        self.before_app_request(self.load_config)

    def load_config(self):
        """
        Used to dynamically load variables from the Flask application config
        into the blueprint. To tell this blueprint to pull configuration from
        the app, just set key-value pairs in the ``from_config`` dict. Keys
        are the name of the local variable to set on the blueprint object,
        and values are the variable name in the Flask application config.
        For example:

            blueprint.from_config["session.client_id"] = "GITHUB_OAUTH_CLIENT_ID"

        """
        for local_var, config_var in self.from_config.items():
            value = flask.current_app.config.get(config_var)
            if value:
                if "." in local_var:
                    # this is a dotpath -- needs special handling
                    body, tail = local_var.rsplit(".", 1)
                    obj = getattrd(self, body)
                    setattr(obj, tail, value)
                else:
                    # just use a normal setattr call
                    setattr(self, local_var, value)

    @property
    def token(self):
        _token = self.backend.get(self)
        if _token and _token.get("expires_in") and _token.get("expires_at"):
            # Update the `expires_in` value, so that requests-oauthlib
            # can handle automatic token refreshing. Assume that
            # `expires_at` is a valid Unix timestamp.
            expires_at = datetime.utcfromtimestamp(_token["expires_at"])
            expires_in = expires_at - datetime.utcnow()
            _token["expires_in"] = expires_in.total_seconds()
        return _token

    @token.setter
    def token(self, value):
        _token = value
        if _token and _token.get("expires_in"):
            # Set the `expires_at` value, overwriting any value
            # that may already be there.
            delta = timedelta(seconds=_token["expires_in"])
            expires_at = datetime.utcnow() + delta
            _token["expires_at"] = timestamp_from_datetime(expires_at)
        self.backend.set(self, _token)
        lazy.invalidate(self.session, "token")

    @token.deleter
    def token(self):
        self.backend.delete(self)
        lazy.invalidate(self.session, "token")

    @abstractproperty
    def session(self):
        raise NotImplementedError()

    @abstractmethod
    def login(self):
        raise NotImplementedError()

    @abstractmethod
    def authorized(self):
        raise NotImplementedError()
Example #11
0
class BaseOAuthConsumerBlueprint(six.with_metaclass(ABCMeta, flask.Blueprint)):
    def __init__(self, name, import_name,
            static_folder=None, static_url_path=None, template_folder=None,
            url_prefix=None, subdomain=None, url_defaults=None, root_path=None,
            login_url=None, authorized_url=None, backend=None):

        bp_kwargs = dict(
            name=name,
            import_name=import_name,
            static_folder=static_folder,
            static_url_path=static_url_path,
            template_folder=template_folder,
            url_prefix=url_prefix,
            subdomain=subdomain,
            url_defaults=url_defaults,
            root_path=root_path,
        )
        # `root_path` didn't exist until 1.0
        if StrictVersion(flask.__version__) < StrictVersion('1.0'):
            del bp_kwargs["root_path"]
        flask.Blueprint.__init__(self, **bp_kwargs)

        login_url = login_url or "/{bp.name}"
        authorized_url = authorized_url or "/{bp.name}/authorized"

        self.add_url_rule(
            rule=login_url.format(bp=self),
            endpoint="login",
            view_func=self.login,
        )
        self.add_url_rule(
            rule=authorized_url.format(bp=self),
            endpoint="authorized",
            view_func=self.authorized,
        )

        if backend is None:
            self.backend = SessionBackend()
        elif callable(backend):
            self.backend = backend()
        else:
            self.backend = backend

        self.logged_in_funcs = []
        self.from_config = {}
        invalidate_token = lambda d: lazy.invalidate(self.session, "token")
        self.config = CallbackDict(on_update=invalidate_token)
        self.before_app_request(self.load_config)

    def load_config(self):
        """
        Used to dynamically load variables from the Flask application config
        into the blueprint. To tell this blueprint to pull configuration from
        the app, just set key-value pairs in the ``from_config`` dict. Keys
        are the name of the local variable to set on the blueprint object,
        and values are the variable name in the Flask application config.
        For example:

            blueprint.from_config["session.client_id"] = "GITHUB_OAUTH_CLIENT_ID"

        """
        for local_var, config_var in self.from_config.items():
            value = flask.current_app.config.get(config_var)
            if value:
                if "." in local_var:
                    # this is a dotpath -- needs special handling
                    body, tail = local_var.rsplit(".", 1)
                    obj = getattrd(self, body)
                    setattr(obj, tail, value)
                else:
                    # just use a normal setattr call
                    setattr(self, local_var, value)

    @property
    def token(self):
        return self.backend.get(self)

    @token.setter
    def token(self, value):
        self.backend.set(self, value)
        lazy.invalidate(self.session, "token")

    @token.deleter
    def token(self):
        self.backend.delete(self)
        lazy.invalidate(self.session, "token")

    @abstractproperty
    def session(self):
        raise NotImplementedError()

    @abstractmethod
    def login(self):
        raise NotImplementedError()

    @abstractmethod
    def authorized(self):
        raise NotImplementedError()