Esempio n. 1
0
    def post(self, username=None, userMethod=None, ip=None, duplicate=None):

        # Have to be an admin to access admin pages.
        if not is_administrator:
            raise HTTPNotFound()

        # Seed the initial results.
        users = User.objects()

        # Limit to users with the specified username.
        if username:
            if userMethod == 'contains':
                users = users.filter(username__icontains=username)
            elif userMethod == 'starts':
                users = users.filter(username__istartswith=username)
            elif userMethod == 'is':
                users = users.filter(username__iexact=username)
            else:
                return 'json:', dict(
                    success=False,
                    message=_("You broke the web page. Good Job."))

        # Limit to users with the specified IP address.
        if ip:
            users = users.filter(host=ip)

        # Limit to users with the specified duplicate status
        if duplicate.lower() == "ip":
            users = users.filter(other_accs_IP__exists=True)
        elif duplicate.lower() == "char":
            users = users.filter(other_accs_char_key__exists=True)

        return 'brave.core.admin.template.searchUser', dict(area='admin',
                                                            result=users,
                                                            success=True)
Esempio n. 2
0
 def post(self, username=None, userMethod=None, ip=None, duplicate=None):
     
     # Have to be an admin to access admin pages.            
     if not is_administrator:
         raise HTTPNotFound()
         
     # Seed the initial results.
     users = User.objects()
     
     # Limit to users with the specified username.
     if username:
         if userMethod == 'contains':
             users = users.filter(username__icontains=username)
         elif userMethod == 'starts':
             users = users.filter(username__istartswith=username)
         elif userMethod == 'is':
             users = users.filter(username__iexact=username)
         else:
             return 'json:', dict(success=False, message=_("You broke the web page. Good Job."))
     
     # Limit to users with the specified IP address.
     if ip:
         users = users.filter(host=ip)
         
     # Limit to users with the specified duplicate status
     if duplicate.lower() == "ip":
         users = users.filter(other_accs_IP__exists=True)
     elif duplicate.lower() == "char":
         users = users.filter(other_accs_char_key__exists=True)
         
     return 'brave.core.admin.template.searchUser', dict(area='admin', result=users, success=True)
Esempio n. 3
0
def migrate():
    failures = []
    for u in User.objects():
        username_success = ensure_lowercase(u, 'username')
        email_success = ensure_lowercase(u, 'email')
        if not username_success or not email_success:
            failures.append(u)
    return failures
Esempio n. 4
0
def migrate():
    failures = []
    for u in User.objects():
        username_success = ensure_lowercase(u, 'username')
        email_success = ensure_lowercase(u, 'email')
        if not username_success or not email_success:
            failures.append(u)
    return failures
Esempio n. 5
0
def lookup(identifier):
    """Thaw current user data based on session-stored user ID."""
    
    user = User.objects(id=identifier).first()
    
    if user:
        user.update(set__seen=datetime.utcnow())  # , set__host=request.remote_addr -- chicken-egg problem
    
    return user
Esempio n. 6
0
def ensure_lowercase(u, field):
    if u[field] != u[field].lower():
        try:
            updated = User.objects(id=u.id, **{field:u[field]}).update_one(
                    **{'set__'+field: u[field].lower()})
            if not updated:
                print("failure updating {} (raced user?): {}".format(field, u))
                return False
        except OperationError:
            print("collision updating {}: {}".format(field, u))
            return False
    return True
Esempio n. 7
0
def authenticate(identifier, password):
    """Given an e-mail address (or Yubikey OTP) and password, authenticate a user."""
    
    ts = time()  # Record the 
    query = dict(active=True)
    
    # Gracefully handle extended characters in passwords.
    # The password storage algorithm works in binary.
    if isinstance(password, unicode):
        password = password.encode('utf8')
    
    # Build the MongoEngine query to find 
    if '@' in identifier:
        query[b'email'] = identifier
    elif len(identifier) == 44:
        query[b'otp'] = identifier[:12]
    else:
        query[b'username'] = identifier
    
    user = User.objects(**query).first()
    
    if not user or not User.password.check(user.password, password) or (user.rotp and len(user.otp) != 0 and not 'otp' in query):
        if user:
            LoginHistory(user, False, request.remote_addr).save()
        
        # Prevent basic timing attacks; always take at least one second to process.
        sleep(max(min(1 - (time() - ts), 0), 1))
        
        return None
    
    # Validate Yubikey OTP
    if 'otp' in query:
        client = yubico.Yubico(
                config['yubico.client'],
                config['yubico.key'],
                boolean(config.get('yubico.secure', False))
            )
        
        try:
            status = client.verify(identifier, return_response=True)
        except:
            return None
        
        if not status:
            return None
    
    user.update(set__seen=datetime.utcnow())
    
    # Record the fact the user signed in.
    LoginHistory(user, True, request.remote_addr).save()
    
    return user.id, user
Esempio n. 8
0
def ensure_lowercase(u, field):
    if u[field] != u[field].lower():
        try:
            updated = User.objects(id=u.id, **{
                field: u[field]
            }).update_one(**{'set__' + field: u[field].lower()})
            if not updated:
                print("failure updating {} (raced user?): {}".format(field, u))
                return False
        except OperationError:
            print("collision updating {}: {}".format(field, u))
            return False
    return True
Esempio n. 9
0
 def check_user(self, id, username, email):
     u = User.objects(id=id)[0]
     self.assertEqual(u.username, username)
     self.assertEqual(u.email, email)
Esempio n. 10
0
    def post(self, **post):
        try:
            data = Bunch(post)
        except Exception as e:
            if config.get('debug', False):
                raise
            return 'json:', dict(success=False, message=_("Unable to parse data."), data=post, exc=str(e))

        query = dict(active=True)
        query[b'username'] = data.id

        query_user = User.objects(**query).first()
        if query_user.id != user.id:
            raise HTTPForbidden

        if data.form == "changepassword":
            passwd_ok, error_msg = _check_password(data.passwd, data.passwd1)

            if not passwd_ok:
                return 'json:', dict(success=False, message=error_msg, data=data)

            if isinstance(data.old, unicode):
                data.old = data.old.encode('utf-8')

            if not User.password.check(user.password, data.old):
                return 'json:', dict(success=False, message=_("Old password incorrect."), data=data)

            #If the password isn't strong enough, reject it
            if(zxcvbn.password_strength(data.passwd).get("score") < MINIMUM_PASSWORD_STRENGTH):
                return 'json:', dict(success=False, message=_("Password provided is too weak. please add more characters, or include lowercase, uppercase, and special characters."), data=data)

            user.password = data.passwd
            user.save()
        elif data.form == "addotp":
            if isinstance(data.password, unicode):
                data.password = data.password.encode('utf-8')

            identifier = data.otp
            client = yubico.Yubico(
                config['yubico.client'],
                config['yubico.key'],
                boolean(config.get('yubico.secure', False))
            )

            if not User.password.check(user.password, data.password):
                return 'json:', dict(success=False, message=_("Password incorrect."), data=data)

            try:
                status = client.verify(identifier, return_response=True)
            except:
                return 'json:', dict(success=False, message=_("Failed to contact YubiCloud."), data=data)

            if not status:
                return 'json:', dict(success=False, message=_("Failed to verify key."), data=data)

            if not User.addOTP(user, identifier[:12]):
                return 'json:', dict(success=False, message=_("YubiKey already exists."), data=data)
        elif data.form == "removeotp":
            identifier = data.otp

            if not User.removeOTP(user, identifier[:12]):
                return 'json:', dict(success=False, message=_("YubiKey invalid."), data=data)

        elif data.form == "configureotp":
            if isinstance(data.password, unicode):
                data.password = data.password.encode('utf-8')
            rotp = True if 'rotp' in data else False

            if not User.password.check(user.password, data.password):
                return 'json:', dict(success=False, message=_("Password incorrect."), data=data)
            
            user.rotp = rotp
            user.save()
            
        #Handle the user attempting to delete their account
        elif data.form == "deleteaccount":
            if isinstance(data.passwd, unicode):
                data.passwd = data.passwd.encode('utf-8')
             
            #Make the user enter their username so they know what they're doing.
            if not user.username == data.username.lower():
                return 'json:', dict(success=False, message=_("Username incorrect."), data=data)
        
            #Check whether the user's supplied password is correct
            if not User.password.check(user.password, data.passwd):
                return 'json:', dict(success=False, message=_("Password incorrect."), data=data)
                
            #Make them type "delete" exactly
            if not data.confirm == "delete":
                return 'json:', dict(success=False, message=_("Delete was either misspelled or not lowercase."), data=data)
            
            #Delete the user account and then deauthenticate the browser session
            log.info("User %s authorized the deletion of their account.", user)
            user.delete()
            deauthenticate()
            
            #Redirect user to the root of the server instead of the settings page
            return 'json:', dict(success=True, location="/")
            
        #Handle the user attempting to change the email address associated with their account
        elif data.form == "changeemail":
            if isinstance(data.passwd, unicode):
                data.passwd = data.passwd.encode('utf-8')
        
            #Check whether the user's supplied password is correct
            if not User.password.check(user.password, data.passwd):
                return 'json:', dict(success=False, message=_("Password incorrect."), data=data)

            #Check that the two provided email addresses match
            if not data.newEmail.lower() == data.newEmailConfirm.lower():
                return 'json:', dict(success=False, message=_("Provided email addresses do not match."), data=data)
            
            #Make sure that the provided email address is a valid form for an email address
            v = EmailValidator()
            email = data.newEmail
            email, err = v.validate(email)
            if err:
                return 'json:', dict(success=False, message=_("Invalid email address provided."), data=data)
                
            #Make sure that the new email address is not already taken
            count = User.objects.filter(**{"email": data.newEmail.lower()}).count()
            if not count == 0:
                return 'json:', dict(success=False, message=_("The email address provided is already taken."), data=data)
       
            #Change the email address in the database and catch any email validation exceptions that mongo throws
            user.email = data.newEmail.lower()
            try:
                user.save()
            except ValidationError:
                return 'json:', dict(success=False, message=_("Invalid email address provided."), data=data)
            except NotUniqueError:
                return 'json:', dict(success=False, message=_("The email address provided is already taken."), data=data)
        
        #Handle the user attempting to merge 2 accounts together
        elif data.form == "mergeaccount":
            if isinstance(data.passwd, unicode):
                data.passwd = data.passwd.encode('utf-8')
                
            if isinstance(data.passwd2, unicode):
                data.passwd2 = data.passwd2.encode('utf-8')
                
            #Make the user enter their username so they know what they're doing.
            if user.username != data.username.lower() and user.username != data.username:
                return 'json:', dict(success=False, message=_("First username incorrect."), data=data)
                
            #Check whether the user's supplied password is correct
            if not User.password.check(user.password, data.passwd):
                return 'json:', dict(success=False, message=_("First password incorrect."), data=data)
                
            #Make sure the user isn't trying to merge their account into itself.
            if data.username.lower() == data.username2.lower():
                return 'json:', dict(success=False, message=_("You can't merge an account into itself."), data=data)
                
            #Make the user enter the second username so we can get the User object they want merged in.
            if not User.objects(username=data.username2.lower()) and not User.objects(username=data.username2):
                return 'json:', dict(success=False, message=_("Unable to find user by second username."), data=data)
                
            other = User.objects(username=data.username2).first()
            if not other:
                other = User.objects(username=data.username2.lower()).first()
                
            #Check whether the user's supplied password is correct
            if not User.password.check(other.password, data.passwd2):
                return 'json:', dict(success=False, message=_("Second password incorrect."), data=data)
                
            #Make them type "merge" exactly
            if data.confirm != "merge":
                return 'json:', dict(success=False, message=_("Merge was either misspelled or not lowercase."), data=data)
                
            log.info("User %s merged account %s into %s.", user.username, other.username, user.username)
            user.merge(other)
            
            #Redirect user to the root of the server instead of the settings page
            return 'json:', dict(success=True, location="/")
            
        else:
            return 'json:', dict(success=False, message=_("Form does not exist."), location="/")
        
        return 'json:', dict(success=True, location="/account/settings")
Esempio n. 11
0
def lookup_email(email):
    """get user by email address"""
    user = User.objects(email=email).first()
    return user
Esempio n. 12
0
    def post(self, **kw):
        data = Bunch(kw)

        try:
            data.key = int(data.key)
            if data.key <= int(config["core.minimum_key_id"]):
                return (
                    "json:",
                    dict(
                        success=False,
                        message=_(
                            "The key given (%d) must be above minimum reset floor value of %d. "
                            "Please reset your EVE API Key." % (data.key, int(config["core.minimum_key_id"]))
                        ),
                        field="key",
                    ),
                )

        except ValueError:
            return "json:", dict(success=False, message=_("Key ID must be a number."), field="key")

        record = EVECredential(data.key, data.code, owner=user.id)

        try:
            record.save()
            # Necessary to guarantee that the pull finished before returning.
            record.pull()
            characters = []
            for character in record.characters:
                characters.append(dict(identifier=character.identifier, name=character.name))

            if request.is_xhr:
                return (
                    "json:",
                    dict(
                        success=True,
                        message=_("Successfully added EVE API key."),
                        identifier=str(record.id),
                        key=record.key,
                        code=record.code,
                        characters=characters,
                        violation=record.violation,
                    ),
                )

        except ValidationError:
            if request.is_xhr:
                return (
                    "json:",
                    dict(success=False, message=_("Validation error: one or more fields are incorrect or missing.")),
                )
        except NotUniqueError:
            if EVECredential.objects(key=data.key):
                # Mark both of these accounts as duplicates to each other.
                acc = User.objects(username=user.username).first()
                other = EVECredential.objects(key=data.key).first().owner

                User.add_duplicate(acc, other)

            return (
                "json:",
                dict(success=False, message=_("This key has already been added to this or another account.")),
            )

        raise HTTPFound(location="/key/")
Esempio n. 13
0
def authenticate(identifier, password):
    """Given an e-mail address (or Yubikey OTP) and password, authenticate a user."""

    ts = time()  # Record the
    query = dict(active=True)

    # Gracefully handle extended characters in passwords.
    # The password storage algorithm works in binary.
    if isinstance(password, unicode):
        password = password.encode("utf8")

    # Build the MongoEngine query to find
    if "@" in identifier:
        query[b"email"] = identifier
    elif len(identifier) == 44:
        query[b"otp"] = identifier[:12]
    else:
        query[b"username"] = identifier

    user = User.objects(**query).first()

    if (
        not user
        or not User.password.check(user.password, password)
        or (user.rotp and len(user.otp) != 0 and not "otp" in query)
    ):
        if user:
            LoginHistory(user, False, request.remote_addr).save()

        # Prevent basic timing attacks; always take at least one second to process.
        sleep(max(min(1 - (time() - ts), 0), 1))

        return None

    # Validate Yubikey OTP
    if "otp" in query:
        client = yubico.Yubico(
            config["yubico.client"], config["yubico.key"], boolean(config.get("yubico.secure", False))
        )

        try:
            status = client.verify(identifier, return_response=True)
        except:
            return None

        if not status:
            return None

    user.update(set__seen=datetime.utcnow())

    # Record the fact the user signed in.
    LoginHistory(user, True, request.remote_addr).save()

    # Update the user's host
    user.host = request.remote_addr

    # Check for other accounts with this IP address
    if len(User.objects(host=request.remote_addr)) > 1:
        # Quite possibly the worst code ever
        for u in User.objects(host=request.remote_addr):
            User.add_duplicate(user, u, IP=True)

    user.save()

    return user.id, user
Esempio n. 14
0
    def post(self, **post):
        try:
            data = Bunch(post)
        except Exception as e:
            if config.get('debug', False):
                raise
            return 'json:', dict(success=False,
                                 message=_("Unable to parse data."),
                                 data=post,
                                 exc=str(e))

        query = dict(active=True)
        query[b'username'] = data.id

        user = User.objects(**query).first()

        if data.form == "changepassword":
            passwd_ok, error_msg = _check_password(data.passwd, data.passwd1)

            if not passwd_ok:
                return 'json:', dict(success=False,
                                     message=error_msg,
                                     data=data)

            if isinstance(data.old, unicode):
                data.old = data.old.encode('utf-8')

            if not User.password.check(user.password, data.old):
                return 'json:', dict(success=False,
                                     message=_("Old password incorrect."),
                                     data=data)

            #If the password isn't strong enough, reject it
            if (zxcvbn.password_strength(data.passwd).get("score") <
                    MINIMUM_PASSWORD_STRENGTH):
                return 'json:', dict(
                    success=False,
                    message=
                    _("Password provided is too weak. please add more characters, or include lowercase, uppercase, and special characters."
                      ),
                    data=data)

            user.password = data.passwd
            user.save()
        elif data.form == "addotp":
            if isinstance(data.password, unicode):
                data.password = data.password.encode('utf-8')

            identifier = data.otp
            client = yubico.Yubico(config['yubico.client'],
                                   config['yubico.key'],
                                   boolean(config.get('yubico.secure', False)))

            if not User.password.check(user.password, data.password):
                return 'json:', dict(success=False,
                                     message=_("Password incorrect."),
                                     data=data)

            try:
                status = client.verify(identifier, return_response=True)
            except:
                return 'json:', dict(success=False,
                                     message=_("Failed to contact YubiCloud."),
                                     data=data)

            if not status:
                return 'json:', dict(success=False,
                                     message=_("Failed to verify key."),
                                     data=data)

            if not User.addOTP(user, identifier[:12]):
                return 'json:', dict(success=False,
                                     message=_("YubiKey already exists."),
                                     data=data)
        elif data.form == "removeotp":
            identifier = data.otp

            if not User.removeOTP(user, identifier[:12]):
                return 'json:', dict(success=False,
                                     message=_("YubiKey invalid."),
                                     data=data)

        elif data.form == "configureotp":
            if isinstance(data.password, unicode):
                data.password = data.password.encode('utf-8')
            rotp = True if 'rotp' in data else False

            if not User.password.check(user.password, data.password):
                return 'json:', dict(success=False,
                                     message=_("Password incorrect."),
                                     data=data)

            user.rotp = rotp
            user.save()

        #Handle the user attempting to delete their account
        elif data.form == "deleteaccount":
            if isinstance(data.passwd, unicode):
                data.passwd = data.passwd.encode('utf-8')

            #Make the user enter their username so they know what they're doing.
            if not user.username == data.username.lower():
                return 'json:', dict(success=False,
                                     message=_("Username incorrect."),
                                     data=data)

            #Check whether the user's supplied password is correct
            if not User.password.check(user.password, data.passwd):
                return 'json:', dict(success=False,
                                     message=_("Password incorrect."),
                                     data=data)

            #Make them type "delete" exactly
            if not data.confirm == "delete":
                return 'json:', dict(
                    success=False,
                    message=_(
                        "Delete was either misspelled or not lowercase."),
                    data=data)

            #Delete the user account and then deauthenticate the browser session
            log.info("User %s authorized the deletion of their account.", user)
            user.delete()
            deauthenticate()

            #Redirect user to the root of the server instead of the settings page
            return 'json:', dict(success=True, location="/")

        #Handle the user attempting to change the email address associated with their account
        elif data.form == "changeemail":
            if isinstance(data.passwd, unicode):
                data.passwd = data.passwd.encode('utf-8')

            #Check whether the user's supplied password is correct
            if not User.password.check(user.password, data.passwd):
                return 'json:', dict(success=False,
                                     message=_("Password incorrect."),
                                     data=data)

            #Check that the two provided email addresses match
            if not data.newEmail.lower() == data.newEmailConfirm.lower():
                return 'json:', dict(
                    success=False,
                    message=_("Provided email addresses do not match."),
                    data=data)

            #Make sure that the provided email address is a valid form for an email address
            v = EmailValidator()
            email = data.newEmail
            email, err = v.validate(email)
            if err:
                return 'json:', dict(
                    success=False,
                    message=_("Invalid email address provided."),
                    data=data)

            #Make sure that the new email address is not already taken
            count = User.objects.filter(**{
                "email": data.newEmail.lower()
            }).count()
            if not count == 0:
                return 'json:', dict(
                    success=False,
                    message=_("The email address provided is already taken."),
                    data=data)

            #Change the email address in the database and catch any email validation exceptions that mongo throws
            user.email = data.newEmail.lower()
            try:
                user.save()
            except ValidationError:
                return 'json:', dict(
                    success=False,
                    message=_("Invalid email address provided."),
                    data=data)
            except NotUniqueError:
                return 'json:', dict(
                    success=False,
                    message=_("The email address provided is already taken."),
                    data=data)

        #Handle the user attempting to merge 2 accounts together
        elif data.form == "mergeaccount":
            if isinstance(data.passwd, unicode):
                data.passwd = data.passwd.encode('utf-8')

            if isinstance(data.passwd2, unicode):
                data.passwd2 = data.passwd2.encode('utf-8')

            #Make the user enter their username so they know what they're doing.
            if user.username != data.username.lower(
            ) and user.username != data.username:
                return 'json:', dict(success=False,
                                     message=_("First username incorrect."),
                                     data=data)

            #Check whether the user's supplied password is correct
            if not User.password.check(user.password, data.passwd):
                return 'json:', dict(success=False,
                                     message=_("First password incorrect."),
                                     data=data)

            #Make sure the user isn't trying to merge their account into itself.
            if data.username.lower() == data.username2.lower():
                return 'json:', dict(
                    success=False,
                    message=_("You can't merge an account into itself."),
                    data=data)

            #Make the user enter the second username so we can get the User object they want merged in.
            if not User.objects(
                    username=data.username2.lower()) and not User.objects(
                        username=data.username2):
                return 'json:', dict(
                    success=False,
                    message=_("Unable to find user by second username."),
                    data=data)

            other = User.objects(username=data.username2).first()
            if not other:
                other = User.objects(username=data.username2.lower()).first()

            #Check whether the user's supplied password is correct
            if not User.password.check(other.password, data.passwd2):
                return 'json:', dict(success=False,
                                     message=_("Second password incorrect."),
                                     data=data)

            #Make them type "merge" exactly
            if data.confirm != "merge":
                return 'json:', dict(
                    success=False,
                    message=_("Merge was either misspelled or not lowercase."),
                    data=data)

            log.info("User %s merged account %s into %s.", user.username,
                     other.username, user.username)
            user.merge(other)

            #Redirect user to the root of the server instead of the settings page
            return 'json:', dict(success=True, location="/")

        else:
            return 'json:', dict(success=False,
                                 message=_("Form does not exist."),
                                 location="/")

        return 'json:', dict(success=True, location="/account/settings")
Esempio n. 15
0
    def post(self, **kw):
        data = Bunch(kw)

        try:
            data.key = int(data.key)
            if data.key <= int(config['core.minimum_key_id']):
                return 'json:', dict(
                    success=False,
                    message=_(
                        "The key given (%d) must be above minimum reset floor value of %d. "
                        "Please reset your EVE API Key." %
                        (data.key, int(config['core.minimum_key_id']))),
                    field='key')

        except ValueError:
            return 'json:', dict(success=False,
                                 message=_("Key ID must be a number."),
                                 field='key')

        record = EVECredential(data.key, data.code, owner=user.id)

        try:
            record.save()
            #Necessary to guarantee that the pull finished before returning.
            record.pull()
            characters = []
            for character in record.characters:
                characters.append(
                    dict(identifier=character.identifier, name=character.name))

            if request.is_xhr:
                return 'json:', dict(
                    success=True,
                    message=_("Successfully added EVE API key."),
                    identifier=str(record.id),
                    key=record.key,
                    code=record.code,
                    characters=characters,
                    violation=record.violation)

        except ValidationError:
            if request.is_xhr:
                return 'json:', dict(
                    success=False,
                    message=
                    _("Validation error: one or more fields are incorrect or missing."
                      ),
                )
        except NotUniqueError:
            if EVECredential.objects(key=data.key):
                # Mark both of these accounts as duplicates to each other.
                acc = User.objects(username=user.username).first()
                other = EVECredential.objects(key=data.key).first().owner

                User.add_duplicate(acc, other)

            return 'json:', dict(
                success=False,
                message=
                _("This key has already been added to this or another account."
                  ),
            )

        raise HTTPFound(location='/key/')