示例#1
0
async def change_password(document):
    decoded_token = document['authToken']
    current_password = document['currentPassword']
    new_password = document['newPassword']
    user = await conf.mongo_accounts.find_one(
        filter = {'authTokens': {'$elemMatch': {'authToken': decoded_token}}},
        projection = ['_id', 'password']
    )
    if not bcrypt.verify(current_password, user['password']):
        return Response(
            {
                "success": False,
                "error": _("Incorrect password.")
            },
            403
        )
    if bcrypt.verify(new_password, user['password']):
        return Response({
            "success": False,
            "error": _("This is the same password as the current one.")
        })
    hashed_new_password = bcrypt.hash(new_password)
    await conf.mongo_accounts.update_one(
        filter = {'_id': user['_id']},
        update = {'$set': {'password': hashed_new_password}}
    )
    return Response({"success": True})
示例#2
0
async def _can_change_email(user, new_email):
    if 'emailLower' not in user:
        return False, _("This user didn't register via email.")
    if user['emailLower'] == new_email.lower():
        return False, _("This is already the email address of this account.")
    if await email_exists(new_email, case_sensitive = False):
        return False, _("This email address already belongs to a registered account.")
    return True, None
示例#3
0
async def login(document):
    user = await conf.mongo_accounts.find_one(
        {'email': document['email']},
        projection = (
            _actual_fields_names(document.get('fields', []))
            +
            ['authTokens', 'password', 'active']
        )
    )
    if user:
        if 'password' not in user:
            return Response({"success": False, "error": _("Password is not set for this user.")}, 403)
        if 'active' not in user or user['active'] is not True:
            return Response({"success": False, "error": _("This user isn't active.")}, 403)
        try:
            password_matches = bcrypt.verify(document['password'], user['password'])
        except ValueError: # this should not happen if the hash has been correctly computed
            return Response(
                {
                    "success": False,
                    "error": _("The password verification failed because of a server side issue.")
                },
                status = 500
            )
        else:
            if password_matches:
                dct = {
                    "success": True
                }
                fields = document.get('fields')
                if fields:
                    dct['user'] = _rec_objectids_to_str(_fields_document(user, fields))
                dct['authToken'] = await _generate_auth_token(user)
                return Response(dct)
            else:
                return Response(
                    {
                        "success": False,
                        "error": _("Incorrect password.")
                    },
                    403
                )
    else:
        return Response(
            {
                "success": False,
                "error": _("No such user.")
            },
            403
        )
示例#4
0
 def send_registration_code(self, code, to, lang=None):
     self.send_email(
         self.code_email(_('Registration'),
                         self.REGISTRATION_CODE_MESSAGE,
                         code,
                         to,
                         lang=lang), to)
示例#5
0
 def send_change_email_code(self, code, to, lang=None):
     self.send_email(
         self.code_email(_('Validation of your new email address'),
                         self.CHANGE_EMAIL_CODE_MESSAGE,
                         code,
                         to,
                         lang=lang), to)
示例#6
0
async def change_email(document):
    decoded_token = document['authToken']
    user_id = document['decoded']['_id']
    new_email = document['decoded']['newEmail']
    new_email_lower = new_email.lower()

    try:
        update_result = await conf.mongo_accounts.update_one(
            filter = {
                '_id': user_id,
                'authTokens': {
                    '$elemMatch': {'authToken': decoded_token}
                }
            },
            update = {'$set': {
                'emailLower': new_email_lower,
                'email': new_email
            }}
        )
    except DuplicateKeyError:
        return Response(email_used_result)
    else:
        if update_result.matched_count == 1:
            return Response({"success": True})
        else:
            return Response(
                {
                    "success": False,
                    "error": _("No user account matches this user id and this authToken.")
                },
                403
            )
示例#7
0
 def send_reset_password_code(self, code, to, lang=None):
     self.send_email(
         self.code_email(_('Password reset'),
                         self.RESET_PASSWORD_CODE_MESSAGE,
                         code,
                         to,
                         lang=lang), to)
示例#8
0
async def delete_user(document):
    delete_result = await conf.mongo_accounts.delete_one(
        {'_id': document['_id']},
    )
    if delete_result.deleted_count == 1:
        return Response({"success": True})
    else:
        return Response({"success": False, "error": _("No such user.")})
示例#9
0
文件: core.py 项目: leforestier/naval
 def __init__(self, regex, flags = 0, error_message = _("Incorrect value.")):
     if isinstance(regex, basestring):
         if not regex.startswith('^'):
             regex = '^' + regex
         if not regex.endswith('$'):
             regex = regex + '$'
         self.regex = re.compile(regex, flags)
     else:
         self.regex = regex        
     self.error_message = error_message
示例#10
0
async def check_reset_password_code(document):
    if await email_exists(document['decoded']['email'], case_sensitive = True):
        return Response({"success": True})
    else:
        # The user could have changed their email address in the meantime.
        # That would be strange but not impossible.
        return Response({
            "success": False,
            "error": _("No such email in the database.")
        })
示例#11
0
文件: core.py 项目: leforestier/naval
 def run(self, value):
     result = []
     for i, val in enumerate(value):
         try:
             result.append(self._filter.run(val))
         except ValidationError as exc:
             raise ValidationError(
                 _("Item #%s: ") % (i + self.__class__.ITEM_START) + exc.error_details
             )
     if isinstance(value, (tuple, set)):
         result = type(value)(result)
     return result
示例#12
0
async def _set_active(user_id, value):
    update = {'$set': {'active': value}}
    if not value:
        update['$unset'] = {'authTokens': ''}
    update_result = await conf.mongo_accounts.update_one(
        filter = {'_id': user_id},
        update = update
    )
    if update_result.matched_count == 1:
        return Response({"success": True})
    else:
        return Response({"success": False, "error": _("No such user.")})
示例#13
0
async def del_user_fields(document):
    if document['del']:
        decoded_token = document['authToken']
        update_result = await conf.mongo_accounts.update_one(
            filter = {'authTokens': {'$elemMatch': {'authToken': decoded_token}}},
            update = {'$unset': {('fields.' + k): '' for k in document['del']}}
        )
        if update_result.matched_count == 0:
            return Response(
                {"success": False, "error": _("Unrecognized authToken.")},
                403
            )
    return Response({"success": True})
示例#14
0
async def check_change_email_code(document):
    user = await conf.mongo_accounts.find_one(
        filter = {'_id': document['decoded']['_id']},
        projection = ['_id', 'emailLower']
    )
    if user:
        can_change, error = await _can_change_email(user, document['decoded']['newEmail'])
        if can_change:
            return Response({"success": True})
        else:
            return Response({"success": False, "error": error})
    else:
            return Response({"success": False, "error": _("No such user.")})
示例#15
0
async def reset_password(document):
    email = document['decoded']['email']
    new_password = document['newPassword']
    update_result = await conf.mongo_accounts.update_one(
        filter = {'email': email},
        update = {'$set': {'password': new_password}}
    )
    if update_result.matched_count:
        return Response({"success": True})
    else:
        return Response({
            "success": False,
            "error": _("No such email in the database.")
        })
示例#16
0
文件: core.py 项目: leforestier/naval
 def run(self, value):
     type_ = type(value)
     if (
         (self._subclasses and not (any (issubclass(type_, t) for t in self.types)))
         or
         (not self._subclasses and type_ not in self.types)
     ):
         types_str = ', '.join(t.__name__ for t in self.types)
         if len(self.types) == 1:
             raise ValidationError(
                 _("Wrong type. Expected {type}. Got {wrong_type} instead.").format(
                     type = types_str,
                     wrong_type = type_.__name__
                 )
             )
         else:
             raise ValidationError(
                 _("Wrong type. Expected one of {types}. Got {wrong_type} instead.").format(
                     types = types_str,
                     wrong_type = type_.__name__
                 )
             )
     return value
示例#17
0
async def send_reset_password_code(document, lang):
    email = document['email']
    if await email_exists(email, case_sensitive = True):
        reset_password_code = create_signed_document({
            'email': email,
            'action': 'resetPassword'
        })
        mailer.send_reset_password_code(
            reset_password_code,
            to = email,
            lang = lang
        )
        return Response({"success": True})
    else:
        return Response({"success": False, "error": _("No such email in the database.")})
示例#18
0
async def get_user_fields(document):
    decoded_token = document['authToken']
    user_document = await conf.mongo_accounts.find_one(
        {'authTokens': {'$elemMatch': {'authToken': decoded_token}}},
        projection = _actual_fields_names(document['fields'])
    )
    if user_document is None:
        return Response(
            {"success": False, "error": _("Unrecognized authToken.")},
            403
        )
    return Response({
        "success": True,
        "user": _rec_objectids_to_str(_fields_document(user_document, document['fields']))
    })
示例#19
0
async def get_user_id(document):
    # TODO document in the readthedoc
    decoded_token = document['authToken']
    user_document = await conf.mongo_accounts.find_one(
        {'authTokens': {'$elemMatch': {'authToken': decoded_token}}},
        projection = ['_id']
    )
    if user_document is None:
        return Response(
            {"success": False, "error": _("Unrecognized authToken.")},
            403
        )
    else:
        return Response(
            {"success": True, "_id": str(user_document['_id'])}
        )
示例#20
0
async def send_change_email_code(document, lang):
    decoded_token = document['authToken']
    new_email = document['email']
    user = await conf.mongo_accounts.find_one(
        filter = {'authTokens': {'$elemMatch': {'authToken': decoded_token}}},
        projection = ['_id', 'emailLower']
    )
    if user:
        can_change, error = await _can_change_email(user, new_email)
        if can_change:
            change_email_code = create_signed_document({
                'action': 'changeEmail',
                '_id': str(user['_id']),
                'newEmail': new_email
            })
            mailer.send_change_email_code(change_email_code, new_email, lang = lang)
            return Response({"success": True})
        else:
            return Response({"success": False, "error": error})
    else:
        return Response(
            {"success": False, "error": _("Unrecognized authToken.")},
            403
        )
示例#21
0
                validated_document = self.run(clear_document)
            except ValidationError as exc:
                if clear_document.get('authToken') and 'authToken' in exc.error_details:
                    error_status = 403
                else:
                    error_status = 400
                return Response(
                    {self.success_key: False, "error": exc.error_details},
                    status = error_status
                )
            else:
                return await func(validated_document, *args, **kwargs)
        return new_func

email_used_result = {
    "success": False, "error": _("This email address already belongs to a registered account.")
}

async def email_exists(email, case_sensitive):
    if case_sensitive:
        filter = {"email": email}
    else:
        filter = {"emailLower": email.lower()}
    result = await conf.mongo_accounts.find_one(filter)
    return bool(result)

@Sch(['email', Email])
async def send_registration_code(document, lang):
    email = document['email']
    if await email_exists(email, case_sensitive = False):
        return Response(email_used_result)
示例#22
0
文件: core.py 项目: leforestier/naval
    def run(self, dict_):
        Type(dict, subclasses = True).run(dict_)
        dct = dict_.copy()
        errors = {}
        policy = self.unexpected_keys_policy
        if policy is not Schema.KEEP:
            for key in dict_:
                if key not in self.expected_fields:
                    if policy is Schema.FAIL:
                        errors[key] = _("Unexpected key {key}.").format(key = repr(key))
                    del dct[key]

        for chain in self.chains:

            if chain.field:
                field = chain.field[0]
                if field in dct:
                    if dct[field] in chain.discard:
                        del dct[field]
                try:
                    value = dct[field]
                except KeyError:
                    if chain.optional:
                        continue
                    if chain.default:
                        if errors and isinstance(chain.default, DefaultFunc):
                            continue # avoid working with potentially invalid data
                        dct[field] = value = chain.default.getvalue(dct)
                    else:
                        errors[field] = _("Field is missing.")
                        continue
            else:
                # we work on the whole document
                if errors:
                    continue # avoid working with potentially invalid data
                value = dct
            
            # applying filters
            error = False
            for f in chain.filters:
                try:
                    value = f.run(value)
                except ValidationError as exc:
                    if chain.field:
                        errors[chain.field[0]] = exc.error_details
                    else:
                        errors['*'] = exc.error_details               
                    error = True
                    break
            if error:
                if isinstance(chain.storage_instruction, (SaveAs, MoveTo)):
                    errors[chain.storage_instruction.name] = _("Couldn't compute field.")
                continue

            if chain.storage_instruction:
                if not chain.field and chain.storage_instruction is Save:
                    dct = value
                else:
                    chain.storage_instruction.execute(dct, chain.field[0] if chain.field else None, value)

        if errors:
            raise ValidationError(errors)
        return dct
示例#23
0
文件: core.py 项目: leforestier/naval
 def __init__(self, unary_test, error_message = _("Incorrect value.")):
     self.unary_test = unary_test
     self.error_message = error_message
示例#24
0
文件: core.py 项目: leforestier/naval
 def __init__(self, collection, error_message = _("Incorrect value.")):
     self.collection = collection
     self.error_message = error_message
示例#25
0
文件: core.py 项目: leforestier/naval
            if not regex.startswith('^'):
                regex = '^' + regex
            if not regex.endswith('$'):
                regex = regex + '$'
            self.regex = re.compile(regex, flags)
        else:
            self.regex = regex        
        self.error_message = error_message

    def run(self, value):
        if not self.regex.match(value):
            raise ValidationError(self.error_message)
        return value

def to_filter(f):
    if isinstance(f, Filter):
        return f
    elif f is int:
        return ToInt # to get the i18ned error messages
    elif f is float:
        return ToFloat # same as above
    elif callable(f):
        return Apply(f)
    elif hasattr(f, '__contains__'):
        return In(f) 
    else:
        raise ValueError("%s is not a valid filter" % repr(f)) 

ToInt = Apply(int, error_message = _("This should be an integer.")) # useful to get i18ned error messages
ToFloat = Apply(float, error_message = _("This should be a number."))
示例#26
0
def DecodeSignedString(max_age_days, error_message=None):
    return Do(
        Apply(lambda message: decode_signed_string(message, max_age_days)),
        Assert(lambda result: result is not None),
        error_message=error_message or _("Invalid or expired code"))
示例#27
0
class Mailer(object):

    REGISTRATION_CODE_MESSAGE = _("""Hello,

please follow this link to confirm your email address and register at {serviceName}:

{url}

See you in a minute!
""")

    RESET_PASSWORD_CODE_MESSAGE = _("""Hello,

you asked to reset your password at {serviceName}.
To set a new password, follow this link:

{url}
""")

    CHANGE_EMAIL_CODE_MESSAGE = _("""Hello,

you asked to change the email address associated with your account.
Please follow this link to confirm your new email address:

{url}
""")

    def __init__(self):
        self.serviceName = conf.get('serviceName')
        self.emailCodeLink = conf.get('emailCodeLink')
        smtp_params = conf.get('smtp')
        self.email_account = EmailAccount(
            smtp_params['from'],
            smtp_params['smtpServer'],
            smtp_params['username'],
            smtp_params['password'],
            port=smtp_params['port'],
            sender_name=smtp_params.get('senderName'))

    def code_email(self, subject, template, code, to, lang=None):
        msg = MIMEText(
            translate(
                template.format(serviceName=self.serviceName,
                                url=url_add_element(self.emailCodeLink, code)),
                lang or 'en'))
        msg['From'] = self.email_account.email
        msg['To'] = to
        msg['Date'] = formatdate(localtime=True)
        msg['Subject'] = '[%s] %s' % (self.serviceName,
                                      translate(subject, lang or 'en'))
        return msg

    def send_email(self, email, to):
        IOLoop.current().spawn_callback(self.email_account.send_email, email,
                                        to)

    def send_registration_code(self, code, to, lang=None):
        self.send_email(
            self.code_email(_('Registration'),
                            self.REGISTRATION_CODE_MESSAGE,
                            code,
                            to,
                            lang=lang), to)

    def send_reset_password_code(self, code, to, lang=None):
        self.send_email(
            self.code_email(_('Password reset'),
                            self.RESET_PASSWORD_CODE_MESSAGE,
                            code,
                            to,
                            lang=lang), to)

    def send_change_email_code(self, code, to, lang=None):
        self.send_email(
            self.code_email(_('Validation of your new email address'),
                            self.CHANGE_EMAIL_CODE_MESSAGE,
                            code,
                            to,
                            lang=lang), to)
from coolsignup.conf import conf
from naval import Type, Length, Assert, Do
from postpone import LazyString as _
from zxcvbn import zxcvbn

PASSWORD_MAX_LENGTH = 800

NewPasswordValidator = Do(
    Type(str), Length(conf.get('passwordMinLength'), PASSWORD_MAX_LENGTH),
    Assert(
        (lambda p: zxcvbn(p)['guesses_log10'] >= conf.get('passwordStrength')),
        error_message=_("Password too easy to guess.")))
示例#29
0
文件: util.py 项目: leforestier/naval
from validators import email, ValidationFailure
from naval.core import *
from postpone import LazyString as _

__all__ = ['Email', 'Url']

Email = Assert(
    lambda v: not isinstance(email(v, whitelist = ()), ValidationFailure),
    error_message = _("This is not a valid email address.")
)

Email.__doc__ = """
    Email validator.
    This validator uses the email validator from the "validators" library: https://github.com/kvesteri/validators
"""

Url = Do(
    Type(str),
    Length(max=2083),
    # regex stolen from the php Spoon Library: https://github.com/spoon/library/blob/master/spoon/filter/filter.php
    Regex(
        r'(?:(?:https?|ftp)://)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\xa1-\xff0-9]+-?)*[a-z\xa1-\xff0-9]+)(?:\.(?:[a-z\xa1}-\xff0-9]+-?)*[a-z\xa1-\xff0-9]+)*(?:\.(?:[a-z\xa1-\xff]{2,})))(?::\d{2,5})?(?:/[^\s]*)?'
    ),
    error_message = _("This is not a valid url.")
)
Url.__doc__ = """
    Url validator.
    The regex used is stolen from the php Spoon Library: https://github.com/spoon/library/blob/master/spoon/filter/filter.php    
"""