Пример #1
0
 def __init__(self, logfile=None, debug=False):
     self.__conns = {}  # username:connection
     self.__logger = LoggerBuilder().build_logger("Database",
                                                  logfile=logfile,
                                                  debug=debug)
     self.__logger.info("The database has been initialized.")
     self.__demos = {}
     atexit.register(self.__confirm_demo_user_deletion)
Пример #2
0
 def __init__(self, logfile=SERVER_LOG, debug=True):
     self.__db = Database(debug=debug, logfile=DB_LOG)
     self.__authenticated_users = {}
     self.__timers = {}
     self.__blacklist = {}
     self.__logger = LoggerBuilder().build_logger("Server",
                                                  logfile=logfile,
                                                  debug=debug)
     self.__logger.info("The server been initialized.")
Пример #3
0
 def __init__(self, logfile=SERVER_LOG, debug=True):
     self.__db = Database(debug=debug, logfile=DB_LOG)
     self.__authenticated_users = {}
     self.__timers = {}
     self.__blacklist = {}
     self.__logger = LoggerBuilder().build_logger("Server", logfile=logfile, debug=debug)
     self.__logger.info("The server been initialized.")
Пример #4
0
class Server(object):
    def __init__(self, logfile=SERVER_LOG, debug=True):
        self.__db = Database(debug=debug, logfile=DB_LOG)
        self.__authenticated_users = {}
        self.__timers = {}
        self.__blacklist = {}
        self.__logger = LoggerBuilder().build_logger("Server",
                                                     logfile=logfile,
                                                     debug=debug)
        self.__logger.info("The server been initialized.")

    def register(self, uname, h, token):
        if not self.__check_credentials(uname, h):
            return m.Message(m.T_ERR, {m.K_REASON: m.R_BLACKLIST},
                             "Welcome to blacklist.")
        self.__logger.info("We are registering " + uname + ".")
        creds = self.__db.get_user_credentials()
        if uname in creds:
            self.__logger.info("Registration for " + uname +
                               " failed. Username already in use.")
            return m.Message(m.T_FAIL, {m.K_REASON: m.R_UNAME_TAKEN},
                             "Username already in use.")
        f = open(REG_TOKENS, "r")
        tokens = f.read()
        write_back = ""
        success = False
        result = m.Message(m.T_FAIL, {m.K_REASON: m.R_INVALID_TOKEN},
                           "Invalid token.")
        for t in tokens.split('\n'):
            if t == token and len(t) == 32:
                self.__db.store_user_credentials(uname, h)
                result = m.Message(
                    m.T_ACK, {}, "User " + uname + " registered successfully.")
                success = True
                self.__logger.info("Registration for " + uname +
                                   " successful.")
            elif len(t) == 32:
                write_back += t + '\n'
            else:
                pass
        f.close()
        if success == False:
            self.__logger.info("Registration for " + uname +
                               " failed. Invalid token.")
        else:
            f = open(REG_TOKENS, "w")
            f.write(write_back)
        return result

    def demo(self):
        self.__logger.info("We are requesting a demo session.")
        user = self.__db.generate_demo_user()
        if user == {}:
            self.__logger.info("We denied session. Demo user limit reached.")
            return m.Message(m.T_ERR, {m.K_REASON: m.R_LIMIT_REACHED},
                             "Demo user limit reached. Try again later.")

        else:
            uname = user[m.K_USER]
            self.__logger.info("We granted session " + uname + ".")
            session_token = self.__generate_session_token()
            user[m.K_SID] = session_token
            self.__authenticated_users[uname] = session_token
            self.__logger.debug("We have added " + uname + " into auth_users.")
            self.__set_reset_timer(uname)
            return m.Message(m.T_RESPONSE, user, "Demo session granted.")

    def authenticate(self, uname, h):
        self.__logger.info("We are authenticating user '" + uname + "': '" +
                           h + "'...")
        creds = self.__db.get_user_credentials()
        if creds == {}:
            self.__logger.debug("Authentication failed. User " + uname +
                                " not registered.")
            return m.Message(m.T_FAIL, {m.K_REASON: m.R_EMPTY},
                             "User not registered.")
        self.__logger.debug("The credentials are: " + str(creds))
        if uname in creds.keys():
            match = self.__match_safely(creds[uname], h)
            # Success if user is found and hash is correct.
            if match:
                session_token = self.__generate_session_token()
                self.__authenticated_users[uname] = session_token
                self.__logger.debug("We have added " + uname +
                                    " into auth_users.")
                self.__set_reset_timer(uname)
                self.__logger.debug("Authenticated users: " +
                                    self.__authenticated_users.__str__())
                self.__logger.info(uname + "'s authentication successful.")
                return m.Message(m.T_RESPONSE, {m.K_SID: session_token},
                                 "Authentication successful.")
            else:
                if uname in self.__authenticated_users.keys():
                    del self.__authenticated_users[uname]
                self.__logger.debug("Authentication failed. Invalid hash.")
                return m.Message(m.T_FAIL, {m.K_REASON: m.R_NOT_AUTH},
                                 "authentication failed.")
        else:
            self.__logger.debug("failed. User " + uname + " not registered.")
            return m.Message(m.T_FAIL, {m.K_REASON: m.R_NOT_AUTH},
                             "user not registered.")

    def logout(self, uname):
        if uname in self.__authenticated_users.keys():
            del self.__authenticated_users[uname]
            self.__logger.debug("We have removed " + uname +
                                " from auth_users.")
        if uname in self.__timers.keys():
            self.__timers[uname].die()
            del self.__timers[uname]
            self.__logger.debug("We have removed " + uname + " from timers.")
        self.__logger.info("User " + uname + " logged out.")
        if uname.startswith('demo_'):
            self.__db.delete_user(uname)
            self.__logger.debug("We have deleted demouser " + uname + ".")
        return m.Message(m.T_ACK, {}, "Bye.")

    def read_passwords(self, uname, sid):
        if self.__check_auth(uname, sid):
            pwd_dict = self.__db.read_passwords(uname)
            return m.Message(m.T_RESPONSE, pwd_dict, "Read successful.")
        else:
            return m.Message(m.T_FAIL, {m.K_REASON: m.R_NOT_AUTH},
                             "Authentication required.")

    def store_password(self, uname, sid, id, pwd):
        if self.__check_auth(uname, sid):
            result = self.__db.add_password(uname, identifier=id, pwd=pwd)
            if result == True:
                return m.Message(m.T_ACK, {}, "Storage successful.")
            else:
                raise Exception("Store password faced unexpected db response.")
        else:
            return m.Message(m.T_FAIL, {m.K_REASON: m.R_NOT_AUTH},
                             "Authentication required.")

    def remove_password(self, uname, sid, id):
        if self.__check_auth(uname, sid):
            result = self.__db.delete_password(uname, identifier=id)
            if result == True:
                return m.Message(m.T_ACK, {}, "Removal successful.")
            else:
                raise Exception(
                    "Remove password faced unexpected db response.")
        else:
            return m.Message(m.T_FAIL, {m.K_REASON: m.R_NOT_AUTH},
                             "Authentication required.")

    def respond(self, request, ip):
        if ip in self.__blacklist.keys():
            time_passed = time.time() - self.__blacklist[ip]
            if time_passed > BLACKLIST_DURATION:
                del self.__blacklist[ip]
            else:
                self.__blacklist[ip] = time.time()
                msg = m.Message(
                    m.T_ERR, {m.K_REASON: m.R_BLACKLIST},
                    "You are in blacklist. If you think this is a mistake\nand you have done nothing wrong, contact\nthe administrator."
                )
                self.__logger.info("We have reset the blaclist timer for " +
                                   ip + ".")
                return msg.toJson()
        msg = self.__construct_response(request)
        if msg.type == m.T_ERR:
            args = msg.args
            if m.K_REASON in args.keys() and args[m.K_REASON] == m.R_BLACKLIST:
                self.__blacklist[ip] = time.time()
                self.__logger.info("We have blacklisted " + ip +
                                   " for 5 minutes.")
        return msg.toJson()

    def __construct_response(self, request):
        try:
            msg = m.construct_message(request)
        except Exception as e:
            self.__logger.info("Exception in construct_message: " +
                               e.__str__())
        if msg:
            response = self.__exact_response(msg)
            return response
        else:
            return m.Message(m.T_ERR, {m.K_REASON: m.R_BLACKLIST},
                             "Welcome to blacklist.")

    def __exact_response(self, msg):
        '''
        Sorts the requests to right handlers according to type.
        If the client has been tampered with, it is blacklisted.
        '''
        if msg.type == m.T_LOGIN:
            keys = msg.args.keys()
            if len(keys) == 2 and m.K_USER in keys and m.K_HASH in keys:
                return self.authenticate(uname=msg.args[m.K_USER],
                                         h=msg.args[m.K_HASH])
            elif len(keys) == 2 and m.K_USER in keys and m.K_SID in keys:
                self.__timer_reset(uname=msg.args[m.K_USER])
                self.__logger.debug("User " + msg.args[m.K_USER] +
                                    " still alive.")
                return m.Message(m.T_ACK, {}, "We have refreshed your login.")
            else:
                return m.Message(m.T_ERR, {m.K_REASON: m.R_BLACKLIST},
                                 "Welcome to blacklist.")
        elif msg.type == m.T_GET:
            keys = msg.args.keys()
            if len(keys) == 2 and m.K_USER in keys and m.K_SID in keys:
                return self.read_passwords(msg.args[m.K_USER],
                                           msg.args[m.K_SID])
            else:
                return m.Message(m.T_ERR, {m.K_REASON: m.R_BLACKLIST},
                                 "Welcome to blacklist.")
        elif msg.type == m.T_PUT:
            keys = msg.args.keys()
            if len(
                    keys
            ) == 4 and m.K_USER in keys and m.K_SID in keys and m.K_ID in keys and m.K_PWD in keys:
                return self.store_password(msg.args[m.K_USER],
                                           msg.args[m.K_SID],
                                           id=msg.args[m.K_ID],
                                           pwd=msg.args[m.K_PWD])
            else:
                return m.Message(m.T_ERR, {m.K_REASON: m.R_BLACKLIST},
                                 "Welcome to blacklist.")
        elif msg.type == m.T_REMOVE:
            keys = msg.args.keys()
            args = msg.args
            if len(
                    keys
            ) == 3 and m.K_USER in keys and m.K_SID in keys and m.K_ID in keys:
                return self.remove_password(args[m.K_USER],
                                            args[m.K_SID],
                                            id=args[m.K_ID])
            else:
                return m.Message(m.T_ERR, {m.K_REASON: m.R_BLACKLIST},
                                 "Welcome to blacklist.")
        elif msg.type == m.T_LOGOUT:
            keys = msg.args.keys()
            args = msg.args
            if len(keys) == 1 and m.K_USER in keys:
                return self.logout(args[m.K_USER])
            else:
                return m.Message(m.T_ERR, {m.K_REASON: m.R_BLACKLIST},
                                 "Welcome to blacklist.")
        elif msg.type == m.T_REGISTER:
            keys = msg.args.keys()
            args = msg.args
            if len(
                    keys
            ) == 3 and m.K_USER in keys and m.K_HASH in keys and m.K_TOKEN in keys:
                return self.register(args[m.K_USER], args[m.K_HASH],
                                     args[m.K_TOKEN])
            else:
                return m.Message(m.T_ERR, {m.K_REASON: m.R_BLACKLIST},
                                 "Welcome to blacklist.")
        elif msg.type == m.T_DEMO:
            keys = msg.args.keys()
            if len(keys) == 0:
                return self.demo()
            else:
                return m.Message(m.T_ERR, {m.K_REASON: m.R_BLACKLIST},
                                 "Welcome to blacklist.")

        else:
            return m.Message(m.T_ERR, {m.K_REASON: m.R_BLACKLIST},
                             "Welcome to blacklist.")

    def __check_auth(self, uname, sid):
        auth_users = self.__authenticated_users
        if uname in auth_users and self.__match_safely(sid, auth_users[uname]):
            return True
        else:
            self.__logger.debug("Check auth failed. User " + uname +
                                " not authenticated.")
            return False

    def __match_safely(self, str1, str2):
        try:
            match = True
            if len(str1) != len(str2):
                return False
            for i in range(len(str1)):
                if str1[i] != str2[i]:
                    match = False
            return match
        except Exception as e:
            self.__logger.debug("Exception caught in __match_safely:", e)
            return False

    def __generate_session_token(self):
        secret = rng.get_random_bytes(50)
        return secret.encode('base64').replace('\n', '')

    def __expire_user(self, uname):
        if uname in self.__authenticated_users.keys():
            del self.__authenticated_users[uname]
            del self.__timers[uname]
            self.__logger.debug("We have removed " + uname +
                                " from timers and auth_users.")

    def __timer_reset(self, uname):
        self.__timers[uname].reset()

    def __set_reset_timer(self, uname, time=300):
        # Set a timer in seconds how long user's authentication stays valid.
        self.__logger.debug("We have added " + uname + " into timers.")
        self.__timers[uname] = ExpirationTimer(uname, self.__expire_user, time)
        self.__timers[uname].start()

    def __check_credentials(self, uname, h):
        if self.__is_valid_id(uname) and self.__is_valid_hash(h):
            return True
        else:
            return False

    def __is_valid_id(self, id):
        normal = False
        for letter in id:
            if not (64 < ord(str(letter)) < 91 or 96 < ord(str(letter)) < 123
                    or 47 < ord(str(letter)) < 58):
                return False
        if 3 < len(id) < 16:
            return True
        else:
            return False

    def __is_valid_hash(self, h):
        if len(h) == 128:
            try:
                h.decode('hex')
                return True
            except:
                return False
        else:
            return False
Пример #5
0
class Database(object):
    def __init__(self, logfile=None, debug=False):
        self.__conns = {}  # username:connection
        self.__logger = LoggerBuilder().build_logger("Database",
                                                     logfile=logfile,
                                                     debug=debug)
        self.__logger.info("The database has been initialized.")
        self.__demos = {}
        atexit.register(self.__confirm_demo_user_deletion)

    def __confirm_demo_user_deletion(self):
        for uname in self.__demos.keys():
            self.delete_user(uname)

    def __get_db_conn(self, uname):
        '''
        Returns an open connection object to the parameter user's database.
        If the connection is not open, it will be opened and returned.
        '''
        self.__logger.debug("We are opening connection to db " + uname + ".")
        if not uname in self.__conns.keys():
            conn = sqlite3.connect(uname)
            self.__conns[uname] = conn
            return conn
        else:
            return self.__conns[uname]

    def __close_db_conn(self, uname):
        '''
        Closes the connection (if open) to parameter users database.
        '''
        try:
            self.__logger.debug("We are closing connection to db " + uname +
                                ".")
            self.__conns[uname].close()
            del self.__conns[uname]
        except Exception as e:
            self.__logger.error(
                "We caught an exception in close_conn:\nType: " +
                str(type(e)) + "\nArgs: " + e.__str__())

    def __pad_data(self, data):
        while len(data) % 16 != 0:
            data = data + " "
        return data

    def store_user_credentials(self, uname, password_hash_hex):
        '''
        Stores a user's credential info for authentication purposes. 
        '''
        conn = self.__get_db_conn(CREDENTIALS)
        cursor = conn.cursor()
        added = False
        updated = False
        #Create the table for credentials if it doesn't already exist.
        cursor.execute("create table if not exists " + CREDENTIALS +
                       " (username text, password text)")
        #Insert into credentials table if user was not already there.
        if not cursor.execute(
                "select * from " + CREDENTIALS + " where username=?",
            (uname, )).fetchone():
            cursor.execute("insert into " + CREDENTIALS + " values (?,?)", (
                uname,
                password_hash_hex,
            ))
            conn.commit()
            added = True
            self.__logger.info("We have successfully registered " + uname +
                               ".")
        else:
            cursor.execute(
                "update " + CREDENTIALS + " set password=? where username=?", (
                    password_hash_hex,
                    uname,
                ))
            conn.commit()
            updated = True
            self.__logger.info("We have updated " + uname +
                               "'s password in credentials library.")
        self.__close_db_conn(CREDENTIALS)
        if added:
            return ADDED
        elif updated:
            return UPDATED
        else:
            raise Exception(
                'Store user credentials neither updated or added anything.')

    def get_user_credentials(self):
        conn = self.__get_db_conn(CREDENTIALS)
        # We are avoiding any complications in opening and closing the connection.
        result = self.__get_user_credentials(conn)
        self.__close_db_conn(CREDENTIALS)
        return result

    def __get_user_credentials(self, conn):
        '''
        Reads user credentials and returns them in a dictionary.
        '''
        cursor = conn.cursor()
        exists = cursor.execute(
            "select name from sqlite_master where type='table' and name='" +
            CREDENTIALS + "'").fetchall()
        if len(exists) == 0:
            self.__logger.info(
                "We don't have any registered users. Returning {}.")
            return {}
        credentials = cursor.execute("select * from " + CREDENTIALS).fetchall()
        dict_format = {}
        for tuple in credentials:
            dict_format[str(tuple[0])] = str(tuple[1])
        credentials = dict_format
        self.__logger.info("We are reading credentials.")
        self.__logger.debug("Credentials: " + str(credentials))
        return credentials

    def add_password(self, uname, identifier, pwd):
        '''
        Stores a password in the users database.
        Opens a connection to the db if neccessary.
        '''
        iv = rng.get_random_bytes(16)
        ec = AES.new(DB_KEY, AES.MODE_CBC, iv)
        #Encrypting the password for storage
        pwd = ec.encrypt(self.__pad_data(pwd)).encode('hex')
        conn = self.__get_db_conn(uname)
        cursor = conn.cursor()
        added = False
        #Create the table for the user if it doesn't already exist.
        cursor.execute(
            "create table if not exists sites (id text, iv text, password text)"
        )
        #Insert into db if identifier is not already there.
        if not cursor.execute("select * from sites where id=?",
                              (identifier.encode('hex'), )).fetchone():
            #IV and ID encoded in hex for storage.
            self.__logger.debug("We are adding a new password " + identifier +
                                " for user " + uname + ".")
            cursor.execute("insert into sites values (?,?,?)", (
                identifier.encode('hex'),
                iv.encode('hex'),
                pwd,
            ))
        # Else update pwd value.
        else:
            cursor.execute("update sites set iv=? ,password=? where id=?", (
                iv.encode('hex'),
                pwd,
                identifier.encode('hex'),
            ))
            self.__logger.debug("We have updated " + uname + "'s password " +
                                identifier + ".")
        conn.commit()
        added = True
        self.__close_db_conn(uname)
        return added

    def read_passwords(self, uname):
        '''
        Reads all the passwords of the parameter user and returns them in a list.
        '''
        conn = self.__get_db_conn(uname)
        # We are avoiding any complications in opening and closing the connection.
        result = self.__read_passwords(conn, uname)
        self.__close_db_conn(uname)
        return result

    def __read_passwords(self, conn, uname):
        self.__logger.info("We are reading passwords for " + uname + ".")
        cursor = conn.cursor()
        exists = cursor.execute(
            "select name from sqlite_master where type='table' and name='sites'"
        ).fetchall()
        if len(exists) == 0:
            self.__logger.info("We have no passwords for " + uname +
                               " Returning {}.")
            return {}
        passwords = cursor.execute("select * from sites").fetchall()
        self.__logger.info("Read for " + uname + " successful.")
        self.__logger.debug("Passwords:")
        result = {}
        for line in passwords:
            iv_hex = str(line[1])
            id_hex = line[0]
            dc = AES.new(DB_KEY, AES.MODE_CBC, iv_hex.decode('hex'))
            password = dc.decrypt(line[2].decode('hex')).rstrip(' ')
            self.__logger.debug([id_hex.decode('hex'), iv_hex, password])
            result[id_hex.decode('hex')] = password
        return result

    def delete_password(self, uname, identifier):
        self.__logger.info("We are trying to delete " + uname +
                           "'s password " + identifier + ".")
        conn = self.__get_db_conn(uname)
        cursor = conn.cursor()
        removed = False
        exists = cursor.execute(
            "select name from sqlite_master where type='table' and name='sites'"
        ).fetchall()
        if len(exists) == 0:
            self.__logger.info("We have no passwords for " + uname +
                               ". Returning False.")
            return False
        if cursor.execute("select * from sites where id=?",
                          (identifier.encode('hex'), )).fetchone():
            cursor.execute("delete from sites where id=?",
                           (identifier.encode('hex'), ))
            conn.commit()
            removed = True
            self.__logger.info("We are deleting user " + uname +
                               "'s password " + identifier)
            #If table is empty after removal, it is dropped.
            if not cursor.execute("select * from sites").fetchone():
                cursor.execute("drop table sites")
                conn.commit()
                self.__logger.debug("We are deleting user " + uname +
                                    "'s empty table sites.")
        self.__close_db_conn(uname)
        return removed

    def delete_user(self, uname):
        self.__logger.info("We are deleting user " + uname + ".")
        conn = self.__get_db_conn(uname)
        cursor = conn.cursor()
        cursor.execute("drop table if exists sites")
        self.__logger.debug("We are dropping table sites if it exists.")
        conn.commit()
        try:
            os.remove(uname)
        except:
            pass
        self.__close_db_conn(uname)
        conn = self.__get_db_conn(CREDENTIALS)
        cursor = conn.cursor()
        removed = False
        exists = cursor.execute(
            "select name from sqlite_master where type='table' and name='" +
            CREDENTIALS + "'").fetchall()
        if len(exists) == 0:
            self.__logger.info(
                "We don't have any registered users. Returning False.")
            return False
        if cursor.execute("select * from " + CREDENTIALS + " where username=?",
                          (uname, )).fetchone():
            cursor.execute("delete from " + CREDENTIALS + " where username=?",
                           (uname, ))
            conn.commit()
            removed = True
            self.__logger.info("We have deleted user " + uname + ".")
            #If table is empty after removal, it is dropped.
            if not cursor.execute("select * from " + CREDENTIALS).fetchone():
                cursor.execute("drop table " + CREDENTIALS)
                conn.commit()
                self.__logger.debug("We are deleting empty table " +
                                    CREDENTIALS + ".")
        else:
            self.__logger.debug("We didn't delete " + uname +
                                ", he's gone already.")
        self.__close_db_conn(CREDENTIALS)
        return removed

    def generate_demo_user(self):
        if len(self.__demos.keys()) > 10:
            return None
        demo = {}
        dname = "demo_" + str(len(self.__demos.keys()))
        dpass = rng.get_random_bytes(20).encode('hex')
        demo[m.K_USER] = dname
        demo[m.K_DEMO_PASS] = dpass
        self.__demos[dname] = dpass
        return demo
Пример #6
0
class Server(object):
   
    def __init__(self, logfile=SERVER_LOG, debug=True):
        self.__db = Database(debug=debug, logfile=DB_LOG)
        self.__authenticated_users = {}
        self.__timers = {}
        self.__blacklist = {}
        self.__logger = LoggerBuilder().build_logger("Server", logfile=logfile, debug=debug)
        self.__logger.info("The server been initialized.")

    def register(self, uname, h, token):
        if not self.__check_credentials(uname, h):
            return m.Message(m.T_ERR, {m.K_REASON:m.R_BLACKLIST}, "Welcome to blacklist.")
        self.__logger.info("We are registering "+uname+".")
        creds = self.__db.get_user_credentials()
        if uname in creds:
            self.__logger.info("Registration for "+uname+" failed. Username already in use.")
            return m.Message(m.T_FAIL, {m.K_REASON:m.R_UNAME_TAKEN}, "Username already in use.")  
        f = open(REG_TOKENS, "r")
        tokens = f.read()
        write_back = ""
        success = False
        result = m.Message(m.T_FAIL, {m.K_REASON:m.R_INVALID_TOKEN}, "Invalid token.") 
        for t in tokens.split('\n'):
            if t == token and len(t) == 32: 
                self.__db.store_user_credentials(uname, h)
                result = m.Message(m.T_ACK, {}, "User "+uname+" registered successfully.") 
                success = True
                self.__logger.info("Registration for "+uname+" successful.")
            elif len(t) == 32:
                write_back += t+'\n'
            else:
                pass  
        f.close()
        if success == False:
            self.__logger.info("Registration for "+uname+" failed. Invalid token.")
        else:
            f = open(REG_TOKENS, "w")
            f.write(write_back) 
        return result
   
    def demo(self):
        self.__logger.info("We are requesting a demo session.")
        user = self.__db.generate_demo_user()
        if user == {}:
            self.__logger.info("We denied session. Demo user limit reached.")
            return m.Message(m.T_ERR, {m.K_REASON:m.R_LIMIT_REACHED}, "Demo user limit reached. Try again later.")

        else:
            uname = user[m.K_USER]
            self.__logger.info("We granted session "+uname+".")
            session_token = self.__generate_session_token()
            user[m.K_SID] = session_token
            self.__authenticated_users[uname] = session_token
            self.__logger.debug("We have added "+uname+ " into auth_users.")
            self.__set_reset_timer(uname)
            return m.Message(m.T_RESPONSE, user, "Demo session granted.")

    def authenticate(self, uname, h):
        self.__logger.info("We are authenticating user '"+uname+"': '"+h+"'...")
        creds = self.__db.get_user_credentials()
        if creds == {}:
            self.__logger.debug("Authentication failed. User "+uname+" not registered.")         
            return m.Message(m.T_FAIL, {m.K_REASON : m.R_EMPTY}, "User not registered.")
        self.__logger.debug("The credentials are: "+str(creds))            
        if uname in creds.keys():
            match = self.__match_safely(creds[uname], h)
            # Success if user is found and hash is correct.
            if match:
                session_token = self.__generate_session_token()
                self.__authenticated_users[uname] = session_token
                self.__logger.debug("We have added "+uname+ " into auth_users.")
                self.__set_reset_timer(uname)
                self.__logger.debug("Authenticated users: "+self.__authenticated_users.__str__())
                self.__logger.info(uname+"'s authentication successful.")
                return m.Message(m.T_RESPONSE, {m.K_SID : session_token}, "Authentication successful.")
            else:
                if uname in self.__authenticated_users.keys():
                    del self.__authenticated_users[uname]
                self.__logger.debug("Authentication failed. Invalid hash.")
                return m.Message(m.T_FAIL, {m.K_REASON : m.R_NOT_AUTH}, "authentication failed.")
        else:
            self.__logger.debug("failed. User "+uname+" not registered.")         
            return m.Message(m.T_FAIL, {m.K_REASON : m.R_NOT_AUTH}, "user not registered.")

    def logout(self, uname):
        if uname in self.__authenticated_users.keys():
            del self.__authenticated_users[uname]
            self.__logger.debug("We have removed "+uname+" from auth_users.")
        if uname in self.__timers.keys():
            self.__timers[uname].die()
            del self.__timers[uname]
            self.__logger.debug("We have removed "+uname+" from timers.")
        self.__logger.info("User "+uname+" logged out.")
        if uname.startswith('demo_'):
            self.__db.delete_user(uname)
            self.__logger.debug("We have deleted demouser "+uname+".")
        return m.Message(m.T_ACK, {}, "Bye.")
           
    def read_passwords(self, uname, sid):
        if self.__check_auth(uname, sid):
            pwd_dict = self.__db.read_passwords(uname)
            return m.Message(m.T_RESPONSE, pwd_dict, "Read successful.")
        else:       
            return m.Message(m.T_FAIL, {m.K_REASON : m.R_NOT_AUTH}, "Authentication required.") 
        
    def store_password(self, uname, sid, id, pwd):
        if self.__check_auth(uname, sid):
            result = self.__db.add_password(uname, identifier=id, pwd=pwd)
            if result == True:
                return m.Message(m.T_ACK, {}, "Storage successful.")
            else:
                raise Exception("Store password faced unexpected db response.")
        else:
            return m.Message(m.T_FAIL, {m.K_REASON : m.R_NOT_AUTH}, "Authentication required.") 

    def remove_password(self, uname, sid, id):
        if self.__check_auth(uname, sid):
            result = self.__db.delete_password(uname, identifier=id)
            if result == True:
                return m.Message(m.T_ACK, {}, "Removal successful.")
            else:
                raise Exception("Remove password faced unexpected db response.")
        else:
            return m.Message(m.T_FAIL, {m.K_REASON : m.R_NOT_AUTH}, "Authentication required.") 

    def respond(self, request, ip):
        if ip in self.__blacklist.keys():
            time_passed = time.time() - self.__blacklist[ip]
            if time_passed > BLACKLIST_DURATION:
                del self.__blacklist[ip]
            else:
                self.__blacklist[ip] = time.time()
                msg = m.Message(m.T_ERR, {m.K_REASON:m.R_BLACKLIST}, "You are in blacklist. If you think this is a mistake\nand you have done nothing wrong, contact\nthe administrator.")
                self.__logger.info("We have reset the blaclist timer for "+ip+".")
                return msg.toJson()
        msg = self.__construct_response(request)
        if msg.type == m.T_ERR:
            args = msg.args
            if m.K_REASON in args.keys() and args[m.K_REASON] == m.R_BLACKLIST:
                self.__blacklist[ip] = time.time()
                self.__logger.info("We have blacklisted "+ip+" for 5 minutes.")
        return msg.toJson()

    def __construct_response(self, request):
        try:
            msg = m.construct_message(request)
        except Exception as e:
            self.__logger.info("Exception in construct_message: "+e.__str__())
        if msg:
            response = self.__exact_response(msg)
            return response
        else:
            return m.Message(m.T_ERR, {m.K_REASON:m.R_BLACKLIST}, "Welcome to blacklist.")

    def __exact_response(self, msg):
        '''
        Sorts the requests to right handlers according to type.
        If the client has been tampered with, it is blacklisted.
        '''
        if msg.type == m.T_LOGIN:
            keys = msg.args.keys()
            if len(keys) == 2 and m.K_USER in keys and m.K_HASH in keys:
                return self.authenticate(uname=msg.args[m.K_USER], h=msg.args[m.K_HASH])
            elif len(keys) == 2 and m.K_USER in keys and m.K_SID in keys:
                self.__timer_reset(uname=msg.args[m.K_USER])
                self.__logger.debug("User "+msg.args[m.K_USER]+" still alive.")         
                return m.Message(m.T_ACK, {}, "We have refreshed your login.")
            else:
                return m.Message(m.T_ERR, {m.K_REASON:m.R_BLACKLIST}, "Welcome to blacklist.")
        elif msg.type == m.T_GET:
            keys = msg.args.keys()
            if len(keys) == 2 and m.K_USER in keys and m.K_SID in keys:
                return self.read_passwords(msg.args[m.K_USER], msg.args[m.K_SID])
            else:
                return m.Message(m.T_ERR, {m.K_REASON:m.R_BLACKLIST}, "Welcome to blacklist.")
        elif msg.type == m.T_PUT:
            keys = msg.args.keys()
            if len(keys) == 4 and m.K_USER in keys and m.K_SID in keys and m.K_ID in keys and m.K_PWD in keys:
                return self.store_password(msg.args[m.K_USER], msg.args[m.K_SID], id=msg.args[m.K_ID], pwd=msg.args[m.K_PWD])
            else:
                return m.Message(m.T_ERR, {m.K_REASON:m.R_BLACKLIST}, "Welcome to blacklist.")
        elif msg.type == m.T_REMOVE:
            keys = msg.args.keys()
            args = msg.args
            if len(keys) == 3 and m.K_USER in keys and m.K_SID in keys and m.K_ID in keys:
                return self.remove_password(args[m.K_USER], args[m.K_SID], id=args[m.K_ID])
            else:
                return m.Message(m.T_ERR, {m.K_REASON:m.R_BLACKLIST}, "Welcome to blacklist.")
        elif msg.type == m.T_LOGOUT:
            keys = msg.args.keys()
            args = msg.args
            if len(keys) == 1 and m.K_USER in keys:
                return self.logout(args[m.K_USER])
            else:
                return m.Message(m.T_ERR, {m.K_REASON:m.R_BLACKLIST}, "Welcome to blacklist.")
        elif msg.type == m.T_REGISTER:
            keys = msg.args.keys()
            args = msg.args
            if len(keys) == 3 and m.K_USER in keys and m.K_HASH in keys and m.K_TOKEN in keys:
                return self.register(args[m.K_USER], args[m.K_HASH], args[m.K_TOKEN])
            else:
                return m.Message(m.T_ERR, {m.K_REASON:m.R_BLACKLIST}, "Welcome to blacklist.")
        elif msg.type == m.T_DEMO:
            keys = msg.args.keys()
            if len(keys) == 0:
                return self.demo()
            else:
                return m.Message(m.T_ERR, {m.K_REASON:m.R_BLACKLIST}, "Welcome to blacklist.")
        
        else:
            return m.Message(m.T_ERR, {m.K_REASON:m.R_BLACKLIST}, "Welcome to blacklist.")

    def __check_auth(self, uname, sid):
        auth_users = self.__authenticated_users
        if uname in auth_users and self.__match_safely(sid, auth_users[uname]):
            return True
        else:
            self.__logger.debug("Check auth failed. User "+uname+" not authenticated.")         
            return False

    def __match_safely(self, str1, str2):
        try:
            match = True
            if len(str1) != len(str2):
                return False 
            for i in range(len(str1)):
                if str1[i] != str2[i] :
                    match = False
            return match
        except Exception as e:
            self.__logger.debug("Exception caught in __match_safely:", e)                    
            return False 

    def __generate_session_token(self):
        secret = rng.get_random_bytes(50)
        return secret.encode('base64').replace('\n', '')

    def __expire_user(self, uname):
        if uname in self.__authenticated_users.keys():
            del self.__authenticated_users[uname] 
            del self.__timers[uname]
            self.__logger.debug("We have removed "+uname+" from timers and auth_users.")

    def __timer_reset(self, uname):
        self.__timers[uname].reset()

    def __set_reset_timer(self, uname, time=300):
        # Set a timer in seconds how long user's authentication stays valid.
        self.__logger.debug("We have added "+uname+" into timers.")
        self.__timers[uname] = ExpirationTimer(uname, self.__expire_user, time)
        self.__timers[uname].start()

    def __check_credentials(self, uname, h):
        if self.__is_valid_id(uname) and self.__is_valid_hash(h):
            return True
        else:
            return False
        

    def __is_valid_id(self, id):
        normal = False
        for letter in id:
            if not (64 < ord(str(letter)) < 91 or 96 < ord(str(letter)) < 123 or 47 < ord(str(letter)) < 58):
                return False
        if 3 < len(id) < 16:
            return True
        else:
            return False 

    def __is_valid_hash(self, h):
        if len(h) == 128:
            try:
                h.decode('hex')
                return True
            except:
                return False
        else:
            return False 
Пример #7
0
 def __init__(self, logfile=None, debug=False):
     self.__conns = {} # username:connection
     self.__logger = LoggerBuilder().build_logger("Database", logfile=logfile, debug=debug)
     self.__logger.info("The database has been initialized.")
     self.__demos = {}
     atexit.register(self.__confirm_demo_user_deletion)
Пример #8
0
class Database(object):

    def __init__(self, logfile=None, debug=False):
        self.__conns = {} # username:connection
        self.__logger = LoggerBuilder().build_logger("Database", logfile=logfile, debug=debug)
        self.__logger.info("The database has been initialized.")
        self.__demos = {}
        atexit.register(self.__confirm_demo_user_deletion)
    
    def __confirm_demo_user_deletion(self):
        for uname in self.__demos.keys():
            self.delete_user(uname)
        
    def __get_db_conn(self, uname):
        '''
        Returns an open connection object to the parameter user's database.
        If the connection is not open, it will be opened and returned.
        '''
        self.__logger.debug("We are opening connection to db "+uname+".")
        if not uname in self.__conns.keys():
            conn = sqlite3.connect(uname)
            self.__conns[uname] = conn
            return conn
        else:
            return self.__conns[uname]
    
    def __close_db_conn(self, uname):
        '''
        Closes the connection (if open) to parameter users database.
        '''
        try:
            self.__logger.debug("We are closing connection to db "+uname+".")
            self.__conns[uname].close()
            del self.__conns[uname]
        except Exception as e:
            self.__logger.error("We caught an exception in close_conn:\nType: "+str(type(e))+"\nArgs: "+e.__str__())

    def __pad_data(self, data):
        while len(data)%16 != 0:
            data = data+" "
        return data

    def store_user_credentials(self, uname, password_hash_hex):
        '''
        Stores a user's credential info for authentication purposes. 
        '''
        conn = self.__get_db_conn(CREDENTIALS)
        cursor = conn.cursor() 
        added = False
        updated = False
        #Create the table for credentials if it doesn't already exist.
        cursor.execute("create table if not exists "+CREDENTIALS+" (username text, password text)")
        #Insert into credentials table if user was not already there.
        if not cursor.execute("select * from "+CREDENTIALS+" where username=?", (uname,)).fetchone():
            cursor.execute("insert into "+CREDENTIALS+" values (?,?)", (uname, password_hash_hex,))
            conn.commit()
            added = True   
            self.__logger.info("We have successfully registered "+uname+".")
        else:
            cursor.execute("update "+CREDENTIALS+" set password=? where username=?", (password_hash_hex, uname,))
            conn.commit()
            updated = True 
            self.__logger.info("We have updated "+uname+"'s password in credentials library.")
        self.__close_db_conn(CREDENTIALS)
        if added:
            return ADDED
        elif updated:
            return UPDATED
        else:
            raise Exception('Store user credentials neither updated or added anything.')

    def get_user_credentials(self):
        conn = self.__get_db_conn(CREDENTIALS)
        # We are avoiding any complications in opening and closing the connection.
        result = self.__get_user_credentials(conn)
        self.__close_db_conn(CREDENTIALS)
        return result

    def __get_user_credentials(self, conn):
        '''
        Reads user credentials and returns them in a dictionary.
        '''
        cursor = conn.cursor()
        exists = cursor.execute("select name from sqlite_master where type='table' and name='"+CREDENTIALS+"'").fetchall()
        if len(exists) == 0:
            self.__logger.info("We don't have any registered users. Returning {}.")
            return {}    
        credentials = cursor.execute("select * from "+CREDENTIALS).fetchall()    
        dict_format = {}
        for tuple in credentials:
            dict_format[str(tuple[0])] = str(tuple[1])
        credentials = dict_format
        self.__logger.info("We are reading credentials.")
        self.__logger.debug("Credentials: "+str(credentials))
        return credentials

    def add_password(self, uname, identifier, pwd):
        '''
        Stores a password in the users database.
        Opens a connection to the db if neccessary.
        '''
        iv = rng.get_random_bytes(16)
        ec = AES.new(DB_KEY, AES.MODE_CBC, iv)
        #Encrypting the password for storage
        pwd = ec.encrypt(self.__pad_data(pwd)).encode('hex')
        conn = self.__get_db_conn(uname)
        cursor = conn.cursor()
        added = False
        #Create the table for the user if it doesn't already exist.
        cursor.execute("create table if not exists sites (id text, iv text, password text)")
        #Insert into db if identifier is not already there.
        if not cursor.execute("select * from sites where id=?", (identifier.encode('hex'),)).fetchone():
            #IV and ID encoded in hex for storage.
            self.__logger.debug("We are adding a new password "+identifier+" for user "+uname+".")
            cursor.execute("insert into sites values (?,?,?)",(identifier.encode('hex'), iv.encode('hex'), pwd,))
        # Else update pwd value.
        else:
            cursor.execute("update sites set iv=? ,password=? where id=?", (iv.encode('hex'), pwd, identifier.encode('hex'),))
            self.__logger.debug("We have updated "+uname+"'s password "+identifier+".")
        conn.commit()
        added = True   
        self.__close_db_conn(uname)
        return added

    def read_passwords(self, uname):
        '''
        Reads all the passwords of the parameter user and returns them in a list.
        '''
        conn = self.__get_db_conn(uname)
        # We are avoiding any complications in opening and closing the connection.
        result = self.__read_passwords(conn, uname)
        self.__close_db_conn(uname)
        return result

    def __read_passwords(self, conn, uname):        
        self.__logger.info("We are reading passwords for "+uname+".")
        cursor = conn.cursor()
        exists = cursor.execute("select name from sqlite_master where type='table' and name='sites'").fetchall()
        if len(exists) == 0:
            self.__logger.info("We have no passwords for "+uname+" Returning {}.")
            return {} 
        passwords = cursor.execute("select * from sites").fetchall()
        self.__logger.info("Read for "+uname+" successful.")
        self.__logger.debug("Passwords:")
        result = {}
        for line in passwords:
            iv_hex = str(line[1])
            id_hex = line[0]
            dc = AES.new(DB_KEY, AES.MODE_CBC, iv_hex.decode('hex'))
            password = dc.decrypt(line[2].decode('hex')).rstrip(' ')
            self.__logger.debug([id_hex.decode('hex'), iv_hex, password])
            result[id_hex.decode('hex')] = password 
        return result
            
    def delete_password(self, uname, identifier):
        self.__logger.info("We are trying to delete "+uname+"'s password "+identifier+".")
        conn = self.__get_db_conn(uname)
        cursor= conn.cursor()
        removed = False
        exists = cursor.execute("select name from sqlite_master where type='table' and name='sites'").fetchall()
        if len(exists) == 0:
            self.__logger.info("We have no passwords for "+uname+". Returning False.")
            return False
        if cursor.execute("select * from sites where id=?", (identifier.encode('hex'),)).fetchone():
            cursor.execute("delete from sites where id=?", (identifier.encode('hex'),))
            conn.commit()
            removed = True
            self.__logger.info("We are deleting user "+uname+"'s password "+identifier)
            #If table is empty after removal, it is dropped.
            if not cursor.execute("select * from sites").fetchone():
                cursor.execute("drop table sites")
                conn.commit()
                self.__logger.debug("We are deleting user "+uname+"'s empty table sites.")
        self.__close_db_conn(uname)
        return removed

    def delete_user(self, uname):
        self.__logger.info("We are deleting user "+uname+".")
        conn = self.__get_db_conn(uname)
        cursor= conn.cursor()
        cursor.execute("drop table if exists sites")
        self.__logger.debug("We are dropping table sites if it exists.")
        conn.commit()
        try:
            os.remove(uname)
        except:
            pass
        self.__close_db_conn(uname)
        conn = self.__get_db_conn(CREDENTIALS)
        cursor= conn.cursor()
        removed = False
        exists = cursor.execute("select name from sqlite_master where type='table' and name='"+CREDENTIALS+"'").fetchall()
        if len(exists) == 0:
            self.__logger.info("We don't have any registered users. Returning False.")
            return False  
        if cursor.execute("select * from "+CREDENTIALS+" where username=?", (uname,)).fetchone():
            cursor.execute("delete from "+CREDENTIALS+" where username=?", (uname,))
            conn.commit()
            removed = True
            self.__logger.info("We have deleted user "+uname+".")
            #If table is empty after removal, it is dropped.
            if not cursor.execute("select * from "+CREDENTIALS).fetchone():
                cursor.execute("drop table "+CREDENTIALS)
                conn.commit()
                self.__logger.debug("We are deleting empty table "+CREDENTIALS+".")
        else:
            self.__logger.debug("We didn't delete "+uname+", he's gone already.")
        self.__close_db_conn(CREDENTIALS)
        return removed

    def generate_demo_user(self):
        if len(self.__demos.keys()) > 10:
            return None
        demo = {}
        dname = "demo_"+str(len(self.__demos.keys())) 
        dpass = rng.get_random_bytes(20).encode('hex')
        demo[m.K_USER] = dname 
        demo[m.K_DEMO_PASS] = dpass
        self.__demos[dname] = dpass
        return demo