LOC-2 Implemented user password storage
LOC-2 Implemented user password storage

* The backend now stores a (bcrypt) hash of a user's password.
* When a room is created, a hash of the owner's password is also inserted into the invite table so that they can join the room

Currently, the password isn't actually needed to join (that's the next step)

Within the client, when a room is created output is generated to show how to join the room. This commit amends that output to include the admin user's password, for example

To join the room, do /join BenTest123 SD8WXY1OKC39CI0Y:QCVN7CEPUCHAQU2S ben

The first section (i.e. before :) of that password is the room password (so will be used for the E2E encryption). The second half is the users password.

When invites are generated (this doesn't yet happen) the generated output will differ only in the second half of the password and the username to sign in as.

--- a/client/LocalChatClient.py
+++ b/client/LocalChatClient.py
@@ -144,12 +144,12 @@
         ''' Join a room
         '''
         
-        # TODO - this functionality isn't on the 
-        # backend yet, so haven't defined the hashing mechanism etc
-        passhash = self.hashpw(passw)
+        # We only want to send the user password section of the password
+        p = passw.split(":")
+        userpass = p[1]
         
         payload = {"roomName": room, 
-                   "passhash": passhash,
+                   "userpass": userpass,
                    "user": user
                    }
         
@@ -167,7 +167,7 @@
         self.room = room
         self.user = user
         self.last = resp['last']
-        self.roompass = passw
+        self.roompass = p[0] # The room password is the first section of the password
         return True
 
 
@@ -211,9 +211,14 @@
         
         if not user:
             user = self.user
+
+
+        # Generate a password for the admin
+        passw = self.genpassw()
                 
         payload = {"roomName": room, 
-                   "owner": user
+                   "owner": user,
+                   "pass": passw
                    }
         
         request = {"action":"createRoom",
@@ -225,7 +230,7 @@
         if resp == "BROKENLINK" or resp['status'] != "ok":
             return False
         
-        return resp['name']
+        return [resp['name'],passw]
     
 
     def closeRoom(self):
@@ -353,7 +358,7 @@
         ''' Generate a random string of chars to act as a password
         '''
         
-        return ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(N))
+        return ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits).encode('utf-8') for _ in range(N))
 
 
 
@@ -460,11 +465,15 @@
                         raise UnableTo('create room',line)
                         return
                     
+                    # Seperate out the return value
+                    rm = n[0]
+                    up = n[1] # user specific password
                     global c
+                    
+                    # Generate a room password
                     p = msg.genpassw()
-                    c.output('Created Room %s' %(n))
-                    c.output('Use "%s" as the Room password' %(p))
-                    c.output('To join the room, do /join %s %s %s' %(args[1],p,args[2]))
+                    c.output('Created Room %s' %(rm))
+                    c.output('To join the room, do /join %s %s:%s %s' %(args[1],p,up,args[2]))
                     return
                 
                 elif args[0] == "invite":

--- a/server/LocalChat.py
+++ b/server/LocalChat.py
@@ -7,6 +7,7 @@
 # apt-get install:
 #   python-flask
 #   python-openssl
+#   python-bcrypt
 #
 
 from flask import Flask
@@ -18,6 +19,7 @@
 import time
 import os
 import json
+import bcrypt
 
 app = Flask(__name__)
 
@@ -80,6 +82,7 @@
             username TEXT NOT NULL,
             room INTEGER NOT NULL,
             active INTEGER DEFAULT 0,
+            passhash TEXT NOT NULL,
             PRIMARY KEY (username,room)
         );
         
@@ -172,6 +175,15 @@
         }'
         
         '''
+        
+        # Validate the request
+        #
+        # All validation snippets will change to this format soon
+        required = ['roomName','owner','pass']
+        for i in required:
+            if i not in reqjson['payload']:
+                return 400
+        
         print "Creating room %s" % (reqjson['payload'])
         
         # Create a tuple for sqlite3
@@ -187,7 +199,10 @@
             return 500
         
         
-        self.cursor.execute("INSERT INTO users (username,room) values (?,?)",(reqjson['payload']['owner'],roomid))
+        # Generate a password hash for the owners password
+        passhash = bcrypt.hashpw(reqjson['payload']['pass'].encode('utf-8'),bcrypt.gensalt())
+        
+        self.cursor.execute("INSERT INTO users (username,room,passhash) values (?,?,?)",(reqjson['payload']['owner'],roomid,passhash))
         self.conn.commit()
         
         return {
@@ -245,13 +260,9 @@
 
     def inviteUser(self,reqjson):
         ''' Link a username into a room
-
-
-        curl -v -X POST http://127.0.0.1:8090/ -H "Content-Type: application/json" --data '{"action":"inviteUser","payload":"{\"roomName\":\"BenTest\",\"user\":\"ben2\"}"}'
-
-        '''
-        
-        if "roomName" not in reqjson['payload']:
+        '''
+        
+        if "roomName" not in reqjson['payload'] or "pass" not in reqjson['payload'] or "invite" not in reqjson['payload']:
             return 400
         
         room = self.getRoomID(reqjson['payload']["roomName"])
@@ -274,9 +285,11 @@
             return 403
        
         
+        # Generate a hash of the submitted password
+        passhash = bcrypt.hashpw(reqjson['payload']['pass'].encode('utf-8'),bcrypt.gensalt())
         
         # Otherwise, link the user in
-        self.cursor.execute("INSERT INTO users (username,room) values (?,?)",(reqjson['payload']['invite'],room))
+        self.cursor.execute("INSERT INTO users (username,room) values (?,?,?)",(reqjson['payload']['invite'],room,passhash))
         
         # Push a notification into the group
         m = {