Ejemplo n.º 1
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
Ejemplo n.º 2
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
Ejemplo n.º 3
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)
Ejemplo n.º 4
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 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"
Ejemplo n.º 6
0
#!/usr/bin/python

import os
import sys


try:
  from trac.env import Environment
  from acct_mgr.api import AccountManager
  env = Environment(sys.argv[1])
  user, passwd = sys.argv[2], sys.argv[3]
  account_manager = AccountManager(env)
  account_manager.set_password(user, passwd)
except Exception, e:
  print "Oops !! %s" % e
  print "Usage: python set_password <env> <user> <password>"
Ejemplo n.º 7
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):
        """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:
            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:
            self.acctmgr.set_password(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:
            self.acctmgr.delete_user(username)
            # 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())

    def _do_reset_password(self, req):
        email = req.args.get('email')
        username = req.args.get('username')
        if not username:
            add_warning(req, _("Username is required."))
        elif not email:
            add_warning(req, _("Email is required."))
        else:
            for username_, name, email_ in self.env.get_known_users():
                if username_ == username and email_ == email:
                    self._reset_password(req, username, email)
                    return
            add_warning(req,
                        _("Email and username must match a known account."))

    @property
    def _random_password(self):
        return ''.join([
            random.choice(self._password_chars)
            for _ in xrange(self.password_length)
        ])

    def _reset_password(self, req, 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)
            # No message, if method has been called from user admin panel.
            if not req.path_info.startswith('/admin'):
                add_notice(
                    req,
                    _("A new password has been sent to you at "
                      "<%(email)s>.",
                      email=email))
        except Exception, e:
            add_warning(
                req,
                _("Cannot reset password: %(error)s",
                  error=', '.join(map(to_unicode, e.args))))
            return
        if acctmgr.force_passwd_change:
            set_user_attribute(self.env, username, 'force_change_passwd', 1)
Ejemplo n.º 8
0
#!/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])
Ejemplo n.º 9
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)
Ejemplo n.º 10
0
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(),
                        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 credetialy, 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')
        cursor = self.db.cursor()
        cursor.execute("""
            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')
        cursor.execute("""
            SELECT value
            FROM   session_attribute
            WHERE  sid='user'
            AND    authenticated=1
            AND    name='password'
        """)
        self.assertNotEqual(cursor.fetchall(), [])

        cursor.execute("""
            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')
        cursor.execute("""
            SELECT value
            FROM   session_attribute
            WHERE  sid='user'
            AND    authenticated=1
            AND    name='password'
        """)
        self.assertEqual(cursor.fetchall(), [])
        cursor.execute("""
            SELECT value
            FROM   session_attribute
            WHERE  sid='user'
            AND    authenticated=1
            AND    name='password_refreshed'
        """)
        self.assertEqual(cursor.fetchall(), [('1',)])
Ejemplo n.º 11
0
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.")