def test_diff_fields_check_mismatch(dummy_user_dict, dummy_group_dict): """ Check we cannot diff two different objects""" user = User(dummy_user_dict) group = Group(dummy_group_dict) with pytest.raises(ValueError): user.diff_fields(group)
def group(ipa, groupname): group = Group(group_or_404(ipa, groupname)) sponsor_form = AddGroupMemberForm(groupname=groupname) remove_form = RemoveGroupMemberForm(groupname=groupname) members = [User(u) for u in ipa.user_find(in_group=groupname)['result']] batch_methods = [ {"method": "user_find", "params": [[], {"uid": sponsorname, 'all': True}]} for sponsorname in group.sponsors ] sponsors = [ User(u['result'][0]) for u in ipa.batch(methods=batch_methods)['results'] ] # We can safely assume g.current_user exists after @with_ipa current_user_is_sponsor = g.current_user.username in group.sponsors return render_template( 'group.html', group=group, members=members, sponsors=sponsors, sponsor_form=sponsor_form, remove_form=remove_form, current_user_is_sponsor=current_user_is_sponsor, )
def user(ipa, username): user = User(user_or_404(ipa, username)) # As a speed optimization, we make two separate calls. # Just doing a group_find (with all=True) is super slow here, with a lot of # groups. member_groups = [ Group(group) for group in ipa.group_find( o_user=username, o_all=False, fasgroup=True)['result'] ] managed_groups = [ Group(group) for group in ipa.group_find(o_membermanager_user=username, o_all=False, fasgroup=True)['result'] ] groups = [group for group in managed_groups if group not in member_groups ] + member_groups # Privacy setting if user != g.current_user and user.is_private: user.anonymize() return render_template( 'user.html', user=user, groups=groups, managed_groups=managed_groups, member_groups=member_groups, )
def test_spamcheck(client, dummy_stageuser, mocker, spamcheck_status, spamcheck_on): user = User(ipa_admin.stageuser_show("dummy")["result"]) assert user.status_note != spamcheck_status token = make_token({"sub": "dummy"}, audience=Audience.spam_check) with mailer.record_messages() as outbox: response = client.post( "/register/spamcheck-hook", json={ "token": token, "status": spamcheck_status }, ) assert response.status_code == 200 assert response.json == {"status": "success"} # Check that the status was changed user = User(ipa_admin.stageuser_show("dummy")["result"]) assert user.status_note == spamcheck_status # Sent email if spamcheck_status == "active": assert len(outbox) == 1 message = outbox[0] assert message.subject == "Verify your email address" assert message.recipients == ["*****@*****.**"] else: assert len(outbox) == 0
def test_diff_fields(dummy_user_dict): """ Check the method to compare the diff between two User objects works""" user = User(dummy_user_dict) new_data = dummy_user_dict.copy() new_data["fasgithubusername"] = ["newusername"] changed_user = User(new_data) diff = user.diff_fields(changed_user) assert diff == ['github']
def user_settings_profile(ipa, username): user = User(user_or_404(ipa, username)) form = UserSettingsProfileForm(obj=user) if form.validate_on_submit(): result = _user_mod( ipa, form, user, { 'first_name': form.firstname.data, 'last_name': form.lastname.data, 'full_name': '%s %s' % (form.firstname.data, form.lastname.data), 'display_name': '%s %s' % (form.firstname.data, form.lastname.data), 'mail': form.mail.data, 'fasircnick': form.ircnick.data, 'faslocale': form.locale.data, 'fastimezone': form.timezone.data, 'fasgithubusername': form.github.data.lstrip('@'), 'fasgitlabusername': form.gitlab.data.lstrip('@'), 'fasrhbzemail': form.rhbz_mail.data, 'faswebsiteurl': form.website_url.data, }, "user_settings_profile", ) if result: return result return render_template('user-settings-profile.html', user=user, form=form, activetab="profile")
def search_json(ipa): username = request.args.get('username') groupname = request.args.get('group') res = [] if username: users_ = [ User(u) for u in ipa.user_find( username, fasuser=True, sizelimit=10)['result'] ] for user_ in users_: res.append({ 'uid': user_.username, 'cn': user_.name, 'url': url_for(".user", username=user_.username), }) if groupname: groups_ = [ Group(g) for g in ipa.group_find( groupname, fasgroup=True, sizelimit=10)['result'] ] for group_ in groups_: res.append({ 'cn': group_.name, 'description': group_.description, 'url': url_for(".group", groupname=group_.name), }) return jsonify(res)
def confirm_registration(): username = request.args.get('username') if not username: abort(400, "No username provided") try: user = User(ipa_admin.stageuser_show(a_uid=username)['result']) except python_freeipa.exceptions.NotFound: flash(_("The registration seems to have failed, please try again."), "warning") return redirect(f"{url_for('.root')}?tab=register") if current_app.config["BASSET_URL"] and user.status_note != "active": abort(401, "You should not be here") form = ResendValidationEmailForm() if form.validate_on_submit(): _send_validation_email(user) flash( _('The address validation email has be sent again. Make sure it did not land in ' 'your spam folder'), 'success', ) return redirect(request.url) return render_template('registration-confirmation.html', user=user, form=form)
def test_user_no_displayname_no_gcos_no_cn(dummy_user_dict): """Test that we fallback to cn if there is no displayname nor gcos""" del dummy_user_dict["displayname"] del dummy_user_dict["gecos"] del dummy_user_dict["cn"] user = User(dummy_user_dict) assert user.name is None
def user_settings_keys(ipa, username): user = User(user_or_404(ipa, username)) form = UserSettingsKeysForm(obj=user) if form.validate_on_submit(): result = _user_mod( ipa, form, user, { 'ipasshpubkey': form.sshpubkeys.data, 'fasgpgkeyid': form.gpgkeys.data }, "user_settings_keys", ) if result: return result # if the form has errors, we don't want to add new fields. otherwise, # more fields will show up with every validation error if not form.errors: # Append 2 empty entries at the bottom of the gpgkeys fieldlist for i in range(2): form.gpgkeys.append_entry() form.sshpubkeys.append_entry() return render_template('user-settings-keys.html', user=user, form=form, activetab="keys")
def _user_mod(ipa, form, user, details, redirect_to): with handle_form_errors(form): try: updated_user = User( ipa.user_mod(user.username, **details, all=True)) except python_freeipa.exceptions.BadRequest as e: if e.message == 'no modifications to be performed': raise FormError("non_field_errors", e.message) else: app.logger.error( f'An error happened while editing user {user.username}: {e.message}' ) raise FormError("non_field_errors", e.message) flash( Markup( f'Profile Updated: <a href=\"{url_for("user", username=user.username)}\">' 'view your profile</a>'), 'success', ) messaging.publish( UserUpdateV1({ "msg": { "agent": user.username, "user": user.username, "fields": user.diff_fields(updated_user), } })) return redirect(url_for(redirect_to, username=user.username))
def spamcheck_hook(): if not current_app.config.get("BASSET_URL"): return jsonify({"error": "Spamcheck disabled"}), 501 data = request.get_json() if not data: return jsonify({"error": "Bad payload"}), 400 try: token = data["token"] status = data["status"] except KeyError as e: return jsonify({"error": f"Missing key: {e}"}), 400 try: token_data = read_token(token, audience=Audience.spam_check) except jwt.ExpiredSignatureError: return jsonify({"error": "The token has expired"}), 400 except jwt.InvalidTokenError as e: return jsonify({"error": f"Invalid token: {e}"}), 400 username = token_data["sub"] if status not in ("active", "spamcheck_denied", "spamcheck_manual"): return jsonify({"error": f"Invalid status: {status}."}), 400 result = ipa_admin.stageuser_mod(a_uid=username, fasstatusnote=status) user = User(result["result"]) if status == "active": # Send the address validation email _send_validation_email(user) return jsonify({"status": "success"})
def fn(*args, **kwargs): ipa = maybe_ipa_session(app, session) if ipa: g.ipa = ipa g.current_user = User(g.ipa.user_find(whoami=True)['result'][0]) return f(*args, **kwargs, ipa=ipa) flash('Please log in to continue.', 'warning') return redirect(url_for('root'))
def forgot_password_ask(): form = ForgottenPasswordForm() if form.validate_on_submit(): username = form.username.data lock = PasswordResetLock(username) valid_until = lock.valid_until() now = datetime.datetime.now() with handle_form_errors(form): if valid_until is not None and now < valid_until: wait_min = int((valid_until - now).total_seconds() / 60) wait_sec = int((valid_until - now).total_seconds() % 60) raise FormError( "non_field_errors", _( 'You have already requested a password reset, you need to wait ' '%(wait_min)s minute(s) and %(wait_sec)s seconds before you can request ' 'another.', wait_min=wait_min, wait_sec=wait_sec, ), ) try: user = User(ipa_admin.user_show(username)) except python_freeipa.exceptions.NotFound: raise FormError( "username", _("User %(username)s does not exist", username=username)) token = PasswordResetToken.from_user(user).as_string() # Send the email email_context = {"token": token, "username": username} email = Message( body=render_template("forgot-password-email.txt", **email_context), html=render_template("forgot-password-email.html", **email_context), recipients=[user.mail], subject="Password reset procedure", ) try: mailer.send(email) except ConnectionRefusedError as e: app.logger.error( f"Impossible to send a password reset email: {e}") flash(_("We could not send you an email, please retry later"), "danger") return redirect(url_for('root')) if app.config["DEBUG"]: # pragma: no cover app.logger.debug(email) lock.store() app.logger.info( f'{username} forgot their password and requested a token') flash( _('An email has been sent to your address with instructions on how to reset ' 'your password'), "success", ) return redirect(url_for('root')) return render_template('forgot-password-ask.html', form=form)
def test_strip(client, post_data_step_1, cleanup_dummy_user, field_name): """Register a user with fields that contain trailing spaces""" post_data_step_1[f"register-{field_name}"] = "Dummy " with mailer.record_messages() as outbox: result = client.post('/', data=post_data_step_1) assert result.status_code == 302, str(result.data, "utf8") user = User(ipa_admin.stageuser_show(a_uid="dummy")['result']) assert getattr(user, field_name) == "Dummy" assert len(outbox) == 1
def token_for_dummy_user(dummy_user): user = User(ipa_admin.user_show("dummy")["result"]) return make_token( { "sub": user.username, "lpc": user.last_password_change }, audience=Audience.password_reset, )
def fn(*args, **kwargs): ipa = maybe_ipa_session(current_app, session) if ipa: g.ipa = ipa g.current_user = User(g.ipa.user_find(whoami=True)['result'][0]) return f(*args, **kwargs, ipa=ipa) coming_from = quote(request.full_path) flash('Please log in to continue.', 'warning') return redirect(f"{url_for('.root')}?next={coming_from}")
def test_signal_basset(client, mocker, dummy_user): mocked_requests = mocker.patch("noggin.signals.requests") mocker.patch.dict(current_app.config, {"BASSET_URL": "http://basset.test"}) user = User(ipa_admin.user_show("dummy")["result"]) with current_app.test_request_context('/'): request_basset_check(user) call_args = mocked_requests.post.call_args_list[0] assert list(call_args[0]) == ["http://basset.test"] json_data = call_args[1]["json"] assert json_data["action"] == "fedora.noggin.registration" expected_dict = user.as_dict() expected_dict["human_name"] = user.commonname expected_dict["email"] = user.mail assert json_data["data"]["user"] == expected_dict assert json_data["data"]["request_headers"] == {"Host": "localhost"} assert json_data["data"]["callback"] == "http://localhost/register/spamcheck-hook" token = json_data["data"]["token"] token_data = read_token(token, audience=Audience.spam_check) assert token_data["sub"] == "dummy"
def test_gecos(client, post_data_non_ascii, cleanup_dummy_user, mocker): record_signal = mocker.Mock() with mailer.record_messages() as _, stageuser_created.connected_to( record_signal): result = client.post('/', data=post_data_non_ascii) assert result.status_code == 302 # Check that default values are added user = User(ipa_admin.stageuser_show("dummy")['result']) assert user.gecos == "Xi Jin Ping aeoeue ss AeOeUe Ss An Bei Jin San"
def user(ipa, username): user = User(user_or_404(ipa, username)) # As a speed optimization, we make two separate calls. # Just doing a group_find (with all=True) is super slow here, with a lot of # groups. batch_methods = [{ "method": "group_show", "params": [[name], { "no_members": True }] } for name in user.groups] # Don't call remote batch method with an empty list if batch_methods: member_groups = [ Group(g["result"]) for g in ipa.batch(batch_methods)["results"] if g["result"].get("fasgroup", False) ] else: member_groups = [] managed_groups = [ Group(group) for group in ipa.group_find(o_membermanager_user=username, o_all=False, fasgroup=True)['result'] ] groups = sorted(list(set(managed_groups + member_groups)), key=lambda g: g.name) # Privacy setting if user != g.current_user and user.is_private: user.anonymize() return render_template( 'user.html', user=user, groups=groups, managed_groups=managed_groups, member_groups=member_groups, )
def user_settings_otp(ipa, username): addotpform = UserSettingsAddOTPForm() user = User(user_or_404(ipa, username)) if addotpform.validate_on_submit(): try: maybe_ipa_login(current_app, session, username, addotpform.password.data) result = ipa.otptoken_add( o_ipatokenowner=username, o_ipatokenotpalgorithm='sha512', o_description=addotpform.description.data, )['result'] uri = urlparse(result['uri']) # Use the provided description in the token, so it shows up in the user's app instead of # the token's UUID principal = uri.path.split(":", 1)[0] new_uri = uri._replace( path=f"{principal.lower()}:{quote(addotpform.description.data)}" ) session['otp_uri'] = new_uri.geturl() except python_freeipa.exceptions.InvalidSessionPassword: addotpform.password.errors.append(_("Incorrect password")) except python_freeipa.exceptions.FreeIPAError as e: current_app.logger.error( f'An error happened while creating an OTP token for user {username}: {e.message}' ) addotpform.non_field_errors.errors.append( _('Cannot create the token.')) else: return redirect(url_for('.user_settings_otp', username=username)) otp_uri = session.get('otp_uri') session['otp_uri'] = None tokens = [ OTPToken(t) for t in ipa.otptoken_find(o_ipatokenowner=username)["result"] ] tokens.sort(key=lambda t: t.description or "") return render_template( 'user-settings-otp.html', addotpform=addotpform, user=user, activetab="otp", tokens=tokens, otp_uri=otp_uri, )
def dummy_stageuser(ipa_testing_config): now = datetime.datetime.utcnow().replace(microsecond=0) user = ipa_admin.stageuser_add( "dummy", "Dummy", 'User', mail="*****@*****.**", login_shell='/bin/bash', fascreationtime=f"{now.isoformat()}Z", ) yield User(user) try: ipa_admin.stageuser_del("dummy") except python_freeipa.exceptions.NotFound: pass
def handle_register_form(form): username = form.username.data now = datetime.datetime.utcnow().replace(microsecond=0) common_name = form.firstname.data + " " + form.lastname.data gecos = (unidecode(codecs.encode(common_name, "translit/long")).replace(" ", " ").strip()) # First, create the stage user. try: user = ipa_admin.stageuser_add( a_uid=username, o_givenname=form.firstname.data, o_sn=form.lastname.data, o_cn=common_name, o_mail=form.mail.data, o_loginshell='/bin/bash', o_gecos=gecos, fascreationtime=f"{now.isoformat()}Z", faslocale=guess_locale(), fastimezone=current_app.config["USER_DEFAULTS"]["timezone"], fasstatusnote=current_app.config["USER_DEFAULTS"]["status_note"], )['result'] user = User(user) except python_freeipa.exceptions.DuplicateEntry: raise FormError( "username", _("This username is already taken, please choose another one.")) except python_freeipa.exceptions.ValidationError as e: # for example: invalid username. We don't know which field to link it to _handle_registration_validation_error(username, e) except python_freeipa.exceptions.FreeIPAError as e: current_app.logger.error( f'An unhandled error {e.__class__.__name__} happened while registering user ' f'{username}: {e.message}') raise FormError( "non_field_errors", _('An error occurred while creating the account, please try again.' ), ) stageuser_created.send(user, request=request._get_current_object()) if current_app.config["BASSET_URL"]: return redirect(f"{url_for('.spamcheck_wait')}?username={username}") else: # Send the address validation email _send_validation_email(user) return redirect( f"{url_for('.confirm_registration')}?username={username}")
def test_signal_basset_failed(client, mocker, dummy_user): mocked_requests = mocker.patch("noggin.signals.requests") failure = requests.Response() failure.status_code = 500 failure.reason = "Server Error" failure.raw = BytesIO(b"nope.") mocked_requests.post.return_value = failure mocker.patch.dict(current_app.config, {"BASSET_URL": "http://basset.test"}) logger = mocker.patch.object(current_app._get_current_object(), "logger") user = User(ipa_admin.user_show("dummy")) with current_app.test_request_context('/'): request_basset_check(user) mocked_requests.post.assert_called() logger.warning.assert_called_with( "Error requesting a Basset check: 500 Server Error: nope." )
def dummy_stageuser(ipa_testing_config): now = datetime.datetime.utcnow().replace(microsecond=0) user = ipa_admin.stageuser_add( a_uid="dummy", o_givenname="Dummy", o_sn="User", o_cn="Dummy User", o_mail="*****@*****.**", o_loginshell='/bin/bash', fascreationtime=f"{now.isoformat()}Z", )['result'] yield User(user) try: ipa_admin.stageuser_del(a_uid="dummy") except python_freeipa.exceptions.NotFound: pass
def user(ipa, username): user = User(user_or_404(ipa, username)) # As a speed optimization, we make two separate calls. # Just doing a group_find (with all=True) is super slow here, with a lot of # groups. groups = [ Group(g) for g in ipa.group_find( user=username, all=False, fasgroup=True)['result'] ] managed_groups = [ Group(g) for g in ipa.group_find( membermanager_user=username, all=False, fasgroup=True)['result'] ] return render_template('user.html', user=user, groups=groups, managed_groups=managed_groups)
def spamcheck_wait(): username = request.args.get('username') if not username: abort(400, "No username provided") try: user = User(ipa_admin.stageuser_show(a_uid=username)["result"]) except python_freeipa.exceptions.NotFound: flash(_("The registration seems to have failed, please try again."), "warning") return redirect(f"{url_for('.root')}?tab=register") if user.status_note == "active": return redirect( f"{url_for('.confirm_registration')}?username={username}") return render_template('registration-spamcheck-wait.html', user=user)
def search_json(ipa): username = request.args.get('username') groupname = request.args.get('group') res = [] if username: users_ = [User(u) for u in ipa.user_find(username)['result']] for user_ in users_: res.append({'uid': user_.username, 'cn': user_.name}) if groupname: groups_ = [Group(g) for g in ipa.group_find(groupname)['result']] for group_ in groups_: res.append({'cn': group_.name, 'description': group_.description}) return jsonify(res)
def user_settings_agreements(ipa, username): user = User(user_or_404(ipa, username)) agreements = [ Agreement(a) for a in ipa.fasagreement_find(all=False, ipaenabledflag=True) ] form = UserSettingsAgreementSign() if form.validate_on_submit(): agreement_name = form.agreement.data if agreement_name not in [a.name for a in agreements]: flash(_("Unknown agreement: %(name)s.", name=agreement_name), "warning") return redirect( url_for('.user_settings_agreements', username=username)) try: ipa.fasagreement_add_user(agreement_name, user=user.username) except python_freeipa.exceptions.BadRequest as e: current_app.logger.error( f"Cannot sign the agreement {agreement_name!r}: {e}") flash( _( 'Cannot sign the agreement "%(name)s": %(error)s', name=agreement_name, error=e, ), 'danger', ) else: flash( _('You signed the "%(name)s" agreement.', name=agreement_name), "success", ) return redirect(url_for('.user_settings_agreements', username=username)) return render_template( 'user-settings-agreements.html', user=user, activetab="agreements", agreementslist=agreements, raw=ipa.fasagreement_find(all=True), )
def test_user(dummy_user_dict): """Test the User representation""" user = User(dummy_user_dict) assert user.username == "dummy" assert user.firstname == "Dummy" assert user.lastname == "User" assert user.name == "Dummy User" assert user.mail == "*****@*****.**" assert user.sshpubkeys == [ 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCtX/SK86GrOa0xUadeZVbDXCj6wseamJQTpvjzNdKLgIBuQnA2dnR+jBS54rxUzHD1In/yI9r1VXr+KVZG4ULHmSuP3Icl0SUiVs+u+qeHP77Fa9rnQaxxCFL7uZgDSGSgMx0XtiQUrcumlD/9mrahCefU0BIKfS6e9chWwJnDnPSpyWf0y0NpaGYqPaV6Ukg2Z5tBvei6ghBb0e9Tusg9dHGvpv2B23dCzps6s5WBYY2TqjTHAEuRe6xR0agtPUE1AZ/DvSBKgwEz6RXIFOtv/fnZ0tERh238+n2nohMZNo1QAtQ6I0U9Kx2gdAgHRaMN6GzmbThji/MLgKlIJPSh', # noqa: E501 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDuxGxBwWH5xMLAuIUAVU3O8ZViYWW64V3tJRob+eZngeR95PzUDeH0UlZ58bPyucpMowZNgJucsHyUjqal5bctv9Q5r224Of1R3DJqIViE16W3zncGNjbgiuc66wcO2o84HEm2Zi+v4cwU8ykM0m9zeG0257aVW4/L/fDAyR55NRJ7zLIyRmGMcjkN6j02wbGK89xXJKHMtRKa5Kg4GJx3HUae79C3B7SyoRAuyzLT6GmpMZ3XRa/khZ3t4xfUtSMV6DuvR5KJ9Wg5B20ecua1tNXOLHC3dU5L+P6Pb7+HL1sxHiYbaiBPJbosMkM2wqd3VyduQDQTO4BJyly/ruIN', # noqa: E501 ] assert user.timezone == "UTC" assert user.locale == "en-US" assert user.ircnick == ["dummy", "dummy_"] assert user.gpgkeys == ["key1", "key2"] assert user.groups == ["ipausers"] assert user.github == "dummy" assert user.gitlab == "dummy" assert user.rhbz_mail == "*****@*****.**"