the client is now more interactive and allows the user to decide which endpoint to call update architecure in README.mdmaster
| @@ -4,12 +4,25 @@ This are two little python scripts to demonstrate the CHAP workflow. One script | |||
| ##Presets | |||
| - Python 3 | |||
| - Python 3.8.5 | |||
| ##Usage | |||
| To use just first start the server and then the client script | |||
| ## Imported Libraries | |||
| ### Client | |||
| - xmlrpc.client | |||
| - hashlib | |||
| - os | |||
| - sys | |||
| ## Server | |||
| - uuid | |||
| - pickle | |||
| - hashlib | |||
| - xmlrpc.server | |||
| ##Internal workings | |||
| The server script will the serve a XMLRPC server with 4 endpoints on Port 8000 at localhost (the local machine) : | |||
| @@ -17,18 +30,21 @@ The server script will the serve a XMLRPC server with 4 endpoints on Port 8000 a | |||
| - init(): [ *session_id*: *uuid*, *seed*: *uuid* ] | |||
| This endpoint will start a new session and send you the id and seed of this new session | |||
| - auth( *session_id*: *uuid*, *password_hash*: *string*): *bool* | |||
| - auth( *session_id*: *uuid*, *username*: *string*, *password_hash*: *string*): *bool* | |||
| This endpoint requires the session id and your password hash generated from the password concatenated with the seed and hashed using sha256 | |||
| This endpoint requires the session id, an username and your password hash generated from the password concatenated with the seed and hashed using sha256 | |||
| It will return whether the password is correct and you are authorized and set you as authorized in the server instance | |||
| - logout( *session_id*: *uuid*, *password_hash*: *string*): *bool* | |||
| - logout( *session_id*: *uuid*, *username*: *string*, *password_hash*: *string*): *bool* | |||
| This endpoint requires the session id and your password hash generated from the password concatenated with the seed and hashed using sha256 | |||
| It will destroy the session and return you if it worked (should always return true) | |||
| - hello(): *string* | |||
| This endpoint requires the session id, an username and your password hash generated from the password concatenated with the seed and hashed using sha256 | |||
| It will destroy the session and return you if it worked (should always return true) | |||
| - hello( *session_id*: *uuid* ): *string* | |||
| This endpoint will return a string with a hello message if you are authorized and a denial message otherwise | |||
| - create_user( *session*: *uuid*, *username*: *string*, *password*: *string* ) | |||
| This endpoint requires the session id a username and a password for a user to add, adds this user and saves it to a file | |||
| Note: *uuid* is not a the DataType but a string in uuid format | |||
| The client script will then establish a connection, initialize a session, successfully authorize itself, print the return value from hello method and finally destroy the session. | |||
| The client script will then establish a connection, initialize a session, successfully authorize itself, print the return value from hello method and finally destroy the session. | |||
| @@ -2,16 +2,57 @@ | |||
| import xmlrpc.client | |||
| import hashlib | |||
| import os | |||
| import sys | |||
| # connects to the XMLRPC Server | |||
| s = xmlrpc.client.ServerProxy('http://localhost:8000') | |||
| clear = lambda: os.system('clear') | |||
| # initalized the login process | |||
| session, key = s.init() | |||
| password_hash = hashlib.sha256( ( 'Test123' + key ).encode( 'utf-8' ) ).hexdigest(); | |||
| authenticated = False | |||
| username = '' | |||
| password = '' | |||
| # does the actual authentication | |||
| print( s.auth( session, password_hash ) ) | |||
| # tests protected method | |||
| print( s.hello( session ) ) | |||
| # logs out cause anything seems to be done already | |||
| print( s.logout( session, password_hash ) ) | |||
| # concatenates the password and key and returns the hash | |||
| def hash_pass( password, key ): | |||
| return hashlib.sha256( ( password + key ).encode( 'utf-8' ) ).hexdigest() | |||
| # starts main loop | |||
| while True: | |||
| clear() | |||
| # if authenticated offer to user premium methods | |||
| if authenticated: | |||
| print( 'Welcome ' + username ) | |||
| print( 'Please enter what you want to do ( hello, create_user, logout )' ) | |||
| i = input().lower() | |||
| clear() | |||
| if ( i == 'hello' ): | |||
| print( s.hello( session ) ) | |||
| elif ( i == 'create_user' ): | |||
| print( 'Username: ' ) | |||
| un = input() | |||
| clear() | |||
| print( 'Password:' ) | |||
| pw = input() | |||
| clear() | |||
| print( s.create_user( session, un, pw ) ) | |||
| elif( i == 'logout' or i == 'exit' ): | |||
| if ( s.logout( session, username, hash_pass( password, key ) ) ): | |||
| authenticated = False | |||
| sys.exit() | |||
| else: | |||
| print( 'Unknown action ' + i ) | |||
| print( 'Press any key to continue' ) | |||
| input(); | |||
| # otherwise offer to authenticate | |||
| else: | |||
| print( 'Username: ' ) | |||
| username = input() | |||
| clear() | |||
| print( 'Password:' ) | |||
| password = input() | |||
| clear() | |||
| authenticated = s.auth( session, username, hash_pass( password, key ) ) | |||
| pass | |||
| @@ -1,6 +1,7 @@ | |||
| #!/usr/bin/env python2 | |||
| import uuid | |||
| import pickle | |||
| import hashlib | |||
| from xmlrpc.server import SimpleXMLRPCServer | |||
| from xmlrpc.server import SimpleXMLRPCRequestHandler | |||
| @@ -14,15 +15,35 @@ server = SimpleXMLRPCServer(("localhost", 8000), | |||
| requestHandler=RequestHandler) | |||
| server.register_introspection_functions() | |||
| def save_users( users ): | |||
| pickle.dump( users, open( 'users.data', 'wb' ) ) | |||
| # Register an instance; all the methods of the instance are | |||
| # published as XML-RPC methods | |||
| class CHAP: | |||
| test_password = 'Test123' | |||
| # initializes class-instance and instance variables | |||
| # if no userdata exists, it creates a default user | |||
| def __init__( self ): | |||
| self.keys = {} | |||
| self.authenticated = {} | |||
| try: | |||
| self.users = pickle.load( open( 'users.data', 'rb' ) ) | |||
| except FileNotFoundError: | |||
| self.users = { 'admin': 'Pass123' } | |||
| save_users( self.users ) | |||
| # adds a user to dictionary and saves it to a file | |||
| # won't be executable if not authorized or if user already exists | |||
| def create_user( self, session, username, password ): | |||
| if ( self.authenticated.get( session ) == True ): | |||
| if not username in self.users: | |||
| self.users[ username ] = password | |||
| save_users( self.users ) | |||
| return 'Created user successfully' | |||
| else: | |||
| return 'User already exists' | |||
| else: | |||
| return 'Sorry, please authenticate first' | |||
| # tells the server to start the autentification process | |||
| # and send the generated random salt | |||
| @@ -37,9 +58,9 @@ class CHAP: | |||
| # checks if send hash is same as internally generated to validate if the correct | |||
| # password was used | |||
| def auth( self, session, password_hash ): | |||
| if session in self.keys: | |||
| combined = CHAP.test_password + self.keys[ session ] | |||
| def auth( self, session, username, password_hash ): | |||
| if session in self.keys and username in self.users: | |||
| combined = self.users.get( username ) + self.keys.get( session ) | |||
| passhash = hashlib.sha256( combined.encode( 'utf-8' ) ).hexdigest() | |||
| self.authenticated[ session ] = passhash == password_hash | |||
| return self.authenticated[ session ] == True | |||
| @@ -48,8 +69,8 @@ class CHAP: | |||
| # adds functionality for users to log them selfes off, but also need the | |||
| # the password_hash to ensure, that nobody else logs you off | |||
| def logout( self, session, password_hash ): | |||
| if self.auth( session, password_hash ): | |||
| def logout( self, session, username, password_hash ): | |||
| if self.auth( session, username, password_hash ): | |||
| del self.authenticated[ session ] | |||
| del self.keys[ session ] | |||
| return self.authenticated.get( session ) != True | |||