def pwd_expired_change(self, **kw): require_authenticated() return_to = kw.get('return_to') kw = F.password_change_form.to_python(kw, None) ap = plugin.AuthenticationProvider.get(request) try: expired_username = session.get('expired-username') expired_user = M.User.query.get( username=expired_username) if expired_username else None ap.set_password(expired_user or c.user, kw['oldpw'], kw['pw']) expired_user.set_tool_data('allura', pwd_reset_preserve_session=session.id) expired_user.set_tool_data( 'AuthPasswordReset', hash='', hash_expiry='') # Clear password reset token except wexc.HTTPUnauthorized: flash('Incorrect password', 'error') redirect(tg.url('/auth/pwd_expired', dict(return_to=return_to))) flash('Password changed') session.pop('pwd-expired', None) session['username'] = session.get('expired-username') session.pop('expired-username', None) session.save() h.auditlog_user('Password reset (via expiration process)') if return_to and return_to != request.url: redirect(return_to) else: redirect('/')
def pwd_expired_change(self, **kw): require_authenticated() return_to = kw.get("return_to") kw = F.password_change_form.to_python(kw, None) ap = plugin.AuthenticationProvider.get(request) try: expired_username = session.get("expired-username") expired_user = M.User.query.get(username=expired_username) if expired_username else None ap.set_password(expired_user or c.user, kw["oldpw"], kw["pw"]) expired_user.set_tool_data("allura", pwd_reset_preserve_session=session.id) expired_user.set_tool_data("AuthPasswordReset", hash="", hash_expiry="") # Clear password reset token except wexc.HTTPUnauthorized: flash("Incorrect password", "error") redirect(tg.url("/auth/pwd_expired", dict(return_to=return_to))) flash("Password changed") session.pop("pwd-expired", None) session["username"] = session.get("expired-username") session.pop("expired-username", None) session.save() h.auditlog_user("Password reset (via expiration process)") if return_to and return_to != request.url: redirect(return_to) else: redirect("/")
def pwd_expired_change(self, **kw): require_authenticated() return_to = kw.get('return_to') kw = F.password_change_form.to_python(kw, None) ap = plugin.AuthenticationProvider.get(request) try: expired_username = session.get('expired-username') expired_user = M.User.query.get(username=expired_username) if expired_username else None ap.set_password(expired_user or c.user, kw['oldpw'], kw['pw']) expired_user.set_tool_data('allura', pwd_reset_preserve_session=session.id) expired_user.set_tool_data('AuthPasswordReset', hash='', hash_expiry='') # Clear password reset token except wexc.HTTPUnauthorized: flash('Incorrect password', 'error') redirect(tg.url('/auth/pwd_expired', dict(return_to=return_to))) flash('Password changed') session.pop('pwd-expired', None) session['username'] = session.get('expired-username') session.pop('expired-username', None) session.save() h.auditlog_user('Password reset (via expiration process)') if return_to and return_to != request.url: redirect(return_to) else: redirect('/')
def make_password_reset_url(self, username): user = M.User.by_username(username) if not user or user.is_anonymous(): raise HTTPNotFound() h.auditlog_user( 'Generated new password reset URL and shown to admin user', user=user) return user.make_password_reset_url()
def password_recovery_hash(self, email=None, **kw): provider = plugin.AuthenticationProvider.get(request) if not provider.forgotten_password_process: raise wexc.HTTPNotFound() if not email: redirect('/') user_record = M.User.by_email_address(email) allow_non_primary_email_reset = asbool( config.get('auth.allow_non_primary_email_password_reset', True)) if not re.match(r"[^@]+@[^@]+\.[^@]+", email): flash('Enter email in correct format!', 'error') redirect('/auth/forgotten_password') if not allow_non_primary_email_reset: message = 'If the given email address is on record, '\ 'a password reset email has been sent to the account\'s primary email address.' email_record = M.EmailAddress.get( email=provider.get_primary_email_address( user_record=user_record), confirmed=True) else: message = 'A password reset email has been sent, if the given email address is on record in our system.' email_record = M.EmailAddress.get(email=email, confirmed=True) if user_record and email_record and email_record.confirmed: hash = h.nonce(42) user_record.set_tool_data( 'AuthPasswordReset', hash=hash, hash_expiry=datetime.datetime.utcnow() + datetime.timedelta(seconds=int( config.get('auth.recovery_hash_expiry_period', 600)))) log.info('Sending password recovery link to %s', email_record.email) subject = '%s Password recovery' % config['site_name'] text = g.jinja2_env.get_template( 'allura:templates/mail/forgot_password.txt').render( dict( user=user_record, config=config, hash=hash, )) allura.tasks.mail_tasks.sendsimplemail.post( toaddr=email_record.email, fromaddr=config['forgemail.return_path'], reply_to=config['forgemail.return_path'], subject=subject, message_id=h.gen_message_id(), text=text) h.auditlog_user('Password recovery link sent to: %s', email, user=user_record) flash(message) redirect('/')
def set_random_password(self, username=None): user = M.User.by_username(username) if not user or user.is_anonymous(): raise HTTPNotFound() pwd = h.random_password() AuthenticationProvider.get(request).set_password(user, None, pwd) h.auditlog_user('Set random password', user=user) flash('Password is set', 'ok') redirect(request.referer)
def set_new_password(self, hash=None, pw=None, pw2=None): provider = plugin.AuthenticationProvider.get(request) if not provider.forgotten_password_process: raise wexc.HTTPNotFound() user = self._validate_hash(hash) user.set_password(pw) user.set_tool_data('AuthPasswordReset', hash='', hash_expiry='') # Clear password reset token h.auditlog_user('Password changed (through recovery process)', user=user) flash('Password changed') redirect('/auth/?return_to=/') # otherwise the default return_to would be the forgotten_password referrer page
def password_recovery_hash(self, email=None, **kw): provider = plugin.AuthenticationProvider.get(request) if not provider.forgotten_password_process: raise wexc.HTTPNotFound() if not email: redirect("/") user_record = M.User.by_email_address(email) allow_non_primary_email_reset = asbool(config.get("auth.allow_non_primary_email_password_reset", True)) if not re.match(r"[^@]+@[^@]+\.[^@]+", email): flash("Enter email in correct format!", "error") redirect("/auth/forgotten_password") if not allow_non_primary_email_reset: message = ( "If the given email address is on record, " "a password reset email has been sent to the account's primary email address." ) email_record = M.EmailAddress.get( email=provider.get_primary_email_address(user_record=user_record), confirmed=True ) else: message = "A password reset email has been sent, if the given email address is on record in our system." email_record = M.EmailAddress.get(email=email, confirmed=True) if user_record and email_record and email_record.confirmed: hash = h.nonce(42) user_record.set_tool_data( "AuthPasswordReset", hash=hash, hash_expiry=datetime.datetime.utcnow() + datetime.timedelta(seconds=int(config.get("auth.recovery_hash_expiry_period", 600))), ) log.info("Sending password recovery link to %s", email_record.email) subject = "%s Password recovery" % config["site_name"] text = g.jinja2_env.get_template("allura:templates/mail/forgot_password.txt").render( dict(user=user_record, config=config, hash=hash) ) allura.tasks.mail_tasks.sendsimplemail.post( toaddr=email_record.email, fromaddr=config["forgemail.return_path"], reply_to=config["forgemail.return_path"], subject=subject, message_id=h.gen_message_id(), text=text, ) h.auditlog_user("Password recovery link sent to: %s", email, user=user_record) flash(message) redirect("/")
def change_password(self, **kw): kw = F.password_change_form.to_python(kw, None) ap = plugin.AuthenticationProvider.get(request) try: ap.set_password(c.user, kw["oldpw"], kw["pw"]) c.user.set_tool_data("allura", pwd_reset_preserve_session=session.id) c.user.set_tool_data("AuthPasswordReset", hash="", hash_expiry="") except wexc.HTTPUnauthorized: flash("Incorrect password", "error") redirect(".") flash("Password changed") h.auditlog_user("Password changed") redirect(".")
def update(self, preferences=None, **kw): if asbool(config.get("auth.allow_edit_prefs", True)): if not preferences.get("display_name"): flash("Display Name cannot be empty.", "error") redirect(".") old = c.user.get_pref("display_name") c.user.set_pref("display_name", preferences["display_name"]) if old != preferences["display_name"]: h.auditlog_user("Display Name changed %s => %s", old, preferences["display_name"]) for k, v in preferences.iteritems(): if k == "results_per_page": v = int(v) c.user.set_pref(k, v) redirect(".")
def test_audit_log(self, req1, req2): req1.url = req2.url = 'http://host.domain/path/' c.user = M.User.by_username('test-user-1') h.auditlog_user('test activity user 1') h.auditlog_user('test activity user 2', user=M.User.by_username('test-user-2')) r = self.app.get('/nf/admin/user/test-admin') assert_in('Add comment', r) assert_not_in('test activity', r) r = self.app.get('/nf/admin/user/test-user-1') assert_in('test activity user 1', r) assert_not_in('test activity user 2', r) r = self.app.get('/nf/admin/user/test-user-2') assert_not_in('test activity user 1', r) assert_in('test activity user 2', r)
def set_new_password(self, hash=None, pw=None, pw2=None): provider = plugin.AuthenticationProvider.get(request) if not provider.forgotten_password_process: raise wexc.HTTPNotFound() user = self._validate_hash(hash) user.set_password(pw) user.set_tool_data('AuthPasswordReset', hash='', hash_expiry='') # Clear password reset token h.auditlog_user('Password changed (through recovery process)', user=user) flash('Password changed') redirect( '/auth/?return_to=/' ) # otherwise the default return_to would be the forgotten_password referrer page
def update(self, preferences=None, **kw): if asbool(config.get('auth.allow_edit_prefs', True)): if not preferences.get('display_name'): flash("Display Name cannot be empty.", 'error') redirect('.') old = c.user.get_pref('display_name') c.user.set_pref('display_name', preferences['display_name']) if old != preferences['display_name']: h.auditlog_user('Display Name changed %s => %s', old, preferences['display_name']) for k, v in preferences.iteritems(): if k == 'results_per_page': v = int(v) c.user.set_pref(k, v) redirect('.')
def change_password(self, **kw): kw = F.password_change_form.to_python(kw, None) ap = plugin.AuthenticationProvider.get(request) try: ap.set_password(c.user, kw['oldpw'], kw['pw']) c.user.set_tool_data('allura', pwd_reset_preserve_session=session.id) c.user.set_tool_data('AuthPasswordReset', hash='', hash_expiry='') except wexc.HTTPUnauthorized: flash('Incorrect password', 'error') redirect('.') flash('Password changed') h.auditlog_user('Password changed') redirect('.')
def password_recovery_hash(self, email=None, **kw): provider = plugin.AuthenticationProvider.get(request) if not provider.forgotten_password_process: raise wexc.HTTPNotFound() if not email: redirect('/') user_record = M.User.by_email_address(email) allow_non_primary_email_reset = asbool(config.get('auth.allow_non_primary_email_password_reset', True)) if not re.match(r"[^@]+@[^@]+\.[^@]+", email): flash('Enter email in correct format!','error') redirect('/auth/forgotten_password') if not allow_non_primary_email_reset: message = 'If the given email address is on record, '\ 'a password reset email has been sent to the account\'s primary email address.' email_record = M.EmailAddress.get(email=provider.get_primary_email_address(user_record=user_record), confirmed=True) else: message = 'A password reset email has been sent, if the given email address is on record in our system.' email_record = M.EmailAddress.get(email=email, confirmed=True) if user_record and email_record and email_record.confirmed: hash = h.nonce(42) user_record.set_tool_data('AuthPasswordReset', hash=hash, hash_expiry=datetime.datetime.utcnow() + datetime.timedelta(seconds=int(config.get('auth.recovery_hash_expiry_period', 600)))) log.info('Sending password recovery link to %s', email_record.email) subject = '%s Password recovery' % config['site_name'] text = g.jinja2_env.get_template('allura:templates/mail/forgot_password.txt').render(dict( user=user_record, config=config, hash=hash, )) allura.tasks.mail_tasks.sendsimplemail.post( toaddr=email_record.email, fromaddr=config['forgemail.return_path'], reply_to=config['forgemail.return_path'], subject=subject, message_id=h.gen_message_id(), text=text) h.auditlog_user('Password recovery link sent to: %s', email, user=user_record) flash(message) redirect('/')
def _verify_addr(self, addr): confirmed_by_other = M.EmailAddress.find( dict(email=addr.email, confirmed=True)).all() if addr else [] confirmed_by_other = filter(lambda item: item != addr, confirmed_by_other) if addr and not confirmed_by_other: addr.confirmed = True user = addr.claimed_by_user(include_pending=True) flash('Email address confirmed') h.auditlog_user('Email address verified: %s', addr.email, user=user) if user.pending: plugin.AuthenticationProvider.get(request).activate_user(user) else: flash('Unknown verification link', 'error')
def _verify_addr(self, addr): confirmed_by_other = M.EmailAddress.find(dict(email=addr.email, confirmed=True)).all() if addr else [] confirmed_by_other = filter(lambda item: item != addr, confirmed_by_other) if addr and not confirmed_by_other: addr.confirmed = True user = addr.claimed_by_user(include_pending=True) flash('Email address confirmed') h.auditlog_user('Email address verified: %s', addr.email, user=user) if(user.get_pref('email_address') == None): user.set_pref('email_address', addr.email) if user.pending: plugin.AuthenticationProvider.get(request).activate_user(user) projectname = plugin.AuthenticationProvider.get(request).user_project_shortname(user) n = M.Neighborhood.query.get(name='Users') n.register_project(projectname, user=user, user_project=True) else: flash('Unknown verification link', 'error')
def _verify_addr(self, addr): confirmed_by_other = M.EmailAddress.find( dict(email=addr.email, confirmed=True)).all() if addr else [] confirmed_by_other = filter(lambda item: item != addr, confirmed_by_other) if addr and not confirmed_by_other: addr.confirmed = True user = addr.claimed_by_user(include_pending=True) flash('Email address confirmed') h.auditlog_user('Email address verified: %s', addr.email, user=user) if (user.get_pref('email_address') == None): user.set_pref('email_address', addr.email) if user.pending: plugin.AuthenticationProvider.get(request).activate_user(user) projectname = plugin.AuthenticationProvider.get( request).user_project_shortname(user) n = M.Neighborhood.query.get(name='Users') n.register_project(projectname, user=user, user_project=True) else: flash('Unknown verification link', 'error')
def test_message_html(self): al = h.auditlog_user('our message <script>alert(1)</script>') assert_equal( al.message, textwrap.dedent('''\ IP Address: 127.0.0.1 User-Agent: None our message <script>alert(1)</script>''')) assert_equal( al.message_html, textwrap.dedent('''\ IP Address: 127.0.0.1<br> User-Agent: None<br> <strong>our message <script>alert(1)</script></strong>''' ))
def disable_users(cls, usernames, message): auth_provider = AuthenticationProvider.get(request=None) # would be nice to use the BatchIndexer extension around this but that only works for artifacts not users for username in usernames: user = M.User.query.get(username=username) if not user: log.info('Could not find user: %s', username) elif user.disabled: log.info('User is already disabled: %s', username) session(user).expunge(user) else: log.info('Disabling user: %s', username) auth_provider.disable_user(user) session(user).flush(user) if message: log_entry = h.auditlog_user(message, user=user) session(log_entry).flush(log_entry)
def execute(cls, options): provider = ProjectRegistrationProvider.get() auth_provider = AuthenticationProvider.get(Request.blank('/')) for proj in options.projects: proj = cls.get_project(proj) if proj: if proj.is_user_project: # disable user as well user = proj.user_project_of if user: auth_provider.disable_user(user, audit=False) msg = u'Account disabled because user-project was specified for deletion. Reason: {}'.format( options.reason) log_entry = h.auditlog_user(msg, user=user) session(log_entry).flush(log_entry) else: log.info('Could not find associated user for user-project %s', proj.shortname) log.info('Purging %s Reason: %s', proj.url(), options.reason) provider.purge_project(proj, disable_users=options.disable_users, reason=options.reason)
def execute(cls, options): provider = ProjectRegistrationProvider.get() auth_provider = AuthenticationProvider.get(Request.blank('/')) for proj in options.projects: proj = cls.get_project(proj) if proj: if proj.is_user_project: # disable user as well user = proj.user_project_of if user: auth_provider.disable_user(user, audit=False) msg = u'Account disabled because user-project was specified for deletion. Reason: {}'.format( options.reason) log_entry = h.auditlog_user(msg, user=user) session(log_entry).flush(log_entry) else: log.info( 'Could not find associated user for user-project %s', proj.shortname) log.info('Purging %s Reason: %s', proj.url(), options.reason) provider.purge_project(proj, disable_users=options.disable_users, reason=options.reason)
def _update_emails(self, user, admin=False, form_params={}): # not using **kw in method signature, to ensure 'admin' can't be passed in via a form submit kw = form_params addr = kw.pop('addr', None) new_addr = kw.pop('new_addr', None) primary_addr = kw.pop('primary_addr', None) provider = plugin.AuthenticationProvider.get(request) for i, (old_a, data) in enumerate(zip(user.email_addresses, addr or [])): obj = user.address_object(old_a) if data.get('delete') or not obj: if not admin and (not kw.get('password') or not provider.validate_password( user, kw.get('password'))): flash( 'You must provide your current password to delete an email', 'error') return if primary_addr == user.email_addresses[i]: if select_new_primary_addr(user, ignore_emails=primary_addr) is None \ and asbool(config.get('auth.require_email_addr', False)): flash( 'You must have at least one verified email address.', 'error') return else: # clear it now, a new one will get set below user.set_pref('email_address', None) primary_addr = None user.set_tool_data('AuthPasswordReset', hash='', hash_expiry='') h.auditlog_user('Email address deleted: %s', user.email_addresses[i], user=user) del user.email_addresses[i] if obj: obj.delete() if new_addr.get('claim') or new_addr.get('addr'): user.set_tool_data('AuthPasswordReset', hash='', hash_expiry='') # Clear password reset token claimed_emails_limit = config.get( 'user_prefs.maximum_claimed_emails', None) if claimed_emails_limit and len( user.email_addresses) >= int(claimed_emails_limit): flash( 'You cannot claim more than %s email addresses.' % claimed_emails_limit, 'error') return if not admin and (not kw.get('password') or not provider.validate_password( user, kw.get('password'))): flash( 'You must provide your current password to claim new email', 'error') return claimed_emails = M.EmailAddress.find({ 'email': new_addr['addr'] }).all() if any(email.claimed_by_user_id == user._id for email in claimed_emails): flash('Email address already claimed', 'error') elif mail_util.isvalid(new_addr['addr']): em = M.EmailAddress.create(new_addr['addr']) if em: user.email_addresses.append(em.email) em.claimed_by_user_id = user._id confirmed_emails = filter(lambda email: email.confirmed, claimed_emails) if not confirmed_emails: if not admin: em.send_verification_link() else: AuthController()._verify_addr(em) else: em.send_claim_attempt() if not admin: user.set_tool_data('AuthPasswordReset', hash='', hash_expiry='') flash( 'A verification email has been sent. Please check your email and click to confirm.' ) h.auditlog_user('New email address: %s', new_addr['addr'], user=user) else: flash('Email address %s is invalid' % new_addr['addr'], 'error') else: flash('Email address %s is invalid' % new_addr['addr'], 'error') if not primary_addr and not user.get_pref( 'email_address') and user.email_addresses: primary_addr = select_new_primary_addr(user) if primary_addr: if user.get_pref('email_address') != primary_addr: if not admin and (not kw.get('password') or not provider.validate_password( user, kw.get('password'))): flash( 'You must provide your current password to change primary address', 'error') return h.auditlog_user('Primary email changed: %s => %s', user.get_pref('email_address'), primary_addr, user=user) user.set_pref('email_address', primary_addr) user.set_tool_data('AuthPasswordReset', hash='', hash_expiry='')
def test_user_backfill_login_details(): with h.push_config(request, user_agent='TestBrowser/55'): # these shouldn't match h.auditlog_user('something happened') h.auditlog_user('blah blah Password changed') with h.push_config(request, user_agent='TestBrowser/56'): # these should all match, but only one entry created for this ip/ua h.auditlog_user('Account activated') h.auditlog_user('Successful login') h.auditlog_user('Password changed') with h.push_config(request, user_agent='TestBrowser/57'): # this should match too h.auditlog_user('Set up multifactor TOTP') ThreadLocalORMSession.flush_all() auth_provider = plugin.AuthenticationProvider.get(None) c.user.backfill_login_details(auth_provider) details = M.UserLoginDetails.query.find({ 'user_id': c.user._id }).sort('ua').all() assert_equal(len(details), 2, details) assert_equal(details[0].ip, '127.0.0.1') assert_equal(details[0].ua, 'TestBrowser/56') assert_equal(details[1].ip, '127.0.0.1') assert_equal(details[1].ua, 'TestBrowser/57')
def test_user_backfill_login_details(): with h.push_config(request, user_agent='TestBrowser/55'): # these shouldn't match h.auditlog_user('something happened') h.auditlog_user('blah blah Password changed') with h.push_config(request, user_agent='TestBrowser/56'): # these should all match, but only one entry created for this ip/ua h.auditlog_user('Account activated') h.auditlog_user('Successful login') h.auditlog_user('Password changed') with h.push_config(request, user_agent='TestBrowser/57'): # this should match too h.auditlog_user('Set up multifactor TOTP') ThreadLocalORMSession.flush_all() auth_provider = plugin.AuthenticationProvider.get(None) c.user.backfill_login_details(auth_provider) assert_equal(sorted(c.user.previous_login_details), [ {'ip': '127.0.0.1', 'ua': 'TestBrowser/56'}, {'ip': '127.0.0.1', 'ua': 'TestBrowser/57'}, ])
def _update_emails(self, user, admin=False, form_params={}): # not using **kw in method signature, to ensure 'admin' can't be passed in via a form submit kw = form_params addr = kw.pop('addr', None) new_addr = kw.pop('new_addr', None) primary_addr = kw.pop('primary_addr', None) provider = plugin.AuthenticationProvider.get(request) for i, (old_a, data) in enumerate(zip(user.email_addresses, addr or [])): obj = user.address_object(old_a) if data.get('delete') or not obj: if not admin and (not kw.get('password') or not provider.validate_password(user, kw.get('password'))): flash('You must provide your current password to delete an email', 'error') return if primary_addr == user.email_addresses[i]: if select_new_primary_addr(user, ignore_emails=primary_addr) is None \ and asbool(config.get('auth.require_email_addr', False)): flash('You must have at least one verified email address.', 'error') return else: # clear it now, a new one will get set below user.set_pref('email_address', None) primary_addr = None user.set_tool_data('AuthPasswordReset', hash='', hash_expiry='') h.auditlog_user('Email address deleted: %s', user.email_addresses[i], user=user) del user.email_addresses[i] if obj: obj.delete() if new_addr.get('claim') or new_addr.get('addr'): user.set_tool_data('AuthPasswordReset', hash='', hash_expiry='') # Clear password reset token claimed_emails_limit = config.get('user_prefs.maximum_claimed_emails', None) if claimed_emails_limit and len(user.email_addresses) >= int(claimed_emails_limit): flash('You cannot claim more than %s email addresses.' % claimed_emails_limit, 'error') return if not admin and (not kw.get('password') or not provider.validate_password(user, kw.get('password'))): flash('You must provide your current password to claim new email', 'error') return claimed_emails = M.EmailAddress.find({'email': new_addr['addr']}).all() if any(email.claimed_by_user_id == user._id for email in claimed_emails): flash('Email address already claimed', 'error') elif mail_util.isvalid(new_addr['addr']): em = M.EmailAddress.create(new_addr['addr']) if em: user.email_addresses.append(em.email) em.claimed_by_user_id = user._id confirmed_emails = filter(lambda email: email.confirmed, claimed_emails) if not confirmed_emails: if not admin: em.send_verification_link() else: AuthController()._verify_addr(em) else: em.send_claim_attempt() if not admin: user.set_tool_data('AuthPasswordReset', hash='', hash_expiry='') flash('A verification email has been sent. Please check your email and click to confirm.') h.auditlog_user('New email address: %s', new_addr['addr'], user=user) else: flash('Email address %s is invalid' % new_addr['addr'], 'error') else: flash('Email address %s is invalid' % new_addr['addr'], 'error') if not primary_addr and not user.get_pref('email_address') and user.email_addresses: primary_addr = select_new_primary_addr(user) if primary_addr: if user.get_pref('email_address') != primary_addr: if not admin and (not kw.get('password') or not provider.validate_password(user, kw.get('password'))): flash('You must provide your current password to change primary address', 'error') return h.auditlog_user( 'Primary email changed: %s => %s', user.get_pref('email_address'), primary_addr, user=user) user.set_pref('email_address', primary_addr) user.set_tool_data('AuthPasswordReset', hash='', hash_expiry='')