async def test_identity_is_email(client): # Tests that login sets the user_email and logout removes it login_url = client.app.router["auth_login"].url_for() logout_url = client.app.router["auth_logout"].url_for() session_url = "/session" async with NewUser() as user: resp = await client.get(session_url) session = await resp.json() assert session.get("AIOHTTP_SECURITY") == None # login await client.post(login_url, json={ "email": user["email"], "password": user["raw_password"] }) resp = await client.get(session_url) session = await resp.json() assert session.get("AIOHTTP_SECURITY") == user["email"] # logout await client.post(logout_url) resp = await client.get(session_url) session = await resp.json() assert session.get("AIOHTTP_SECURITY") == None
async def test_add_new_project_from_model_instance( view, client, mocker, ): view["label"] = view.pop("display_name") viewer = ViewerInfo(**view) assert viewer.dict() == view mock_func = mocker.patch( "simcore_service_webserver.director_v2_api.create_or_update_pipeline", return_value=None, ) async with NewUser(app=client.app) as user_db: try: # preparation await auto_add_user_to_groups(client.app, user_db["id"]) user_db = await get_user(client.app, user_db["id"]) # this part is under test --- user = UserInfo( id=user_db["id"], name=user_db["name"], primary_gid=user_db["primary_gid"], email=user_db["email"], ) project_id = "e3ee7dfc-25c3-11eb-9fae-02420a01b846" file_picker_id = "4c69c0ce-00e4-4bd5-9cf0-59b67b3a9343" viewer_id = "fc718e5a-bf07-4abe-b526-d9cafd34830c" project: Project = create_viewer_project_model( project_id, file_picker_id, viewer_id, owner=user, download_link="http://httpbin.org/image/jpeg", viewer_info=viewer, ) await add_new_project(client.app, project, user) # checks start here --- assert mock_func.called # internally validates project injected in db project_db = await get_project_for_user( client.app, str(project.uuid), user.id, include_state=False, include_templates=False, ) assert set( project_db["workbench"].keys()) == {file_picker_id, viewer_id} finally: # tear-down: delete before user gets deleted await delete_all_projects(client.app)
async def test_registration_with_expired_confirmation( client: TestClient, cfg: LoginOptions, db: AsyncpgStorage, mocker, ): mocker.patch( "simcore_service_webserver.login.settings.get_plugin_settings", return_value=LoginSettings( LOGIN_REGISTRATION_CONFIRMATION_REQUIRED=True, LOGIN_REGISTRATION_INVITATION_REQUIRED=True, ), ) url = client.app.router["auth_register"].url_for() async with NewUser({"status": UserStatus.CONFIRMATION_PENDING.name}) as user: confirmation = await db.create_confirmation( user, ConfirmationAction.REGISTRATION.name) r = await client.post( url, json={ "email": user["email"], "password": user["raw_password"], "confirm": user["raw_password"], }, ) await db.delete_confirmation(confirmation) await assert_error(r, web.HTTPConflict, cfg.MSG_EMAIL_EXISTS)
async def standard_groups( client, logged_user: Dict) -> AsyncIterator[List[Dict[str, str]]]: # create a separate admin account to create some standard groups for the logged user sparc_group = { "gid": "5", # this will be replaced "label": "SPARC", "description": "Stimulating Peripheral Activity to Relieve Conditions", "thumbnail": "https://commonfund.nih.gov/sites/default/files/sparc-image-homepage500px.png", "inclusionRules": { "email": r"@(sparc)+\.(io|com)$" }, } team_black_group = { "gid": "5", # this will be replaced "label": "team Black", "description": "THE incredible black team", "thumbnail": None, "inclusionRules": { "email": r"@(black)+\.(io|com)$" }, } async with NewUser({ "name": f"{logged_user['name']}_admin", "role": "USER" }, client.app) as admin_user: # creates two groups sparc_group = await create_user_group(client.app, admin_user["id"], sparc_group) team_black_group = await create_user_group(client.app, admin_user["id"], team_black_group) # adds logged_user to sparc group await add_user_in_group( client.app, admin_user["id"], sparc_group["gid"], new_user_id=logged_user["id"], ) # adds logged_user to team-black group await add_user_in_group( client.app, admin_user["id"], team_black_group["gid"], new_user_email=logged_user["email"], ) _, standard_groups, _ = await list_user_groups(client.app, logged_user["id"]) yield standard_groups # clean groups await delete_user_group(client.app, admin_user["id"], sparc_group["gid"]) await delete_user_group(client.app, admin_user["id"], team_black_group["gid"])
async def test_reset_and_confirm(client: TestClient, cfg: LoginOptions, capsys): async with NewUser(app=client.app) as user: reset_url = client.app.router["auth_reset_password"].url_for() rp = await client.post( reset_url, json={ "email": user["email"], }, ) assert rp.url.path == reset_url.path await assert_status(rp, web.HTTPOk, cfg.MSG_EMAIL_SENT.format(**user)) out, err = capsys.readouterr() confirmation_url = parse_link(out) code = URL(confirmation_url).parts[-1] # emulates user click on email url rp = await client.get(confirmation_url) assert rp.status == 200 assert (rp.url.path_qs == URL(cfg.LOGIN_REDIRECT).with_fragment( "reset-password?code=%s" % code).path_qs) # api/specs/webserver/v0/components/schemas/auth.yaml#/ResetPasswordForm reset_allowed_url = client.app.router[ "auth_reset_password_allowed"].url_for(code=code) new_password = get_random_string(5, 10) rp = await client.post( reset_allowed_url, json={ "password": new_password, "confirm": new_password, }, ) payload = await rp.json() assert rp.status == 200, payload assert rp.url.path == reset_allowed_url.path await assert_status(rp, web.HTTPOk, cfg.MSG_PASSWORD_CHANGED) # TODO: multiple flash messages # Try new password logout_url = client.app.router["auth_logout"].url_for() rp = await client.post(logout_url) assert rp.url.path == logout_url.path await assert_status(rp, web.HTTPUnauthorized, "Unauthorized") login_url = client.app.router["auth_login"].url_for() rp = await client.post( login_url, json={ "email": user["email"], "password": new_password, }, ) assert rp.url.path == login_url.path await assert_status(rp, web.HTTPOk, cfg.MSG_LOGGED_IN)
async def test_change_to_existing_email(client): url = client.app.router["auth_change_email"].url_for() async with LoggedUser(client) as user: async with NewUser() as other: rsp = await client.post(url, json={ "email": other["email"], }) await assert_status(rsp, web.HTTPUnprocessableEntity, "This email cannot be used")
async def test_proxy_login(client, cookie_enabled, expected): restricted_url = client.app.router["get_my_profile"].url_for() assert str(restricted_url) == "/v0/me" def build_proxy_session_cookie(identity: str): # NOTE: Creates proxy session for authenticated uses in the api-server. # Will be used as temporary solution until common authentication # service is in place # import json import base64 import time from cryptography import fernet # Based on aiohttp_session and aiohttp_security # HACK to get secret for testing purposes cfg = client.app[APP_CONFIG_KEY]["session"] secret_key_bytes = cfg["secret_key"].encode("utf-8") while len(secret_key_bytes) < 32: secret_key_bytes += secret_key_bytes secret_key = secret_key_bytes[:32] if isinstance(secret_key, str): pass elif isinstance(secret_key, (bytes, bytearray)): secret_key = base64.urlsafe_b64encode(secret_key) _fernet = fernet.Fernet(secret_key) # builds session cookie cookie_name = "osparc.WEBAPI_SESSION" cookie_data = json.dumps({ "created": int(time.time()), # now "session": { "AIOHTTP_SECURITY": identity }, "path": "/", # extras? e.g. expiration }).encode("utf-8") encrypted_cookie_data = _fernet.encrypt(cookie_data).decode("utf-8") return {cookie_name: encrypted_cookie_data} # --- async with NewUser() as user: cookies = (build_proxy_session_cookie( identity=user["email"]) if cookie_enabled else {}) resp = await client.get(restricted_url, cookies=cookies) data, error = await assert_status(resp, expected) if not error: assert data["login"] == user["email"]
async def test_login_inactive_user(client): url = client.app.router["auth_login"].url_for() r = await client.get(url) assert cfg.MSG_ACTIVATION_REQUIRED not in await r.text() async with NewUser({"status": UserStatus.CONFIRMATION_PENDING.name}) as user: r = await client.post( url, json={"email": user["email"], "password": user["raw_password"]} ) assert r.status == web.HTTPUnauthorized.status_code assert r.url_obj.path == url.path assert cfg.MSG_ACTIVATION_REQUIRED in await r.text()
async def test_registration_with_existing_email(client: TestClient, cfg: LoginOptions): url = client.app.router["auth_register"].url_for() async with NewUser(app=client.app) as user: r = await client.post( url, json={ "email": user["email"], "password": user["raw_password"], "confirm": user["raw_password"], }, ) await assert_error(r, web.HTTPConflict, cfg.MSG_EMAIL_EXISTS)
async def test_banned_user(client, capsys, cfg): reset_url = client.app.router["auth_reset_password"].url_for() async with NewUser({"status": UserStatus.BANNED.name}) as user: rp = await client.post(reset_url, json={ "email": user["email"], }) assert rp.url_obj.path == reset_url.path await assert_status(rp, web.HTTPOk, cfg.MSG_EMAIL_SENT.format(**user)) out, err = capsys.readouterr() assert parse_test_marks(out)["reason"] == cfg.MSG_USER_BANNED
async def test_login_successfully(client: TestClient, login_options: LoginOptions): url = client.app.router["auth_login"].url_for() async with NewUser(app=client.app) as user: r = await client.post( f"{url}", json={"email": user["email"], "password": user["raw_password"]} ) assert r.status == 200 data, error = unwrap_envelope(await r.json()) assert not error assert data assert login_options.MSG_LOGGED_IN in data["message"]
async def test_login_with_wrong_password(client): url = client.app.router["auth_login"].url_for() r = await client.get(url) payload = await r.json() assert cfg.MSG_WRONG_PASSWORD not in await r.text(), str(payload) async with NewUser() as user: r = await client.post(url, json={"email": user["email"], "password": "******",}) payload = await r.json() assert r.status == web.HTTPUnauthorized.status_code, str(payload) assert r.url_obj.path == url.path assert cfg.MSG_WRONG_PASSWORD in await r.text()
async def test_login_banned_user(client: TestClient, login_options: LoginOptions): url = client.app.router["auth_login"].url_for() r = await client.post(f"{url}") assert login_options.MSG_USER_BANNED not in await r.text() async with NewUser({"status": UserStatus.BANNED.name}, app=client.app) as user: r = await client.post( f"{url}", json={"email": user["email"], "password": user["raw_password"]} ) payload = await r.json() assert r.status == web.HTTPUnauthorized.status_code, str(payload) assert r.url_obj.path == url.path assert login_options.MSG_USER_BANNED in payload["error"]["errors"][0]["message"]
async def test_inactive_user(client, capsys, cfg): reset_url = client.app.router["auth_reset_password"].url_for() async with NewUser({"status": UserStatus.CONFIRMATION_PENDING.name}) as user: rp = await client.post(reset_url, json={ "email": user["email"], }) assert rp.url_obj.path == reset_url.path await assert_status(rp, web.HTTPOk, cfg.MSG_EMAIL_SENT.format(**user)) out, err = capsys.readouterr() assert parse_test_marks(out)["reason"] == cfg.MSG_ACTIVATION_REQUIRED
async def test_proxy_login( client: TestClient, cookie_enabled: bool, expected: web.HTTPException ): restricted_url = client.app.router["get_my_profile"].url_for() assert str(restricted_url) == "/v0/me" def _build_proxy_session_cookie(identity: str): # NOTE: Creates proxy session for authenticated uses in the api-server. # Will be used as temporary solution until common authentication # service is in place # # Based on aiohttp_session and aiohttp_security # HACK to get secret for testing purposes session_settings = get_plugin_settings(client.app) _fernet = fernet.Fernet(session_settings.SESSION_SECRET_KEY.get_secret_value()) # builds session cookie cookie_name = "osparc.WEBAPI_SESSION" cookie_data = json.dumps( { "created": int(time.time()), # now "session": {"AIOHTTP_SECURITY": identity}, "path": "/", # extras? e.g. expiration } ).encode("utf-8") encrypted_cookie_data = _fernet.encrypt(cookie_data).decode("utf-8") return {cookie_name: encrypted_cookie_data} # --- async with NewUser(app=client.app) as user: cookies = ( _build_proxy_session_cookie(identity=user["email"]) if cookie_enabled else {} ) resp = await client.get(restricted_url, cookies=cookies) data, error = await assert_status(resp, expected) if not error: assert data["login"] == user["email"]
async def test_too_often(client, capsys, cfg): reset_url = client.app.router["auth_reset_password"].url_for() cfg = client.app[APP_LOGIN_CONFIG] db = cfg.STORAGE async with NewUser() as user: confirmation = await db.create_confirmation( user, ConfirmationAction.RESET_PASSWORD.name) rp = await client.post(reset_url, json={ "email": user["email"], }) await db.delete_confirmation(confirmation) assert rp.url_obj.path == reset_url.path await assert_status(rp, web.HTTPOk, cfg.MSG_EMAIL_SENT.format(**user)) out, err = capsys.readouterr() assert parse_test_marks(out)["reason"] == cfg.MSG_OFTEN_RESET_PASSWORD
async def test_too_often(client: TestClient, cfg: LoginOptions, db: AsyncpgStorage, capsys): reset_url = client.app.router["auth_reset_password"].url_for() async with NewUser(app=client.app) as user: confirmation = await db.create_confirmation( user, ConfirmationAction.RESET_PASSWORD.name) rp = await client.post( reset_url, json={ "email": user["email"], }, ) await db.delete_confirmation(confirmation) assert rp.url.path == reset_url.path await assert_status(rp, web.HTTPOk, cfg.MSG_EMAIL_SENT.format(**user)) out, err = capsys.readouterr() assert parse_test_marks(out)["reason"] == cfg.MSG_OFTEN_RESET_PASSWORD
async def test_registration_with_expired_confirmation(client, monkeypatch): monkeypatch.setitem(cfg, "REGISTRATION_CONFIRMATION_REQUIRED", True) monkeypatch.setitem(cfg, "REGISTRATION_CONFIRMATION_LIFETIME", -1) db = get_storage(client.app) url = client.app.router["auth_register"].url_for() async with NewUser({"status": UserStatus.CONFIRMATION_PENDING.name}) as user: confirmation = await db.create_confirmation( user, ConfirmationAction.REGISTRATION.name) r = await client.post( url, json={ "email": user["email"], "password": user["raw_password"], "confirm": user["raw_password"], }, ) await db.delete_confirmation(confirmation) await assert_error(r, web.HTTPConflict, cfg.MSG_EMAIL_EXISTS)
async def test_login_with_wrong_password( client: TestClient, login_options: LoginOptions ): url = client.app.router["auth_login"].url_for() r = await client.post(f"{url}") payload = await r.json() assert login_options.MSG_WRONG_PASSWORD not in await r.text(), str(payload) async with NewUser(app=client.app) as user: r = await client.post( f"{url}", json={ "email": user["email"], "password": "******", }, ) payload = await r.json() assert r.status == web.HTTPUnauthorized.status_code, str(payload) assert r.url_obj.path == url.path assert login_options.MSG_WRONG_PASSWORD in await r.text()
async def second_user(client: TestClient) -> Callable[..., Dict[str, Any]]: async with NewUser({ "name": "Second User", "role": "USER" }, client.app) as user: yield user
async def second_user( client: TestClient, ) -> AsyncIterable[Dict[str, Any]]: async with NewUser({"name": "Second User", "role": "USER"}, client.app) as user: yield user