Skip to content

Commit

Permalink
Endpoint users/ GET method (#356)
Browse files Browse the repository at this point in the history
* added users get endpoint, with required new database operation, and tests for both
  • Loading branch information
wittejm authored Sep 8, 2019
1 parent 58e5150 commit 06c254a
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 16 deletions.
67 changes: 53 additions & 14 deletions doc/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,59 @@ Returns: auth token
- `400 BAD FORMAT`: missing email or password


**`GET`** `/api/users/`

Fetches the list of existing users

Required headers:

- `Authorization: <JWT string>`

Returns: List of users:

- format: `JSON`
- fields:
* users :: list
* email
* admin
* timestamp

Status codes:

- `200 OK`
- `401 UNAUTHORIZED`: authorization rejected; missing or invalid auth token
- `403 FORBIDDEN`: authorized user is not admin


**`GET`** `/api/users/EMAIL`

Required headers:

- `Authorization: <JWT string>`

Returns: Requested user

- format: `JSON`
- fields:
* email
* admin
* timestamp

Status codes:

- `200 OK`
- `401 UNAUTHORIZED`: authorization rejected; missing or invalid auth token
- `403 FORBIDDEN`: authorized user is not admin


**`POST`** `/api/users/`

Creates a user

Required headers:

- `Authorization: <JWT string>`

`POST` body:

- format: `JSON`
Expand Down Expand Up @@ -189,25 +238,15 @@ Status codes:
- `422 UPROCESSABLE ENTITY`: duplicate user or password too short


**`GET`** `/api/users/EMAIL`

Returns: Requested user

- format: `JSON`
- fields:
* email
* admin permissions
* timestamp

Status codes:

- `200 OK`


**`POST`** `/api/search`

Performs search of remote system

Required headers:

- `Authorization: <JWT string>`

`POST` body:

- format: `JSON`
Expand Down
20 changes: 19 additions & 1 deletion src/backend/expungeservice/database/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ def get_user(database, lookup_field, key):

database.cursor.execute(
sql.SQL("""
SELECT USERS.user_id::text user_id, email, admin, hashed_password, auth_id::text
SELECT USERS.user_id::text user_id, email, admin,
hashed_password, auth_id::text, date_created, date_modified
FROM USERS JOIN AUTH ON USERS.user_id = AUTH.user_id
WHERE users.{} = %(key)s
;
Expand All @@ -43,3 +44,20 @@ def get_user(database, lookup_field, key):
return res._asdict()
else:
return res


def get_all_users(database):

database.cursor.execute(
sql.SQL("""
SELECT USERS.user_id::text user_id, email, admin, hashed_password, auth_id::text, date_created, date_modified
FROM USERS JOIN AUTH ON USERS.user_id = AUTH.user_id
;
"""), {})

res = database.cursor.fetchall()
if res:
return [r._asdict() for r in res]
else:
return res

21 changes: 20 additions & 1 deletion src/backend/expungeservice/endpoints/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from werkzeug.security import generate_password_hash

from flask import g
from expungeservice.database.user import create_user, get_user_by_email
from expungeservice.database.user import create_user, get_user_by_email, get_all_users
from expungeservice.endpoints.auth import admin_auth_required
from expungeservice.request import check_data_fields
from psycopg2.errors import UniqueViolation
Expand Down Expand Up @@ -51,5 +51,24 @@ def post(self):

return jsonify(response_data), 201

@admin_auth_required
def get(self):
"""
Fetch the list of users, including their email, admin clear
"""

user_db_data = get_all_users(g.database)

response_data={'users':[]}
for user_entry in user_db_data:
response_data['users'].append({
'email':user_entry['email'],
'admin':user_entry['admin'],
'timestamp': user_entry['date_created']
})

return jsonify(response_data), 201


def register(app):
app.add_url_rule('/api/users', view_func=Users.as_view('users'))
33 changes: 33 additions & 0 deletions src/backend/tests/database/test_database.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,37 @@ def test_get_user(self):

self.verify_user_data(email, hashed_password, admin)


def test_get_all_users(self):
"""
test inserts two new users, then fetches from the table with the db get function and
raw sql to compare the results. Checks the number of returned rows is the same,
and that all of the columns match from each returned row.
"""

email1 = "[email protected]"
hashed_password = 'examplepasswordhash2'
admin = True
self.create_example_user(email1, hashed_password, admin)

email2 = "[email protected]"
hashed_password = 'examplepasswordhash3'
admin = True
self.create_example_user(email2, hashed_password, admin)

users_get_endpoint_result = user.get_all_users(self.database)

verify_query = """
SELECT * FROM USERS;"""
self.database.cursor.execute(verify_query)

verify_rows = [r._asdict() for r in self.database.cursor.fetchall()]

assert len(verify_rows) == len(users_get_endpoint_result)

for (email, hashed_password, admin) in [
(r['email'], r['hashed_password'], r['admin']) for r in users_get_endpoint_result]:
self.verify_user_data(email, hashed_password, admin)
def test_get_missing_user(self):

email = "[email protected]"
Expand All @@ -92,6 +123,8 @@ def create_example_user(self, email, hashed_password, admin):

#Helper function
def verify_user_data(self, email, hashed_password, admin):
# is passed the data obtained from the app feature e.g. a database function,
# and checks the fields match a second, raw SQL query.

verify_query = """
SELECT USERS.user_id::text, email, admin, hashed_password, auth_id::text, date_created, date_modified
Expand Down
33 changes: 33 additions & 0 deletions src/backend/tests/endpoints/test_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,36 @@ def test_create_user_not_admin(self):
'admin': False})

assert(response.status_code == 403)


def test_get_users_success(self):

generate_auth_response = self.generate_auth_token(self.admin_email, self.admin_password)

response = self.client.get('/api/users', headers={
'Authorization': 'Bearer {}'.format(generate_auth_response.get_json()['auth_token'])},
json = {})


assert(response.status_code == 201)

data = response.get_json()

assert data['users'][0]['email']
assert data['users'][0]['admin']
assert data['users'][0]['timestamp']


def test_get_users_not_admin(self):

new_email = "pytest_create_user@endpoint_test.com"
new_password = "new_password"

generate_auth_response = self.generate_auth_token(self.email, self.password)

response = self.client.get('/api/users', headers={
'Authorization': 'Bearer {}'.format(generate_auth_response.get_json()['auth_token'])},
json = {})

assert(response.status_code == 403)

0 comments on commit 06c254a

Please sign in to comment.