Exemple #1
0
    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)
                )
Exemple #2
0
    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
Exemple #3
0
 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
Exemple #4
0
    def post_process_request(self, req, template, data, content_type):
        if 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.
        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=tag(email), link=link))))
            )
        return template, data, content_type
    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)
                )
Exemple #6
0
 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 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."))
    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")
Exemple #9
0
    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 = '/'
Exemple #10
0
    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)
Exemple #11
0
 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)
Exemple #12
0
 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
Exemple #13
0
    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 __init__(self):
     self.account_manager = None
     try:
         from acct_mgr.api import AccountManager
         self.account_manager = AccountManager(self.env)
     except ImportError:
         pass
Exemple #15
0
 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
Exemple #16
0
    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.")
Exemple #17
0
 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 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))
Exemple #19
0
    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))
Exemple #20
0
    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 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 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'))
Exemple #23
0
 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)
Exemple #24
0
 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
Exemple #25
0
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
Exemple #26
0
    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 _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))
Exemple #28
0
    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."))
Exemple #29
0
 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 or not password:
         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
Exemple #30
0
    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)
Exemple #31
0
    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.")
                )
Exemple #32
0
 def process_admin_request(self, req, category, page, path_info):
     mgr = AccountManager(self.env)
     if path_info:
         return self._process_user_request(req, category, page, 
                                           path_info, mgr)
     else:
         if req.method == 'POST':
             if req.args.get('add'):
                 self._do_add(req, mgr)
             elif req.args.get('remove'):
                 self._do_remove(req, mgr)
     # run user list through a set to work around a bug in
     # account manager
     # see http://trac-hacks.org/ticket/180
     users = list(sets.Set(mgr.get_users()))
     users.sort()
     req.hdf['admin.users'] = \
         [{'name': u, 
           'key': u, 
           'href': self.env.href.admin(category, page, u)
          } for u in users]
     return 'admin_users.cs', None
Exemple #33
0
    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."))
Exemple #34
0
    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
        }
Exemple #35
0
    def get_users(self):

        users = {}
        try:
            from acct_mgr.api import AccountManager, get_user_attribute
            acct_mgr = AccountManager(self.env)
            for username in acct_mgr.get_users():
                users[username] = { 'username': username }

            for username, status in get_user_attribute(self.env, username=None, authenticated=None).iteritems():
                user = users.get(username)
                if user is not None and 1 in status:
                    user['name'] = status[1].get('name')
                    user['email'] = status[1].get('email')
                    user.update(self.get_last_login(username))

        except:
            for username, name, email in self.env.get_known_users():
                user = { 'username': username, 'name': name, 'email': email }
                user.update(self.get_last_login(username))
                users[username] = user

        return users
Exemple #36
0
 def authenticate(self, req):
     if req.method == 'POST' and req.path_info.startswith('/login'):
         user = self._remote_user(req)
         acctmgr = AccountManager(self.env)
         guard = AccountGuard(self.env)
         if guard.login_attempt_max_count > 0:
             if user is None:
                 if req.args.get('user_locked') is None:
                     # get user for failed authentication attempt
                     f_user = req.args.get('user')
                     req.args['user_locked'] = False
                     if acctmgr.user_known(f_user) is True:
                         if guard.user_locked(f_user) is False:
                             # log current failed login attempt
                             guard.failed_count(f_user, req.remote_addr)
                             if guard.user_locked(f_user) is True:
                                 # step up lock time prolongation
                                 # only when just triggering the lock
                                 guard.lock_count(f_user, 'up')
                                 req.args['user_locked'] = True
                         else:
                             # enforce lock
                             req.args['user_locked'] = True
             else:
                 if guard.user_locked(user) is not False:
                     req.args['user_locked'] = True
                     # void successful login as long as user is locked
                     user = None
                 else:
                     req.args['user_locked'] = False
                     if req.args.get('failed_logins') is None:
                         # Reset failed login attempts counter
                         req.args['failed_logins'] = guard.failed_count(
                             user, reset=True)
         if 'REMOTE_USER' not in req.environ:
             req.environ['REMOTE_USER'] = user
     return auth.LoginModule.authenticate(self, req)
Exemple #37
0
 def authenticate(self, req):
     if req.method == 'POST' and req.path_info.startswith('/login'):
         user = self._remote_user(req)
         acctmgr = AccountManager(self.env)
         guard = AccountGuard(self.env)
         if guard.login_attempt_max_count > 0:
             if user is None:
                 if req.args.get('user_locked') is None:
                     # get user for failed authentication attempt
                     f_user = req.args.get('user')
                     req.args['user_locked'] = False
                     if acctmgr.user_known(f_user) is True:
                         if guard.user_locked(f_user) is False:
                             # log current failed login attempt
                             guard.failed_count(f_user, req.remote_addr)
                             if guard.user_locked(f_user) is True:
                                 # step up lock time prolongation
                                 # only when just triggering the lock
                                 guard.lock_count(f_user, 'up')
                                 req.args['user_locked'] = True
                         else:
                             # enforce lock
                             req.args['user_locked'] = True
             else:
                 if guard.user_locked(user) is not False:
                     req.args['user_locked'] = True
                     # void successful login as long as user is locked
                     user = None
                 else:
                     req.args['user_locked'] = False
                     if req.args.get('failed_logins') is None:
                         # Reset failed login attempts counter
                         req.args['failed_logins'] = guard.failed_count(
                                                      user, reset = True)
         if 'REMOTE_USER' not in req.environ:
             req.environ['REMOTE_USER'] = user
     return auth.LoginModule.authenticate(self, req)
Exemple #38
0
def fetch_user_data(env, req):
    acctmgr = AccountManager(env)
    guard = AccountGuard(env)
    accounts = {}
    for username in acctmgr.get_users():
        if req.perm.has_permission('ACCTMGR_USER_ADMIN'):
            url = req.href.admin('accounts', 'users', user=username)
        else:
            url = None
        accounts[username] = {'username': username, 'review_url': url}
        if guard.user_locked(username):
            accounts[username]['locked'] = True
            t_lock = guard.lock_time(username)
            if t_lock > 0:
                t_release = guard.pretty_release_time(req, username)
                accounts[username]['release_hint'] = _(
                        "Locked until %(t_release)s",
                        t_release=t_release)
    for acct, status in get_user_attribute(env, username=None,
                                           authenticated=None).iteritems():
        account = accounts.get(acct)
        if account is not None and 1 in status:
            # Only use attributes related to authenticated
            # accounts.
            account['name'] = status[1].get('name')
            account['email'] = status[1].get('email')
            if account['email']:
                account['email'] = Chrome(env).format_author(req,
                                                             account['email'])
    ts_seen = last_seen(env)
    if ts_seen is not None:
        for username, last_visit in ts_seen:
            account = accounts.get(username)
            if account and last_visit:
                account['last_visit'] = to_datetime(last_visit)
    return sorted(accounts.itervalues(), key=lambda acct: acct['username'])
    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(),
                        chrome={'warnings': [], 'notices': []},
                        href=Mock(prefs=lambda x: None),
                        incookie=incookie, outcookie=Cookie(),
                        redirect=lambda x: None)
        self.req.path_info = '/'
class AccountManagerTestCase(_BaseTestCase):
    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(),
                        chrome={'warnings': [], 'notices': []},
                        href=Mock(prefs=lambda x: None),
                        incookie=incookie, outcookie=Cookie(),
                        redirect=lambda x: None)
        self.req.path_info = '/'

        # Tests

    def test_set_password(self):
        # Can't work without at least one password store.
        self.assertRaises(TracError, self.mgr.set_password, 'user', 'passwd')
        self.env.config.set(
            'account-manager', 'password_store', 'SessionStore')
        self.mgr.set_password('user', 'passwd')
        # Refuse to overwrite existing credentials, if requested.
        self.assertRaises(TracError, self.mgr.set_password, 'user', 'passwd',
                          overwrite=False)

    def test_approval_admin_keep_perm(self):
        self.perm.grant_permission('admin', 'ACCTMGR_ADMIN')

        # Some elevated permission action.
        action = 'USER_VIEW'
        self.assertFalse(action in PermissionCache(self.env))

        req = self.req
        req.perm = PermissionCache(self.env, 'admin')
        req.session = Session(self.env, req)
        req.session.save()
        self.mgr.pre_process_request(req, None)
        self.assertTrue(action in req.perm)

        # Mock an authenticated request with account approval pending.
        req.session['approval'] = 'pending'
        req.session.save()
        # Don't touch admin user requests.
        self.mgr.pre_process_request(req, None)
        self.assertTrue(action in req.perm)

    def test_approval_user_strip_perm(self):
        # Some elevated permission action.
        action = 'USER_VIEW'
        self.assertFalse(action in PermissionCache(self.env))
        self.perm.grant_permission('user', action)

        req = self.req
        req.perm = PermissionCache(self.env, 'user')
        req.session = Session(self.env, req)
        req.session.save()
        self.mgr.pre_process_request(req, None)
        self.assertTrue(action in req.perm)

        # Mock an authenticated request with account approval pending.
        req.session['approval'] = 'pending'
        req.session.save()
        # Remove elevated permission, if account approval is pending.
        self.mgr.pre_process_request(req, None)
        self.assertFalse(action in req.perm)

    def test_maybe_update_hash(self):
        # Configure another, primary password store.
        self.env.config.set('account-manager', 'password_store',
                            'HtDigestStore, SessionStore')
        self.env.config.set('account-manager', 'htdigest_file', '.htdigest')

        self.env.db_transaction("""
                INSERT INTO session_attribute (sid,authenticated,name,value)
                VALUES (%s,%s,%s,%s)
                """, ('user', 1, 'password_refreshed', '1'))

        # Refresh not happening due to 'password_refreshed' attribute.
        self.mgr._maybe_update_hash('user', 'passwd')
        for _, in self.env.db_query("""
                SELECT value FROM session_attribute
                WHERE sid='user'
                 AND authenticated=1
                 AND name='password'
                """):
            break
        else:
            self.fail("Session attribute 'password' not found.")

        self.env.db_transaction("""
            DELETE FROM session_attribute
            WHERE sid='user'
             AND authenticated=1
             AND name='password_refreshed'
            """)
        # Refresh (and effectively migrate) user credentials.
        self.mgr._maybe_update_hash('user', 'passwd')
        for _, in self.env.db_query("""
                SELECT value
                FROM session_attribute
                WHERE sid='user'
                 AND authenticated=1
                 AND name='password'
                """):
            self.fail("Session attribute 'password' should not be found.")

        for value, in self.env.db_query("""
                SELECT value
                FROM session_attribute
                WHERE sid='user'
                 AND authenticated=1
                 AND name='password_refreshed'
                """):
            self.assertEqual('1', value)
            break
        else:
            self.fail("Session attribute 'password_refreshed' not found.")
Exemple #41
0
class PageAuthzPolicyEditor(Component):
    implements(IAdminPanelProvider, ITemplateProvider)

    def __init__(self):
        self.account_manager = AccountManager(self.env)

    # ITemplateProvider methods
    def get_templates_dirs(self):
        return [resource_filename(__name__, 'templates')]
    
    def get_htdocs_dirs(self):
        return []

    # IAdminPanelProvider methods
    def get_admin_panels(self, req):
        if 'TRAC_ADMIN' in req.perm:
            yield ('accounts', translation._('Accounts'), 'pages', translation._('Page Permissions'))

    def _get_filename(self, section, name):
        file_name = self.config.get(section, name)
        if len(file_name):
            if (not file_name.startswith(os.path.sep)) and (not file_name[1] == (':')):
                file_name = os.path.join(self.env.path, file_name)
            return(file_name)
        else:
            return(None)

    def _get_users(self):
        user_list = ', '.join(self.account_manager.get_users())
        return(user_list)

    def _group_filename(self):
        group_file_name = self._get_filename('account-manager', 'group_file')
        if not group_file_name:
            group_file_name = self._get_filename('htgroups', 'group_file')
        if not group_file_name:
            raise TracError('Group filename not found in the config file. In neither sections\
                                "account-manager" nor "htgroups" under the name "group_file".')
        if not os.path.exists(group_file_name):
            raise TracError('Group filename not found: %s.' % group_file_name)
        return(group_file_name)

    # Get the groups and their members so they can easily be included in the
    # groups section of the authz file.  Need it as a dictionary of arrays so it be easily
    # iterated.    
    def _get_groups_and_members(self):
        """
        Get the groups and their members as a dictionary of
        lists.
        """
        # could be in one of two places, depending if the 
        # account-manager is installed or not
        group_file_name = self._group_filename()
        groups_dict = dict()
        group_file = file(group_file_name)
        try:
            for group_line in group_file:
                # Ignore blank lines and lines starting with #
                group_line = group_line.strip()
                if group_line and not group_line.startswith('#'):
                    group_name = group_line.split(':', 1)[0]
                    group_members = group_line.split(':', 2)[1].split(' ')
                    groups_dict[group_name] = [ x for x in [member.strip() for member in group_members] if x ]
        finally:
            group_file.close()
        if len(groups_dict):
            return groups_dict
        else:
            return None



    def render_admin_panel(self, req, cat, page, path_info):
        req.perm.require('TRAC_ADMIN')
        authz_policy_file_name = self._get_filename('authz_policy', 'authz_file')
        group_details = self._get_groups_and_members()
        # Handle the return data
        if req.method == 'POST':
             if req.args.get('authz_file_contents'):
                # The data needs to be validated, otherwise duplicate
                # entries can break things.
                edited_contents = str(req.args.get('authz_file_contents'))
                edited_contents_stringio = StringIO(edited_contents)
                try:
                    test_authz_policy_dict = ConfigObj(edited_contents_stringio)
                except:
                    raise TracError(_('Error in edited file.  Re-edit and check for duplicate entries.'))
                authz_policy_file = open(authz_policy_file_name, 'w')
                test_authz_policy_dict.write(authz_policy_file)
                authz_policy_file.close()

        authz_policy_dict = ConfigObj(authz_policy_file_name)

        # If there isn't a group file, don't destroy the existing entries
        if (group_details):
            authz_policy_dict['groups'] = group_details

        # This is purely to fill in the text area with the contents.
        contents = StringIO()
        authz_policy_dict.write(contents)


        #contents = open(authz_policy_file_name).readlines()
        data = {
            'file_name' : authz_policy_file_name,
            'contents': contents.getvalue(),
            'users' : self._get_users()
        }
        return 'page_authz_policy_editor.html', {'pages_authz': data}
Exemple #42
0
class AccountManagerAdminPage(Component):

    implements(IAdminPanelProvider, ITemplateProvider)

    def __init__(self):
        self.account_manager = AccountManager(self.env)

    # IAdminPageProvider
    def get_admin_panels(self, req):
        if req.perm.has_permission('TRAC_ADMIN'):
            yield ('accounts', 'Accounts', 'config', 'Configuration')
            yield ('accounts', 'Accounts', 'users', 'Users')

    def render_admin_panel(self, req, cat, page, path_info):
        if page == 'config':
            return self._do_config(req)
        elif page == 'users':
            return self._do_users(req)

    def _do_config(self, req):
        stores = StoreOrder(stores=self.account_manager.stores,
                            list=self.account_manager.password_store)
        if req.method == 'POST':
            _setorder(req, stores)
            self.config.set('account-manager', 'password_store',
                            ','.join(stores.get_enabled_store_names()))
            for store in stores.get_all_stores():
                for attr, option in _getoptions(store):
                    newvalue = req.args.get('%s.%s' % (store.__class__.__name__, attr))
                    self.log.debug("%s.%s: %s" % (store.__class__.__name__, attr, newvalue))
                    if newvalue is not None:
                        self.config.set(option.section, option.name, newvalue)
                        self.config.save()
            self.config.set('account-manager', 'force_passwd_change',
                            req.args.get('force_passwd_change'))
            self.config.save()
        sections = []
        for store in self.account_manager.stores:
            options = []
            for attr, option in _getoptions(store):
                opt_val = option.__get__(store, store)
                opt_val = isinstance(opt_val, Component) and \
                          opt_val.__class__.__name__ or opt_val
                options.append(
                            {'label': attr,
                            'name': '%s.%s' % (store.__class__.__name__, attr),
                            'value': opt_val,
                            })
                continue
            sections.append(
                        {'name': store.__class__.__name__,
                        'classname': store.__class__.__name__,
                        'order': stores[store],
                        'options' : options,
                        })
            continue
        sections = sorted(sections, key=lambda i: i['name'])
        numstores = range(0, stores.numstores() + 1)
        data = {'sections': sections,
                'numstores': numstores,
                'force_passwd_change': self.account_manager.force_passwd_change}
        return 'admin_accountsconfig.html', data

    def _do_users(self, req):
        perm = PermissionSystem(self.env)
        listing_enabled = self.account_manager.supports('get_users')
        create_enabled = self.account_manager.supports('set_password')
        password_change_enabled = self.account_manager.supports('set_password')
        delete_enabled = self.account_manager.supports('delete_user')

        data = {
            'listing_enabled': listing_enabled,
            'create_enabled': create_enabled,
            'delete_enabled': delete_enabled,
            'password_change_enabled': password_change_enabled,
            'acctmgr' : { 'username' : None,
                          'name' : None,
                          'email' : None,
                        }
        }

        if req.method == 'POST':
            if req.args.get('add'):
                if create_enabled:
                    try:
                        _create_user(req, self.env, check_permissions=False)
                    except TracError, e:
                        data['registration_error'] = e.message
                        data['acctmgr'] = e.acctmgr
                else:
                    data['registration_error'] = 'The password store does ' \
                                                 'not support creating users'
            elif req.args.get('remove'):
                if delete_enabled:
                    sel = req.args.get('sel')
                    sel = isinstance(sel, list) and sel or [sel]
                    for account in sel:
                        self.account_manager.delete_user(account)
                else:
                    data['deletion_error'] = 'The password store does not ' \
                                             'support deleting users'
            elif req.args.get('change'):
                if password_change_enabled:
                    try:
                        user = req.args.get('change_user')
                        acctmgr = { 'change_username' : user,
                        }
                        error = TracError('')
                        error.acctmgr = acctmgr
                        if not user:
                            error.message = 'Username cannot be empty.'
                            raise error

                        password = req.args.get('change_password')
                        if not password:
                            error.message = 'Password cannot be empty.'
                            raise error

                        if password != req.args.get('change_password_confirm'):
                            error.message = 'The passwords must match.'
                            raise error

                        self.account_manager.set_password(user, password)
                    except TracError, e:
                        data['password_change_error'] = e.message
                        data['acctmgr'] = getattr(e, 'acctmgr', '')
                else:
                    data['password_change_error'] = 'The password store does not ' \
                                                    'support changing passwords'
class GroupsEditorPlugin(Component):
    """ Trac Groups Editor plugin
      Edit the groups in the group file.
      Select a group
      Display it's contents
      Delete or add listed members
      If the fine grained page permissions plugine is enabled, then
      as an option also update it.
    """
    implements(IAdminPanelProvider, ITemplateProvider)

    def __init__(self):
        self.account_manager = AccountManager(self.env)

    ### ITemplateProvider methods

    def get_templates_dirs(self):
        from pkg_resources import resource_filename
        return [resource_filename(__name__, 'templates')]

    def get_htdocs_dirs(self):
        from pkg_resources import resource_filename
        return [('ge', resource_filename(__name__, 'htdocs'))]

    ### IAdminPanelProvider methods

    def get_admin_panels(self, req):
        if 'TRAC_ADMIN' in req.perm:
            yield ('accounts', _("Accounts"), 'groups', _("Groups"))

    def _get_filename(self, section, name):
        file_name = self.config.get(section, name)
        if len(file_name):
            if not file_name.startswith(os.path.sep) and \
                    not file_name[1] == ':':
                file_name = os.path.join(self.env.path, file_name)
            return file_name
        else:
            return None

    def _group_filename(self):
        group_file_name = self._get_filename('account-manager', 'group_file')
        if not group_file_name:
            group_file_name = self._get_filename('htgroups', 'group_file')
        if not group_file_name:
            raise TracError("""Group filename not found in the config file. In
                neither sections "account-manager" nor "htgroups" under the
                name "group_file".""")
        if not os.path.exists(group_file_name):
            raise TracError('Group filename not found: %s.' % group_file_name)

        return group_file_name

    def _get_groups_and_members(self):
        """Get the groups and their members as a dictionary of lists. """
        # could be in one of two places, depending if the  account-manager
        # is installed or not
        group_file_name = self._group_filename()
        groups_dict = dict()
        group_file = file(group_file_name)
        try:
            for group_line in group_file:
                # Ignore blank lines and lines starting with #
                group_line = group_line.strip()
                if group_line and not group_line.startswith('#'):
                    group_name = group_line.split(':', 1)[0]
                    group_members = group_line.split(':', 2)[1].split(' ')
                    groups_dict[group_name] = [member.strip() for member in
                                               group_members if member]
        finally:
            group_file.close()
        if len(groups_dict):
            return groups_dict
        else:
            return None

    def _write_groups_file(self, entries):
        """
        Write the groups and members to the groups file
        """
        group_file = open(self._group_filename(), 'w')
        for group_name in entries.keys():
            group_file.write(group_name + ': ' +
                             ' '.join(entries[group_name]) + '\n')
        group_file.close()

    def _check_for_finegrained(self):
        """Check if the fine grained permission system is enabled."""
        component = self.config.get('components',
                                    'authzpolicy.authz_policy.authzpolicy')
        return component == 'enabled'

    def _update_fine_grained(self, group_details):
        authz_policy_file_name = self._get_filename('authz_policy',
                                                    'authz_file')
        authz_policy_dict = ConfigObj(authz_policy_file_name)
        # If there isn't a group file, don't destroy the existing entries
        if group_details:
            authz_policy_dict['groups'] = group_details
        authz_policy_dict.write()

    def render_admin_panel(self, req, cat, page, path_info):
        """Render the panel.
        When applying deletions and additions, additions
        happen post deletions, so additions in effect have
        a higher precedence.  The way it is done, it shouldn't be
        possible to have both 
        """
        req.perm.require('TRAC_ADMIN')
        add_stylesheet(req, 'ge/css/htgroupeditor.css')

        page_args = {}

        group_details = self._get_groups_and_members()

        # For ease of understanding and future reading
        # being done in the ORDER displayed.
        if not req.method == 'POST':
            groups_list = ['']
            if group_details is not None:
                groups_list += group_details.keys()
            page_args['groups_list'] = groups_list
        else:
            group_name = str(req.args.get('group_name'))
            # put the selected entry at the top of the list
            groups_list = group_details.keys()
            groups_list.remove(group_name)
            groups_list.insert(0, group_name)
            # Get rid of duplicates
            users_list = list()
            for name in group_details[group_name]:
                if name not in users_list:
                    users_list.append(name)
            group_details[group_name] = users_list

            if req.args.get('deletions'):
                deletions = req.args.get('deletions')
                if not isinstance(deletions, list):
                    deletions = [deletions]
                for deletion in deletions:
                    # In case there arer multiple entries
                    while deletion in group_details[group_name]:
                        group_details[group_name].remove(deletion)

            if req.args.get('additional_names'):
                additional_names = req.args.get('additional_names')
                if not isinstance(additional_names, list):
                    additional_names = [additional_names]
                #  If a reload is done after an add, a duplicate can be created
                for name in additional_names:
                    if name not in group_details[group_name]:
                        group_details[group_name].append(name)

            # get the list of users not in the group
            addable_usernames = []
            for username in self.account_manager.get_users():
                username = username.strip()
                if len(username) and not username in group_details[group_name]:
                    addable_usernames.append(username)   

            group_details[group_name].sort()

            page_args['groups_list'] = groups_list
            page_args['users_list'] = group_details[group_name]
            page_args['addable_usernames'] = addable_usernames
            page_args['group_name'] = group_name
            page_args['finegrained'] = self._check_for_finegrained()

            if req.args.get('apply_changes'):
                self._write_groups_file(group_details)
                # update the fine grained permissions, if it is installed    
                if req.args.get('finegrained_check'):
                    self._update_fine_grained(group_details)

        return 'htgroupeditor.html', page_args
class AccountManagerAdminPage(Component):

    implements(IAdminPanelProvider, ITemplateProvider)

    def __init__(self):
        self.account_manager = AccountManager(self.env)

    # IAdminPageProvider
    def get_admin_panels(self, req):
        if req.perm.has_permission("TRAC_ADMIN"):
            yield ("accounts", "Accounts", "config", "Configuration")
            yield ("accounts", "Accounts", "users", "Users")

    def render_admin_panel(self, req, cat, page, path_info):
        if page == "config":
            return self._do_config(req)
        elif page == "users":
            return self._do_users(req)

    def _do_config(self, req):
        stores = StoreOrder(stores=self.account_manager.stores, list=self.account_manager.password_store)
        if req.method == "POST":
            _setorder(req, stores)
            self.config.set("account-manager", "password_store", ",".join(stores.get_enabled_store_names()))
            for store in stores.get_all_stores():
                for attr, option in _getoptions(store):
                    newvalue = req.args.get("%s.%s" % (store.__class__.__name__, attr))
                    self.log.debug("%s.%s: %s" % (store.__class__.__name__, attr, newvalue))
                    if newvalue is not None:
                        self.config.set(option.section, option.name, newvalue)
                        self.config.save()
            self.config.set("account-manager", "force_passwd_change", req.args.get("force_passwd_change"))
            self.config.set("account-manager", "persistent_sessions", req.args.get("persistent_sessions"))
            self.config.save()
        sections = []
        for store in self.account_manager.stores:
            options = []
            for attr, option in _getoptions(store):
                opt_val = option.__get__(store, store)
                opt_val = isinstance(opt_val, Component) and opt_val.__class__.__name__ or opt_val
                options.append({"label": attr, "name": "%s.%s" % (store.__class__.__name__, attr), "value": opt_val})
                continue
            sections.append(
                {
                    "name": store.__class__.__name__,
                    "classname": store.__class__.__name__,
                    "order": stores[store],
                    "options": options,
                }
            )
            continue
        sections = sorted(sections, key=lambda i: i["name"])
        numstores = range(0, stores.numstores() + 1)
        data = {
            "sections": sections,
            "numstores": numstores,
            "force_passwd_change": self.account_manager.force_passwd_change,
            "persistent_sessions": self.account_manager.persistent_sessions,
        }
        return "admin_accountsconfig.html", data

    def _do_users(self, req):
        perm = PermissionSystem(self.env)
        listing_enabled = self.account_manager.supports("get_users")
        create_enabled = self.account_manager.supports("set_password")
        password_change_enabled = self.account_manager.supports("set_password")
        delete_enabled = self.account_manager.supports("delete_user")

        data = {
            "listing_enabled": listing_enabled,
            "create_enabled": create_enabled,
            "delete_enabled": delete_enabled,
            "password_change_enabled": password_change_enabled,
            "acctmgr": {"username": None, "name": None, "email": None},
        }

        if req.method == "POST":
            if req.args.get("add"):
                if create_enabled:
                    try:
                        _create_user(req, self.env, check_permissions=False)
                    except TracError, e:
                        data["registration_error"] = e.message
                        data["acctmgr"] = e.acctmgr
                else:
                    data["registration_error"] = "The password store does " "not support creating users"
            elif req.args.get("remove"):
                if delete_enabled:
                    sel = req.args.get("sel")
                    sel = isinstance(sel, list) and sel or [sel]
                    for account in sel:
                        self.account_manager.delete_user(account)
                else:
                    data["deletion_error"] = "The password store does not " "support deleting users"
            elif req.args.get("change"):
                if password_change_enabled:
                    try:
                        user = req.args.get("change_user")
                        acctmgr = {"change_username": user}
                        error = TracError("")
                        error.acctmgr = acctmgr
                        if not user:
                            error.message = "Username cannot be empty."
                            raise error

                        password = req.args.get("change_password")
                        if not password:
                            error.message = "Password cannot be empty."
                            raise error

                        if password != req.args.get("change_password_confirm"):
                            error.message = "The passwords must match."
                            raise error

                        self.account_manager.set_password(user, password)
                    except TracError, e:
                        data["password_change_error"] = e.message
                        data["acctmgr"] = getattr(e, "acctmgr", "")
                else:
                    data["password_change_error"] = "The password store does not " "support changing passwords"
Exemple #45
0
 def __init__(self):
     self.acctmgr = AccountManager(self.env)
     self.store = ResetPwStore(self.env)
     self._write_check(log=True)
Exemple #46
0
class RegistrationModule(Component):
    """Provides users the ability to register a new account.

    Requires configuration of the AccountManager module in trac.ini.
    """

    implements(INavigationContributor, IRequestHandler, ITemplateProvider)

    def __init__(self):
        self.acctmgr = AccountManager(self.env)
        self._enable_check(log=True)

    def _enable_check(self, log=False):
        env = self.env
        writable = self.acctmgr.supports('set_password')
        ignore_case = auth.LoginModule(env).ignore_case
        if log:
            if not writable:
                self.log.warn('RegistrationModule is disabled because the '
                              'password store does not support writing.')
            if ignore_case:
                self.log.debug('RegistrationModule will allow lowercase '
                               'usernames only and convert them forcefully '
                               'as required, while \'ignore_auth_case\' is '
                               'enabled in [trac] section of your trac.ini.')
        return is_enabled(env, self.__class__) and writable

    enabled = property(_enable_check)

    # INavigationContributor methods

    def get_active_navigation_item(self, req):
        return 'register'

    def get_navigation_items(self, req):
        loginmod = LoginModule(self.env)
        if not self.enabled:
            return
        if req.authname == 'anonymous':
            yield 'metanav', 'register', tag.a(_("Register"),
                                               href=req.href.register())

    # IRequestHandler methods

    def match_request(self, req):
        return req.path_info == '/register' and self._enable_check(log=True)

    def process_request(self, req):
        if req.authname != 'anonymous':
            req.redirect(req.href.prefs('account'))
        action = req.args.get('action')
        data = {'acctmgr' : { 'username' : None,
                              'name' : None,
                              'email' : None,
                            },
                '_dgettext': dgettext,
               }
        data['verify_account_enabled'] = is_enabled(
            self.env, EmailVerificationModule) and self.acctmgr.verify_email
        if req.method == 'POST' and action == 'create':
            try:
                _create_user(req, self.env)
            except TracError, e:
                data['registration_error'] = e.message
                data['acctmgr'] = getattr(e, 'acctmgr', '')
            else:
                chrome.add_notice(req, Markup(tag.span(Markup(_(
                     """Registration has been finished successfully.
                     You may login as user %(user)s now.""",
                     user=tag.b(req.args.get('username')))))))
                req.redirect(req.href.login())
        data['reset_password_enabled'] = AccountModule(self.env
                                                      ).reset_password_enabled
        return 'register.html', data, None
class GroupsEditorPlugin(Component):
    implements(IAdminPanelProvider, ITemplateProvider, IPermissionGroupProvider)

    def __init__(self):
        self.account_manager = AccountManager(self.env)

    # ITemplateProvider methods
    # Used to add the plugin's templates and htdocs 
    def get_templates_dirs(self):
        return [resource_filename(__name__, 'templates')]

    def get_htdocs_dirs(self):
        return []

    # IPermissionGroupProvider methdos
    def get_permission_groups(self, username):
        """Return a list of names of the groups that the user with the specified
        name is a member of."""

        groups_list = []
        groups_dict = self._get_groups_and_members() or {}

        for group,usernames in groups_dict.items():
            if username in usernames:
                groups_list.append(group)
        
        return groups_list

    # IAdminPanelProvider methods
    def get_admin_panels(self, req):
        """Return a list of available admin panels.
            The items returned by this function must be tuples of the form
            `(category, category_label, page, page_label)`.
        """
        if 'TRAC_ADMIN' in req.perm:
            # Simply put, it's what goes in the menu on the left!
            # the page is the name of the page that will be called for the menu entry
            # it will go ...../category/page
            yield ('accounts', translation._('Accounts'), 'groups', translation._('Groups'))


    def _get_filename(self, section, name):
        file_name = self.config.get(section, name)
        if len(file_name):
            if (not file_name.startswith(os.path.sep)) and (not file_name[1] == (':')):
                file_name = os.path.join(self.env.path, file_name)
            return(file_name)
        else:
            return(None)

    def _group_filename(self):
        group_file_name = self._get_filename('account-manager', 'group_file')
        if not group_file_name:
            group_file_name = self._get_filename('htgroups', 'group_file')
        if not group_file_name:
            raise TracError('Group filename not found in the config file. In neither sections\
                                "account-manager" nor "htgroups" under the name "group_file".')
        if not os.path.exists(group_file_name):
            raise TracError('Group filename not found: %s.' % group_file_name)
        return(group_file_name)


    def _get_groups_and_members(self):
        """
        Get the groups and their members as a dictionary of
        lists.
        """
        # could be in one of two places, depending if the 
        # account-manager is installed or not
        group_file_name = self._group_filename()
        groups_dict = dict()
        group_file = file(group_file_name)
        try:
            for group_line in group_file:
                # Ignore blank lines and lines starting with #
                group_line = group_line.strip()
                if group_line and not group_line.startswith('#'):
                    group_name = group_line.split(':', 1)[0]
                    group_members = group_line.split(':', 2)[1].split(' ')
                    groups_dict[group_name] = [ x for x in [member.strip() for member in group_members] if x ]
        finally:
            group_file.close()
        if len(groups_dict):
            return groups_dict
        else:
            return None


    def _write_groups_file(self, entries):
        """
        Write the groups and members to the groups file
        """
        group_file = open(self._group_filename(), 'w')
        for group_name in entries.keys():
            group_file.write(group_name + ': ' + ' '.join(entries[group_name]) + '\n')
        group_file.close()


    def _check_for_finegrained(self):
        """
        Check if the fine grained permission system is installed
        """
        return (self.config.getbool('components', 'authzpolicy.authz_policy.authzpolicy') or
                self.config.getbool('components', 'authz_policy.authzpolicy') )

    def _check_for_svnauthz(self):
        """
        Check if the SVN Authz Plugin is installed
        """
        return self.config.getbool('components','svnauthz.svnauthz.svnauthzplugin')

    def _update_fine_grained(self, group_details):
        #import ConfigObj
        authz_policy_file_name = self._get_filename('authz_policy', 'authz_file')
        authz_policy_dict = ConfigObj(authz_policy_file_name)
        # If there isn't a group file, don't destroy the existing entries
        if (group_details):
            authz_policy_dict['groups'] = group_details
        authz_policy_dict.write()

    def _update_svnauthz(self, group_details):
        svnauthz_policy_file_name = self._get_filename('trac','authz_file')
        svnauthz_policy_dict = ConfigObj(svnauthz_policy_file_name)
        # If there isn't a group file, don't destroy the existing entries
        if (group_details):
            svnauthz_policy_dict['groups'] = group_details
        svnauthz_policy_dict.write()
        

    def render_admin_panel(self, req, cat, page, path_info):
        """
        Render up the panel.
        When applying deletions and additions, additions
        happen post deletions, so additions in effect have
        a higher precedence.  The way it is done, it shouldn't be
        possible to have both 
        """
        req.perm.require('TRAC_ADMIN')
        add_stylesheet(req, 'ge/css/htgroupeditor.css')

        page_args = {}

        group_details = self._get_groups_and_members()
        
        # This option needs to be set if the admin can add and delete groups
        # usage in trac.ini:
        # [htgroupedit]
        # allowgroupedit = enabled
        allowgroupedit = self.config.getbool("htgroupeditor","allowgroupedit")

        # For ease of understanding and future reading
        # being done in the ORDER displayed.
        if not req.method == 'POST':
            groups_list = [''];
            if group_details is not None:
               groups_list = groups_list + group_details.keys()

            page_args['groups_list'] = groups_list
            page_args['allowgroupedit'] = allowgroupedit

            return 'htgroupeditor.html', page_args
        else:
            if req.args.get('new_group') and group_details is None:
                # It can only happen for new_group, that group_details is None
                groups_list = []
                group_details = {}
            else:
                groups_list = group_details.keys()
            
            if req.args.get('new_group') and allowgroupedit:
                # Create new group and select it
                if not req.args.get('new_group_name') or len(str(req.args.get('new_group_name')).strip()) == 0:
                    raise TracError("Group name was empty")
                
                group_name = str(req.args.get('new_group_name')).strip()
                if group_name in group_details.keys():
                    raise TracError("Group already exists")
                
                group_details[group_name] = []
                groups_list.append(group_name)
            elif req.args.get('delete_group') and allowgroupedit:
                # Deleting a group involves removing it from list and details
                delete_group_name = str(req.args.get('group_name')).strip()
                if not delete_group_name in groups_list:
                    raise TracError("Invalid group for deletion")
                
                del group_details[delete_group_name]
                groups_list.remove(delete_group_name)
                
                if len(groups_list) == 0:
                    # In case that was the last group, the subsequent won't work
                    # Thus we have to do the writing of files here and return the empty page
                    self._write_groups_file(group_details)
                    if req.args.get('finegrained_check'):
                        self._update_fine_grained(group_details)
                    if req.args.get('svnauthz_check'):
                        self._update_svnauthz(group_details)
                    
                    # Finally send the empty page
                    page_args['groups_list'] = ['']
                    page_args['allowgroupedit'] = allowgroupedit

                    return 'htgroupeditor.html', page_args
                
                # Select first group in list
                group_name = groups_list[0]
            else:
                # Select group based on the request
                group_name = str(req.args.get('group_name'))
            
            # put the selected entry at the top of the list
            groups_list.remove(group_name)
            groups_list.insert(0, group_name)
            # Get rid of duplicates
            users_list = list()
            for name in group_details[group_name]:
                if name not in users_list:
                    users_list.append(name)
            group_details[group_name] = sorted(users_list)

            if req.args.get('deletions'):
                deletions = req.args.get('deletions')
                # if only on entry it will be a string, so need to make it a list
                if not isinstance(deletions, list):
                    deletions = [deletions]
                for deletion in deletions:
                    # In case there arer multiple entries
                    while deletion in group_details[group_name]:
                        group_details[group_name].remove(deletion)

            if req.args.get('additional_names'):
                additional_names = req.args.get('additional_names')
                if not isinstance(additional_names, list):
                    additional_names = [additional_names]
                #  If a reload is done after an add, a duplicate can be created.
                for name in additional_names:
                    if name not in group_details[group_name]:
                        group_details[group_name].append(name)

            # get the list of users not in the group
            addable_usernames = []
            for username in self.account_manager.get_users():
                username = username.strip()
                if len(username) and not username in group_details[group_name]:
                    addable_usernames.append(username)   

            group_details[group_name].sort()
            addable_usernames.sort()

            page_args['groups_list'] = groups_list
            page_args['users_list'] = group_details[group_name]
            page_args['addable_usernames'] = addable_usernames
            page_args['group_name'] = group_name
            page_args['finegrained'] = self._check_for_finegrained()
            page_args['svnauthz'] = self._check_for_svnauthz()
            page_args['allowgroupedit'] = allowgroupedit

            if req.args.get('apply_changes') or req.args.get('new_group') or req.args.get('delete_group'):
                self._write_groups_file(group_details)
                # update the fine grained permissions, if it is installed    
                if req.args.get('finegrained_check'):
                    self._update_fine_grained(group_details)
                if req.args.get('svnauthz_check'):
                    self._update_svnauthz(group_details)

            return 'htgroupeditor.html', page_args
#!/usr/bin/env python

import os
import sys

from trac.env import Environment
from acct_mgr.api import AccountManager

env = Environment(sys.argv[1])
mgr = AccountManager(env)
mgr.set_password(sys.argv[2], sys.argv[3])
Exemple #49
0
class AccountModule(CommonTemplateProvider):
    """Exposes methods for users to do account management on their own.

    Allows users to change their password, reset their password, if they've
    forgotten it, even delete their account.  The settings for the
    AccountManager module must be set in trac.ini in order to use this.
    Password reset procedure depends on both, ResetPwStore and an
    IPasswordHashMethod implementation being enabled as well.
    """

    implements(IPreferencePanelProvider, IRequestHandler,
               INavigationContributor, IRequestFilter)

    _password_chars = string.ascii_letters + string.digits
    password_length = IntOption(
        'account-manager', 'generated_password_length', 8,
        """Length of the randomly-generated passwords created when resetting
        the password for an account.""")

    reset_password = BoolOption(
        'account-manager', 'reset_password', True,
        'Set to False, if there is no email system setup.')

    def __init__(self):
        self.acctmgr = AccountManager(self.env)
        self.store = ResetPwStore(self.env)
        self._write_check(log=True)

    def _write_check(self, log=False):
        writable = self.acctmgr.get_all_supporting_stores('set_password')
        if not writable and log:
            self.log.warn("AccountModule is disabled because the password "
                          "store does not support writing.")
        return writable

    # IPreferencePanelProvider methods

    def get_preference_panels(self, req):
        writable = self._write_check()
        if not writable:
            return
        if req.authname and req.authname != 'anonymous':
            user_store = self.acctmgr.find_user_store(req.authname)
            if user_store in writable:
                yield 'account', _("Account")

    def render_preference_panel(self, req, panel):
        data = {'account': self._do_account(req),
                '_dgettext': dgettext,
               }
        return 'prefs_account.html', data

    # IRequestHandler methods

    def match_request(self, req):
        return req.path_info == '/reset_password' and \
               self._reset_password_enabled(log=True)

    def process_request(self, req):
        data = {'_dgettext': dgettext,
                'reset': self._do_reset_password(req)
               }
        return 'reset_password.html', data, None

    # IRequestFilter methods

    def pre_process_request(self, req, handler):
        if req.path_info == '/prefs/account' and \
                not (req.authname and req.authname != 'anonymous'):
            # An anonymous session has no account associated with it, and
            # no account properies too, but general session preferences should
            # always be available.
            req.redirect(req.href.prefs())
        return handler

    def post_process_request(self, req, template, data, content_type):
        if req.authname and req.authname != 'anonymous':
            if req.session.get('force_change_passwd', False):
                redirect_url = req.href.prefs('account')
                if req.href(req.path_info) != redirect_url:
                    req.redirect(redirect_url)
        return (template, data, content_type)

    # INavigationContributor methods

    def get_active_navigation_item(self, req):
        return 'reset_password'

    def get_navigation_items(self, req):
        if not self.reset_password_enabled or LoginModule(self.env).enabled:
            return
        if req.authname == 'anonymous':
            yield 'metanav', 'reset_password', tag.a(
                _("Forgot your password?"), href=req.href.reset_password())

    def _reset_password_enabled(self, log=False):
        return is_enabled(self.env, self.__class__) and \
               self.reset_password and (self._write_check(log) != []) and \
               is_enabled(self.env, self.store.__class__) and \
               self.store.hash_method

    reset_password_enabled = property(_reset_password_enabled)

    def _do_account(self, req):
        assert(req.authname and req.authname != 'anonymous')
        action = req.args.get('action')
        delete_enabled = self.acctmgr.supports('delete_user') and \
                             self.acctmgr.allow_delete_account
        data = {'delete_enabled': delete_enabled,
                'delete_msg_confirm': _(
                    "Are you sure you want to delete your account?"),
               }
        force_change_password = req.session.get('force_change_passwd', False)
        if req.method == 'POST':
            if action == 'save':
                data.update(self._do_change_password(req))
                if force_change_password:
                    del(req.session['force_change_passwd'])
                    req.session.save()
                    chrome.add_notice(req, Markup(tag.span(tag_(
                        "Thank you for taking the time to update your password."
                    ))))
                    force_change_password = False
            elif action == 'delete' and delete_enabled:
                data.update(self._do_delete(req))
            else:
                data.update({'error': 'Invalid action'})
        if force_change_password:
            chrome.add_warning(req, Markup(tag.span(_(
                "You are required to change password because of a recent "
                "password change request. "),
                tag.b(_("Please change your password now.")))))
        return data

    def _do_reset_password(self, req):
        if req.authname and req.authname != 'anonymous':
            return {'logged_in': True}
        if req.method != 'POST':
            return {}
        username = req.args.get('username')
        email = req.args.get('email')
        if not username:
            return {'error': _("Username is required")}
        if not email:
            return {'error': _("Email is required")}
        for username_, name, email_ in self.env.get_known_users():
            if username_ == username and email_ == email:
                error = self._reset_password(username, email)
                return error and error or {'sent_to_email': email}
        return {'error': _(
            "The email and username must match a known account.")}

    def _reset_password(self, username, email):
        acctmgr = self.acctmgr
        new_password = self._random_password()
        try:
            self.store.set_password(username, new_password)
            acctmgr._notify('password_reset', username, email, new_password)
        except Exception, e:
            return {'error': ','.join(map(to_unicode, e.args))}
        if acctmgr.force_passwd_change:
            set_user_attribute(self.env, username, 'force_change_passwd', 1)
 def expand_macro(self, formatter, name, content):
     env = formatter.env
     req = formatter.req
     if not content:
         args = []
         kw = {}
     else:
         args, kw = parse_args(content)
     if name == 'ProjectStats':
         if 'wiki' in kw.keys():
             prefix = 'prefix' in kw.keys() and kw['prefix'] or None
             wiki = WikiSystem(env)
             if kw['wiki'] == 'count' or 'count' in args:
                 return tag(len(list(wiki.get_pages(prefix))))
     elif name == 'UserQuery':
         msg_no_perm = tag.p(tag_("(required %(perm)s missing)",
                                  perm=tag.strong('USER_VIEW')),
                             class_='hint')
         if 'perm' in kw.keys():
             perm_sys = PermissionSystem(self.env)
             users = perm_sys.get_users_with_permission(kw['perm'].upper())
         else:
             acct_mgr = AccountManager(env)
             users = list(set(acct_mgr.get_users()))
         if 'locked' in kw.keys() or 'locked' in args:
             guard = AccountGuard(env)
             locked = []
             for user in users:
                 if guard.user_locked(user):
                     locked.append(user)
             if kw.get('locked', 'True').lower() in ('true', 'yes', '1'):
                 users = locked
             else:
                 users = list(set(users) - set(locked))
         elif 'visit' in kw.keys() or 'visit' in args:
             if 'USER_VIEW' not in req.perm:
                 return msg_no_perm
             cols = []
             data = {'accounts': fetch_user_data(env, req), 'cls': 'wiki'}
             for col in ('email', 'name'):
                 if col in args:
                     cols.append(col)
             data['cols'] = cols
             return Chrome(env).render_template(
                 req, 'user_table.html', data, 'text/html', True)
         if kw.get('format') == 'count' or 'count' in args:
             return tag(len(users))
         if 'USER_VIEW' not in req.perm:
             return msg_no_perm
         if 'email' in args or 'name' in args:
             # Replace username with full name, add email if available.
             for username, name, email in self.env.get_known_users():
                 if username in users:
                     if 'name' not in args or name is None:
                         name = username
                     if 'email' in args and email is not None:
                         email = ''.join(['<', email, '>'])
                         name = ' '.join([name, email])
                     if not username == name:
                         users.pop(users.index(username))
                         users.append(name)
         if not users and 'nomatch' in kw.keys():
             return format_to_oneliner(env, formatter.context,
                                       kw['nomatch'])
         users = sorted(users)
         if kw.get('format') == 'list':
             return tag.ul([tag.li(Chrome(env).format_author(req, user))
                            for user in users])
         else:
             # Default output format: comma-separated list.
             return tag(', '.join([Chrome(env).format_author(req, user)
                                   for user in users]))
 def __init__(self):
     self.acctmgr = AccountManager(self.env)
     self._enable_check(log=True)
class RegistrationModule(CommonTemplateProvider):
    """Provides users the ability to register a new account.

    Requires configuration of the AccountManager module in trac.ini.
    """

    implements(chrome.INavigationContributor, IRequestHandler)

    require_approval = BoolOption(
        'account-manager', 'require_approval', False, doc="""
        Whether account registration requires administrative approval to
        enable the account or not.
        """)

    def __init__(self):
        self.acctmgr = AccountManager(self.env)
        self._enable_check(log=True)

    def _enable_check(self, log=False):
        env = self.env
        writable = self.acctmgr.supports('set_password')
        ignore_case = auth.LoginModule(env).ignore_case
        if log:
            if not writable:
                self.log.warning("RegistrationModule is disabled because the "
                                 "password store does not support writing.")
            if ignore_case:
                self.log.debug("RegistrationModule will allow lowercase "
                               "usernames only and convert them forcefully "
                               "as required, while 'ignore_auth_case' is "
                               "enabled in [trac] section of your trac.ini.")
        return env.is_enabled(self.__class__) and writable

    enabled = property(_enable_check)

    # INavigationContributor methods

    def get_active_navigation_item(self, req):
        return 'register'

    def get_navigation_items(self, req):
        if not self.enabled:
            return
        if req.authname == 'anonymous':
            yield 'metanav', 'register', tag.a(_("Register"),
                                               href=req.href.register())

    # IRequestHandler methods

    def match_request(self, req):
        return req.path_info == '/register' and self._enable_check(log=True)

    def process_request(self, req):
        acctmgr = self.acctmgr
        if req.authname != 'anonymous':
            req.redirect(req.href.prefs('account'))
        action = req.args.get('action')
        name = req.args.get('name', '')
        if isinstance(name, list):
            raise HTTPBadRequest(_("Invalid request arguments."))
        name = name.strip()
        username = req.args.get('username', '')
        if isinstance(username, list):
            raise HTTPBadRequest(_("Invalid request arguments."))
        username = acctmgr.handle_username_casing(username.strip())
        data = {
            '_dgettext': dgettext,
            'acctmgr': {'name': name, 'username': username},
            'ignore_auth_case': self.config.getbool('trac',
                                                    'ignore_auth_case')
        }
        verify_enabled = self.env.is_enabled(EmailVerificationModule) and \
                         EmailVerificationModule(self.env).verify_email
        data['verify_account_enabled'] = verify_enabled
        if req.method == 'POST' and action == 'create':
            try:
                try:
                    # Check request and prime account on success.
                    acctmgr.validate_account(req, True)
                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))
            except RegistrationError, e:
                chrome.add_warning(req, e)
            else:
                if self.require_approval:
                    set_user_attribute(self.env, username, 'approval',
                                       N_('pending'))
                    # Notify admin user about registration pending for review.
                    try:
                        acctmgr._notify('registration_approval_required',
                                        username)
                    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 admin notification: %s',
                            exception_to_unicode(e, traceback=True))
                    else:
                        chrome.add_notice(req, tag_(
                            "Your username has been registered successfully, "
                            "but your account requires administrative "
                            "approval. Please proceed according to local "
                            "policy."))
                if verify_enabled:
                    chrome.add_notice(req, tag_(
                        "Your username has been successfully registered but "
                        "your account still requires activation. Please "
                        "login as user %(user)s, and follow the "
                        "instructions.", user=tag.b(username)))
                    req.redirect(req.href.login())
                chrome.add_notice(req, tag_(
                    "Registration has been finished successfully. "
                    "You may log in as user %(user)s now.",
                    user=tag.b(username)))
                req.redirect(req.href.login())
class AccountModule(CommonTemplateProvider):
    """Exposes methods for users to do account management on their own.

    Allows users to change their password, reset their password, if they've
    forgotten it, even delete their account.  The settings for the
    AccountManager module must be set in trac.ini in order to use this.
    Password reset procedure depends on both, ResetPwStore and an
    IPasswordHashMethod implementation being enabled as well.
    """

    implements(IPreferencePanelProvider, IRequestHandler,
               INavigationContributor, IRequestFilter)

    _password_chars = string.ascii_letters + string.digits
    password_length = IntOption(
        'account-manager', 'generated_password_length', 8,
        """Length of the randomly-generated passwords created when resetting
        the password for an account.""")
    reset_password = BoolOption(
        'account-manager', 'reset_password', True,
        'Set to False, if there is no email system setup.')

    def __init__(self):
        self.acctmgr = AccountManager(self.env)
        self.store = ResetPwStore(self.env)
        self._write_check(log=True)

    def _write_check(self, log=False):
        """Returns all configured write-enabled password stores."""
        writable = self.acctmgr.get_all_supporting_stores('set_password')
        if writable:
            try:
                writable = writable.remove(self.store)
            except ValueError:
                # ResetPwStore is not enabled.
                if log:
                    self.log.warn("ResetPwStore is disabled, therefor "
                                  "password reset won't work.")
        # Require at least one more write-enabled password store.
        if not writable and log:
            self.log.warn("AccountModule is disabled because no configured "
                          "password store supports writing.")
        return writable

    # INavigationContributor methods

    def get_active_navigation_item(self, req):
        return 'reset_password'

    def get_navigation_items(self, req):
        if not self.reset_password_enabled or LoginModule(self.env).enabled:
            return
        if req.authname == 'anonymous':
            yield 'metanav', 'reset_password', tag.a(
                _("Forgot your password?"), href=req.href.reset_password())

    def _reset_password_enabled(self, log=False):
        try:
            self.store.hash_method
        except (AttributeError, ConfigurationError):
            return False
        return is_enabled(self.env, self.__class__) and \
               self.reset_password and (self._write_check(log) != []) and \
               is_enabled(self.env, self.store.__class__) and \
               self.store.hash_method and True or False

    reset_password_enabled = property(_reset_password_enabled)

    # IPreferencePanelProvider methods

    def get_preference_panels(self, req):
        writable = self._write_check()
        if not writable:
            return
        if req.authname and req.authname != 'anonymous':
            user_store = self.acctmgr.find_user_store(req.authname)
            if user_store in writable:
                yield 'account', _("Account")

    def render_preference_panel(self, req, panel):
        data = dict(_dgettext=dgettext)
        data.update(self._do_account(req))
        return 'prefs_account.html', data

    # IRequestFilter methods

    def pre_process_request(self, req, handler):
        if req.path_info == '/prefs/account' and \
                not (req.authname and req.authname != 'anonymous'):
            # An anonymous session has no account associated with it, and
            # no account properies too, but general session preferences should
            # always be available.
            req.redirect(req.href.prefs())
        return handler

    def post_process_request(self, req, template, data, content_type):
        if req.authname and req.authname != 'anonymous':
            if req.session.get('force_change_passwd', False):
                # Prevent authenticated usage before another password change.
                redirect_url = req.href.prefs('account')
                if req.href(req.path_info) != redirect_url:
                    req.redirect(redirect_url)
        return (template, data, content_type)

    # IRequestHandler methods

    def match_request(self, req):
        return req.path_info == '/reset_password' and \
               self._reset_password_enabled(log=True)

    def process_request(self, req):
        data = dict(_dgettext=dgettext)
        if req.authname and req.authname != 'anonymous':
            add_notice(req, Markup(tag_(
                "You're already logged in. If you need to change your "
                "password please use the %(prefs_href)s page.",
                prefs_href=tag.a(_("Account Preferences"),
                                 href=req.href.prefs('account')))))
            data['authenticated'] = True
        if req.method == 'POST':
            self._do_reset_password(req)
        return 'reset_password.html', data, None

    def _do_account(self, req):
        assert(req.authname and req.authname != 'anonymous')
        action = req.args.get('action')
        delete_enabled = self.acctmgr.supports('delete_user') and \
                             self.acctmgr.allow_delete_account
        data = {'delete_enabled': delete_enabled,
                'delete_msg_confirm': _(
                    "Are you sure you want to delete your account?"),
               }
        force_change_password = req.session.get('force_change_passwd', False)
        if req.method == 'POST':
            if action == 'save':
                if self._do_change_password(req) and force_change_password:
                    del req.session['force_change_passwd']
                    req.session.save()
                    add_notice(req, _("Thank you for taking the time to "
                                      "update your password."))
                    force_change_password = False
            elif action == 'delete' and delete_enabled:
                self._do_delete(req)
        if force_change_password:
            add_warning(req, Markup(_(
                "You are required to change password because of a recent "
                "password change request. %(invitation)s",
                invitation=tag.b(_("Please change your password now.")))))
        return data

    def _do_change_password(self, req):
        username = req.authname

        old_password = req.args.get('old_password')
        if not self.acctmgr.check_password(username, old_password):
            if old_password:
                add_warning(req, _("Old password is incorrect."))
            else:
                add_warning(req, _("Old password cannot be empty."))
            return
        password = req.args.get('password')
        if not password:
            add_warning(req, _("Password cannot be empty."))
        elif password != req.args.get('password_confirm'):
            add_warning(req, _("The passwords must match."))
        elif password == old_password:
            add_warning(req, _("Password must not match old password."))
        else:
            _set_password(self.env, req, username, password, old_password)
            if req.session.get('password') is not None:
                # Fetch all session_attributes in case new user password is in
                # SessionStore, preventing overwrite by session.save().
                req.session.get_session(req.authname, authenticated=True)
            add_notice(req, _("Password updated successfully."))
            return True

    def _do_delete(self, req):
        username = req.authname

        password = req.args.get('password')
        if not password:
            add_warning(req, _("Password cannot be empty."))
        elif not self.acctmgr.check_password(username, password):
            add_warning(req, _("Password is incorrect."))
        else:
            try:
                self.acctmgr.delete_user(username)
            except NotificationError, e:
                # User wont care for notification, only care for logging here.
                self.log.error(
                       'Unable to send account deletion notification: %s',
                       exception_to_unicode(e, traceback=True))
            # Delete the whole session, since records in session_attribute
            # would get restored on logout otherwise.
            req.session.clear()
            req.session.save()
            req.redirect(req.href.logout())
Exemple #54
0
class RegistrationModule(CommonTemplateProvider):
    """Provides users the ability to register a new account.

    Requires configuration of the AccountManager module in trac.ini.
    """

    implements(chrome.INavigationContributor, IRequestHandler)

    require_approval = BoolOption(
        'account-manager', 'require_approval', False,
        doc="Whether account registration requires administrative approval "
            "to enable the account or not.")

    def __init__(self):
        self.acctmgr = AccountManager(self.env)
        self._enable_check(log=True)

    def _enable_check(self, log=False):
        env = self.env
        writable = self.acctmgr.supports('set_password')
        ignore_case = auth.LoginModule(env).ignore_case
        if log:
            if not writable:
                self.log.warn('RegistrationModule is disabled because the '
                              'password store does not support writing.')
            if ignore_case:
                self.log.debug('RegistrationModule will allow lowercase '
                               'usernames only and convert them forcefully '
                               'as required, while \'ignore_auth_case\' is '
                               'enabled in [trac] section of your trac.ini.')
        return is_enabled(env, self.__class__) and writable

    enabled = property(_enable_check)

    # INavigationContributor methods

    def get_active_navigation_item(self, req):
        return 'register'

    def get_navigation_items(self, req):
        if not self.enabled:
            return
        if req.authname == 'anonymous':
            yield 'metanav', 'register', tag.a(_("Register"),
                                               href=req.href.register())

    # IRequestHandler methods

    def match_request(self, req):
        return req.path_info == '/register' and self._enable_check(log=True)

    def process_request(self, req):
        acctmgr = self.acctmgr
        if req.authname != 'anonymous':
            req.redirect(req.href.prefs('account'))
        action = req.args.get('action')
        name = req.args.get('name', '').strip()
        username = acctmgr.handle_username_casing(req.args.get('username',
                                                               '').strip())
        data = {
                '_dgettext': dgettext,
                  'acctmgr': dict(name=name, username=username),
         'ignore_auth_case': self.config.getbool('trac', 'ignore_auth_case')
        }
        verify_enabled = is_enabled(self.env, EmailVerificationModule) and \
                         EmailVerificationModule(self.env).verify_email
        data['verify_account_enabled'] = verify_enabled
        if req.method == 'POST' and action == 'create':
            try:
                # Check request and prime account on success.
                acctmgr.validate_account(req, True)
            except RegistrationError, e:
                # Attempt deferred translation.
                message = gettext(e.message)
                # Check for (matching number of) message arguments before
                #   attempting string substitution.
                if e.msg_args and \
                        len(e.msg_args) == len(re.findall('%s', message)):
                    message = message % e.msg_args
                chrome.add_warning(req, Markup(message))
            else:
                if self.require_approval:
                    set_user_attribute(self.env, username, 'approval',
                                       N_('pending'))
                    # Notify admin user about registration pending for review.
                    acctmgr._notify('registration_approval_required',
                                    username)
                    chrome.add_notice(req, Markup(tag.span(Markup(_(
                        "Your username has been registered successfully, but "
                        "your account requires administrative approval. "
                        "Please proceed according to local policy."))))
                    )
                if verify_enabled:
                    chrome.add_notice(req, Markup(tag.span(Markup(_(
                        """Your username has been successfully registered but
                        your account still requires activation. Please login
                        as user %(user)s, and follow the instructions.""",
                        user=tag.b(username)))))
                    )
                    req.redirect(req.href.login())
                chrome.add_notice(req, Markup(tag.span(Markup(_(
                     """Registration has been finished successfully.
                     You may log in as user %(user)s now.""",
                     user=tag.b(username)))))
                )
                req.redirect(req.href.login())
        # Collect additional fields from IAccountRegistrationInspector's.
        fragments = dict(required=[], optional=[])
        for inspector in acctmgr.register_checks:
            try:
                fragment, f_data = inspector.render_registration_fields(req,
                                                                        data)
            except TypeError, e:
                # Add some robustness by logging the most likely errors.
                self.env.log.warn("%s.render_registration_fields failed: %s"
                                  % (inspector.__class__.__name__, e))
                fragment = None
            if fragment:
                try:
                    # Python<2.5: Can't have 'except' and 'finally' in same
                    #   'try' statement together.
                    try:
                        if 'optional' in fragment.keys():
                            fragments['optional'].append(fragment['optional'])
                    except AttributeError:
                        # No dict, just append Genshi Fragment or str/unicode.
                        fragments['required'].append(fragment)
                    else:
                        fragments['required'].append(fragment.get('required',
                                                                  ''))
                finally:
                    data.update(f_data)
Exemple #55
0
def _create_user(req, env, check_permissions=True):
    acctmgr = AccountManager(env)
    username = acctmgr.handle_username_casing(
                        req.args.get('username').strip())
    name = req.args.get('name')
    email = req.args.get('email').strip()
    account = {'username' : username,
               'name' : name,
               'email' : email,
              }
    error = TracError('')
    error.account = account

    if not username:
        error.message = _("Username cannot be empty.")
        raise error

    # Prohibit some user names that are important for Trac and therefor
    # reserved, even if they're not in the permission store for some reason.
    if username in ['authenticated', 'anonymous']:
        error.message = _("Username %s is not allowed.") % username
        raise error

    # NOTE: A user may exist in the 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 obfuscate whether an existing user or group name
    #   was responsible for rejection of this user name.
    if acctmgr.has_user(username):
        error.message = _(
            "Another account or group named %s already exists.") % username
        raise error

    # Check whether there is also a user or a group with that name.
    if check_permissions:
        # 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(env).get_all_permissions():
            if perm_user == username:
                error.message = _(
                    "Another account or group named %s already exists.") \
                    % username
                raise error

    # 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 containsAny(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), '\'')
        error.message = tag(_(
            "The username must not contain any of these characters:"),
            pretty_blacklist)
        raise error

    # Validation of username passed.

    password = req.args.get('password')
    if not password:
        error.message = _("Password cannot be empty.")
        raise error

    if password != req.args.get('password_confirm'):
        error.message = _("The passwords must match.")
        raise error

    # Validation of password passed.

    if if_enabled(EmailVerificationModule) and acctmgr.verify_email:
        if not email:
            error.message = _("You must specify a valid email address.")
            raise error
        elif not re.match('^[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$',
                          email, re.IGNORECASE):
            error.message = _("""The email address specified appears to be
                              invalid. Please specify a valid email address.
                              """)
            raise error
        elif acctmgr.has_email(email):
            error.message = _("""The email address specified is already in
                              use. Please specify a different one.
                              """)
            raise error

    # Validation of email address passed.

    acctmgr.set_password(username, password)

    # INSERT new sid, needed as foreign key in some db schemata later on,
    # at least for PostgreSQL.
    db = env.get_db_cnx()
    cursor = db.cursor()
    cursor.execute("""
        SELECT  COUNT(*)
        FROM    session
        WHERE   sid=%s
        """, (username,))
    exists = cursor.fetchone()
    if not exists:
        cursor.execute("""
            INSERT INTO session
                    (sid,authenticated,last_visit)
            VALUES  (%s,0,0)
            """, (username,))

    for attribute in ('name', 'email'):
        value = req.args.get(attribute)
        if not value:
            continue
        set_user_attribute(env, username, attribute, value)
class ComponentPermissionsPolicy(Component):
    """
    This component provides permissions based on ticket components for Trac.
    """

    implements(IPermissionRequestor, IPermissionPolicy, IRequestFilter)

    ticket_field_name = Option('component-permissions', 'ticket_field_name', '',
        """The name of the field which should be checked to see if the component permission is required.
        If not defined or empty, component permission is always required.""")

    allow_reporter = BoolOption('component-permissions', 'allow_reporter', 'false',
        """"Whether the reporter of a ticket should have access to that ticket even if
        they do not have COMPONENT_VIEW or COMPONENT_*_VIEW privileges.""")

    allow_cc = BoolOption('component-permissions', 'allow_cc', 'false',
        """Whether users listed in the cc field of a ticket should have access to that ticket even
        if they do not have COMPONENT_VIEW or COMPONENT_*_VIEW privileges.""")

    allow_owner = BoolOption('component-permissions', 'allow_owner', 'false',
        """Whether the owner of a ticket should have access to that ticket even if
        they do not have COMPONENT_VIEW or COMPONENT_*_VIEW privileges.""")

    allow_cc_email = BoolOption('component-permissions', 'allow_cc_email', 'false',
        """Whether users with their e-mail listed in the cc field of a ticket should have access to
        that ticket even if they do not have COMPONENT_VIEW or COMPONENT_*_VIEW privileges. Make sure
        e-mail is verified and cannot be freely changed.""")

    hide_components = BoolOption('component-permissions', 'hide_components', 'false',
        """Whether components the user does not have permissions for should be hidden.""")

    def __init__(self):
        self.account_manager = None
        try:
            from acct_mgr.api import AccountManager
            self.account_manager = AccountManager(self.env)
        except ImportError:
            pass

    # IPermissionRequestor methods
    
    def _get_permission_name(self, component):
        name = re.sub('[^a-zA-Z0-9]+', '_', component).strip('_').upper()
        if name:
            return 'COMPONENT_%s_VIEW' % (name,)
        else:
            return None

    def _get_email(self, username):
        cnx = self.env.get_db_cnx()
        cursor = cnx.cursor()
        cursor.execute("""SELECT DISTINCT e.value FROM session AS s LEFT JOIN session_attribute AS e
                          ON (e.sid=s.sid AND e.authenticated=1 AND e.name = 'email')
                          WHERE s.authenticated=1 AND s.sid=%s""", (username,))
        for email, in cursor:
            return email
        return None

    def _get_bypass(self, ticket, username):
        if not username or username == 'anonymous':
            return False
        if self.allow_owner and ticket['owner'] == username:
            return True
        if self.allow_reporter and ticket['reporter'] == username:
            return True

        if not self.allow_cc and not self.allow_cc_email:
            return False

        cc_list = [user for user in NotifyEmail.addrsep_re.split(ticket['cc']) if user]

        if self.allow_cc and username in cc_list:
            return True

        if self.allow_cc_email:
            email = self._get_email(username)
            if email and email in cc_list:
                if self.account_manager:
                    if self.account_manager.email_verified(username, email):
                        return True
                else:
                    return True

        return False

    def get_permission_actions(self):
        """Return a list of actions defined by this component."""

        permissions = ['COMPONENT_VIEW']

        for component in model.Component.select(self.env):
            permission = self._get_permission_name(component.name)
            if permission:
                permissions.append(permission)

        return permissions

    # IPermissionPolicy methods

    def check_permission(self, action, username, resource, perm):
        """Check that the action can be performed by username on the resource."""

        # To prevent recursion
        if action in self.get_permission_actions():
            return
        # To prevent recursion when used together with sensitive tickets
        if action == 'SENSITIVE_VIEW':
            return

        # Check whether we're dealing with a ticket resource
        while resource:
            if resource.realm == 'ticket':
                break
            resource = resource.parent
        
        if resource and resource.realm == 'ticket' and resource.id is not None:
            component_permission = 'COMPONENT_VIEW' # Default just to make check logic simpler
            bypass = False
            try:
                ticket = model.Ticket(self.env, int(resource.id))
                should_check_permissions = not self.ticket_field_name or ticket.values.get(self.ticket_field_name, 0)
                if as_bool(should_check_permissions):
                    if 'component' in ticket.values and ticket['component'] and self._get_permission_name(ticket['component']) in self.get_permission_actions():
                        component_permission = self._get_permission_name(ticket['component'])
                    bypass = self._get_bypass(ticket, username)
            except ResourceNotFound:
                should_check_permissions = 1 # Fail safe to prevent a race condition

            if as_bool(should_check_permissions):
                if component_permission not in perm and 'COMPONENT_VIEW' not in perm and not bypass:
                    return False

    # IRequestFilter methods

    def pre_process_request(self, req, handler):
        return handler

    def post_process_request(self, req, template, data, content_type):
        if self.hide_components and not self.ticket_field_name and 'COMPONENT_VIEW' not in req.perm and template in ['ticket_box.html', 'ticket.html', 'ticket_preview.html', 'query.html']:
            objects = []
            if data.get('fields', None):
                objects.append(data['fields'].values() if hasattr(data['fields'], 'values') else data['fields'])
            if req.chrome.get('script_data', None) and req.chrome['script_data'].get('properties', None):
                properties = []
                for name, prop in req.chrome['script_data']['properties'].items():
                    prop['name'] = name
                    properties.append(prop)
                objects.append(properties)
            for obj in objects:
                for field in obj:
                    if field['name'] == 'component':
                        field['options'] = [component for component in field['options'] if self._get_permission_name(component) in req.perm]
                        break
            query = data.get('query', None)
            if query and query.group == 'component' and 'groups' in data:
                groups = []
                for (component, tickets) in data['groups']:
                    if self._get_permission_name(component) in req.perm:
                        groups.append((component, tickets))
                data['groups'] = groups
        return (template, data, content_type)
 def __init__(self):
     self.account_manager = AccountManager(self.env)
Exemple #58
0
class AccountManagerAdminPage(Component):

    implements(IAdminPageProvider)

    def __init__(self):
        self.account_manager = AccountManager(self.env)

    # IAdminPageProvider
    def get_admin_pages(self, req):
        if req.perm.has_permission('TRAC_ADMIN'):
            yield ('accounts', 'Accounts', 'config', 'Configuration') 
            yield ('accounts', 'Accounts', 'users', 'Users')

    def process_admin_request(self, req, cat, page, path_info):
        if page == 'config': 
            return self._do_config(req)
        elif page == 'users': 
            return self._do_users(req) 
            
    def _do_config(self, req):
        if req.method == 'POST':
            selected_class = req.args.get('selected')
            self.config.set('account-manager', 'password_store', selected_class)
            selected = self.account_manager.password_store
            for attr, option in _getoptions(selected):
                newvalue = req.args.get('%s.%s' % (selected_class, attr))
                if newvalue is not None:
                    self.config.set(option.section, option.name, newvalue)
                    self.config.save()
        try:
            selected = self.account_manager.password_store
        except AttributeError:
            selected = None
        sections = [
            {'name': store.__class__.__name__,
             'classname': store.__class__.__name__,
             'selected': store is selected,
             'options': [
                {'label': attr,
                 'name': '%s.%s' % (store.__class__.__name__, attr),
                 'value': option.__get__(store, store),
                }
                for attr, option in _getoptions(store)
             ],
            } for store in self.account_manager.stores
        ]
        sections = sorted(sections, key=lambda i: i['name'])
        req.hdf['sections'] = sections
        return 'admin_accountsconfig.cs', None
            
    def _do_users(self, req):
        perm = PermissionSystem(self.env)
        listing_enabled = self.account_manager.supports('get_users')
        create_enabled = self.account_manager.supports('set_password') 
        delete_enabled = self.account_manager.supports('delete_user')
        
        req.hdf['listing_enabled'] = listing_enabled
        req.hdf['create_enabled'] = create_enabled
        req.hdf['delete_enabled'] = delete_enabled
        
        if req.method == 'POST':
            if req.args.get('add'):
                if create_enabled:
                    try:
                        _create_user(req, self.env, check_permissions=False)
                    except TracError, e:
                        req.hdf['registration.error'] = e.message
                else:
                    req.hdf['registration_error'] = 'The password store does ' \
                                                    'not support creating users'
            elif req.args.get('remove'):
                if delete_enabled:
                    sel = req.args.get('sel')
                    sel = isinstance(sel, list) and sel or [sel]
                    for account in sel:
                        self.account_manager.delete_user(account)
                else:
                    req.hdf['deletion_error'] = 'The password store does not ' \
                                                'support deleting users'
        if listing_enabled:
            accounts = {}
            for username in self.account_manager.get_users():
                accounts[username] = {'username': username}

            for username, name, email in self.env.get_known_users():
                account = accounts.get(username)
                if account:
                    account['name'] = name
                    account['email'] = email

            db = self.env.get_db_cnx()
            cursor = db.cursor()
            cursor.execute("SELECT sid,last_visit FROM session WHERE authenticated=1")
            for username, last_visit in cursor:
                account = accounts.get(username)
                if account and last_visit:
                    account['last_visit'] = format_datetime(last_visit)

            req.hdf['accounts'] = sorted(accounts.itervalues(),
                                         key=lambda acct: acct['username'])

        return 'admin_users.cs', None