def login_user(): if not os.environ.get("ALLOW_MOCK_USER_API", None): raise Forbidden() data = current_request.get_json() sub = data["sub"] # oidc sub maps to sbs uid - see user_claims user = User.query.filter(User.uid == sub).first() or User(created_by="system", updated_by="system") add_user_claims(data, sub, user, replace_none_values=False) db.session.merge(user) res = {"admin": is_admin_user(user), "guest": False, "confirmed_admin": user.confirmed_super_user} session_data = { "id": user.id, "uid": user.uid, "name": user.name, "email": user.email, "user_accepted_aup": user.has_agreed_with_aup(), "second_factor_confirmed": True } session["user"] = {**session_data, **res} return None, 201
def resume_session(): logger = ctx_logger("oidc") cfg = current_app.app_config oidc_config = cfg.oidc code = query_param("code", required=False, default=None) if not code: # This means that we are not in the redirect callback, but at the redirect from eduTeams logger.debug("Redirect to login in resume-session to start OIDC flow") authorization_endpoint = _get_authorization_url() return redirect(authorization_endpoint) scopes = " ".join(oidc_config.scopes) payload = { "code": code, "grant_type": "authorization_code", "scope": scopes, "redirect_uri": oidc_config.redirect_uri } headers = { "Content-Type": "application/x-www-form-urlencoded", "Cache-Control": "no-cache", "Accept": "application/json, application/json;charset=UTF-8" } response = requests.post(oidc_config.token_endpoint, data=urllib.parse.urlencode(payload), headers=headers, auth=(oidc_config.client_id, oidc_config.client_secret)) if response.status_code != 200: return _redirect_with_error(logger, f"Server error: Token endpoint error (http {response.status_code}") token_json = response.json() access_token = token_json["access_token"] headers = { "Accept": "application/json, application/json;charset=UTF-8", "Authorization": f"Bearer {access_token}" } response = requests.get(oidc_config.userinfo_endpoint, headers=headers) if response.status_code != 200: return _redirect_with_error(logger, f"Server error: User info endpoint error (http {response.status_code}") logger = ctx_logger("user") user_info_json = response.json() logger.debug(f"Userinfo endpoint results {user_info_json}") uid = user_info_json["sub"] user = User.query.filter(User.uid == uid).first() if not user: user = User(uid=uid, created_by="system", updated_by="system") add_user_claims(user_info_json, uid, user) # last_login_date is set later in this method user.last_accessed_date = datetime.datetime.now() logger.info(f"Provisioning new user {user.uid}") else: logger.info(f"Updating user {user.uid} with new claims / updated at") add_user_claims(user_info_json, uid, user) encoded_id_token = token_json["id_token"] id_token = decode_jwt_token(encoded_id_token) no_mfa_required = not oidc_config.second_factor_authentication_required idp_mfa = id_token.get("acr") == ACR_VALUES idp_allowed = mfa_idp_allowed(user, user.schac_home_organisation, None) second_factor_confirmed = no_mfa_required or idp_mfa or idp_allowed if second_factor_confirmed: user.last_login_date = datetime.datetime.now() user = db.session.merge(user) db.session.commit() user_accepted_aup = user.has_agreed_with_aup() store_user_in_session(user, second_factor_confirmed, user_accepted_aup) if not user_accepted_aup: location = f"{cfg.base_url}/aup" elif not second_factor_confirmed: location = f"{cfg.base_url}/2fa" else: location = session.get("original_destination", cfg.base_url) return redirect(location)
def test_add_user_claims_user_name(self): user = User() add_user_claims({"given_name": "John", "family_name": "Doe"}, "urn:johny", user) self.assertEqual("jdoe", user.username)
def test_bugfix_empty_user_claims_affiliation_list(self): user = User() add_user_claims({"voperson_external_id": []}, "urn:johny", user) self.assertIsNone(user.schac_home_organisation)
def test_user_claims_schac_home_org(self): user = User() user_info_json_str = self.read_file("user_info.json") user_info_json = json.loads(user_info_json_str) add_user_claims(user_info_json, "urn:new_user", user) self.assertEqual("rug", user.schac_home_organisation)
def test_add_user_claims_empty_entitlements(self): user = User() add_user_claims({"eduperson_entitlement": []}, "urn:johny", user) self.assertIsNone(user.entitlement)
def test_add_user_claims_no_voperson_external_id(self): user = User() add_user_claims({}, "urn:johny", user) self.assertIsNone(user.schac_home_organisation)
def test_add_user_claims_affiliation_defensive(self): user = User() add_user_claims({"voperson_external_id": "university"}, "urn:johny", user) self.assertIsNone(user.schac_home_organisation)
def test_add_user_claims_affiliation_list(self): user = User() add_user_claims({"voperson_external_id": ["*****@*****.**"]}, "urn:johny", user) self.assertEqual("sub.uni.org", user.schac_home_organisation)
def test_add_user_claims_affiliation(self): user = User() add_user_claims({"voperson_external_id": "teacher@university"}, "urn:johny", user) self.assertEqual("university", user.schac_home_organisation)
def test_add_user_claims(self): user = User() add_user_claims({}, "urn:johny", user) self.assertEqual("urn:johny", user.name)