def test_08_remove_user(self):
     """Test User.remove() method"""
     with self.app.app_context():
         filters = {'username': '******'}
         users = User.get_list(filters)
         self.assertEqual(len(users), 1)
         users[0].remove()
         users = User.get_list(filters)
         self.assertEqual(len(users), 0)
 def test_04_user_get_list_by_firstname_lastname(self):
     """Test User.get_list() method with firstname and lastname"""
     with self.app.app_context():
         filters = {'firstname': 'Linda', 'lastname': 'Sample'}
         users = User.get_list(filters)
         self.assertEqual(len(users), 1)
         self.assertEqual(users[0].username, 'lindas')
 def test_03_user_get_list_by_username(self):
     """Test User.get_list() method with username=johne"""
     with self.app.app_context():
         filters = {'username': '******'}
         users = User.get_list(filters)
         self.assertEqual(len(users), 1)
         self.assertEqual(users[0].username, 'johne')
 def test_02_user_get_list_empty_filters(self):
     """Test User.get_list() method with empty filters dictionary"""
     with self.app.app_context():
         filters = {}
         users = User.get_list(filters)
         self.assertEqual(len(users), 4)
         self.assertIsInstance(users[0], User)
def create_user(data):
    """
    Create User Resource

    Args:
        data - dictionary with all User Resource attributes, loaded from
            Request body JSON and validated with models.user_schema
        JWT Baerer Authorization in request.headers - admin privilege required

    Returns:
        Confirmation or Error Message
        'Location' Response Header
    """
    if User.get_list({'username': data['username']}):
        current_app.logger.warning(
            f'create_user() failed. Username={data["username"]} already exists'
            )
        return make_response('Bad request', 400)

    # Ignore 'userid' if present in request data
#    if 'userid' in data:
#        del(data['userid'])
#
    new_user = User(**data)

    response = make_response('Created', 201)
    response.headers['Location'] = url_for(
        'users.retrieve_user',
        userid=new_user.userid,
        _external=True
        )

    return response
def list_users():
    """
    List and filter Users Collection

    Args:
        request.args - Query String parameters: filtering, sorting
            and pagination
        X-API-Key in request.headers

    Returns:
        JSON array of User Resource Representations
    """
    try:
        filters = users_filters_schema.load(request.args)
    except ValidationError as e:
        current_app.logger.warning(
            f'list_group() Query String validation failed.\nValidationError: {e}'
            )
        return make_response('Bad request', 400)

    filtered_list = User.get_list(filters)
    if 'return_fields' in filters:
        return_fields = filters['return_fields'].split(',') + ['href']
        users = UserListSchema(many=True, only=return_fields).dump(filtered_list)
    else:
        users = user_list_schema.dump(filtered_list)
    return jsonify(users)
def update_user(userid, data):
    """
    Update User Resource Representation

    Args:
        userid: Path Parameter - Unique ID of User Resource (int)
        data - dictionary with partial User Resource attributes, loaded from
            Request body JSON and validated with models.user_schema
        JWT Baerer Authorization in request.headers - account owner or
            admin privilege required

    Returns:
        Confirmation or Error Message
    """
    user = User.retrieve(userid)
    if not user:
        return make_response('Not found', 404)

    if 'username' in data and User.get_list({'username': data['username']}):
        current_app.logger.warning(
            f'create_user() failed. Username={data["username"]} already exists'
            )
        return make_response('Bad request', 400)

    if current_user.userid != userid and not current_user.get_admin():
        current_app.logger.warning(
            f'update_user(userid={userid}) failed. Userid={current_user.userid} not authorized'
            )
        return make_response('Unauthorized', 401)

    user.update(**data)

    return make_response('OK', 200)
 def test_12_user_get_list_pagination(self):
     """Test User.get_list() pagination filters"""
     with self.app.app_context():
         # Database should contain johne, lindas and lin
         filters = {'sortBy': '-lastname', 'offset': 1, 'limit': 2}
         users = User.get_list(filters)
         self.assertEqual(len(users), 2)
         self.assertEqual(users[0].username, 'lin')
 def test_09_set_password(self):
     """Test User.set_password() method"""
     with self.app.app_context():
         filters = {'username': '******'}
         users = User.get_list(filters)
         self.assertEqual(len(users), 1)
         users[0].set_password('pass')
         user = User.retrieve(users[0].userid)
         self.assertEqual(user.password, 'pass')
 def test_05_user_retrieve(self):
     """Test User.retrieve() method"""
     with self.app.app_context():
         filters = {'username': '******'}
         users = User.get_list(filters)
         self.assertEqual(len(users), 1)
         user = User.retrieve(users[0].userid)
         self.assertIsNotNone(user)
         self.assertIsInstance(user, User)
         self.assertEqual(users[0].username, user.username)
 def test_11_admin(self):
     """Test User admin methods"""
     with self.app.app_context():
         filters = {'username': '******'}
         users = User.get_list(filters)
         self.assertEqual(len(users), 1)
         users[0].grant_admin()
         user = User.retrieve(users[0].userid)
         self.assertTrue(user.get_admin())
         user.revoke_admin()
         user = User.retrieve(users[0].userid)
         self.assertFalse(user.get_admin())
 def test_07_update_user(self):
     """Test User.retrieve() method"""
     with self.app.app_context():
         filters = {'username': '******'}
         users = User.get_list(filters)
         self.assertEqual(len(users), 1)
         user = User.retrieve(users[0].userid)
         user.update(lastname='example')
         user = User.retrieve(users[0].userid)
         self.assertEqual(users[0].username, user.username)
         self.assertEqual(user.lastname, 'example')
         user.update(lastname='Example')
 def test_10_lock(self):
     """Test User lock methods"""
     with self.app.app_context():
         filters = {'username': '******'}
         users = User.get_list(filters)
         self.assertEqual(len(users), 1)
         users[0].set_lock()
         user = User.retrieve(users[0].userid)
         self.assertTrue(user.get_lock())
         user.unlock()
         user = User.retrieve(users[0].userid)
         self.assertFalse(user.get_lock())
         self.assertEqual(user.failed_logins, 0)
         self.assertIsNone(user.last_failed_login)
    def test_3_retrieve_user(self):
        """Test Retrieve User Resource Representation operation"""
        # This test assumes 'admin' is in Database with password 'pass',
        # is unlocked and can log in, has admin privilege
        # Test expects User 'johne' in Database with following values:
        # username : johne
        # firstname : John
        # lastname : Example
        # email: [email protected]
        # phone: 123-444-6666
        #
        # Test correct response - 200
        # Get 'johne' userid directly from Database
        with self.app.app_context():
            johne_userid = User.get_list({'username': '******'})[0].userid
        # Retrieve User Representation of 'johne'
        resp = self.client.get(
            f'/users/{johne_userid}',
            headers={'X-API-Key': self.app.config['API_KEY']})
        self.assertEqual(resp.status_code, 200)
        # Assert response data is JSON
        self.assertTrue(resp.is_json)
        user_data = resp.get_json()
        # Assert response data is exactly what's expected
        johne = {
            'userid': johne_userid,
            'username': '******',
            'firstname': 'John',
            'lastname': 'Example',
            'contactInfo': {
                'email': '*****@*****.**',
                'phone': '123-444-6666'
            }
        }
        self.assertDictEqual(johne, user_data)

        # Test Unauthorized response - 401
        jwt_token = self.login('admin', 'pass')
        bad_headers = [
            # Missing Authorization header
            {},
            # Invalid X-API-Key
            {
                'X-API-Key': self.app.config['API_KEY'] + 'incorrect'
            },
            # valid JWT Bearer token instead of X-API-Key
            {
                'Authorization': f'Bearer {jwt_token}'
            }
        ]
        # Test bad authorization headers
        for headers in bad_headers:
            resp = self.client.get(f'/users/{johne_userid}', headers=headers)
            # Assert Status Code is 401
            self.assertEqual(resp.status_code, 401, msg=f'headers={headers}')

            # Assert no JSON in response data
            self.assertFalse(resp.is_json, msg=f'headers={headers}')
            self.assertIsNone(resp.get_json(), msg=f'headers={headers}')

        # Test response Not Found - 404
        # Find largest userid in Database and add 1
        with self.app.app_context():
            non_existent_userid = User.get_list({'sortBy': '-userid'
                                                 })[0].userid + 1
        resp = self.client.get(
            f'/users/{non_existent_userid}',
            headers={'X-API-Key': self.app.config['API_KEY']})
        self.assertEqual(resp.status_code, 404)
        # Assert no JSON in response data
        self.assertFalse(resp.is_json)
        self.assertIsNone(resp.get_json())
Beispiel #15
0
def login(data):
    """
    Login operation

    Args:
        data - dictionary with all Login operation attributes, loaded from
            Request body JSON and validated with models.login_body_schema

    Returns:
        JSON with JWT Token and User link or Error Message. Token contains
        'identity' field set to userid of authenticated User.
        Token expires after JWT_ACCESS_TOKEN_EXPIRES.
    """
    user_list = User.get_list({'username': data['username']})
    if len(user_list) == 0:
        current_app.logger.warning(
            f'authenticate_user() failed. No such user: {data["username"]}'
            )
        return make_response('Unathorized', 401)
    else:
        user = user_list[0]

    # check if User account is locked, lift lock if lock interval passed
    if user.last_failed_login and datetime.now() > user.last_failed_login + current_app.config['LOCK_TIMEOUT']:
        user.unlock()
        current_app.logger.info(
            f'authenticate_user() - userid={user.userid} unlocked due to lock timeout'
            )

    if user.get_lock():
        current_app.logger.warning(
            f'authenticate_user() failed. Userid={user.userid} is locked'
            )
        return make_response('Unathorized', 401)

    if safe_str_cmp(user.password.encode('utf-8'), data['password'].encode('utf-8')):
        # clear lock info on successful login
        user.unlock()
        access_token = create_access_token(identity=user.userid)
        response = {'jwtToken': access_token,
            'userHref': url_for(
                'users.retrieve_user',
                userid=user.userid,
                _external=True
                )}
        current_app.logger.info(
            f'authenticate_user() successful. {user.username} logged in'
            )
        return(jsonify(response), 200)
    else:
        current_app.logger.warning(
            f'authenticate_user() failed. Incorrect password for userid={user.userid}'
            )
        # update lock status
        user.failed_logins = user.failed_logins + 1
        user.last_failed_login = datetime.now() # consider datetime.utcnow()
        if user.failed_logins > current_app.config['MAX_FAILED_LOGIN_ATTEMPTS']:
            user.set_lock()
            current_app.logger.warning(
                f'Too many failed logins for userid={user.userid}, account locked'
                )
        user.update()
        return make_response('Unauthorized', 401)