Exemple #1
0
    def post(self):
        if any(f for f in ('email', 'password', 'name') if f not in self.data):
            raise APIError('Missing parameter(s)')
        if not utils.is_valid_email(self.data['email']):
            raise APIError('Invalid email address')
        if len(self.data['password']) < 8:
            raise APIError('Invalid password')
        if len(self.data['name']) < 3:
            raise APIError('Invalid name')

        try:
            models.get_active_account(email=self.data['email'])
            raise APIError('Email already registered')
        except models.Account.DoesNotExist:
            account = models.Account(name=self.data['name'], email=self.data['email'],
                                     password=generate_password_hash(self.data['password']),
                                     calories_goal=settings['default_calories_goal'])
            account.save()

            # make this account a user
            models.AccountRole(account=account, role=models.Role.get(code='user')).save()

            update_token = urlserializer.dumps({'id': account.id, 'active': True}, salt='account-update')
            url = url_for('public', path='apply-account-changes', account_id=account.id, token=update_token,
                          _external=True)

            tasks.send_activation_email.delay(account.id, url, update_token)
            return created_response('account', account_id=account.id)
Exemple #2
0
def login():
    if any(f for f in ('grant_type', 'username', 'password') if f not in request.form) or \
            any(k for k in request.form if k not in ('grant_type', 'username', 'password')):
        raise AppError('invalid_request')

    if request.form['grant_type'] != 'password':
        # only implementing Resource Owner Password Credentials Grant from RFC 6749
        raise AppError('unsupported_grant_type')

    try:
        account = models.get_active_account(email=request.form['username'])
    except models.Account.DoesNotExist:
        raise AppError('invalid_grant')

    if not check_password_hash(account.password, request.form['password']):
        raise AppError('invalid_grant')

    if not account.active:
        update_token = urlserializer.dumps({'id': account.id, 'active': True}, salt='account-update')
        url = url_for('public', path='apply-account-changes', account_id=account.id, token=update_token,
                      _external=True)
        tasks.send_activation_email.delay(account.id, url, update_token)
        raise AppError('invalid_grant', error_description='Your account is not active. An email has been sent '
                                                                 'to you with instructions to activate your account.')

    roles = map(lambda x: x.code, models.Role.select().join(models.AccountRole).where(models.AccountRole.account == account))

    # not generating refresh tokens as it's making it all the way to the user's browser. If an attacker got their
    # hands on a refresh token, they could do anything without a time restriction
    return jsonify({
        'access_token': urlserializer.dumps({'id': account.id, 'roles': roles}, salt='access-token'),
        'token_type': 'bearer',
        'expires_in': settings['oauth-token-expiration-seconds'],
        'scope': ' '.join(roles)
    })
Exemple #3
0
            def decorated_function(*args, **kwargs):
                account_id, token = verify_token_get_account(roles, accountid_free_roles, account_should_have_roles, kwargs)

                try:
                    account = models.get_active_account(id=account_id)
                except models.Account.DoesNotExist:
                    raise APIError('Not Found', 404)

                if 'account_id' in funct.__code__.co_varnames:
                    kwargs['account_id'] = account_id

                if 'account' in funct.__code__.co_varnames:
                    kwargs['account'] = account

                if 'roles' in funct.__code__.co_varnames:
                    kwargs['roles'] = token['roles']

                if len(args) and isinstance(args[0], Resource):
                    args[0].logged_in_account_id = token['id']
                    args[0].account = account
                    args[0].roles = token['roles']

                return funct(*args, **kwargs)
Exemple #4
0
    def put(self, account_id):
        # if the account is being updated through the update_token, then we don't need to authenticate
        # the user (they may not even be able to log in)
        if 'update_token' in self.data:
            return self.update_account(account_id)

        # verify authorization
        account_id, token = verify_token_get_account(['user', 'user-manager', 'admin'], ['user-manager', 'admin'], kwargs={'account_id': account_id})

        try:
            account = models.get_active_account(id=account_id)
        except models.Account.DoesNotExist:
            raise APIError('Account not found', 404)

        # perform updates
        if 'roles' in self.data:
            if not isinstance(self.data['roles'], list) or any(r for r in self.data['roles'] if not isinstance(r, basestring)):
                raise APIError('Invalid roles value')

            final_roles = models.Role.select().where(models.Role.code << self.data['roles'])
            if len(final_roles) != len(self.data['roles']):
                raise APIError('Invalid roles value')

            current_roles = models.Role.select().join(models.AccountRole).where(models.AccountRole.account == account)

            # changing roles is something only an admin can do
            if any(r for r in current_roles if r not in final_roles) or any(r for r in final_roles if r not in current_roles):
                verify_token_get_account(['admin'], ['admin'], kwargs={'account_id': account_id})

            # handle deletions
            for role in [r for r in current_roles if r not in final_roles]:
                models.AccountRole.get(models.AccountRole.account == account, models.AccountRole.role == role).delete_instance()

            # handle additions
            for role in [r for r in final_roles if r not in current_roles]:
                models.AccountRole(account=account, role=role).save()

        if 'name' in self.data:
            if not isinstance(self.data['name'], basestring):
                raise APIError('Invalid name')

            account.name = self.data['name']

        if 'email' in self.data and self.data['email'] != account.email:
            if not utils.is_valid_email(self.data['email']):
                raise APIError('Invalid email')

            try:
                models.get_active_account(email=self.data['email'])
                raise APIError('Email already used')
            except models.Account.DoesNotExist:
                pass

            if any(r for r in token['roles'] if r in ('user-manager', 'admin')):
                account.email = self.data['email']
            else:
                # verify the current password and send a link to update the email
                if 'current_password' not in self.data:
                    raise APIError('Missing current password')

                if not check_password_hash(account.password, self.data['current_password']):
                    raise APIError('Invalid current password')

                update_token = urlserializer.dumps({'id': account.id, 'email': self.data['email']}, salt='account-update')
                url = url_for('public', path='apply-account-changes', account_id=account.id, token=update_token,
                              _external=True)
                tasks.send_account_update_email.delay(account.id, url, update_token, email=self.data['email'])

        if 'password' in self.data:
            if len(self.data['password']) < 8:
                raise APIError('Invalid password')

            if not any(r for r in token['roles'] if r in ('user-manager', 'admin')):
                if 'current_password' not in self.data:
                    raise APIError('Missing current password')

                if not check_password_hash(account.password, self.data['current_password']):
                    raise APIError('Invalid current password')

            account.password = generate_password_hash(self.data['password'])

        if 'calories_goal' in self.data:
            try:
                account.calories_goal = int(self.data['calories_goal'])
            except:
                raise APIError('Invalid calories amount')

        account.save()
        return '', 204
Exemple #5
0
        token = urlserializer.loads(token, settings['oauth-token-expiration-seconds'], salt='access-token')
    except BadData, e:
        log.error(e)
        raise APIError('Unauthorized', 401)

    if roles and not any(r for r in roles if r in token['roles']):
        raise APIError('Unauthorized', 401)

    account_id = (kwargs['account_id'] if 'account_id' in kwargs else 0) or token['id']
    if account_id != token['id'] and not any(r for r in accountid_free_roles if r in token['roles']):
        raise APIError('Unauthorized', 401)

    # this is the destination account, to allow certain actions only if the account the request
    # is affecting if that account has certain roles
    if account_should_have_roles:
        roles = map(lambda x: x.code, models.Role.select().join(models.AccountRole).where(models.AccountRole.account == models.get_active_account(id=account_id)))
        if not any(r for r in account_should_have_roles if r in roles):
            raise APIError("This action can't be performed on this account")

    return account_id, token


def require_auth(roles=None, accountid_free_roles=[], account_should_have_roles=[]):
    def decorator(funct_or_class):
        def decorate_function(funct):
            def decorated_function(*args, **kwargs):
                account_id, token = verify_token_get_account(roles, accountid_free_roles, account_should_have_roles, kwargs)

                try:
                    account = models.get_active_account(id=account_id)
                except models.Account.DoesNotExist: