Пример #1
0
    def test_get_user_attribute(self):
        self.assertEqual(get_user_attribute(self.env, authenticated=None), {})

        with self.env.db_transaction as db:
            db.executemany("""
                INSERT INTO session_attribute (sid,authenticated,name,value)
                VALUES (%s,%s,%s,%s)
                """, [('user', 0, 'attribute1', 'value1'),
                      ('user', 0, 'attribute2', 'value2'),
                      ('user', 1, 'attribute1', 'value1'),
                      ('user', 1, 'attribute2', 'value2'),
                      ('another', 1, 'attribute2', 'value3')])

        no_constraints = get_user_attribute(self.env, authenticated=None)
        # Distinct session IDs form top-level keys.
        self.assertEqual(set(no_constraints.keys()),
                         set([u'user', u'another']))
        # There are probably anonymous sessions named equally to
        # authenticated ones, causing different nested dicts below each
        # session ID.  Btw, only authenticated ones are real usernames.
        self.assertTrue(0 in no_constraints['user'])
        self.assertTrue(1 in no_constraints['user'])
        self.assertFalse(0 in no_constraints['another'])
        self.assertTrue(1 in no_constraints['another'])
        # Touch some of the attributes stored before.
        self.assertTrue(no_constraints['user'][0]['attribute1'], 'value1')
        self.assertTrue(no_constraints['user'][1]['attribute2'], 'value2')
        self.assertEqual(no_constraints['another'].get(0), None)
        self.assertTrue(no_constraints['another'][1]['attribute2'], 'value3')
Пример #2
0
 def test_get_user_attribute(self):
     self.assertEqual(get_user_attribute(self.env, authenticated=None), {})
     cursor = self.db.cursor()
     cursor.executemany(
         """
         INSERT INTO session_attribute
                (sid,authenticated,name,value)
         VALUES (%s,%s,%s,%s)
     """, [('user', 0, 'attribute1', 'value1'),
           ('user', 0, 'attribute2', 'value2'),
           ('user', 1, 'attribute1', 'value1'),
           ('user', 1, 'attribute2', 'value2'),
           ('another', 1, 'attribute2', 'value3')])
     no_constraints = get_user_attribute(self.env, authenticated=None)
     # Distinct session IDs form top-level keys.
     self.assertEqual(set(no_constraints.keys()), set([u'user',
                                                       u'another']))
     # There are probably anonymous sessions named equally to
     # authenticated ones, causing different nested dicts below each
     # session ID.  Btw, only authenticated ones are real usernames.
     self.assertTrue(0 in no_constraints['user'])
     self.assertTrue(1 in no_constraints['user'])
     self.assertFalse(0 in no_constraints['another'])
     self.assertTrue(1 in no_constraints['another'])
     # Touch some of the attributes stored before.
     self.assertTrue(no_constraints['user'][0]['attribute1'], 'value1')
     self.assertTrue(no_constraints['user'][1]['attribute2'], 'value2')
     self.assertEqual(no_constraints['another'].get(0), None)
     self.assertTrue(no_constraints['another'][1]['attribute2'], 'value3')
 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))
Пример #4
0
    def failed_count(self, user, ipnr=None, reset=False):
        """Report number of previously logged failed login attempts.

        Enforce login policy with regards to tracking of login attempts
        and user account lock behavior.
        Default `False` for reset value causes logging of another attempt.
        `None` value for reset just reads failed login attempts count.
        `True` value for reset triggers final log deletion.
        """
        value = get_user_attribute(self.env, user, 1, 'failed_logins_count')
        count = value and int(value[user][1].get('failed_logins_count')) or 0
        if reset is None:
            # Report failed attempts count only.
            return count
        if not reset:
            # Trigger the failed attempt logger.
            attempts = self.get_failed_log(user)
            log_length = len(attempts)
            if log_length > self.login_attempt_max_count:
                # Truncate attempts list preserving most recent events.
                del attempts[:(log_length - self.login_attempt_max_count)]
            attempts.append({'ipnr': ipnr,
                             'time': to_utimestamp(to_datetime(None))})
            count += 1
            # Update or create attempts counter and list.
            set_user_attribute(self.env, user, 'failed_logins', str(attempts))
            set_user_attribute(self.env, user, 'failed_logins_count', count)
            self.log.debug("AcctMgr:failed_count(%s): %s" % (user, count))
        else:
            # Delete existing attempts counter and list.
            del_user_attribute(self.env, user, 1, 'failed_logins')
            del_user_attribute(self.env, user, 1, 'failed_logins_count')
            # Delete the lock count too.
            self.lock_count(user, 'reset')
        return count
Пример #5
0
    def get_failed_log(self, user):
        """Returns an iterable of previously logged failed login attempts.

        The iterable contains a list of dicts in the following form:
        {'ipnr': ipnr, 'time': time_stamp} or an empty list.
        The time stamp format depends on Trac support for POSIX seconds
        (before 0.12) or POSIX microseconds in more recent Trac versions.
        """
        attempts = get_user_attribute(self.env, user, 1, 'failed_logins')
        return attempts and eval(attempts[user][1].get('failed_logins')) or []
Пример #6
0
    def get_failed_log(self, user):
        """Returns an iterable of previously logged failed login attempts.

        The iterable contains a list of dicts in the following form:
        {'ipnr': ipnr, 'time': time_stamp} or an empty list.
        The time stamp format depends on Trac support for POSIX seconds
        (before 0.12) or POSIX microseconds in more recent Trac versions.
        """
        attempts = get_user_attribute(self.env, user, 1, 'failed_logins')
        return attempts and eval(attempts[user][1].get('failed_logins')) or []
Пример #7
0
 def _maybe_update_hash(self, user, password):
     if not get_user_attribute(self.env, 1, user, 'password_refreshed', 1):
         self.log.debug("Refresh password for user: %s" % user)
         store = self.find_user_store(user)
         pwstore = self.get_supporting_store('set_password')
         if pwstore.set_password(user, password) == True:
             # Account re-created according to current settings.
             if store and not (store.delete_user(user) == True):
                 self.log.warn("Failed to remove old entry for user: %s" %
                               user)
         set_user_attribute(self.env, user, 'password_refreshed', 1)
Пример #8
0
 def _maybe_update_hash(self, user, password):
     if not get_user_attribute(self.env, 1, user, 'password_refreshed', 1):
         self.log.debug("Refresh password for user: %s" % user)
         store = self.find_user_store(user)
         pwstore = self.get_supporting_store('set_password')
         if pwstore.set_password(user, password) == True:
             # Account re-created according to current settings.
             if store and not (store.delete_user(user) == True):
                 self.log.warn(
                     "Failed to remove old entry for user: %s" % user)
         set_user_attribute(self.env, user, 'password_refreshed', 1)
Пример #9
0
 def _maybe_update_hash(self, user, password):
     from acct_mgr.model import get_user_attribute, set_user_attribute
     if get_user_attribute(self.env, user, 1,
                           'password_refreshed', 1) == [0]:
         self.log.debug("Refresh password for user: %s", user)
         store = self.find_user_store(user)
         pwstore = self.get_supporting_store('set_password')
         if pwstore.set_password(user, password) is True:
             # Account re-created according to current settings.
             if store and not (store.delete_user(user) is True):
                 self.log.warning("Failed to remove old entry for user: "******"%s", user)
         set_user_attribute(self.env, user, 'password_refreshed', 1)
Пример #10
0
 def _maybe_update_hash(self, user, password):
     from acct_mgr.model import get_user_attribute, set_user_attribute
     if get_user_attribute(self.env, user, 1, 'password_refreshed',
                           1) == [0]:
         self.log.debug("Refresh password for user: %s", user)
         store = self.find_user_store(user)
         pwstore = self.get_supporting_store('set_password')
         if pwstore.set_password(user, password) is True:
             # Account re-created according to current settings.
             if store and not (store.delete_user(user) is True):
                 self.log.warning(
                     "Failed to remove old entry for user: "******"%s", user)
         set_user_attribute(self.env, user, 'password_refreshed', 1)
Пример #11
0
    def lock_count(self, user, action='get'):
        """Count, log and report, how often in succession user account
        lock conditions have been met.

        This is the exponent for lock time prolongation calculation too.
        """
        key = 'lock_count'
        if not action == 'reset':
            value = get_user_attribute(self.env, user, 1, key)
            count = value and int(value[user][1].get(key)) or 0
            if not action == 'get':
                # Push and create or update cached count.
                count += 1
                set_user_attribute(self.env, user, key, count)
        else:
            # Reset/delete lock count cache.
            del_user_attribute(self.env, user, 1, key)
            count = 0
        return count
Пример #12
0
    def lock_count(self, user, action='get'):
        """Count, log and report, how often in succession user account
        lock conditions have been met.

        This is the exponent for lock time prolongation calculation too.
        """
        key = 'lock_count'
        if not action == 'reset':
            value = get_user_attribute(self.env, user, 1, key)
            count = value and int(value[user][1].get(key)) or 0
            if not action == 'get':
                # Push and create or update cached count.
                count += 1
                set_user_attribute(self.env, user, key, count)
        else:
            # Reset/delete lock count cache.
            del_user_attribute(self.env, user, 1, key)
            count = 0
        return count
Пример #13
0
    def failed_count(self, user, ipnr=None, reset=False):
        """Report number of previously logged failed login attempts.

        Enforce login policy with regards to tracking of login attempts
        and user account lock behavior.
        Default `False` for reset value causes logging of another attempt.
        `None` value for reset just reads failed login attempts count.
        `True` value for reset triggers final log deletion.
        """
        if not user or not user_known(self.env, user):
            return 0
        key = 'failed_logins_count'
        value = get_user_attribute(self.env, user, 1, key)
        count = value and user in value and int(value[user][1].get(key)) or 0
        if reset is None:
            # Report failed attempts count only.
            return count
        if not reset:
            # Trigger the failed attempt logger.
            attempts = self.get_failed_log(user)
            log_length = len(attempts)
            if log_length > self.login_attempt_max_count:
                # Truncate attempts list preserving most recent events.
                del attempts[:(log_length - self.login_attempt_max_count)]
            attempts.append({
                'ipnr': ipnr,
                'time': to_timestamp(to_datetime(None))
            })
            count += 1
            # Update or create attempts counter and list.
            set_user_attribute(self.env, user, 'failed_logins', str(attempts))
            set_user_attribute(self.env, user, key, count)
            self.log.debug("AccountGuard.failed_count(%s) = %s" %
                           (user, count))
        else:
            # Delete existing attempts counter and list.
            del_user_attribute(self.env, user, 1, 'failed_logins')
            del_user_attribute(self.env, user, 1, key)
            # Delete the lock count too.
            self.lock_count(user, 'reset')
        return count
Пример #14
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'])
Пример #15
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'])
Пример #16
0
    def _do_db_cleanup(self, req):
        if req.perm.has_permission('ACCTMGR_ADMIN'):
            env = self.env
            changed = False
            # Get all data from 'session_attributes' db table.
            attr = get_user_attribute(self.env, username=None,
                                      authenticated=None)
            attrs = {}
            sel = req.args.get('sel')
            if req.args.get('purge') and sel is not None:
                sel = isinstance(sel, list) and sel or [sel]
                sel_len = len(sel)
                matched = []
                for acct, states in attr.iteritems():
                    for state in states['id'].keys():
                        for elem, id in states[state]['id'].iteritems():
                            if id in sel:
                                if acct in attrs.keys():
                                    if state in attrs[acct].keys():
                                        attrs[acct][state] \
                                            .append(elem)
                                    else:
                                        attrs[acct][state] = [elem]
                                else:
                                    attrs[acct] = {state: [elem]}
                                matched.append(id)
                                if len(matched) == sel_len:
                                    break
                        if len(matched) == sel_len:
                            break
                    if len(matched) == sel_len:
                        break
                for id in (frozenset(sel) - frozenset(matched)):
                    for acct, states in attr.iteritems():
                        for state, id_ in states['id'].iteritems():
                            if id == id_:
                                # Full account is marked, forget attributes.
                                if acct in attrs.keys():
                                    attrs[acct].update({state: []})
                                else:
                                    attrs[acct] = {state: []}
                                matched.append(id)
                                if len(matched) == sel_len:
                                    break
                        if len(matched) == sel_len:
                            break
                # DEVEL: for Python>2.4 better use defaultdict for counters
                del_count = {'acct': 0, 'attr': 0}
                for account, states in attrs.iteritems():
                    for state, elem in states.iteritems():
                        if len(elem) == 0:
                            del_user_attribute(env, account, state)
                            del_count['acct'] += 1
                        else:
                            for attribute in elem:
                                del_user_attribute(env, account, state,
                                                   attribute)
                                del_count['attr'] += 1
                    changed = True
            elif req.args.get('list'):
                req.redirect(req.href.admin('accounts', 'users'))

            if changed == True:
                # Update the dict after changes.
                attr = get_user_attribute(env, username=None,
                                          authenticated=None)
            data = {'_dgettext': dgettext}
            data.update(self._prepare_attrs(req, attr))

            if req.args.get('purge') and sel is not None:
                accounts = attributes = ''
                n_plural=del_count['acct']
                if n_plural > 0:
                    accounts = tag.li(tag.span(tag(ngettext(
                    "%(count)s account",
                    "%(count)s accounts",
                    n_plural, count=n_plural
                ))))
                n_plural=del_count['attr']
                if n_plural > 0:
                    attributes = tag.li(tag.span(tag(ngettext(
                    "%(count)s account attribute",
                    "%(count)s account attributes",
                    n_plural, count=n_plural
                ))))
                data['result'] = tag(_("Successfully deleted:"),
                                     tag.ul(accounts, attributes))
            add_stylesheet(req, 'acct_mgr/acct_mgr.css')
            return 'db_cleanup.html', data
Пример #17
0
    def _do_db_cleanup(self, req):
        if req.perm.has_permission('ACCTMGR_ADMIN'):
            env = self.env
            changed = False
            # Get all data from 'session_attributes' db table.
            attr = get_user_attribute(self.env,
                                      username=None,
                                      authenticated=None)
            attrs = {}
            sel = req.args.get('sel')
            if req.args.get('purge') and sel is not None:
                sel = isinstance(sel, list) and sel or [sel]
                sel_len = len(sel)
                matched = []
                for acct, states in attr.iteritems():
                    for state in states['id'].keys():
                        for elem, id in states[state]['id'].iteritems():
                            if id in sel:
                                if acct in attrs.keys():
                                    if state in attrs[acct].keys():
                                        attrs[acct][state] \
                                            .append(elem)
                                    else:
                                        attrs[acct][state] = [elem]
                                else:
                                    attrs[acct] = {state: [elem]}
                                matched.append(id)
                                if len(matched) == sel_len:
                                    break
                        if len(matched) == sel_len:
                            break
                    if len(matched) == sel_len:
                        break
                for id in (frozenset(sel) - frozenset(matched)):
                    for acct, states in attr.iteritems():
                        for state, id_ in states['id'].iteritems():
                            if id == id_:
                                # Full account is marked, forget attributes.
                                if acct in attrs.keys():
                                    attrs[acct].update({state: []})
                                else:
                                    attrs[acct] = {state: []}
                                matched.append(id)
                                if len(matched) == sel_len:
                                    break
                        if len(matched) == sel_len:
                            break
                # DEVEL: for Python>2.4 better use defaultdict for counters
                del_count = {'acct': 0, 'attr': 0}
                for account, states in attrs.iteritems():
                    for state, elem in states.iteritems():
                        if len(elem) == 0:
                            del_user_attribute(env, account, state)
                            del_count['acct'] += 1
                        else:
                            for attribute in elem:
                                del_user_attribute(env, account, state,
                                                   attribute)
                                del_count['attr'] += 1
                    changed = True
            elif req.args.get('list'):
                req.redirect(req.href.admin('accounts', 'users'))

            if changed == True:
                # Update the dict after changes.
                attr = get_user_attribute(env,
                                          username=None,
                                          authenticated=None)
            data = {'_dgettext': dgettext}
            data.update(self._prepare_attrs(req, attr))

            if req.args.get('purge') and sel is not None:
                accounts = attributes = ''
                n_plural = del_count['acct']
                if n_plural > 0:
                    accounts = tag.li(
                        tag.span(
                            tag(
                                ngettext("%(count)s account",
                                         "%(count)s accounts",
                                         n_plural,
                                         count=n_plural))))
                n_plural = del_count['attr']
                if n_plural > 0:
                    attributes = tag.li(
                        tag.span(
                            tag(
                                ngettext("%(count)s account attribute",
                                         "%(count)s account attributes",
                                         n_plural,
                                         count=n_plural))))
                data['result'] = tag(_("Successfully deleted:"),
                                     tag.ul(accounts, attributes))
            add_stylesheet(req, 'acct_mgr/acct_mgr.css')
            return 'db_cleanup.html', data