about to be able to create user

master
Marc Wäckerlin 8 years ago
parent d2a6a0f518
commit d6beeefe74
  1. 201
      nodejs/public/javascripts/safechat.js
  2. 17
      nodejs/sockets/index.js
  3. 23
      nodejs/views/index.ejs

@ -41,9 +41,15 @@ function SafeChat() {
/// Create UID from a name by appending an E-Mail /// Create UID from a name by appending an E-Mail
function uid(name) { function uid(name) {
return name+' <'+name+'@'+hostname+'>' return name+' <'+mail(name)+'>'
} }
function mail(name) {
var hostname = window.location.hostname!='localhost'?window.location.hostname:'safechat.ch'
return name+'@'+hostname
}
//==============================================================================
/// @class Crypto cryptographic functions /// @class Crypto cryptographic functions
/** @param view is of class SafeChat.View */ /** @param view is of class SafeChat.View */
function Crypto(controller) { function Crypto(controller) {
@ -51,42 +57,23 @@ function SafeChat() {
/// cache client's key from local strorage /// cache client's key from local strorage
var k = null var k = null
/// detect hosstname, default to safechat.ch /// detect hostname, default to safechat.ch
var hostname = window.location.hostname!='localhost'?window.location.hostname:'safechat.ch'
/// get user key /// get user key
/** @internal key ist cached in k /** @internal key ist cached in k
@return key */ @return key */
function key() { this.key = function() {
if (k) return k if (k) return k // cached key
if (typeof localStorage.key == 'undefined') return null if (typeof localStorage.privkey === 'undefined') return null
return k = openpgp.key.readArmored(localStorage.key) return k = openpgp.key.readArmored(localStorage.privkey)
} }
/// get own user name /// get own user name
/** get user name as user id of first public key */ /** get user name as user id of first public key */
function user() { this.user = function() {
if (k || key()) return k.pub.keys[0].getUserIds()[0] if (k || key()) return k.pub.keys[0].getUserIds()[0]
return null return null
} }
/// create New User
function createuser(user, email, pwd) {
controller.notice("generating keys")
openpgp.generateKey({
numBits: 4096,
userIds: [{name: user, email: email}],
passphrase: pwd
}).then(function(keyPair) {
controller.success("keys generated")
localStorage.key = keyPair.privateKeyArmored
k = keyPair.key
}).catch(function(e) {
console.log(e)
controller.fatal("generating key pairs failed")
})
}
/// open private key with password /// open private key with password
/** @return @c true if password matches */ /** @return @c true if password matches */
function password(pwd) { function password(pwd) {
@ -116,8 +103,15 @@ function SafeChat() {
return true return true
} }
//------------------------------------------------------------------------------
if (openpgp.initWorker("openpgp.worker.min.js"))
console.log("asynchronous openpgp enabled")
else
console.log("asynchronous openpgp failed")
} }
//==============================================================================
/// database that stores in indexed db /// database that stores in indexed db
function DataBase() { function DataBase() {
@ -126,6 +120,7 @@ function SafeChat() {
} }
//==============================================================================
/// manage local copy of users /// manage local copy of users
function Users() { function Users() {
@ -142,6 +137,7 @@ function SafeChat() {
} }
//------------------------------------------------------------------------------
/// manage local copy of messages /// manage local copy of messages
function Messages() { function Messages() {
@ -152,6 +148,7 @@ function SafeChat() {
} }
//==============================================================================
/// @class Communication client socket communication /// @class Communication client socket communication
/** @param view is of class SafeChat.View */ /** @param view is of class SafeChat.View */
function Communication(controller) { function Communication(controller) {
@ -163,9 +160,13 @@ function SafeChat() {
socket.broadcast.emit(signal, data) socket.broadcast.emit(signal, data)
} }
function emit(signal, data) { function emit(signal, data, next) {
console.log("<-snd "+signal) console.log("<-snd "+signal)
socket.emit(signal, data) socket.emit(signal, data, next)
}
this.lookup = function(usr, next) {
emit('user', usr, next)
} }
socket socket
@ -183,6 +184,7 @@ function SafeChat() {
} }
//==============================================================================
/// @class View provides the glue to the GUI in the index.ejs file /// @class View provides the glue to the GUI in the index.ejs file
/** View provides the following callbacks: /** View provides the following callbacks:
- status updates: - status updates:
@ -299,7 +301,7 @@ function SafeChat() {
@param msg (optional) the success message text */ @param msg (optional) the success message text */
function show(id, msg) { function show(id, msg) {
console.log("state: "+id) console.log("state: "+id)
if (msg) success(msg) else $("#status").hide() if (msg) success(msg); else $("#status").hide();
$("#main").children(":not(#"+id+")").hide() $("#main").children(":not(#"+id+")").hide()
$("#main #"+id).show() $("#main #"+id).show()
$("#main #"+id+" form input:first-child").focus() $("#main #"+id+" form input:first-child").focus()
@ -327,21 +329,15 @@ function SafeChat() {
} }
function checkFeature(id, query) { function checkFeature(id, query) {
if (query) $('#'+id+':before')
.css('color', 'green')
.css('content', '&#x2714;')
else $('#'+id+':before')
.css('color', 'red')
.css('content', '&#x2718;')
if (query) $('#'+id) if (query) $('#'+id)
.css('color', 'green') .css('color', 'green')
.css('text-decoration', 'line-through') .prepend('<span>&#x2714;</span>')
else $('#'+id) else $('#'+id)
.css('color', 'red') .css('color', 'red')
.css('text-decoration', 'none') .prepend('<span>&#x2718;</span>')
} }
function checkFeatures() { this.checkFeatures = function() {
$('ul.features').css('list-style-type', 'none') $('ul.features').css('list-style-type', 'none')
checkFeature("localstorage", Storage) checkFeature("localstorage", Storage)
checkFeature("indexeddb", window.indexedDB) checkFeature("indexeddb", window.indexedDB)
@ -350,6 +346,54 @@ function SafeChat() {
checkFeature("filereader", window.FileReader) checkFeature("filereader", window.FileReader)
} }
/// @name create new user
/// @{
this.newuser = function() {
show('newuser')
}
var user = null
var pwd = false
function invalid(usr) {
return !user || !user.exists && user.name.length<3
}
this.available = function(usr) {
user = usr
console.log("props:", invalid(user) || !pwd)
$("#createuser").prop(":disabled", invalid(user) || !pwd)
if (user.length==0)
notice("please chose a user name")
else if (user.length<3)
notice("please chose a longer user name")
else if (user.exists)
notice("user name is already in use")
else if (!pwd)
notice("please chose a password")
else
success("user is ready to be created")
}
this.passwords = function(pwd1, pwd2) {
pwd = pwd1==pwd2 && pwd1.length>5
console.log("props:", invalid(user) || !pwd)
$("#createuser").prop(":disabled", invalid(user) || !pwd)
if (pwd1.length==0)
notice('please chose a password')
else if (pwd1.length<6)
notice('please chose a longer password')
else if (pwd1 != pwd2)
notice("passwords don't match")
else if (invalid(user))
notice("please chose a user name")
else
success("user is ready to be created")
}
/// @}
function DataTransfer() { function DataTransfer() {
var reboottimer = null var reboottimer = null
@ -405,10 +449,11 @@ function SafeChat() {
} }
//==============================================================================
/// @class Controller defines the programm flow /// @class Controller defines the programm flow
function Controller(view) { function Controller(view) {
var db = new Database() var db = new DataBase()
var crypto = new Crypto(this) var crypto = new Crypto(this)
var communication = new Communication(this) var communication = new Communication(this)
var users = new Users() var users = new Users()
@ -461,6 +506,33 @@ function SafeChat() {
// @} // @}
/// @name signals from view
/// @{
/// @name new user registration
/// @{
this.lookup = function(usr) {
if (usr.length > 2) communication.lookup(uid(usr), function(res) {
view.available(res)
})
}
this.checkpasswords = view.passwords
this.createuser = function(name, pwd) {
crypto.createuser(name, name+'@'+hostname, pwd).then(function() {
if (!crypto.password(pwd))
fatal("private key decryption failed")
else
chat()
})
}
/// @}
/// @}
function initBrowser() { function initBrowser() {
window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB
window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction
@ -469,43 +541,48 @@ function SafeChat() {
return window.indexedDB && window.crypto.getRandomValues && Storage return window.indexedDB && window.crypto.getRandomValues && Storage
} }
function register() { var newuser = view.newuser
function chat() {
}
function password() {
} }
function login() { function login() {
if (!crypto.key()) register() if (!crypto.key()) newuser(); else password();
else password()
} }
function run() { this.run = function() {
login() login()
} }
function start() { this.start = function() {
view.reboot = run view.reboot = this.run
var compatible = initBrowser() var compatible = initBrowser()
view.checkFeatures() view.checkFeatures()
if (!compatible) if (!compatible)
view.fatal("your browser is not supported") view.fatal("your browser is not supported")
else else
run() this.run()
} }
} }
//============================================================================== //==============================================================================
//------------------------------------------------------------------------------
return new Controller(new View()) return new Controller(new View())
} }
//==============================================================================
//------------------------------------------------------------------------------
var filecontent = new Array() ///< temporary storage for attachments var filecontent = new Array() ///< temporary storage for attachments
var reboottimer = null var reboottimer = null
function connectionstatus() { function connectionstatus() {
if (socket.connected) connected() else disconnected() if (socket.connected) connected(); else disconnected();
} }
/// Configure local groups /// Configure local groups
@ -673,7 +750,8 @@ function SafeChat() {
if (recorder) { if (recorder) {
$("#videorecorder").hide() $("#videorecorder").hide()
recorder.release() recorder.release()
delete recorder recorder = null delete recorder
recorder = null
} }
} }
@ -852,10 +930,10 @@ function SafeChat() {
console.log("rcv-> messages("+msgs.length+")") console.log("rcv-> messages("+msgs.length+")")
if (!password || !privateKey()) if (!password || !privateKey())
return setTimeout(function() {emit("messages")}, 1000) // try again later return setTimeout(function() {emit("messages")}, 1000) // try again later
status("allmessages") show("allmessages")
notice("load messages, please wait …") notice("load messages, please wait …")
msgs.forEach(function(msg) {message(msg, true)}) msgs.forEach(function(msg) {message(msg, true)})
status("chat") show("chat")
} }
/// Received a message from server /// Received a message from server
@ -905,6 +983,7 @@ function SafeChat() {
// not for me // not for me
success() success()
}) })
})
} }
/// Send Message To Server /// Send Message To Server
@ -970,7 +1049,7 @@ function SafeChat() {
function getpwd() { function getpwd() {
if (password) return if (password) return
$("#removeKey").show() $("#removeKey").show()
status("getpwd") show("getpwd")
} }
function deleteUser() { function deleteUser() {
@ -983,7 +1062,7 @@ function SafeChat() {
function removeKey() { function removeKey() {
togglemenu() togglemenu()
$("#removeKey").hide() $("#removeKey").hide()
status('forgotpassword') show('forgotpassword')
} }
/// Main Chat Window /// Main Chat Window
@ -992,7 +1071,7 @@ function SafeChat() {
var firsttime = true var firsttime = true
function chat() { function chat() {
if (!password) return getpwd() if (!password) return getpwd()
status("chat") show("chat")
if (firsttime && $('#msgs').is(':empty')) { if (firsttime && $('#msgs').is(':empty')) {
firsttime = false firsttime = false
notice("getting previous messages, please wait …") notice("getting previous messages, please wait …")
@ -1022,7 +1101,7 @@ function SafeChat() {
/** Shows user creation form. On submit, a private key is generated in /** Shows user creation form. On submit, a private key is generated in
createkeypair(), then login() creates the user. */ createkeypair(), then login() creates the user. */
function newuser() { function newuser() {
status("newuser") show("newuser")
} }
/// Check if local storage is available /// Check if local storage is available
@ -1033,7 +1112,7 @@ function SafeChat() {
localStorage.removeItem(test) localStorage.removeItem(test)
return true return true
} catch(e) { } catch(e) {
status("nolocalstorage") show("nolocalstorage")
error("local storage not available") error("local storage not available")
} }
return false return false
@ -1043,7 +1122,7 @@ function SafeChat() {
/** Decide whether to login or to create a new user */ /** Decide whether to login or to create a new user */
function start() { function start() {
$("#menu").hide() $("#menu").hide()
//status("startup") //show("startup")
if (checkLocalStorage()) if (checkLocalStorage())
try { try {
if (!userid()) { if (!userid()) {
@ -1057,7 +1136,13 @@ function SafeChat() {
} }
} }
var safechat = new SafeChat()
function init() { function init() {
safechat.start()
}
function old() {
/// On Load, Call @ref start /// On Load, Call @ref start
$(window.onbeforeunload = function() { $(window.onbeforeunload = function() {
return "Are you sure you want to navigate away?" return "Are you sure you want to navigate away?"

@ -79,7 +79,7 @@ module.exports = function(sql) {
}); });
}); });
socket.on("user", function(name) { socket.on("user", function(name, fn) {
console.log("-> signal: user("+name+")"); console.log("-> signal: user("+name+")");
var result = {name: name, exists: false, pubkey: null}; var result = {name: name, exists: false, pubkey: null};
sql.query("select pubkey from user where name = ?", [name], function(err, res, flds) { sql.query("select pubkey from user where name = ?", [name], function(err, res, flds) {
@ -87,7 +87,20 @@ module.exports = function(sql) {
result.exists = true; result.exists = true;
result.pubkey = res[0].pubkey; result.pubkey = res[0].pubkey;
} }
emit('user', result); fn(result);
});
});
socket.on("lookup", function(name, fn) {
console.log("-> signal: lookup("+name+")")
var result = false
sql.query("select pubkey from user where name = ?",
[name],
function(err, res, flds) {
if (!err && res && res.length) {
result = true;
}
fn(result)
}); });
}); });

@ -51,21 +51,12 @@
<div id="newuser" style="display: none"> <div id="newuser" style="display: none">
<h2>Register User</h2> <h2>Register User</h2>
<p>All you need to start is a username and a password:</p> <p>All you need to start is a username and a password:</p>
<form id="register" autocomplete="off"> <form id="register" autocomplete="off" onsubmit="safechat.createuser(document.getElementById('user').value, document.getElementById('pwd').value)">
<input placeholder="username" autocomplete="off" type="text" id="user"/> <input placeholder="username" autocomplete="off" type="text" id="user" oninput="safechat.lookup(this.value)" />
<input placeholder="password" autocomplete="off" type="password" id="pwd" oninput="checkpwd(this.value, document.getElementById('pwd2').value)"/> <input placeholder="password" autocomplete="off" type="password" id="pwd" oninput="safechat.checkpasswords(this.value, document.getElementById('pwd2').value)"/>
<input placeholder="repeat password" autocomplete="off" type="password" id="pwd2" oninput="checkpwd(document.getElementById('pwd').value, this.value)"/> <input placeholder="repeat password" autocomplete="off" type="password" id="pwd2" oninput="safechat.checkpasswords(document.getElementById('pwd').value, this.value)"/>
<button id="createuser" disabled>register</button> <input id="createuser" type="submit" value="register" disabled />
</form> </form>
<script>
$("#user").on("input", function() {
queryuser($('#user').val());
});
$("#createuser").on("click", function(event) {
createkeypair($('#user').val(), $('#pwd').val());
return false;
});
</script>
<p>Please chose any username, e.g. a pseudonym, your e-mail, <p>Please chose any username, e.g. a pseudonym, your e-mail,
your phone number, your real name, and chose a safe your phone number, your real name, and chose a safe
password.</p> password.</p>
@ -184,9 +175,9 @@
<!-- Fatal: Abort --> <!-- Fatal: Abort -->
<div id="fatal"> <div id="fatal" style="display: none">
<h2 id="fatal-msg">Failure</h2> <h2 id="fatal-msg">Failure</h2>
<p>The SafeChat has been aborted due to a fatal error.</p> <p>SafeChat has been aborted due to a fatal error.</p>
<p>There is a problem in your browser. Please try to reload. If the problem persists, please update your web browser or try SafeChat in another browser.</p> <p>There is a problem in your browser. Please try to reload. If the problem persists, please update your web browser or try SafeChat in another browser.</p>
<p>The following java script features are required:</p> <p>The following java script features are required:</p>
<ul class="features"> <ul class="features">

Loading…
Cancel
Save