Skip to content

Commit 46b7c40

Browse files
authored
Merge pull request #192 from CodeForPhilly/user_api_testing
New secrets, new user api tests, prefixed usernames with base_
2 parents d092f9d + 8b406ca commit 46b7c40

File tree

4 files changed

+159
-21
lines changed

4 files changed

+159
-21
lines changed

src/server/Dockerfile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,7 @@ RUN set FLASK_APP=server/app.py
4141
RUN export FLASK_APP
4242

4343
# This abomination ensures that the PG server has finished its restart cycle
44-
CMD echo "SLEEPING 10"; sleep 10; echo "WAKING"; alembic upgrade head ; python -m flask run --host=0.0.0.0
44+
CMD echo "SLEEPING 10"; sleep 10; echo "WAKING"; alembic upgrade head ; python -m flask run --host=0.0.0.0 --no-reload
45+
46+
# --no-reload prevents Flask restart, which usually happens in middle of create_base_users()
47+
#TODO: SECURITY - ensure we are not running in debug mode in production

src/server/app.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,17 @@
44

55
from flask_jwt_extended import JWTManager
66

7+
from secrets import JWT_SECRET, APP_SECRET_KEY
78

89
app = Flask(__name__)
910

10-
app.config["JWT_SECRET_KEY"] = "super-secret" # TODO: SECURITY Change this!
11+
app.config["JWT_SECRET_KEY"] = JWT_SECRET
1112
app.config["JWT_ACCESS_TOKEN_EXPIRES"] = 300 # Seconds for timeout. 60 for testing.
1213
jwt = JWTManager(app)
1314

1415

1516
# def create_app():
16-
app.secret_key = "1u9L#*&I3Ntc" # TODO: SECURITY Change this!
17+
app.secret_key = APP_SECRET_KEY
1718
app.config["MAX_CONTENT_LENGTH"] = 500 * 1024 * 1024 # 500 Megs
1819
app.config["SEND_FILE_MAX_AGE_DEFAULT"] = 0
1920
from api.admin_api import admin_api

src/server/test_api.py

Lines changed: 142 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import pytest, socket, requests, os
22

3+
from secrets import BASEUSER_PW, BASEADMIN_PW
4+
5+
jwt_token = ''
6+
37
#
48
# Run 'pytest' from the command line
59
#
@@ -21,43 +25,170 @@
2125
SERVER_URL = "http://server:5000"
2226
IS_LOCAL = False
2327

24-
### DNS lookup tests
28+
### DNS lookup tests ##############################
2529

26-
# Ensure DNS not resolving bad host names
2730
def test_bad_dns():
31+
"""Verify DNS not resolving bad host names."""
2832
with pytest.raises(socket.gaierror):
2933
socket.getaddrinfo("bad_server_name_that_should_not_resolve", "5000")
3034

3135

32-
# Do we get IPs for good names?
33-
34-
3536
@pytest.mark.skipif(IS_LOCAL, reason="Not run when IS_LOCAL")
3637
def test_db_dns():
37-
assert (
38-
len(socket.getaddrinfo("db", "5000")) > 0
39-
) # getaddrinfo works for IPv4 and v6
38+
"""Verify we get IP for DB server."""
39+
40+
# getaddrinfo works for IPv4 and v6
41+
try:
42+
gai = socket.getaddrinfo("db", "5000")
43+
except:
44+
pytest.fail('getaddrinfo() failed for db', pytrace=False)
45+
46+
assert len(gai) > 0
4047

4148

4249
@pytest.mark.skipif(IS_LOCAL, reason="Not run when IS_LOCAL")
4350
def test_server_dns():
44-
assert len(socket.getaddrinfo("server", "5000")) > 0
51+
"""Verify we get IP for API server."""
52+
try:
53+
gai = socket.getaddrinfo("server", "5000")
54+
except socket.gaierror:
55+
pytest.fail('getaddrinfo() failed for server', pytrace=False)
56+
57+
assert len(gai) > 0
4558

4659

4760
@pytest.mark.skipif(IS_LOCAL, reason="Not run when IS_LOCAL")
4861
def test_client_dns():
49-
assert len(socket.getaddrinfo("client", "5000")) > 0
62+
"""Verify we get IP for client."""
63+
try:
64+
gai = socket.getaddrinfo("client", "5000")
65+
except socket.gaierror:
66+
pytest.fail('getaddrinfo() failed for client', pytrace=False)
5067

68+
assert len(gai) > 0
5169

52-
# Simple API tests
70+
# Simple API tests ################################################
5371

5472

5573
def test_currentFiles():
74+
"""360 view Current Files list"""
5675
response = requests.get(SERVER_URL + "/api/listCurrentFiles")
5776
assert response.status_code == 200
5877

5978

6079
def test_statistics():
80+
"""360 view Statistics"""
6181
response = requests.get(SERVER_URL + "/api/statistics")
6282
assert response.status_code == 200
6383

84+
85+
def test_usertest():
86+
"""Verify liveness test works"""
87+
response = requests.get(SERVER_URL + "/api/user/test")
88+
assert response.status_code == 200
89+
90+
######## Dependent tests #################################
91+
92+
# Store info across tests
93+
class State:
94+
def __init__(self):
95+
self.state = {}
96+
97+
@pytest.fixture(scope='session')
98+
def state() -> State:
99+
state = State()
100+
state.state['from_fixture'] = 0
101+
return state
102+
103+
104+
def test_userlogin(state: State):
105+
"""Verify base_user can log in/get JWT."""
106+
data = {"username":"base_user", "password" : BASEUSER_PW}
107+
108+
response = requests.post(SERVER_URL + "/api/user/login_json", json=data)
109+
assert response.status_code == 200
110+
111+
try:
112+
jwt_token = response.json()['access_token']
113+
except:
114+
pytest.fail('Did not get access token', pytrace=False)
115+
116+
assert len(jwt_token) > 16
117+
118+
# Store the token for later use
119+
state.state['base_user'] = jwt_token
120+
121+
122+
def test_useraccess(state: State):
123+
"""Verify logged-in base_user can use JWT to access test_auth"""
124+
# Build auth string value including token from state
125+
b_string = 'Bearer ' + state.state['base_user']
126+
127+
assert len(b_string) > 24
128+
129+
auth_hdr = {'Authorization' : b_string}
130+
response = requests.get(SERVER_URL + "/api/user/test_auth", headers=auth_hdr)
131+
assert response.status_code == 200
132+
133+
134+
def test_user_bad_pw():
135+
"""Verify base_user with bad pw fails"""
136+
data = {"username":"base_user", "password" : 'some_bad_password'}
137+
138+
response = requests.post(SERVER_URL + "/api/user/login_json", json=data)
139+
assert response.status_code == 401
140+
141+
142+
def test_inact_userblocked(state: State):
143+
"""Verify base_user_inact can't login because marked inactive."""
144+
# Same pw as base_user
145+
data = {"username":"base_user_inact", "password" : BASEUSER_PW}
146+
response = requests.post(SERVER_URL + "/api/user/login_json", json=data)
147+
assert response.status_code == 401
148+
149+
150+
### Admin-level tests ######################################
151+
152+
def test_adminlogin(state: State):
153+
"""Verify base_admin can log in/get JWT."""
154+
data = {"username":"base_admin", "password" : BASEADMIN_PW}
155+
156+
response = requests.post(SERVER_URL + "/api/user/login_json", json=data)
157+
assert response.status_code == 200
158+
159+
try:
160+
jwt_token = response.json()['access_token']
161+
except:
162+
pytest.fail('Did not get access token', pytrace=False)
163+
164+
assert len(jwt_token) > 16
165+
166+
# Store the token for later use
167+
state.state['base_admin'] = jwt_token
168+
169+
170+
def test_admingetusers(state: State):
171+
"""Verify logged-in base_admin can use JWT to get user list """
172+
# Build auth string value including token from state
173+
b_string = 'Bearer ' + state.state['base_admin']
174+
175+
assert len(b_string) > 24
176+
177+
auth_hdr = {'Authorization' : b_string}
178+
response = requests.get(SERVER_URL + "/api/admin/user/get_users", headers=auth_hdr)
179+
assert response.status_code == 200
180+
181+
userlist = response.json()
182+
assert len(userlist) > 1
183+
184+
185+
def test_usergetusers(state: State):
186+
"""Verify logged-in base_user *cannot* use JWT to get user list """
187+
# Build auth string value including token from state
188+
b_string = 'Bearer ' + state.state['base_user']
189+
190+
assert len(b_string) > 24
191+
192+
auth_hdr = {'Authorization' : b_string}
193+
response = requests.get(SERVER_URL + "/api/admin/user/get_users", headers=auth_hdr)
194+
assert response.status_code == 403

src/server/user_mgmt/base_users.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
from api import user_api
44
import sqlalchemy as sa
55

6+
from secrets import BASEUSER_PW, BASEEDITOR_PW, BASEADMIN_PW
7+
8+
69
from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey
710

811
metadata = sa.MetaData()
@@ -35,30 +38,30 @@ def create_base_users(): # TODO: Just call create_user for each
3538
pu = sa.Table("pdp_users", metadata, autoload=True, autoload_with=engine)
3639

3740
# user
38-
pw_hash = user_api.hash_password("userpw")
41+
pw_hash = user_api.hash_password(BASEUSER_PW)
3942
ins_stmt = pu.insert().values(
40-
username="user", password=pw_hash, active="Y", role=1,
43+
username="base_user", password=pw_hash, active="Y", role=1,
4144
)
4245
connection.execute(ins_stmt)
4346

4447
# INactive user
4548
# Reuse pw hash
4649
ins_stmt = pu.insert().values(
47-
username="user_inact", password=pw_hash, active="N", role=1,
50+
username="base_user_inact", password=pw_hash, active="N", role=1,
4851
)
4952
connection.execute(ins_stmt)
5053

5154
# editor
52-
pw_hash = user_api.hash_password("editorpw")
55+
pw_hash = user_api.hash_password(BASEEDITOR_PW)
5356
ins_stmt = pu.insert().values(
54-
username="editor", password=pw_hash, active="Y", role=2,
57+
username="base_editor", password=pw_hash, active="Y", role=2,
5558
)
5659
connection.execute(ins_stmt)
5760

5861
# admin
59-
pw_hash = user_api.hash_password("adminpw")
62+
pw_hash = user_api.hash_password(BASEADMIN_PW)
6063
ins_stmt = pu.insert().values(
61-
username="admin", password=pw_hash, active="Y", role=9,
64+
username="base_admin", password=pw_hash, active="Y", role=9,
6265
)
6366
connection.execute(ins_stmt)
6467

0 commit comments

Comments
 (0)