• Non ci sono risultati.

4.6 Users module

4.6.1 Controllers

1 {% extends " layout . html " %}

2 {% block content %}

3 <h1>Your account has been s u c c e s s f u l l y confirmed !</h1>

4 {% endblock content %}

Listing 4.9: user_confirmed.html file

7

8 # c r e a t e the SYSTEM_ADMIN

9 hashed_password = bcrypt . generate_password_hash ( ’ admin ’ ) . decode ( ’ utf −8 ’)

10 user = User ( email =’admin ’ , password=hashed_password ,

11 name=’admin ’ , surname=’admin ’ , b i r t h =’1990−01−01’,

12 r o l e =’SYSTEM_ADMIN’ , confirmed=True , a c t i v e=True , counter =0,

13 reset_pass=False )

14 db . s e s s i o n . add ( user )

15 db . s e s s i o n . commit ( )

Listing 4.10: create_system_admin function

Register resource

The “Register” class is defined in this file and is marked as a resource. This means that it is possible to define functions that can be contacted through the REST APIs. Two functions have been defined within it, one reachable through the POST method, has the task of creating a new user and saving his data in the DB by also sending the email to confirm the account. This procedure is started only if the request body conforms to the predefined JSON schema in the “schemas.py” file.

Each user record within the DB has the following structure.

Figure 4.2: User record structure

The confirmed field is used to indicate whether the user has confirmed its email, the active field indicates whether the user is currently active and therefore has access to all the features offered by the service. The counter represents the number of accesses performed by the user. This is for the login phase and is explained later.

Finally, the rest_pass field indicates whether the user has started the password change procedure.

Below the source code of the first function in showed. It is possible to note that the password is hashed before beeing saved on the DB. For this task the "bcrypt"

module is used.

1 def post ( s e l f ) :

2 data = r e q u e s t . get_json ( )

3 # check i f there i s a json data a v a i l a b l e in the r e q u e s t

4 i f not data :

5 return make_response ( { " msg " : "Bad r e q u e s t " } , s t a t u s . HTTP_400_BAD_REQUEST)

6 # v a l i d a t i o n o f the json data a g a i n s t the c o r r e c t json schema

7 try :

8 jsonschema . v a l i d a t e ( data , register_schema )

9 except jsonschema . ValidationError :

10 return make_response ( { " msg " : "Bad r e q u e s t " } , s t a t u s . HTTP_400_BAD_REQUEST)

11

12 # c r e a t e a new user to be saved in the bd and send a confirmation email

13 hashed_password = bcrypt . generate_password_hash ( data [ ’ password ’ ] ) . decode ( ’ utf −8 ’)

14 user = User ( email=data [ ’ email ’ ] , password=hashed_password ,

15 name=data [ ’ name ’ ] , surname=data [ ’ surname ’ ] , b i r t h

=data [ ’ birth ’ ] ,

16 r o l e =’USER’ , confirmed=False , a c t i v e=True ,

counter =0,

17 reset_pass=False )

18 db . s e s s i o n . add ( user )

19 db . s e s s i o n . commit ( )

20 send_confirm_email ( user )

21 return make_response ( { " msg " : " Created S u c c e s s f u l l y " } , s t a t u s . HTTP_201_CREATED)

Listing 4.11: POST function

The second function that can be contacted through the GET method has the task of marking the user as confirmed = true. To do that it has to verify the validity of the token contained in the request.

1 def get ( s e l f ) :

2 token = r e q u e s t . args . get ( ’ token ’ )

3 # check i f the r e q u e s t c o n t a i n s the token

4 i f token :

5 user = User . verify_confirm_token ( token )

6 i f user i s None :

7 return make_response ( { " msg " : "Bad r e q u e s t " } , s t a t u s . HTTP_400_BAD_REQUEST)

8 # in t h i s case the user i s not confirmed yet

9 i f user == −1:

10 headers = { ’ Content−Type ’ : ’ te x t /html ’ }

11 return make_response ( render_template ( ’

user_already_confirmed . html ’ , t i t l e =’ Success ’ ) ,

12 s t a t u s .HTTP_200_OK,

13 headers )

14 # s e t the user to confirmed

15 user . confirmed = True

16 db . s e s s i o n . commit ( )

17 headers = { ’ Content−Type ’ : ’ tex t /html ’ }

18 return make_response ( render_template ( ’ user_confirmed . html

’ , t i t l e =’ Success ’ ) , s t a t u s .HTTP_200_OK, headers )

19 # i f there i s not a token in the r e q u e s t return e r r o r

20 return make_response ( { " msg " : "Bad r e q u e s t " } , s t a t u s . HTTP_400_BAD_REQUEST)

Listing 4.12: GET function

Both functions respond with messages with standard http codes such as eg. “200 OK” or “400 Bad request” in case of success or failure.

Login resource

A resource class called “Login” is also defined in the file. Inside, only one function accessible via the POST method has been defined that allows the user to log in to the system.

1 c l a s s Login ( Resource ) :

2 # http : / / 1 2 7 . 0 . 0 . 1 : 5 0 0 0 / l o g i n

3 # used to l o g i n i n t o the s e r v i c e

4 def post ( s e l f ) :

5 data = r e q u e s t . get_json ( )

6 # check i f there i s a json data with the r e q u e s t

7 i f not data :

8 return make_response ( { " msg " : "Bad r e q u e s t "} , s t a t u s . HTTP_400_BAD_REQUEST)

9

10 # v a l i d a t e the json data a g a i n s t the c o r r e c t json schema

11 try :

12 jsonschema . v a l i d a t e ( data , login_schema )

13 except jsonschema . ValidationError :

14 return make_response ( { " msg " : "Bad r e q u e s t "} , s t a t u s . HTTP_400_BAD_REQUEST)

15

16 user = User . query . f i l t e r _ b y ( email=data [ ’ email ’ ] ) . f i r s t ( )

17

18 # check i f the user i s confirmed and a c t i v e

19 i f not user or not user . confirmed or not user . a c t i v e :

20 return make_response ( { " msg " : "Bad r e q u e s t "} , s t a t u s . HTTP_400_BAD_REQUEST)

21

22 # check i s the u s e r s password i s c o r r e c t

23 i f not bcrypt . check_password_hash ( user . password , data [ ’ password ’ ] ) :

24 return make_response ( { " msg " : " Unauthorized "} , s t a t u s . HTTP_401_UNAUTHORIZED)

25

26 # c r e a t e the jwt token that have to be add to any other r e q u e s t that r e q u i r e s to be authenticated

27 # t h i s c o n t a i n s a counter that w i l l be d i f f e r e n t at the next l o g i n j u s t to avoid the re−usage o f old tokens

28 token = jwt . encode ({ ’ user_id ’ : user . user_id , ’ counter ’ : user . counter } , current_app . c o n f i g [ ’SECRET_KEY’ ] )

29

30 content = j s o n i f y ({ ’ token ’ : token . decode ( ’UTF−8 ’) })

31 return make_response ( content , s t a t u s .HTTP_200_OK)

Listing 4.13: Login resource

As you can see from the code, the function, after extracting the email and password from the body of the request, proceeds with the verification of the existence of this user and the verification of his active and confirmed status. If the checks are successfully passed, the token is created which must be attached to the user’s next requests to confirm his identity. This token is encrypted through the "jwt" library configured when the application is started. Inside, the information on the user ID and the number of accesses, necessary for its verification, are saved.

The token as it was designed does not have a validity period, to give the user the opportunity to remain logged into the system even after a long period of non-use of the Android client application. This means that to invalidate the token, i.e. log out, it is necessary to change a data present on the server in order to make its verification no longer valid. This data is the "counter" field which is increased at each logout. At this point, if a request arrives with a token whose "counter" field does not match the one saved on the server, the function will not be executed.

Logout resource

This resource is declared in the same way as the resources previously described, inside it contains only one function accessible through the POST method. The

“@token_required” decorator indicates that for its execution it is necessary to have a valid token. Among the most important points of the function it is important to note that the user’s counter is incremented to invalidate the token and log out.

1 counter = current_user . counter

2 counter = counter + 1

3 current_user . counter = counter

Listing 4.14: counter incrementation

Recover resource

This resource class contains two functions, one accessible through the POST method to be able to request the password change, and one accessible through the GET method which allows the actual password change. The operation of the two functions is similar to the operation seen during user registration and email confirmation (Register resource section). Also in this case, the first function is intended to send the user the email with a token. The second displays the static page for changing the password and saves the changed data within the DB. The only peculiarity of this function is that the data is taken from an HTML form as the new password is entered within a web page.

1 form = RecoverPasswordForm ( )

2 i f form . validate_on_submit ( ) :

3 hashed_password = bcrypt . generate_password_hash ( form .

password . data ) . decode ( ’ utf −8 ’)

4 user . password = hashed_password

5 user . reset_pass = False

6 db . s e s s i o n . commit ( )

Listing 4.15: getting data from the form

Users resource

This resource was created to allow users to obtain their account data along with the ability to change them. A function accessible with the GET method returns the data of the user specified in the address of the request itself. The method does not need any further explanation as the code has no particularities. There are also two other functions in the resource that are accessible with the POST method and the PUT method respectively. The execution of the first is allowed to every user normally logged in to the system and allows you to change user data such as:

email, name, surname and date of birth. The second instead gives the possibility to change the user’s role together with the possibility to change the active status.

This second method is accessible only to users with a higher role than the normal user as it is a very risky operation from the point of view of the security of the system itself. The code of the functions described here is not shown as it does not show anything that has not already been seen before.

Documenti correlati