1-
1+ import re
22from flask import request , jsonify , make_response
3- from flask_restful import Resource
3+ from flask_restful import Resource , abort
44from pbench .server .api import app , bcrypt , db , blacklist
55from pbench .server .api .resources .db_models import UserModel
66from 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+ 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