def use_account_manager_integration(self, member_name): if self.account_manager_is_enabled(): account_already_created = AccountManager( self.env).has_user(member_name) if not account_already_created: return AccountManager(self.env).supports('set_password') return False
def delete_user(self, username): try: from acct_mgr.api import AccountManager if AccountManager(self.env).has_user(username): AccountManager(self.env).delete_user(username) except Exception, e: self.log.error("Unable to delete user's authentication details")
def create_user_and_grant_permissions(self, req, team_member): if self.use_account_manager_integration(team_member.name): password = team_member.name AccountManager(self.env).set_password(team_member.name, password) permission_system = PermissionSystem(self.env) if not permission_system.check_permission(Role.TEAM_MEMBER, team_member.name): permission_system.grant_permission(team_member.name, Role.TEAM_MEMBER)
def process_request(self, req): if not req.session.authenticated: chrome.add_warning( req, Markup( tag.span( tag_( "Please log in to finish email verification procedure." )))) req.redirect(req.href.login()) if 'email_verification_token' not in req.session: chrome.add_notice(req, _("Your email is already verified.")) elif req.method == 'POST' and 'resend' in req.args: AccountManager(self.env)._notify( 'email_verification_requested', req.authname, req.session['email_verification_token']) chrome.add_notice( req, _("A notification email has been resent to <%s>."), req.session.get('email')) elif 'verify' in req.args: # allow via POST or GET (the latter for email links) if req.args['token'] == req.session['email_verification_token']: del req.session['email_verification_token'] chrome.add_notice( req, _("Thank you for verifying your email address.")) req.redirect(req.href.prefs()) else: chrome.add_warning(req, _("Invalid verification token")) data = {'_dgettext': dgettext} if 'token' in req.args: data['token'] = req.args['token'] if 'email_verification_token' not in req.session: data['button_state'] = {'disabled': 'disabled'} return 'verify_email.html', data, None
def setUp(self): self.env = EnvironmentStub(enable=[ 'trac.*', 'acct_mgr.api.*', 'acct_mgr.admin.*', 'acct_mgr.db.*', 'acct_mgr.register.*', 'acct_mgr.pwhash.HtDigestHashMethod', 'acct_mgr.tests.admin.BadCheck', 'acct_mgr.tests.admin.DummyCheck' ]) self.env.path = tempfile.mkdtemp() self.perm = PermissionSystem(self.env) # Create a user reference in the permission system. self.perm.grant_permission('admin', 'ACCTMGR_ADMIN') # Prepare a generic request object for admin actions. self.req = Mock(authname='admin', method='GET', args=dict(), abs_href=self.env.abs_href, chrome=dict(notices=[], warnings=[]), href=self.env.abs_href, locale='', redirect=lambda x: None, session=dict(), tz='') self.req.perm = PermissionCache(self.env, 'admin') self.acctmgr = AccountManager(self.env)
def post_process_request(self, req, template, data, content_type): if not req.session.authenticated: # Don't start the email verification precedure on anonymous users. return template, data, content_type email = req.session.get('email') # Only send verification if the user entered an email address. acctmgr = AccountManager(self.env) if acctmgr.verify_email and self.email_enabled is True and email and \ email != req.session.get('email_verification_sent_to') and \ not req.perm.has_permission('ACCTMGR_ADMIN'): req.session['email_verification_token'] = self._gen_token() req.session['email_verification_sent_to'] = email acctmgr._notify('email_verification_requested', req.authname, req.session['email_verification_token']) # TRANSLATOR: An email has been sent to %(email)s # with a token to ... (the link label for following message) link = tag.a(_("verify your new email address"), href=req.href.verify_email()) # TRANSLATOR: ... verify your new email address chrome.add_notice( req, Markup( tag.span( Markup( _("""An email has been sent to %(email)s with a token to %(link)s.""", email=email, link=link))))) return template, data, content_type
def process_request(self, req): if not req.session.authenticated: chrome.add_warning(req, tag_( "Please log in to finish email verification procedure.")) req.redirect(req.href.login()) if 'email_verification_token' not in req.session: chrome.add_notice(req, _("Your email is already verified.")) elif req.method == 'POST' and 'resend' in req.args: try: AccountManager(self.env)._notify( 'email_verification_requested', req.authname, req.session['email_verification_token'] ) except NotificationError, e: chrome.add_warning(req, _("Error raised while sending a " "change notification.") + _( "You should " "report that issue to a Trac admin.")) self.log.error('Unable to send verification notification: %s', exception_to_unicode(e, traceback=True)) else: chrome.add_notice(req, _("A notification email has been " "resent to <%s>."), req.session.get('email'))
def _do_create_user(self, req): """ """ if not req.args.get('um_newuser_username') or not req.args.get( 'um_newuser_username').strip(): raise TracError(_("Username field is mandatory")) is_trac_managed = req.args.get('um_newuser_type') == 'trac-managed' if is_trac_managed and not req.args.get('um_newuser_password'): raise TracError(_('Password field it\'s mandatory')) user = User(req.args.get('um_newuser_username').strip()) for field in ['name', 'email', 'role' ] + (is_trac_managed and ['password'] or []): if field == 'password': if req.args.get('um_newuser_password') == req.args.get( 'um_newuser_confirm_password'): try: from acct_mgr.api import AccountManager AccountManager(self.env).set_password( user.username, req.args.get('um_newuser_password')) except Exception, e: self.log.error(e) raise TracError( _('Unable to set %s\'s password. Please check out log messages.' % (user.username))) else: raise TracError(_('Passwords don\'t match')) continue if req.args.get('um_newuser_%s' % (field)): user[field] = req.args.get('um_newuser_%s' % (field))
def pre_process_request(self, req, handler): if not req.authname or req.authname == 'anonymous': # Permissions for anonymous users remain unchanged. return handler elif req.path_info == '/prefs' and \ req.method == 'POST' and \ 'restore' not in req.args and \ req.get_header( 'X-Requested-With') != 'XMLHttpRequest': try: AccountManager(self.env).validate_account(req) # Check passed without error: New email address seems good. except RegistrationError, e: # Always warn about issues. chrome.add_warning(req, e) # Look, if the issue existed before. attributes = get_user_attribute(self.env, req.authname, attribute='email') email = req.authname in attributes and \ attributes[req.authname][1].get('email') or None new_email = req.args.get('email', '').strip() if (email or new_email) and email != new_email: # Attempt to change email to an empty or invalid # address detected, resetting to previously stored value. req.redirect(req.href.prefs(None))
def post_process_request(self, req, template, data, content_type): if template is None or not req.session.authenticated: # Don't start the email verification procedure on anonymous users. return template, data, content_type email = req.session.get('email') # Only send verification if the user entered an email address. if self.verify_email and self.email_enabled is True and email and \ email != req.session.get('email_verification_sent_to') and \ 'ACCTMGR_ADMIN' not in req.perm: req.session['email_verification_token'] = self._gen_token() req.session['email_verification_sent_to'] = email try: AccountManager(self.env)._notify( 'email_verification_requested', req.authname, req.session['email_verification_token'] ) except NotificationError, e: chrome.add_warning(req, _( "Error raised while sending a change notification." ) + _("You should report that issue to a Trac admin.")) self.log.error('Unable to send registration notification: %s', exception_to_unicode(e, traceback=True)) else: # TRANSLATOR: An email has been sent to <%(email)s> # with a token to ... (the link label for following message) link = tag.a(_("verify your new email address"), href=req.href.verify_email()) # TRANSLATOR: ... verify your new email address chrome.add_notice(req, tag_( "An email has been sent to <%(email)s> with a token to " "%(link)s.", email=tag(email), link=link))
def _check_password(self, req): header = req.get_header('Authorization') if header: token = header.split()[1] user, passwd = b64decode(token).split(':', 1) if AccountManager(self.env).check_password(user, passwd): return user
def __init__(self): self.authz_file = self.env.config.get("trac", "authz_file") self.authz_module = self.env.config.get("trac", "authz_module_name") self.show_all_repos = self.env.config.getbool("svnauthzadmin", "show_all_repos") if self.authz_module != None and self.authz_module.strip() == "": self.authz_module = None self.account_manager = AccountManager(self.env)
def validate_registration(self, req): if req.authname and req.authname != 'anonymous': return username = AccountManager(self.env).handle_username_casing( req.args.get('username', '').strip()) # NOTE: We can't use 'get_user_permissions(username)' here # as this always returns a list - even if the user doesn't exist. # In this case the permissions of "anonymous" are returned. # # Also note that we can't simply compare the result of # 'get_user_permissions(username)' to some known set of permission, # i.e. "get_user_permissions('authenticated') as this is always # false when 'username' is the name of an existing permission group. # # And again obfuscate whether an existing user or group name # was responsible for rejection of this username. for (perm_user, perm_action) in \ perm.PermissionSystem(self.env).get_all_permissions(): if perm_user.lower() == username.lower(): raise RegistrationError(N_( "Another account or group already exists, who's name " "differs from %s only by case or is identical."), tag.b(username) )
def _do_import_current_users(self, req, dry_run=False): """ """ active_users = [ user.username for user in UserManager(self.env).get_active_users() ] try: from acct_mgr.api import AccountManager known_users = list(AccountManager(self.env).get_users()) except: return [] imported_users = [] for username in known_users: if not username in active_users: imported_users.append(username) if not dry_run: UserManager(self.env).create_user(User(username)) if dry_run: return imported_users if len(imported_users) > 0: return _("Successfully imported the following users [%s].") % ( ','.join(imported_users)) else: return _("No users imported.")
def authenticate(self, req): if req.remote_user: return req.remote_user for path in self.paths: if req.path_info.startswith(path): header = req.get_header('Authorization') if header is None: self.log.info( 'HTTPAuthFilter: No authentication data given, returing 403' ) return None # Run HTTP auth else: token = header.split()[1] user, passwd = b64decode(token).split(':', 1) if AccountManager(self.env).check_password(user, passwd): self.log.debug('HTTPAuthFilter: Authentication okay') # req.environ['REMOTE_USER'] = user # self.log.debug(req.remote_user) return user else: self.log.info( 'HTTPAuthFilter: Bad authentication data given, returing 403' ) return None # Failed auth return None
def authenticate(self, req): """Return the name of the remote user, or `None` if the identity of the user is unknown.""" # check for an authenticated user login_module = LoginModule(self.env) remote_user = login_module.authenticate(req) if remote_user: return remote_user # authenticate via a CAPTCHA if 'captchaauth' in req.args and 'captchaid' in req.args: # ensure CAPTCHA identification captcha = self.captcha(req) if captcha != req.args['captchaauth']: return # ensure sane identity name, email = self.identify(req) if name is None: return if AccountManager and name in AccountManager(self.env).get_users(): return # delete used CAPTCHA on success try: execute_non_query(self.env, "DELETE FROM captcha WHERE id=%s", req.args['captchaid']) except: pass # log the user in req.environ['REMOTE_USER'] = name login_module._do_login(req)
def process_request(self, req): if req.path_info.startswith('/login') and req.authname == 'anonymous': try: referer = self._referer(req) except AttributeError: # Fallback for Trac 0.11 compatibility. referer = req.get_header('Referer') # Steer clear of requests going nowhere or loop to self. if referer is None or \ referer.startswith(str(req.abs_href()) + '/login'): referer = req.abs_href() data = { '_dgettext': dgettext, 'login_opt_list': self.login_opt_list, 'persistent_sessions': AccountManager(self.env).persistent_sessions, 'referer': referer, 'registration_enabled': RegistrationModule(self.env).enabled, 'reset_password_enabled': AccountModule(self.env).reset_password_enabled } if req.method == 'POST': self.log.debug( "LoginModule.process_request: 'user_locked' = %s" % req.args.get('user_locked')) if not req.args.get('user_locked'): # TRANSLATOR: Intentionally obfuscated login error data['login_error'] = _("Invalid username or password") else: f_user = req.args.get('username') release_time = AccountGuard(self.env).pretty_release_time( req, f_user) if not release_time is None: data['login_error'] = _( """Account locked, please try again after %(release_time)s """, release_time=release_time) else: data['login_error'] = _("Account locked") return 'login.html', data, None else: n_plural = req.args.get('failed_logins') if n_plural > 0: add_warning( req, Markup( tag.span( tag( ngettext( "Login after %(attempts)s failed attempt", "Login after %(attempts)s failed attempts", n_plural, attempts=n_plural))))) return auth.LoginModule.process_request(self, req)
def validate_registration(self, req): if req.path_info == '/prefs': return acctmgr = AccountManager(self.env) username = acctmgr.handle_username_casing( req.args.get('username', '').strip()) if not username: raise RegistrationError(N_("Username cannot be empty.")) # Always exclude some special characters, i.e. # ':' can't be used in HtPasswdStore # '[' and ']' can't be used in SvnServePasswordStore blacklist = acctmgr.username_char_blacklist if contains_any(username, blacklist): pretty_blacklist = '' for c in blacklist: if pretty_blacklist == '': pretty_blacklist = tag(' \'', tag.b(c), '\'') else: pretty_blacklist = tag(pretty_blacklist, ', \'', tag.b(c), '\'') raise RegistrationError(N_( "The username must not contain any of these characters: %s"), tag.b(pretty_blacklist) ) # All upper-cased names are reserved for permission action names. if username.isupper(): raise RegistrationError(N_("A username with only upper-cased " "characters is not allowed.")) # Prohibit some user names, that are important for Trac and therefor # reserved, even if not in the permission store for some reason. if username.lower() in ['anonymous', 'authenticated']: raise RegistrationError(N_("Username %s is not allowed."), tag.b(username)) # NOTE: A user may exist in a password store but not in the permission # store. I.e. this happens, when the user (from the password store) # never logged in into Trac. So we have to perform this test here # and cannot just check for the user being in the permission store. # And better obfuscate whether an existing user or group name # was responsible for rejection of this user name. for store_user in acctmgr.get_users(): # Do it carefully by disregarding case. if store_user.lower() == username.lower(): raise RegistrationError(tag_( "Another account or group already exists, who's name " "differs from %(username)s only by case or is identical.", username=tag.b(username))) # Password consistency checks follow. password = req.args.get('password') if not password: raise RegistrationError(N_("Password cannot be empty.")) elif password != req.args.get('password_confirm'): raise RegistrationError(N_("The passwords must match."))
class EmailVerificationModule(CommonTemplateProvider): """Performs email verification on every new or changed address. A working email sender for Trac (!TracNotification or !TracAnnouncer) is strictly required to enable this module's functionality. Anonymous users should register and perms should be tweaked, so that anonymous users can't edit wiki pages and change or create tickets. So this email verification code won't be used on them. """ implements(IRequestFilter, IRequestHandler) def __init__(self, *args, **kwargs): self.email_enabled = True if self.config.getbool('announcer', 'email_enabled') != True and \ self.config.getbool('notification', 'smtp_enabled') != True: self.email_enabled = False if is_enabled(self.env, self.__class__) == True: self.env.log.warn(' '.join([ self.__class__.__name__, "can't work because of missing email setup." ])) # IRequestFilter methods def pre_process_request(self, req, handler): if not req.session.authenticated: # Permissions for anonymous users remain unchanged. return handler elif req.path_info == '/prefs' and req.method == 'POST' and \ not 'restore' in req.args: try: EmailCheck(self.env).validate_registration(req) # Check passed without error: New email address seems good. except RegistrationError, e: # Attempt to change email to an empty or invalid # address detected, resetting to previously stored value. chrome.add_warning(req, Markup(gettext(e.message))) req.redirect(req.href.prefs(None)) if AccountManager(self.env).verify_email and handler is not self and \ 'email_verification_token' in req.session and \ not req.perm.has_permission('ACCTMGR_ADMIN'): # TRANSLATOR: Your permissions have been limited until you ... link = tag.a(_("verify your email address"), href=req.href.verify_email()) # TRANSLATOR: ... verify your email address chrome.add_warning( req, Markup( tag.span( Markup( _("Your permissions have been limited until you %(link)s.", link=link))))) req.perm = perm.PermissionCache(self.env, 'anonymous') return handler
def _set_password(env, req, username, password, old_password=None): try: AccountManager(env).set_password(username, password, old_password=old_password) except NotificationError, e: add_warning(req, _("Error raised while sending a change " "notification.") + _("You should report that issue to a Trac admin.")) env.log.error('Unable to send password change notification: %s', exception_to_unicode(e, traceback=True))
def __init__(self): self.mgr = AccountManager(self.env) # Adjust related value to promote sane configurations, because the # combination of some default values is not meaningful. if not self.login_attempt_max_count > 0 and \ self.user_lock_max_time != 0: self.config.set('account-manager', 'user_lock_max_time', '0') # Write change back to file to make it permanent. self.config.save()
def setUp(self): _BaseTestCase.setUp(self) self.env = EnvironmentStub( enable=['trac.*', 'acct_mgr.api.*', 'acct_mgr.register.*']) self.env.path = tempfile.mkdtemp() self.reg_template = 'register.html' self.req.method = 'POST' self.acctmgr = AccountManager(self.env) self.check = BasicCheck(self.env) self.rmod = RegistrationModule(self.env)
def process_request(self, req): req.perm.assert_permission('TRAC_ADMIN') if not re.match(r'/autocompleteperms/autocompleteperms\.js$', req.path_info): return subjects = set([]) db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute("SELECT username,action FROM permission") rows = cursor.fetchall() while True: num_users = len(subjects) for user, action in rows: if user not in subjects: subjects.add(user) for provider in self.group_providers: subjects.update(provider.get_permission_groups(action)) if not action.isupper() and action not in subjects: subjects.add(action) for provider in self.group_providers: subjects.update(provider.get_permission_groups(action)) if num_users == len(subjects): break try: from acct_mgr.api import AccountManager acc_mgr = AccountManager(self.env) users = acc_mgr.get_users() except: users = [x[0] for x in self.env.get_known_users()] group_list = list(subjects - set(users) - set('#%s' % user for user in users)) subjects.update(users) user_list = list(subjects) user_list.sort() group_list.sort() out = """ var data = "%s".split(" "); var data_groups = "%s".split(" "); $(document).ready(function() { $("#gp_subject").autocomplete(data, {minChars: 0, max:9999}); $("#sg_subject").autocomplete(data, {minChars: 0, max:9999}); $("#sg_group").autocomplete(data_groups, {minChars: 0, max:9999}); }); """ % (" ".join(user_list), " ".join(group_list)) req.send(out.encode("utf-8"), "text/javascript")
def render_usermanager_admin_panel(self, req, panel, user, path_info): data = { 'TYPES': ['trac-managed', 'server-managed'], 'set_password_enabled': AccountManager(self.env).supports('set_password'), 'delete_enabled': AccountManager(self.env).supports('delete_user') } messages = [] errors = [] if req.method == 'POST': if req.args.has_key('um_account_update_type'): if req.args.get('um_account_type' ) == 'trac-managed' and not AccountManager( self.env).has_user(user.username): AccountManager(self.env).set_password( user.username, ''.join([ Random().choice('pleaseChangeThisPassword') for x in range(10) ])) messages.append( _("Successfully changed %s's authentication method") % (user.username)) elif req.args.get('um_account_type') == 'server-managed': AccountManager(self.env).delete_user(user.username) messages.append( _("Successfully changed %s's authentication method") % (user.username)) else: raise TracError("Unknow account type") elif req.args.has_key('um_account_change_password'): if req.args['um_account_confirm_password'] == req.args[ 'um_account_new_password']: AccountManager(self.env).set_password( user.username, req.args['um_account_new_password']) messages.append( _("Successfully changed %s's password") % (user.username)) else: errors.append(_('Passwords don\'t match')) else: raise TracError("Unknow action") # Adding type data.update(type=AccountManager(self.env).has_user(user.username) and 'trac-managed' or 'server-managed') return 'admin_um_account.html', { 'um_account': data, 'messages': messages, 'errors': errors }
def validate_registration(self, req): acctmgr = AccountManager(self.env) email = req.args.get('email', '').strip() if is_enabled(self.env, EmailVerificationModule) and \ acctmgr.verify_email: if not email: raise RegistrationError( N_("You must specify a valid email address.")) elif email_associated(self.env, email): raise RegistrationError( N_("The email address specified is already in use. " "Please specify a different one."))
def setUp(self): _BaseTestCase.setUp(self) self.mgr = AccountManager(self.env) self.store = SessionStore(self.env) self.store.set_password('user', 'passwd') args = dict(username='******', name='', email='') incookie = Cookie() incookie['trac_session'] = '123456' self.req = Mock(authname='', args=args, authenticated=True, base_path='/', callbacks=dict(), href=Mock(prefs=lambda x: None), incookie=incookie, outcookie=Cookie(), redirect=lambda x: None) self.req.path_info = '/'
def setUp(self): _BaseTestCase.setUp(self) self.env = EnvironmentStub(enable=[ 'trac.*', 'acct_mgr.api.*', 'acct_mgr.db.*', 'acct_mgr.register.*', 'acct_mgr.pwhash.HtDigestHashMethod' ]) self.env.path = tempfile.mkdtemp() self.reg_template = 'register.html' self.req.method = 'POST' self.env.config.set('account-manager', 'password_store', 'SessionStore') self.acctmgr = AccountManager(self.env) self.check = BasicCheck(self.env) self.rmod = RegistrationModule(self.env) self.store = SessionStore(self.env)
def validate_registration(self, req): acctmgr = AccountManager(self.env) email = req.args.get('email', '').strip() if is_enabled(self.env, EmailVerificationModule) and \ EmailVerificationModule(self.env).verify_email: # Initial configuration case. if not email and not req.args.get('active'): raise RegistrationError( N_("You must specify a valid email address.")) # User preferences case. elif req.path_info == '/prefs' and email == req.session.get( 'email'): return elif email_associated(self.env, email): raise RegistrationError( N_("The email address specified is already in use. " "Please specify a different one."))
def _remote_user(self, req): """The real authentication using configured providers and stores.""" user = req.args.get('user') self.env.log.debug( "LoginModule._remote_user: Authentication attempted for '%s'" % user) password = req.args.get('password') if not user: return None acctmgr = AccountManager(self.env) acctmod = AccountModule(self.env) if acctmod.reset_password_enabled == True: reset_store = acctmod.store else: reset_store = None if acctmgr.check_password(user, password) == True: if reset_store: # Purge any temporary password set for this user before, # to avoid DOS by continuously triggered resets from # a malicious third party. if reset_store.delete_user(user) == True and \ 'PASSWORD_RESET' not in req.environ: db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute( """ DELETE FROM session_attribute WHERE sid=%s AND name='force_change_passwd' AND authenticated=1 """, (user, )) db.commit() return user # Alternative authentication provided by password reset procedure elif reset_store: if reset_store.check_password(user, password) == True: # Lock, required to prevent another authentication # (spawned by `set_password()`) from possibly deleting # a 'force_change_passwd' db entry for this user. req.environ['PASSWORD_RESET'] = user # Change password to temporary password from reset procedure acctmgr.set_password(user, password) return user return None
def validate_registration(self, req): acctmgr = AccountManager(self.env) username = acctmgr.handle_username_casing( req.args.get('username', '').strip()) if self.username_regexp != "" and \ not re.match(self.username_regexp.strip(), username): raise RegistrationError( N_("Username %s doesn't match local naming policy."), tag.b(username)) email = req.args.get('email', '').strip() if acctmgr.verify_email and is_enabled(self.env, EmailCheck) and \ is_enabled(self.env, EmailVerificationModule): if self.email_regexp.strip() != "" and \ not re.match(self.email_regexp.strip(), email): raise RegistrationError( N_("The email address specified appears to be invalid. " "Please specify a valid email address."))