def canon_password(username, password, allow_weak): ''' N.B. allow_weak = True should be used for lookup but not storage as it allows unassigned codepoints. ''' username = to_utf8(username) password = to_utf8(password) password = demoronize(password) password = password.decode('utf-8') password = saslprep(password, allow_unassigned=allow_weak) if not allow_weak: if len(password) < 6: # FIXME: This error message is wrong -- there is no actual maximum length. raise ValueError( 'Please enter a password of between 6 and 20 characters') try: cpassword = canon_username(password, allow_reserved=True).decode('idna') except: cpassword = password.decode('utf-8') try: username = canon_username(username, allow_reserved=True).decode('idna') except: try: username = username.decode('idna') except: username = username.decode('utf-8') # import here because this is a big, slow module from BTL.canonical.identifier import confuse password_letters = list(set([ch for ch in confuse(cpassword)])) password_letters.sort() username_letters = list(set([ch for ch in confuse(username)])) username_letters.sort() if cpassword in username or u''.join(password_letters) == u''.join( username_letters): raise ValueError('password is too similar to user name') # TODO: password re-use prevention (password history) # TODO: complexity checks (dictionary?) # TODO: lockout (temporary and permanent) after failed login attempts return password
def account_name_graphic(value, prefix_reserved=[u'xn--'], full_name_reserved=[]): ''' Transform an acocunt name for graphic form collation. ''' if value is not None: from BTL.canonical.identifier import confuse value = to_unicode(value) value = unicodedata.normalize('NFC', value) value = value.strip() value = u'-'.join(value.split()) value = username_premap_re.sub(lambda m: username_premap[m.group(0)], value) global _account_name_graphic_premap, _account_name_graphic_premap_re value = value.strip() value = confuse(u'-').join(value.split()) if _account_name_graphic_premap is None: _account_name_graphic_premap = dict([ (confuse(k), confuse(v)) for k, v in username_premap.iteritems() ]) if _account_name_graphic_premap_re is None: _account_name_graphic_premap_re = re.compile( ur'|'.join([ ur'(?:%s)' % re.escape(i) for i in _account_name_graphic_premap ]), re.UNICODE) value = _account_name_graphic_premap_re.sub( lambda m: _account_name_graphic_premap[m.group(0)], value) value = confuse(value) for prefix in prefix_reserved: if value.startswith(confuse(prefix)): raise ValueError( 'The requested user name is reserved. It starts with something a lot like ' + prefix) for full_name in full_name_reserved: if value == confuse(full_name): raise ValueError( 'The requested user name is reserved. It is too much like ' + full_name) return value