def test_signal_oauth_error_authorized(request): responses.add( responses.POST, "https://example.com/oauth/access_token", body="Invalid request token.", status=401, ) app, bp = make_app() calls = [] def callback(*args, **kwargs): calls.append((args, kwargs)) oauth_error.connect(callback) request.addfinalizer(lambda: oauth_error.disconnect(callback)) with app.test_client() as client: resp = client.get( "/login/test-service/authorized?" "oauth_token=faketoken&" "oauth_token_secret=fakesecret&" "oauth_verifier=fakeverifier", base_url="https://a.b.c", ) assert len(calls) == 1 assert calls[0][0] == (bp,) assert ( calls[0][1]["message"] == "Token request failed with code 401, response was 'Invalid request token.'." ) assert resp.status_code == 302
def test_signal_oauth_error(request): app, bp = make_app() calls = [] def callback(*args, **kwargs): calls.append((args, kwargs)) oauth_error.connect(callback) request.addfinalizer(lambda: oauth_error.disconnect(callback)) with app.test_client() as client: resp = client.get( "/login/test-service/authorized?" "error=unauthorized_client&" "error_description=Invalid+redirect+URI&" "error_uri=https%3a%2f%2fexample.com%2fdocs%2fhelp", base_url="https://a.b.c", ) assert len(calls) == 1 assert calls[0][0] == (bp,) assert calls[0][1] == { "error": "unauthorized_client", "error_description": "Invalid redirect URI", "error_uri": "https://example.com/docs/help", } assert resp.status_code == 302
def test_signal_oauth_error_login(request): responses.add( responses.POST, "https://example.com/oauth/request_token", body="oauth_problem=nonce_used", status=401, ) app, bp = make_app() calls = [] def callback(*args, **kwargs): calls.append((args, kwargs)) oauth_error.connect(callback) request.addfinalizer(lambda: oauth_error.disconnect(callback)) with app.test_client() as client: resp = client.get("/login/test-service", base_url="https://a.b.c") assert len(calls) == 1 assert calls[0][0] == (bp,) assert ( calls[0][1]["message"] == "Token request failed with code 401, response was 'oauth_problem=nonce_used'." ) assert resp.status_code == 302 location = resp.headers["Location"] assert location == "https://a.b.c/"
def test_signal_oauth_error(request): app, bp = make_app() calls = [] def callback(*args, **kwargs): calls.append((args, kwargs)) oauth_error.connect(callback) request.addfinalizer(lambda: oauth_error.disconnect(callback)) with app.test_client() as client: resp = client.get( "/login/test-service/authorized?" "error=unauthorized_client&" "error_description=Invalid+redirect+URI&" "error_uri=https%3a%2f%2fexample.com%2fdocs%2fhelp", base_url="https://a.b.c", ) assert len(calls) == 1 assert calls[0][0] == (bp, ) assert calls[0][1] == { "error": "unauthorized_client", "error_description": "Invalid redirect URI", "error_uri": "https://example.com/docs/help", } assert resp.status_code == 302
def test_signal_oauth_notoken_authorized(request): app, bp = make_app() calls = [] def callback(*args, **kwargs): calls.append((args, kwargs)) oauth_error.connect(callback) request.addfinalizer(lambda: oauth_error.disconnect(callback)) with app.test_client() as client: resp = client.get( "/login/test-service/authorized?" "denied=faketoken", base_url="https://a.b.c", ) assert len(calls) == 1 assert calls[0][0] == (bp, ) assert "Response does not contain a token" in calls[0][1]["message"] assert calls[0][1]["response"] == {'denied': 'faketoken'} assert resp.status_code == 302 location = resp.headers["Location"] assert location == "https://a.b.c/"
def test_sqla_flask_login_misconfigured(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) calls = [] def callback(*args, **kwargs): calls.append((args, kwargs)) oauth_error.connect(callback) request.addfinalizer(lambda: oauth_error.disconnect(callback)) with app.test_client() as client: # reset the session before the request 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"] in ("https://a.b.c/oauth_done", "/oauth_done") assert len(calls) == 1 assert calls[0][0] == (blueprint, ) error = calls[0][1]["error"] assert isinstance(error, ValueError) assert str(error) == "Cannot set OAuth token without an associated user"
def test_sqla_flask_login_misconfigured(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) calls = [] def callback(*args, **kwargs): calls.append((args, kwargs)) oauth_error.connect(callback) request.addfinalizer(lambda: oauth_error.disconnect(callback)) with app.test_client() as client: # reset the session before the request 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(calls) == 1 assert calls[0][0] == (blueprint,) error = calls[0][1]["error"] assert isinstance(error, ValueError) assert str(error) == "Cannot set OAuth token without an associated user"
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
def test_signal_oauth_notoken_authorized(request): app, bp = make_app() calls = [] def callback(*args, **kwargs): calls.append((args, kwargs)) oauth_error.connect(callback) request.addfinalizer(lambda: oauth_error.disconnect(callback)) with app.test_client() as client: resp = client.get( "/login/test-service/authorized?" "denied=faketoken", base_url="https://a.b.c", ) assert len(calls) == 1 assert calls[0][0] == (bp,) assert "Response does not contain a token" in calls[0][1]["message"] assert calls[0][1]["response"] == {"denied": "faketoken"} assert resp.status_code == 302 location = resp.headers["Location"] assert location == "https://a.b.c/"