Skip to content

Commit de62e5b

Browse files
committed
Some refactor and add docstrings
1 parent 992050f commit de62e5b

File tree

2 files changed

+181
-62
lines changed

2 files changed

+181
-62
lines changed

lib/pbench/server/api/resources/db_models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ def decode_auth_token(auth_token):
6565
except jwt.InvalidTokenError:
6666
return 'Invalid token. Please log in again.'
6767

68+
# TODO: Add password recovery mechanism
69+
6870

6971
class URLModel(db.Model):
7072
""" User Model for storing user related details """
Lines changed: 179 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
1+
import re
22
from flask import request, jsonify, make_response
3-
from flask_restful import Resource
3+
from flask_restful import Resource, abort
44
from pbench.server.api import app, bcrypt, db, blacklist
55
from pbench.server.api.resources.db_models import UserModel
66
from flask_jwt_extended import jwt_required
@@ -12,48 +12,107 @@ class RegisterUser(Resource):
1212
"""
1313

1414
def post(self):
15+
"""
16+
Post request for registering a new user.
17+
This requires a JSON data with required user metadata fields
18+
{
19+
"username": "username",
20+
"password": "password",
21+
"firstName": first_name,
22+
"lastName": "last_name",
23+
"email": "[email protected]"
24+
}
25+
26+
Required headers include
27+
28+
Content-Type: application/json
29+
Accept: application/json
30+
31+
:return: JSON Payload
32+
if we succeed to add a user entry in database, the returned response_object will include the auth_token
33+
response_object = {
34+
"status": "success"/"fail",
35+
"status_code": 201/status_code_int,
36+
"message": "Successfully registered."/"failure message",
37+
"auth_token": auth_token.decode(), # Will not present if failed
38+
}
39+
"""
1540
# get the post data
16-
post_data = request.get_json()
41+
user_data = request.get_json()
42+
if not user_data:
43+
app.logger.error("Register User: Invalid json object: %s", request.url)
44+
abort(400, message="Register User: Invalid json object in request")
45+
46+
missing_fields = []
47+
if not user_data.get("username"):
48+
missing_fields.append("username")
49+
if not user_data.get("password"):
50+
missing_fields.append("password")
51+
if not user_data.get("email"):
52+
missing_fields.append("email")
53+
if not user_data.get("firstName"):
54+
missing_fields.append("firstName")
55+
if not user_data.get("lastName"):
56+
missing_fields.append("lastName")
57+
58+
if missing_fields:
59+
app.logger.error(
60+
f"Register User: missing fields {' '.join(missing_fields)}"
61+
)
62+
abort(
63+
400,
64+
message=f"Missing fields to Register User: {' '.join(missing_fields)}",
65+
)
66+
67+
# validate the email field
68+
regex = "^[a-z0-9]+[\._]?[a-z0-9]+[@]\w+[.]\w{2,3}$"
69+
if not re.search(regex, user_data.get("email")):
70+
app.logger.error(f"Register User: invalid email {user_data.get('email')}")
71+
abort(400, message=f"Invalid email: {user_data.get('email')}")
72+
1773
# check if user already exist
18-
user = UserModel.query.filter_by(username=post_data.get('username')).first()
74+
user = UserModel.query.filter_by(username=user_data.get("username")).first()
1975
if not user:
2076

2177
try:
2278
user = UserModel(
23-
username=post_data.get('username'),
24-
password=post_data.get('password'),
25-
firstName=post_data.get('firstName'),
26-
lastName=post_data.get('lastName'),
27-
email=post_data.get('email'),
79+
username=user_data.get("username"),
80+
password=user_data.get("password"),
81+
firstName=user_data.get("firstName"),
82+
lastName=user_data.get("lastName"),
83+
email=user_data.get("email"),
2884
)
2985

3086
# insert the user
3187
db.session.add(user)
3288
db.session.commit()
33-
app.logger.info(f"New user registered, id: {self.id}, username: {self.username}")
89+
app.logger.info(
90+
f"New user registered, Name: {user_data.get('firstName')} {user_data.get('lastName')}, "
91+
f"username: {user_data.get('username')}, email: {user_data.get('email')}"
92+
)
3493

3594
# generate the auth token
3695
auth_token = user.encode_auth_token(user.id)
3796
response_object = {
38-
'status': 'success',
39-
'status_code': 201,
40-
'message': 'Successfully registered.',
41-
'auth_token': auth_token.decode()
97+
"status": "success",
98+
"status_code": 201,
99+
"message": "Successfully registered.",
100+
"auth_token": auth_token.decode(),
42101
}
43102
return make_response(jsonify(response_object))
44103

45104
except Exception as e:
46105
response_object = {
47-
'status': 'fail',
48-
'status_code': 401,
49-
'message': 'Some error occurred. Please try again.'
106+
"status": "fail",
107+
"status_code": 401,
108+
"message": "Some error occurred. Please try again.",
50109
}
51110
return make_response(jsonify(response_object))
52111
else:
53112
response_object = {
54-
'status': 'fail',
55-
'status_code': 202,
56-
'message': 'User already exists. Please Log in.',
113+
"status": "fail",
114+
"status_code": 202,
115+
"message": "User already exists. Please Log in.",
57116
}
58117
return make_response(jsonify(response_object))
59118

@@ -63,35 +122,59 @@ class LoginAPI(Resource):
63122
Abstracted pbench API for User Login
64123
"""
65124
def post(self):
125+
"""
126+
Post request for logging in user.
127+
This requires a JSON data with required user metadata fields
128+
{
129+
"username": "username",
130+
"password": "password",
131+
}
132+
133+
Required headers include
134+
135+
Content-Type: application/json
136+
Accept: application/json
137+
138+
:return: JSON Payload
139+
if we succeed to decrypt the password hash, the returned response_object will include the auth_token
140+
response_object = {
141+
"status": "success"/"fail",
142+
"status_code": 200/status_code_int,
143+
"message": "Successfully logged in."/"failure message",
144+
"auth_token": auth_token.decode(), # Will not present if failed
145+
}
146+
"""
66147
# get the post data
67148
post_data = request.get_json()
68149
try:
69150
# fetch the user data
70-
user = UserModel.query.filter_by(username=post_data.get('username')).first()
151+
user = UserModel.query.filter_by(username=post_data.get("username")).first()
71152

72-
if user and bcrypt.check_password_hash(user.password, post_data.get('password')):
153+
if user and bcrypt.check_password_hash(
154+
user.password, post_data.get("password")
155+
):
73156
auth_token = user.encode_auth_token(user.id)
74157
if auth_token:
75158
response_object = {
76-
'status': 'success',
77-
'status_code': 200,
78-
'message': 'Successfully logged in.',
79-
'auth_token': auth_token.decode()
159+
"status": "success",
160+
"status_code": 200,
161+
"message": "Successfully logged in.",
162+
"auth_token": auth_token.decode(),
80163
}
81164
return make_response(jsonify(response_object))
82165
else:
83166
response_object = {
84-
'status': 'fail',
85-
'status_code': 404,
86-
'message': 'User does not exist.'
167+
"status": "fail",
168+
"status_code": 404,
169+
"message": "User does not exist.",
87170
}
88171
return make_response(jsonify(response_object))
89172
except Exception as e:
90173
app.logger.info(f"Exception occurred during user login {e}")
91174
response_object = {
92-
'status': 'fail',
93-
'status_code': 500,
94-
'message': 'Try again'
175+
"status": "fail",
176+
"status_code": 500,
177+
"message": "Try again",
95178
}
96179
return make_response(jsonify(response_object))
97180

@@ -102,8 +185,25 @@ class LogoutAPI(Resource):
102185
"""
103186
@jwt_required
104187
def post(self):
188+
"""
189+
Post request for logging out a user.
190+
This requires a JWT auth token in the header field
191+
192+
Required headers include
193+
194+
Content-Type: application/json
195+
Accept: application/json
196+
X-Auth-Token: JWT token (user received upon login)
197+
198+
:return: JSON Payload
199+
response_object = {
200+
"status": "success"/"fail",
201+
"status_code": 200/status_code_int,
202+
"message": "Successfully logged out."/"failure message",
203+
}
204+
"""
105205
# get auth token
106-
auth_header = request.headers.get('X-Auth-Token')
206+
auth_header = request.headers.get("X-Auth-Token")
107207
if auth_header:
108208
auth_token = auth_header.split(" ")[1]
109209

@@ -112,23 +212,23 @@ def post(self):
112212
# Add the token to the blacklist
113213
blacklist.add(auth_token)
114214
response_object = {
115-
'status': 'success',
116-
'message': 'Successfully logged out.',
117-
'status_code': 200
215+
"status": "success",
216+
"message": "Successfully logged out.",
217+
"status_code": 200,
118218
}
119219
return make_response(jsonify(response_object))
120220
else:
121221
response_object = {
122-
'status': 'fail',
123-
'message': user_id,
124-
'status_code': 401
222+
"status": "fail",
223+
"message": user_id,
224+
"status_code": 401,
125225
}
126226
return make_response(jsonify(response_object))
127227
else:
128228
response_object = {
129-
'status': 'fail',
130-
'message': 'Provide a valid auth token.',
131-
'status_code': 403
229+
"status": "fail",
230+
"message": "Provide a valid auth token.",
231+
"status_code": 403,
132232
}
133233
return make_response(jsonify(response_object))
134234

@@ -137,21 +237,43 @@ class GetUser(Resource):
137237
"""
138238
Abstracted pbench API to get user metadata information
139239
"""
240+
140241
# TODO: We can implement the graphql query to get specific metadata related to a user
141242
# We dont need to pass user_id to get the user, user id will be retrieved from the jwt encoded auth header
142243
@jwt_required
143244
def get(self):
245+
"""
246+
Get request for getting user metadata.
247+
This requires a JWT auth token in the header field
248+
249+
Required headers include
250+
251+
Content-Type: application/json
252+
Accept: application/json
253+
X-Auth-Token: JWT token (user received upon login)
254+
255+
:return: JSON Payload
256+
response_object = {
257+
"status": "success"/"fail",
258+
"status_code": 200/status_code_int,
259+
"message": "Successfully registered."/"failure message",
260+
"data": {
261+
"username": username,
262+
"registered_on": registered_on,
263+
}
264+
}
265+
"""
144266
# get the auth token
145-
auth_header = request.headers.get('X-Auth-Token')
146-
auth_token = ''
267+
auth_header = request.headers.get("X-Auth-Token")
268+
auth_token = ""
147269
if auth_header:
148270
try:
149271
auth_token = auth_header.split(" ")[1]
150272
except IndexError:
151273
response_object = {
152-
'status': 'fail',
153-
'message': 'Bearer token malformed.',
154-
'status_code': 401
274+
"status": "fail",
275+
"message": "Bearer token malformed.",
276+
"status_code": 401,
155277
}
156278
return make_response(jsonify(response_object))
157279

@@ -160,25 +282,20 @@ def get(self):
160282
if not isinstance(user_id, str):
161283
user = UserModel.query.filter_by(id=user_id).first()
162284
response_object = {
163-
'status': 'success',
164-
'data': {
165-
'user_id': user.id,
166-
'username': user.username,
167-
'registered_on': user.registered_on
285+
"status": "success",
286+
"data": {
287+
"username": user.username,
288+
"registered_on": user.registered_on,
168289
},
169-
'status_code': 200
290+
"status_code": 200,
170291
}
171292
return make_response(jsonify(response_object))
172-
response_object = {
173-
'status': 'fail',
174-
'message': user_id,
175-
'status_code': 403
176-
}
293+
response_object = {"status": "fail", "message": user_id, "status_code": 403}
177294
return make_response(jsonify(response_object))
178295
else:
179296
response_object = {
180-
'status': 'fail',
181-
'message': 'Provide a valid auth token.',
182-
'status_code': 401
297+
"status": "fail",
298+
"message": "Provide a valid auth token.",
299+
"status_code": 401,
183300
}
184-
return make_response(jsonify(response_object))
301+
return make_response(jsonify(response_object))

0 commit comments

Comments
 (0)