예제 #1
0
def test_signal_oauth_authorized_response(request):
    app, bp = make_app()

    calls = []

    def callback(*args, **kwargs):
        calls.append((args, kwargs))
        return flask.redirect("/url")

    oauth_authorized.connect(callback)
    request.addfinalizer(lambda: oauth_authorized.disconnect(callback))
    fake_token = {"access_token": "test-token"}

    with app.test_client() as client:
        with client.session_transaction() as sess:
            sess["test-service_oauth_state"] = "random-string"

        bp.session.fetch_token = mock.Mock(return_value=fake_token)

        resp = client.get(
            "/login/test-service/authorized?code=secret-code&state=random-string"
        )
        assert resp.status_code == 302
        assert resp.headers["Location"] == "http://localhost/url"
        # check that we did NOT store the token
        assert "test-service_oauth_token" not in flask.session

    # callback still should have been called
    assert len(calls) == 1
예제 #2
0
def test_signal_oauth_authorized(request):
    app, bp = make_app()

    calls = []
    def callback(*args, **kwargs):
        calls.append((args, kwargs))

    oauth_authorized.connect(callback)
    request.addfinalizer(lambda: oauth_authorized.disconnect(callback))
    fake_token = {"access_token": "test-token"}

    with app.test_client() as client:
        with client.session_transaction() as sess:
            sess["test-service_oauth_state"] = "random-string"

        bp.session.fetch_token = mock.Mock(return_value=fake_token)
        resp = client.get(
            "/login/test-service/authorized?code=secret-code&state=random-string",
        )
        assert resp.status_code == 302
        # check that we stored the token
        assert flask.session["test-service_oauth_token"] == fake_token

    assert len(calls) == 1
    assert calls[0][0] == (bp,)
    assert calls[0][1] == {"token": fake_token}
예제 #3
0
def test_signal_oauth_authorized_response(request):
    app, bp = make_app()

    calls = []
    def callback(*args, **kwargs):
        calls.append((args, kwargs))
        return flask.redirect("/url")

    oauth_authorized.connect(callback)
    request.addfinalizer(lambda: oauth_authorized.disconnect(callback))
    fake_token = {"access_token": "test-token"}

    with app.test_client() as client:
        with client.session_transaction() as sess:
            sess["test-service_oauth_state"] = "random-string"

        bp.session.fetch_token = mock.Mock(return_value=fake_token)

        resp = client.get(
            "/login/test-service/authorized?code=secret-code&state=random-string",
        )
        assert resp.status_code == 302
        assert resp.headers["Location"] == "http://localhost/url"
        # check that we did NOT store the token
        assert "test-service_oauth_token" not in flask.session

    # callback still should have been called
    assert len(calls) == 1
예제 #4
0
def test_signal_oauth_authorized_abort(request):
    app, bp = make_app()

    calls = []

    def callback(*args, **kwargs):
        calls.append((args, kwargs))
        return False

    oauth_authorized.connect(callback)
    request.addfinalizer(lambda: oauth_authorized.disconnect(callback))

    with app.test_client() as client:
        with client.session_transaction() as sess:
            sess["test-service_oauth_state"] = "random-string"

        bp.session.fetch_token = mock.Mock(return_value="test-token")

        resp = client.get(
            "/login/test-service/authorized?code=secret-code&state=random-string",
        )
        # check that we did NOT store the token
        assert "test-service_oauth_token" not in flask.session

    # callback still should have been called
    assert len(calls) == 1
예제 #5
0
def test_signal_oauth_authorized_abort(request):
    app, bp = make_app()

    calls = []
    def callback(*args, **kwargs):
        calls.append((args, kwargs))
        return False

    oauth_authorized.connect(callback)
    request.addfinalizer(lambda: oauth_authorized.disconnect(callback))

    with app.test_client() as client:
        with client.session_transaction() as sess:
            sess["test-service_oauth_state"] = "random-string"

        bp.session.fetch_token = mock.Mock(return_value="test-token")

        resp = client.get(
            "/login/test-service/authorized?code=secret-code&state=random-string",
        )
        # check that we did NOT store the token
        assert "test-service_oauth_token" not in flask.session

    # callback still should have been called
    assert len(calls) == 1
예제 #6
0
def test_signal_oauth_authorized(request):
    app, bp = make_app()

    calls = []

    def callback(*args, **kwargs):
        calls.append((args, kwargs))

    oauth_authorized.connect(callback)
    request.addfinalizer(lambda: oauth_authorized.disconnect(callback))
    fake_token = {"access_token": "test-token"}

    with app.test_client() as client:
        with client.session_transaction() as sess:
            sess["test-service_oauth_state"] = "random-string"

        bp.session.fetch_token = mock.Mock(return_value=fake_token)
        resp = client.get(
            "/login/test-service/authorized?code=secret-code&state=random-string"
        )
        assert resp.status_code == 302
        # check that we stored the token
        assert flask.session["test-service_oauth_token"] == fake_token

    assert len(calls) == 1
    assert calls[0][0] == (bp, )
    assert calls[0][1] == {"token": fake_token}
예제 #7
0
def test_signal_sender_oauth_authorized(request):
    app, bp = make_app()
    bp2 = OAuth1ConsumerBlueprint(
        "test2",
        __name__,
        client_key="client_key",
        client_secret="client_secret",
        base_url="https://example.com",
        request_token_url="https://example.com/oauth/request_token",
        access_token_url="https://example.com/oauth/access_token",
        authorization_url="https://example.com/oauth/authorize",
        redirect_to="index",
    )
    app.register_blueprint(bp2, url_prefix="/login")

    calls = []

    def callback(*args, **kwargs):
        calls.append((args, kwargs))

    oauth_authorized.connect(callback, sender=bp)
    request.addfinalizer(lambda: oauth_authorized.disconnect(callback, sender=bp))

    with app.test_client() as client:
        bp.session.fetch_access_token = mock.Mock(return_value="test-token")
        bp2.session.fetch_access_token = mock.Mock(return_value="test2-token")
        resp = client.get(
            "/login/test2/authorized?oauth_token=foobar&oauth_verifier=xyz"
        )

    assert len(calls) == 0

    with app.test_client() as client:
        bp.session.fetch_access_token = mock.Mock(return_value="test-token")
        bp2.session.fetch_access_token = mock.Mock(return_value="test2-token")
        resp = client.get(
            "/login/test-service/authorized?oauth_token=foobar&oauth_verifier=xyz"
        )

    assert len(calls) == 1
    assert calls[0][0] == (bp,)
    assert calls[0][1] == {"token": "test-token"}

    with app.test_client() as client:
        bp.session.fetch_access_token = mock.Mock(return_value="test-token")
        bp2.session.fetch_access_token = mock.Mock(return_value="test2-token")
        resp = client.get(
            "/login/test2/authorized?oauth_token=foobar&oauth_verifier=xyz"
        )

    assert len(calls) == 1  # unchanged
    def __init__(self, name, import_name, db_session, user_model, token_model,
                 final_redirect_view):
        """
        :param db_session: SQLAlchemy session object
        :param user_model: User model class;
            this should be a flask_login.UserMixin or similar
        :param token_model: Token model class;
            this should be a hydra_oauth2.HydraTokenMixin or similar
        :param final_redirect_view: the endpoint to which to redirect the user
            after login/logout has been completed
        """
        super().__init__(
            name,
            import_name,
            login_url='/login',
            authorized_url='/authorized',
            redirect_to=final_redirect_view,
            storage=SQLAlchemyStorage(token_model,
                                      db_session,
                                      user=current_user),

            # hack to disable SSL certificate verification in a dev environment:
            # this is a sneaky way to make flask-dance set the 'verify' kwarg for
            # requests calls made when fetching tokens; it's not the intended use
            # of token_url_params, but hey-ho
            token_url_params={'verify': get_env() != 'development'},
        )

        self.db_session = db_session
        self.user_model = user_model
        self.token_model = token_model

        self.hydra_public_url = None
        self.userinfo_url = None
        self.logout_url = None

        self.from_config['hydra_public_url'] = 'HYDRA_PUBLIC_URL'
        self.from_config['client_id'] = 'OAUTH2_CLIENT_ID'
        self.from_config['client_secret'] = 'OAUTH2_CLIENT_SECRET'
        self.from_config['scope'] = 'OAUTH2_SCOPES'
        self.from_config['audience'] = 'OAUTH2_AUDIENCE'

        self.add_url_rule('/signup', view_func=self.signup)
        self.add_url_rule('/logout', view_func=self.logout)
        self.add_url_rule('/logged_out', view_func=self.logged_out)

        oauth_authorized.connect(self.hydra_logged_in, sender=self)
        oauth_error.connect(self.hydra_error, sender=self)

        self.create_or_update_local_user = None
예제 #9
0
def test_signal_oauth_authorized():
    app, bp = make_app()
    bp.session.fetch_access_token = mock.Mock(return_value="test-token")

    calls = []
    def callback(*args, **kwargs):
        calls.append((args, kwargs))

    oauth_authorized.connect(callback)

    with app.test_client() as client:
        resp = client.get(
            "/login/test-service/authorized?oauth_token=foobar&oauth_verifier=xyz",
        )

    assert len(calls), 1
    assert calls[0][0] == (bp,)
    assert calls[0][1] == {"token": "test-token"}
예제 #10
0
def test_signal_oauth_authorized_abort(request):
    app, bp = make_app()
    bp.session.fetch_access_token = mock.Mock(return_value="test-token")

    calls = []

    def callback(*args, **kwargs):
        calls.append((args, kwargs))
        return False

    oauth_authorized.connect(callback)
    request.addfinalizer(lambda: oauth_authorized.disconnect(callback))

    with app.test_client() as client:
        resp = client.get("/login/test-service/authorized?oauth_token=foobar&oauth_verifier=xyz")
        # check that we did NOT store the token
        assert "test-token_oauth_token" not in flask.session

    # the callback should still have been called
    assert len(calls) == 1
예제 #11
0
def test_signal_oauth_authorized(request):
    app, bp = make_app()
    bp.session.fetch_access_token = mock.Mock(return_value="test-token")

    calls = []

    def callback(*args, **kwargs):
        calls.append((args, kwargs))

    oauth_authorized.connect(callback)
    request.addfinalizer(lambda: oauth_authorized.disconnect(callback))

    with app.test_client() as client:
        resp = client.get("/login/test-service/authorized?oauth_token=foobar&oauth_verifier=xyz")
        # check that we stored the token
        assert flask.session["test-service_oauth_token"] == "test-token"

    assert len(calls), 1
    assert calls[0][0] == (bp,)
    assert calls[0][1] == {"token": "test-token"}
예제 #12
0
def test_signal_oauth_authorized_abort(request):
    app, bp = make_app()
    bp.session.fetch_access_token = mock.Mock(return_value="test-token")

    calls = []
    def callback(*args, **kwargs):
        calls.append((args, kwargs))
        return False

    oauth_authorized.connect(callback)
    request.addfinalizer(lambda: oauth_authorized.disconnect(callback))

    with app.test_client() as client:
        resp = client.get(
            "/login/test-service/authorized?oauth_token=foobar&oauth_verifier=xyz",
        )
        # check that we did NOT store the token
        assert "test-token_oauth_token" not in flask.session

    # the callback should still have been called
    assert len(calls) == 1
예제 #13
0
def test_signal_oauth_authorized(request):
    app, bp = make_app()
    bp.session.fetch_access_token = mock.Mock(return_value="test-token")

    calls = []
    def callback(*args, **kwargs):
        calls.append((args, kwargs))

    oauth_authorized.connect(callback)
    request.addfinalizer(lambda: oauth_authorized.disconnect(callback))

    with app.test_client() as client:
        resp = client.get(
            "/login/test-service/authorized?oauth_token=foobar&oauth_verifier=xyz",
        )
        # check that we stored the token
        assert flask.session["test-service_oauth_token"] == "test-token"

    assert len(calls), 1
    assert calls[0][0] == (bp,)
    assert calls[0][1] == {"token": "test-token"}
예제 #14
0
def test_signal_oauth_authorized_response(request):
    app, bp = make_app()
    bp.session.fetch_access_token = mock.Mock(return_value="test-token")

    calls = []
    def callback(*args, **kwargs):
        calls.append((args, kwargs))
        return flask.redirect("/url")

    oauth_authorized.connect(callback)
    request.addfinalizer(lambda: oauth_authorized.disconnect(callback))

    with app.test_client() as client:
        resp = client.get(
            "/login/test-service/authorized?oauth_token=foobar&oauth_verifier=xyz",
        )
        assert resp.status_code == 302
        assert resp.headers["Location"] == "http://localhost/url"
        # check that we did NOT store the token
        assert "test-token_oauth_token" not in flask.session

    # the callback should still have been called
    assert len(calls) == 1
예제 #15
0
def test_signal_oauth_authorized_response(request):
    app, bp = make_app()
    bp.session.fetch_access_token = mock.Mock(return_value="test-token")

    calls = []
    def callback(*args, **kwargs):
        calls.append((args, kwargs))
        return flask.redirect("/url")

    oauth_authorized.connect(callback)
    request.addfinalizer(lambda: oauth_authorized.disconnect(callback))

    with app.test_client() as client:
        resp = client.get(
            "/login/test-service/authorized?oauth_token=foobar&oauth_verifier=xyz",
        )
        assert resp.status_code == 302
        assert resp.headers["Location"] == "http://localhost/url"
        # check that we did NOT store the token
        assert "test-token_oauth_token" not in flask.session

    # the callback should still have been called
    assert len(calls) == 1
예제 #16
0
def test_sqla_flask_login_anon_to_authed(app, db, blueprint, request):
    login_manager = LoginManager(app)

    class User(db.Model, UserMixin):
        id = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String(80))

    class OAuth(OAuthConsumerMixin, db.Model):
        user_id = db.Column(db.Integer, db.ForeignKey(User.id))
        user = db.relationship(User)

    blueprint.storage = SQLAlchemyStorage(OAuth, db.session, user=current_user)

    db.create_all()

    def done():
        db.session.remove()
        db.drop_all()

    request.addfinalizer(done)

    # configure login manager
    @login_manager.user_loader
    def load_user(userid):
        return User.query.get(userid)

    # create a user object when OAuth succeeds
    def logged_in(sender, token):
        assert token
        assert blueprint == sender
        resp = sender.session.get("/user")
        user = User(name=resp.json()["name"])
        login_user(user)
        db.session.add(user)
        db.session.commit()
        flask.flash("Signed in successfully")

    oauth_authorized.connect(logged_in, blueprint)
    request.addfinalizer(
        lambda: oauth_authorized.disconnect(logged_in, blueprint))

    # mock out the `/user` API call
    responses.add(
        responses.GET,
        "https://example.com/user",
        body='{"name":"josephine"}',
    )

    with record_queries(db.engine) as queries:
        with app.test_client() as client:
            with client.session_transaction() as sess:
                sess["test-service_oauth_state"] = "random-string"
            # make the request
            resp = client.get(
                "/login/test-service/authorized?code=secret-code&state=random-string",
                base_url="https://a.b.c",
            )
            # check that we redirected the client
            assert resp.status_code == 302
            assert resp.headers["Location"] == "https://a.b.c/oauth_done"

    assert len(queries) == 5

    # check the database
    users = User.query.all()
    assert len(users) == 1
    user = users[0]
    assert user.name == "josephine"

    authorizations = OAuth.query.all()
    assert len(authorizations) == 1
    oauth = authorizations[0]
    assert oauth.provider == "test-service"
    assert oauth.token == {
        "access_token": "foobar",
        "token_type": "bearer",
        "scope": [""],
    }
    assert oauth.user_id == user.id
예제 #17
0
def test_signal_sender_oauth_authorized(request):
    app, bp = make_app()
    bp2 = OAuth2ConsumerBlueprint("test2", __name__,
        client_id="client_id",
        client_secret="client_secret",
        scope="admin",
        state="random-string",
        base_url="https://example.com",
        authorization_url="https://example.com/oauth/authorize",
        token_url="https://example.com/oauth/access_token",
        redirect_to="index",
    )
    app.register_blueprint(bp2, url_prefix="/login")

    calls = []
    def callback(*args, **kwargs):
        calls.append((args, kwargs))

    oauth_authorized.connect(callback, sender=bp)
    request.addfinalizer(lambda: oauth_authorized.disconnect(callback, sender=bp))

    with app.test_client() as client:
        with client.session_transaction() as sess:
            sess["test-service_oauth_state"] = "random-string"

        bp.session.fetch_token = mock.Mock(return_value="test-token")
        bp2.session.fetch_token = mock.Mock(return_value="test2-token")

        resp = client.get(
            "/login/test2/authorized?code=secret-code&state=random-string",
        )

    assert len(calls) == 0

    with app.test_client() as client:
        with client.session_transaction() as sess:
            sess["test-service_oauth_state"] = "random-string"

        bp.session.fetch_token = mock.Mock(return_value="test-token")
        bp2.session.fetch_token = mock.Mock(return_value="test2-token")

        resp = client.get(
            "/login/test-service/authorized?code=secret-code&state=random-string",
        )

    assert len(calls) == 1
    assert calls[0][0] == (bp,)
    assert calls[0][1] == {"token": "test-token"}

    with app.test_client() as client:
        with client.session_transaction() as sess:
            sess["test-service_oauth_state"] = "random-string"

        bp.session.fetch_token = mock.Mock(return_value="test-token")
        bp2.session.fetch_token = mock.Mock(return_value="test2-token")

        resp = client.get(
            "/login/test2/authorized?code=secret-code&state=random-string",
        )

    assert len(calls) == 1  # unchanged
예제 #18
0
def test_sqla_flask_login_anon_to_authed(app, db, blueprint, request):
    login_manager = LoginManager(app)

    class User(db.Model, UserMixin):
        id = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String(80))

    class OAuth(db.Model, OAuthConsumerMixin):
        user_id = db.Column(db.Integer, db.ForeignKey(User.id))
        user = db.relationship(User)

    blueprint.backend = SQLAlchemyBackend(OAuth, db.session, user=current_user)

    db.create_all()

    def done():
        db.session.remove()
        db.drop_all()

    request.addfinalizer(done)

    # configure login manager
    @login_manager.user_loader
    def load_user(userid):
        return User.query.get(userid)

    # create a user object when OAuth succeeds
    def logged_in(sender, token):
        assert token
        assert blueprint == sender
        resp = sender.session.get("/user")
        user = User(name=resp.json()["name"])
        login_user(user)
        db.session.add(user)
        db.session.commit()
        flask.flash("Signed in successfully")

    oauth_authorized.connect(logged_in, blueprint)
    request.addfinalizer(lambda: oauth_authorized.disconnect(logged_in, blueprint))

    # mock out the `/user` API call
    responses.add(responses.GET, "https://example.com/user", body='{"name":"josephine"}')

    with record_queries(db.engine) as queries:
        with app.test_client() as client:
            with client.session_transaction() as sess:
                sess["test-service_oauth_state"] = "random-string"
            # make the request
            resp = client.get(
                "/login/test-service/authorized?code=secret-code&state=random-string", base_url="https://a.b.c"
            )
            # check that we redirected the client
            assert resp.status_code == 302
            assert resp.headers["Location"] == "https://a.b.c/oauth_done"

    assert len(queries) == 5

    # check the database
    users = User.query.all()
    assert len(users) == 1
    user = users[0]
    assert user.name == "josephine"

    authorizations = OAuth.query.all()
    assert len(authorizations) == 1
    oauth = authorizations[0]
    assert oauth.provider == "test-service"
    assert oauth.token == {"access_token": "foobar", "token_type": "bearer", "scope": [""]}
    assert oauth.user_id == user.id
예제 #19
0
def test_signal_sender_oauth_authorized(request):
    app, bp = make_app()
    bp2 = OAuth2ConsumerBlueprint(
        "test2",
        __name__,
        client_id="client_id",
        client_secret="client_secret",
        scope="admin",
        state="random-string",
        base_url="https://example.com",
        authorization_url="https://example.com/oauth/authorize",
        token_url="https://example.com/oauth/access_token",
        redirect_to="index",
    )
    app.register_blueprint(bp2, url_prefix="/login")

    calls = []

    def callback(*args, **kwargs):
        calls.append((args, kwargs))

    oauth_authorized.connect(callback, sender=bp)
    request.addfinalizer(
        lambda: oauth_authorized.disconnect(callback, sender=bp))
    fake_token = {"access_token": "test-token"}
    fake_token2 = {"access_token": "test-token2"}

    with app.test_client() as client:
        with client.session_transaction() as sess:
            sess["test-service_oauth_state"] = "random-string"

        bp.session.fetch_token = mock.Mock(return_value=fake_token)
        bp2.session.fetch_token = mock.Mock(return_value=fake_token2)

        resp = client.get(
            "/login/test2/authorized?code=secret-code&state=random-string")

    assert len(calls) == 0

    with app.test_client() as client:
        with client.session_transaction() as sess:
            sess["test-service_oauth_state"] = "random-string"

        bp.session.fetch_token = mock.Mock(return_value="test-token")
        bp2.session.fetch_token = mock.Mock(return_value="test2-token")

        resp = client.get(
            "/login/test-service/authorized?code=secret-code&state=random-string"
        )

    assert len(calls) == 1
    assert calls[0][0] == (bp, )
    assert calls[0][1] == {"token": "test-token"}

    with app.test_client() as client:
        with client.session_transaction() as sess:
            sess["test-service_oauth_state"] = "random-string"

        bp.session.fetch_token = mock.Mock(return_value=fake_token)
        bp2.session.fetch_token = mock.Mock(return_value=fake_token2)

        resp = client.get(
            "/login/test2/authorized?code=secret-code&state=random-string")

    assert len(calls) == 1  # unchanged