diff --git a/data/lexonomy.sqlite.template b/data/lexonomy.sqlite.template
index 2afa38f4..5fa56138 100644
Binary files a/data/lexonomy.sqlite.template and b/data/lexonomy.sqlite.template differ
diff --git a/website/adminscripts/updates.js b/website/adminscripts/updates.js
index fa58046f..e7f7606c 100644
--- a/website/adminscripts/updates.js
+++ b/website/adminscripts/updates.js
@@ -6,12 +6,28 @@ const sqlite3 = require('sqlite3').verbose(); //https://www.npmjs.com/package/sq
fs.readFile(path.join(__dirname, "../siteconfig.json"), "utf8", function(err, content){
var siteconfig=JSON.parse(content);
- var db=new sqlite3.Database(path.join(siteconfig.dataDir, "lexonomy.sqlite"), sqlite3.OPEN_READWRITE);
+ var db=new sqlite3.Database(path.join("../",siteconfig.dataDir, "lexonomy.sqlite"), sqlite3.OPEN_READWRITE);
db.run("CREATE TABLE IF NOT EXISTS recovery_tokens (email text, requestAddress text, token text, expiration datetime, usedDate datetime, usedAddress text)", {}, function(err) {
if (err) {
return console.error(err.message);
}
console.log("Table recovery_tokens created.");
});
+ db.run("CREATE TABLE IF NOT EXISTS register_tokens (email text, requestAddress text, token text, expiration datetime, usedDate datetime, usedAddress text)", {}, function(err) {
+ if (err) {
+ return console.error(err.message);
+ }
+ console.log("Table register_tokens created.");
+ });
+ db.run("ALTER TABLE users ADD COLUMN ske_id INTEGER", {}, function(err) {
+ if (err) {
+ return console.error(err.message);
+ }
+ });
+ db.run("ALTER TABLE users ADD COLUMN ske_username TEXT", {}, function(err) {
+ if (err) {
+ return console.error(err.message);
+ }
+ });
db.close();
});
diff --git a/website/furniture/public.css b/website/furniture/public.css
index 5e0bbd1a..bfbc2d81 100644
--- a/website/furniture/public.css
+++ b/website/furniture/public.css
@@ -136,6 +136,11 @@ div.usertop a:hover {color: #2c7fba; }
div.usertop > div.title {font-size: 1.5rem; padding-top: 10px;}
div.usertop > div.subtitle {margin-top: 10px; color: #666666;}
+div.skelogin a {text-decoration: none; color: #09587e;}
+div.skelogin a:hover {color: #2c7fba; }
+div.skelogin {font-weight: bold; text-align: center; padding: 10px; margin: 20px 0px 0px 0px; border-radius: 4px; border: 1px solid #279FD2;}
+div.skelogin img {vertical-align: middle;}
+
div.field {margin-top: 10px;}
div.field:first-child {margin-top: 0px;}
div.field div.label {font-weight: bold; margin: 0px 0px 5px 0px; color: #333333;}
diff --git a/website/lexonomy.js b/website/lexonomy.js
index c226efa4..d7145cd4 100644
--- a/website/lexonomy.js
+++ b/website/lexonomy.js
@@ -22,6 +22,7 @@ const sqlite3 = require('sqlite3').verbose(); //https://www.npmjs.com/package/sq
const nodemailer = require('nodemailer');
ops.mailtransporter = nodemailer.createTransport(siteconfig.mailconfig);
const PORT=process.env.PORT||siteconfig.port||80;
+const jwt = require("jsonwebtoken");
//Do this for each request:
app.use(function (req, res, next) {
@@ -60,7 +61,12 @@ app.get(siteconfig.rootPath+":dictID/en/", function(req, res){ res.redirect("/"+
app.get(siteconfig.rootPath, function(req, res){
ops.verifyLogin(req.cookies.email, req.cookies.sessionkey, function(user){
ops.getDictsByUser(user.email, function(dicts){
- res.render("home.ejs", {siteconfig: siteconfig, user: user, dicts: dicts});
+ var error = null;
+ if (req.cookies.jwt_error) {
+ error = req.cookies.jwt_error;
+ res.clearCookie('jwt_error');
+ }
+ res.render("home.ejs", {siteconfig: siteconfig, user: user, dicts: dicts, error: error});
});
});
});
@@ -89,7 +95,7 @@ app.get(siteconfig.rootPath+"make/", function(req, res){
});
app.get(siteconfig.rootPath+"signup/", function(req, res){
ops.verifyLogin(req.cookies.email, req.cookies.sessionkey, function(user){
- res.render("signup.ejs", {user: user, email: siteconfig.admins[0], siteconfig: siteconfig});
+ res.render("signup.ejs", {user: user, redirectUrl: siteconfig.baseUrl, siteconfig: siteconfig});
});
});
app.get(siteconfig.rootPath+"forgotpwd/", function(req, res){
@@ -97,9 +103,17 @@ app.get(siteconfig.rootPath+"forgotpwd/", function(req, res){
res.render("forgotpwd.ejs", {user: user, redirectUrl: siteconfig.baseUrl, siteconfig: siteconfig});
});
});
+app.get(siteconfig.rootPath+"createaccount/:token/", function(req, res){
+ ops.verifyLogin(req.cookies.email, req.cookies.sessionkey, function(user){
+ ops.verifyToken(req.params.token, "register", function(valid){
+ var tokenValid = valid;
+ res.render("createaccount.ejs", {user: user, redirectUrl: siteconfig.baseUrl, siteconfig: siteconfig, token: req.params.token, tokenValid: tokenValid});
+ });
+ });
+});
app.get(siteconfig.rootPath+"recoverpwd/:token/", function(req, res){
ops.verifyLogin(req.cookies.email, req.cookies.sessionkey, function(user){
- ops.verifyToken(req.params.token, function(valid){
+ ops.verifyToken(req.params.token, "recovery", function(valid){
var tokenValid = valid;
res.render("recoverpwd.ejs", {user: user, redirectUrl: siteconfig.baseUrl, siteconfig: siteconfig, token: req.params.token, tokenValid: tokenValid});
});
@@ -145,12 +159,24 @@ app.post(siteconfig.rootPath+"changepwd.json", function(req, res){
}
});
});
+app.post(siteconfig.rootPath+"signup.json", function(req, res){
+ var remoteip = req.connection.remoteAddress.replace('::ffff:','');
+ ops.sendSignupToken(req.body.email, remoteip, function(success){
+ res.json({success: success});
+ });
+});
app.post(siteconfig.rootPath+"forgotpwd.json", function(req, res){
var remoteip = req.connection.remoteAddress.replace('::ffff:','');
ops.sendToken(req.body.email, remoteip, function(success){
res.json({success: success});
});
});
+app.post(siteconfig.rootPath+"createaccount.json", function(req, res){
+ var remoteip = req.connection.remoteAddress.replace('::ffff:','');
+ ops.createAccount(req.body.token, req.body.password, remoteip, function(success){
+ res.json({success: success});
+ });
+});
app.post(siteconfig.rootPath+"recoverpwd.json", function(req, res){
var remoteip = req.connection.remoteAddress.replace('::ffff:','');
ops.resetPwd(req.body.token, req.body.password, remoteip, function(success){
@@ -266,6 +292,35 @@ app.post(siteconfig.rootPath+"dicts/dictread.json", function(req, res){
});
});
+//SKETCHENGINE LOGIN JSON endpoint:
+app.get(siteconfig.rootPath+"skelogin.json/:token", function(req, res){
+ //var token = req.headers.authorization.replace('Bearer ', '');
+ var token = req.params.token;
+ var secret = siteconfig.sketchengineKey;
+ jwt.verify(token, secret, {audience:'lexonomy.eu'}, function(err, decoded) {
+ if (err == null) {
+ console.log(decoded)
+ ops.verifyLogin(req.cookies.email, req.cookies.sessionkey, function(user){
+ ops.processJWT(user, decoded, function(success, email, sessionkey){
+ if (success) {
+ res.cookie("email", email, {});
+ res.cookie("sessionkey", sessionkey, {});
+ res.redirect(siteconfig.baseUrl)
+ } else {
+ res.cookie("jwt_error", email, {});
+ res.redirect(siteconfig.baseUrl)
+ }
+ });
+ });
+ } else {
+ //JWT not verified, error
+ res.cookie("jwt_error",err.message,{})
+ res.redirect(siteconfig.baseUrl)
+ }
+ });
+});
+
+
//ONE-CLICK UI and JSON endpoints:
app.get(siteconfig.rootPath+"oneclick/", function(req, res){
ops.verifyLogin(req.cookies.email, req.cookies.sessionkey, function(user){
diff --git a/website/libs/screenful/screenful-createaccount.css b/website/libs/screenful/screenful-createaccount.css
new file mode 100644
index 00000000..51a5427a
--- /dev/null
+++ b/website/libs/screenful/screenful-createaccount.css
@@ -0,0 +1,19 @@
+#middlebox {max-width: 600px; padding: 40px 30px; min-height: 100px; margin: 75px auto 50px auto; border: 1px solid rgb(38, 122, 181); background-color: #ffffff; border-radius: 4px; box-shadow: 0px 0px 4px #666666; }
+
+#middlebox div.field {margin-top: 30px;}
+#middlebox div.field:first-child {margin-top: 0px;}
+#middlebox div.field div.label {font-weight: bold; margin: 0px 0px 5px 0px; color: #333333;}
+#middlebox div.field input.textbox {box-sizing: border-box; width: 100%; margin: 0px 0px 0px 0px; font: inherit; border-width: 0px; border-radius: 4px; background-color: #ffffff; box-shadow: inset 0px 0px 2px #666666; padding: 9px 8px; min-height: 1.3em; display: inline-block; vertical-align: middle;}
+#middlebox div.field input.button {box-sizing: border-box; margin: 0px 0px 0px 0px; font: inherit; border-width: 0px; border-radius: 4px; background-color: #ffffff; box-shadow: 0px 0px 2px #666666; padding: 9px 30px; min-height: 1.3em; display: inline-block; vertical-align: middle; color: #267ab5; cursor: pointer;}
+#middlebox div.field input.button:hover {color: #4698d1;}
+#middlebox div.field button {box-sizing: border-box; margin: 0px 0px 0px 0px; font: inherit; border-width: 0px; border-radius: 4px; background-color: #ffffff; box-shadow: 0px 0px 2px #666666; padding: 9px 30px; min-height: 1.3em; display: inline-block; vertical-align: middle; color: #267ab5; cursor: pointer;}
+#middlebox div.field button:hover {color: #4698d1;}
+
+#middlebox div.field.submit {text-align: center;}
+#middlebox div.field.submit input.button {font-weight: bold;}
+#middlebox div.field.submit button {font-weight: bold;}
+
+#middlebox div.bigerror {background-color: #ffcdcc; color: #99004d; font-weight: bold; text-align: center; padding: 40px; margin: -40px -30px 0px -30px; text-shadow: 1px 1px 0px #eeeeee;}
+#middlebox div.error {background-color: #ffcdcc; color: #99004d; font-weight: bold; text-align: center; padding: 40px; margin: 30px -30px -40px -30px; text-shadow: 1px 1px 0px #eeeeee;}
+
+#middlebox div.two div.message {text-align: center; margin: 20px 0px 30px 0px;}
diff --git a/website/libs/screenful/screenful-createaccount.js b/website/libs/screenful/screenful-createaccount.js
new file mode 100644
index 00000000..c9991fca
--- /dev/null
+++ b/website/libs/screenful/screenful-createaccount.js
@@ -0,0 +1,47 @@
+Screenful.CreateAccount={
+ start: function(){
+ Screenful.createEnvelope(true);
+ $("#envelope").html("
");
+ if (Screenful.CreateAccount.tokenValid) {
+ $("#middlebox .one").append(""+Screenful.Loc.setPwdMsg+"
");
+ $("#middlebox .one").append("");
+ $("#middlebox .one").append("
");
+ $("#middlebox .one").append("
");
+ $("#middlebox .two").append(""+Screenful.Loc.accountCreated+"
");
+ $("#middlebox .two").append(""+Screenful.Loc.ok+"
");
+ } else {
+ $("#middlebox .one").append(""+Screenful.Loc.invalidSignupToken+"
");
+ $("#middlebox .one").append(""+Screenful.Loc.ok+"
");
+ }
+
+ $("#middlebox div.field.password input").focus();
+
+ $("#middlebox").on("submit", function(e){
+ var password=$("#middlebox div.field.password input").val();
+ if(password=="") { $("#middlebox .error").html(Screenful.Loc.passwordEmpty).show(); return false; }
+ if(password.length<6) { $("#middlebox .error").html(Screenful.Loc.passwordShort).show(); return false; }
+ if($.trim(password)!=password) { $("#middlebox .error").html(Screenful.Loc.passwordWhitespace).show(); return false; }
+ Screenful.CreateAccount.go(password);
+ return false;
+ });
+
+ $("#middlebox button.return").on("click", function(e){
+ window.location=Screenful.CreateAccount.returnUrl;
+ return false;
+ });
+ },
+
+ go: function(password){
+ $.ajax({url: Screenful.CreateAccount.actionUrl, dataType: "json", method: "POST", data: {password: password, token: Screenful.CreateAccount.token}}).done(function(data){
+ if(data.success) {
+ $("#middlebox .one").hide();
+ $("#middlebox .two").show()
+ } else {
+ $("#middlebox .error").html(Screenful.Loc.signupAccountExists).show();
+ }
+ });
+ },
+
+
+};
+$(window).ready(Screenful.CreateAccount.start);
diff --git a/website/libs/screenful/screenful-forgotpwd.js b/website/libs/screenful/screenful-forgotpwd.js
index 5d91625c..f9fbc374 100644
--- a/website/libs/screenful/screenful-forgotpwd.js
+++ b/website/libs/screenful/screenful-forgotpwd.js
@@ -7,7 +7,7 @@ Screenful.ForgotPwd={
$("#middlebox .one").append("
");
$("#middlebox .two").append(""+Screenful.Loc.tokenSent+"
");
$("#middlebox .two").append(""+Screenful.Loc.ok+"
");
- $("#middlebox").append(""+Screenful.Loc.forgotPwdError+"
");
+ $("#middlebox").append(""+Screenful.Loc.badEmailError+"
");
$("#middlebox div.field.email input").focus();
$("#middlebox").on("submit", function(e){
diff --git a/website/libs/screenful/screenful-loc-cs.js b/website/libs/screenful/screenful-loc-cs.js
index 4c0004e7..00547791 100755
--- a/website/libs/screenful/screenful-loc-cs.js
+++ b/website/libs/screenful/screenful-loc-cs.js
@@ -59,13 +59,20 @@ Screenful.Loc={
invalidSourceCode: "Neplatný zdrojový kód",
unsavedConfirm: "Más neuložené změny. Opravdu o ně chceš přijít?",
doItLater: "Udělej to jindy",
+ badEmailError: "Nesprávná e-mailová adresa.",
forgotPwdEmail: "Zapomněli-li jste svoje heslo, zadejte tady svoji e-mailovou adresu a my vám pošleme instrukce, jak si získat nové heslo.",
recoverPwd: "Chci nové heslo",
- forgotPwdError: "Nesprávná e-mailová adresa.",
tokenSent: "Poslali jsme vám instrukce, jak si získat nové heslo.",
recoverPwdMsg: "Nyní si můžete změnit heslo.",
changePwdMsg: "Tady si můžete změnit heslo.",
invalidToken: "Tento odkaz je neplatný. Možná už vypršel, nebo byl už použit.",
+ signupInfoEmail: "To get a new account, enter your e-mail address and we will send you instructions.",
+ signupButton: "Create new account",
+ signupTokenSent: "We have sent you an e-mail with instructions on how to reset your password.",
+ invalidSignupToken: "This signup link is invalid. It may have expired or has been used before.",
+ setPwdMsg: "Please, set your password now.",
+ accountCreated: "We have created your account. You may now log-in with your e-mail and password.",
+ signupAccountExists: "You are trying to create account for e-mail that is already existing. Did you forget your password? ",
};
diff --git a/website/libs/screenful/screenful-loc-en.js b/website/libs/screenful/screenful-loc-en.js
index fd0fa57e..70f0353b 100644
--- a/website/libs/screenful/screenful-loc-en.js
+++ b/website/libs/screenful/screenful-loc-en.js
@@ -59,13 +59,20 @@ Screenful.Loc={
invalidSourceCode: "Invalid source code",
unsavedConfirm: "You have unsaved changes. Are you sure you want to lose them?",
doItLater: "Do it later",
+ badEmailError: "Incorrect e-mail address.",
forgotPwdEmail: "If you have forgotten your password, enter your e-mail address and we will send you instructions on how to create a new one.",
recoverPwd: "Get a new password",
- forgotPwdError: "Incorrect e-mail address.",
tokenSent: "We have sent you an e-mail with instructions on how to reset your password.",
recoverPwdMsg: "You can change your password now.",
changePwdMsg: "You can change your password here.",
invalidToken: "This password reset link is invalid. It may have expired or has been used before.",
+ signupInfoEmail: "To get a new account, enter your e-mail address and we will send you instructions.",
+ signupButton: "Create new account",
+ signupTokenSent: "We have sent you an e-mail with instructions on how to reset your password.",
+ invalidSignupToken: "This signup link is invalid. It may have expired or has been used before.",
+ setPwdMsg: "Please, set your password now.",
+ accountCreated: "We have created your account. You may now log-in with your e-mail and password.",
+ signupAccountExists: "You are trying to create account for e-mail that is already existing. Did you forget your password? ",
};
diff --git a/website/libs/screenful/screenful-loc-ga.js b/website/libs/screenful/screenful-loc-ga.js
index dfa1b1a6..5a066330 100755
--- a/website/libs/screenful/screenful-loc-ga.js
+++ b/website/libs/screenful/screenful-loc-ga.js
@@ -59,13 +59,20 @@ Screenful.Loc={
invalidSourceCode: "Cód foinseach neamhbhailí",
unsavedConfirm: "Tá athruithe déanta agat nach bhfuil sábháilte go fóill. An cuma leat go gcaillfear iad?",
doItLater: "Déan níos déanaí é",
+ badEmailError: "Seoladh ríomhphoist mícheart.",
forgotPwdEmail: "Má tá do phasfhocal dearmadta agat, cuir do sheoladh ríomhphoist isteach anseo. Cuirfidh muidne treoracha chugat maidir leis an dóigh chun do phasfhocal a athrú.",
recoverPwd: "Faigh pasfhocal nua",
- forgotPwdError: "Seoladh ríomhphoist mícheart.",
tokenSent: "Tá treoracha seolta againn chugat maidir leis an dóigh chun do phasfhocal a athrú.",
recoverPwdMsg: "Is féidir leat do phasfhocal a athrú anois.",
changePwdMsg: "Is féidir leat do phasfhocal a athrú anseo.",
invalidToken: "Níl an nasc seo bailí. Seans go bhfuil sé imithe in éag, nó b'fhéidir gur baineadh úsáid as cheana.",
+ signupInfoEmail: "To get a new account, enter your e-mail address and we will send you instructions.",
+ signupButton: "Create new account",
+ signupTokenSent: "We have sent you an e-mail with instructions on how to reset your password.",
+ invalidSignupToken: "This signup link is invalid. It may have expired or has been used before.",
+ setPwdMsg: "Please, set your password now.",
+ accountCreated: "We have created your account. You may now log-in with your e-mail and password.",
+ signupAccountExists: "You are trying to create account for e-mail that is already existing. Did you forget your password? ",
};
diff --git a/website/libs/screenful/screenful-signup.css b/website/libs/screenful/screenful-signup.css
index 15d29622..7604433b 100644
--- a/website/libs/screenful/screenful-signup.css
+++ b/website/libs/screenful/screenful-signup.css
@@ -1,6 +1,16 @@
#middlebox {max-width: 600px; padding: 40px 30px; min-height: 100px; margin: 75px auto 50px auto; border: 1px solid rgb(38, 122, 181); background-color: #ffffff; border-radius: 4px; box-shadow: 0px 0px 4px #666666; }
-#middlebox div.message {text-align: center; margin: 50px 0px 0px 0px;}
-#middlebox div.url {text-align: center; margin: 10px 0px 50px 0px;}
-#middlebox div.url a {text-decoration: none; color: #004f9b;}
-#middlebox div.url a:hover {color: #006ed7;}
+#middlebox div.field {margin-top: 30px;}
+#middlebox div.field:first-child {margin-top: 0px;}
+#middlebox div.field div.label {font-weight: bold; margin: 0px 0px 5px 0px; color: #333333;}
+#middlebox div.field input.textbox {box-sizing: border-box; width: 100%; margin: 0px 0px 0px 0px; font: inherit; border-width: 0px; border-radius: 4px; background-color: #ffffff; box-shadow: inset 0px 0px 2px #666666; padding: 9px 8px; min-height: 1.3em; display: inline-block; vertical-align: middle;}
+#middlebox div.field input.button {box-sizing: border-box; margin: 0px 0px 0px 0px; font: inherit; border-width: 0px; border-radius: 4px; background-color: #ffffff; box-shadow: 0px 0px 2px #666666; padding: 9px 30px; min-height: 1.3em; display: inline-block; vertical-align: middle; color: #267ab5; cursor: pointer;}
+#middlebox div.field input.button:hover {color: #4698d1;}
+#middlebox div.field button {box-sizing: border-box; margin: 0px 0px 0px 0px; font: inherit; border-width: 0px; border-radius: 4px; background-color: #ffffff; box-shadow: 0px 0px 2px #666666; padding: 9px 30px; min-height: 1.3em; display: inline-block; vertical-align: middle; color: #267ab5; cursor: pointer;}
+#middlebox div.field button:hover {color: #4698d1;}
+
+#middlebox div.field.submit {text-align: center;}
+#middlebox div.field.submit input.button {font-weight: bold;}
+#middlebox div.field.submit button {font-weight: bold;}
+
+#middlebox div.error {background-color: #ffcdcc; color: #99004d; font-weight: bold; text-align: center; padding: 40px; margin: 30px -30px -40px -30px; text-shadow: 1px 1px 0px #eeeeee;}
diff --git a/website/libs/screenful/screenful-signup.js b/website/libs/screenful/screenful-signup.js
index 4266e713..83c28543 100644
--- a/website/libs/screenful/screenful-signup.js
+++ b/website/libs/screenful/screenful-signup.js
@@ -1,9 +1,37 @@
Screenful.Signup={
start: function(){
Screenful.createEnvelope(true);
- $("#envelope").html("");
- $("#middlebox").append(""+Screenful.Loc.signupEmail+"
");
- $("#middlebox").append("");
+ $("#envelope").html("");
+ $("#middlebox .one").append(""+Screenful.Loc.signupInfoEmail+"
");
+ $("#middlebox .one").append("");
+ $("#middlebox .one").append("
");
+ $("#middlebox .two").append(""+Screenful.Loc.signupTokenSent+"
");
+ $("#middlebox .two").append(""+Screenful.Loc.ok+"
");
+ $("#middlebox").append(""+Screenful.Loc.badEmailError+"
");
+
+ $("#middlebox div.field.email input").focus();
+ $("#middlebox").on("submit", function(e){
+ var email=$("#middlebox div.field.email input").val();
+ if (email!="") Screenful.Signup.sendToken(email);
+ return false;
+ });
+
+ $("#middlebox button.return").on("click", function(e){
+ window.location=Screenful.Signup.returnUrl;
+ return false;
+ });
+ },
+
+ sendToken: function(email){
+ $("#middlebox div.error").hide();
+ $.ajax({url: Screenful.Signup.actionUrl, dataType: "json", method: "POST", data: {email: email}}).done(function(data){
+ if(data.success) {
+ $("#middlebox .one").hide();
+ $("#middlebox .two").show();
+ } else {
+ $("#middlebox div.error").fadeIn();
+ }
+ });
},
};
$(window).ready(Screenful.Signup.start);
diff --git a/website/ops.js b/website/ops.js
index b6b73514..05c66769 100644
--- a/website/ops.js
+++ b/website/ops.js
@@ -902,6 +902,31 @@ module.exports={
callnext(true);
});
},
+ sendSignupToken: function(email, remoteip, callnext){
+ var db=new sqlite3.Database(path.join(module.exports.siteconfig.dataDir, "lexonomy.sqlite"), sqlite3.OPEN_READWRITE);
+ db.get("select email from users where email=$email", {$email: email}, function(err, row){
+ if (row==undefined) {
+ var expireDate = (new Date()); expireDate.setHours(expireDate.getHours()+48);
+ expireDate = expireDate.toISOString();
+ var token = sha1(sha1(Math.random()));
+ var tokenurl = module.exports.siteconfig.baseUrl + 'createaccount/' + token;
+ var mailSubject="Lexonomy signup";
+ var mailText = `Dear Lexonomy user,\n\n`;
+ mailText+=`Somebody (hopefully you, from the address ${remoteip}) requested to create a new account for Lexonomy. Please follow the link below to create your account:\n\n`
+ mailText+=`${tokenurl}\n\n`;
+ mailText+=`For security reasons this link is only valid for two days (until ${expireDate}). If you did not request an account, you can safely ignore this message. \n\n`;
+ mailText+=`Yours,\nThe Lexonomy team`;
+ db.run("insert into register_tokens (email, requestAddress, token, expiration) values ($email, $remoteip, $token, $expire)", {$email: email, $expire: expireDate, $remoteip: remoteip, $token: token}, function(err, row){
+ module.exports.mailtransporter.sendMail({from: module.exports.siteconfig.admins[0], to: email, subject: mailSubject, text: mailText}, (err, info) => {});
+ db.close();
+ callnext(true);
+ });
+ } else {
+ db.close();
+ callnext(false);
+ }
+ });
+ },
sendToken: function(email, remoteip, callnext){
var db=new sqlite3.Database(path.join(module.exports.siteconfig.dataDir, "lexonomy.sqlite"), sqlite3.OPEN_READWRITE);
db.get("select email from users where email=$email", {$email: email}, function(err, row){
@@ -927,13 +952,34 @@ module.exports={
}
});
},
- verifyToken: function(token, callnext){
+ verifyToken: function(token, type, callnext){
var db=new sqlite3.Database(path.join(module.exports.siteconfig.dataDir, "lexonomy.sqlite"), sqlite3.OPEN_READWRITE);
- db.get("select * from recovery_tokens where token=$token and expiration>=datetime('now') and usedDate is null", {$token: token}, function(err, row){
+ db.get("select * from "+type+"_tokens where token=$token and expiration>=datetime('now') and usedDate is null", {$token: token}, function(err, row){
db.close();
if(!row) callnext(false); else callnext(true);
});
},
+ createAccount: function(token, password, remoteip, callnext){
+ var db=new sqlite3.Database(path.join(module.exports.siteconfig.dataDir, "lexonomy.sqlite"), sqlite3.OPEN_READWRITE);
+ db.get("select * from register_tokens where token=$token and expiration>=datetime('now') and usedDate is null", {$token: token}, function(err, row){
+ if (row) {
+ var email = row.email;
+ db.get("select * from users where email=$email", {$email: email}, function(err, row){
+ if (row==undefined) {
+ var hash = sha1(password);
+ db.run("insert into users (email,passwordHash) values ($email,$hash)", {$hash: hash, $email: email}, function(err, row){
+ db.run("update register_tokens set usedDate=datetime('now'), usedAddress=$remoteip where token=$token", {$remoteip: remoteip, $token: token}, function(err, row){
+ db.close();
+ callnext(true);
+ });
+ });
+ } else {
+ callnext(false);
+ }
+ });
+ }
+ });
+ },
resetPwd: function(token, password, remoteip, callnext){
var db=new sqlite3.Database(path.join(module.exports.siteconfig.dataDir, "lexonomy.sqlite"), sqlite3.OPEN_READWRITE);
db.get("select * from recovery_tokens where token=$token and expiration>=datetime('now') and usedDate is null", {$token: token}, function(err, row){
@@ -949,20 +995,63 @@ module.exports={
}
});
},
+ processJWT: function(user, jwtData, callnext){
+ var db = new sqlite3.Database(path.join(module.exports.siteconfig.dataDir, "lexonomy.sqlite"), sqlite3.OPEN_READWRITE);
+ if (user.loggedin) {
+ //user logged in = save SkE ID in database
+ var key = generateKey();
+ var now = (new Date()).toISOString();
+ db.run("update users set ske_id=$ske_id, ske_username=$ske_username, sessionKey=$key, sessionLast=$now where email=$email", {$ske_id: jwtData.user.id, $ske_username: jwtData.user.username, $email:user.email, $key:key, $now:now}, function(err, row){
+ db.close();
+ callnext(true, user.email, key);
+ });
+ } else {
+ //user not logged in =
+ // if SkE ID in database = log in user
+ // if SkE ID not in database = register and log in user
+ db.get("select email from users where ske_id=$ske_id", {$ske_id: jwtData.user.id}, function(err, row){
+ if (!row) {
+ var email = jwtData.user.username + '@sketchengine.co.uk';
+ db.get("select * from users where email=$email", {$email: email}, function(err, row){
+ if (row == undefined) {
+ var key = generateKey();
+ var now = (new Date()).toISOString();
+ db.run("insert into users (email, passwordHash, ske_id, ske_username, sessionKey, sessionLast) values ($email, null, $ske_id, $ske_username, $key, $now)", {$ske_id: jwtData.user.id, $ske_username: jwtData.user.username, $email: email, $key:key, $now:now}, function(err, row){
+ db.close();
+ callnext(true, email, key);
+ });
+ } else {
+ db.close();
+ callnext(false, "user already exists "+email, "");
+ }
+ });
+ } else {
+ var email = row.email;
+ var key = generateKey();
+ var now = (new Date()).toISOString();
+ db.run("update users set sessionKey=$key, sessionLast=$now where ske_id=$ske_id", {$key: key, $now: now, $ske_id: jwtData.user.id}, function(err, row){
+ db.close();
+ callnext(true, email, key);
+ });
+ }
+ });
+ }
+ },
verifyLogin: function(email, sessionkey, callnext){
var yesterday=(new Date()); yesterday.setHours(yesterday.getHours()-24); yesterday=yesterday.toISOString();
var db=new sqlite3.Database(path.join(module.exports.siteconfig.dataDir, "lexonomy.sqlite"), sqlite3.OPEN_READWRITE);
- db.get("select email from users where email=$email and sessionKey=$key and sessionLast>=$yesterday", {$email: email, $key: sessionkey, $yesterday: yesterday}, function(err, row){
+ db.get("select email, ske_username from users where email=$email and sessionKey=$key and sessionLast>=$yesterday", {$email: email, $key: sessionkey, $yesterday: yesterday}, function(err, row){
if(!row || module.exports.siteconfig.readonly){
db.close();
callnext({loggedin: false, email: null});
} else {
email=row.email;
+ var ske_username = row.ske_username;
var now=(new Date()).toISOString();
db.run("update users set sessionLast=$now where email=$email", {$now: now, $email: email}, function(err, row){
db.close();
module.exports.readSiteConfig(function(siteconfig){
- callnext({loggedin: true, email: email, isAdmin: (siteconfig.admins.indexOf(email)>-1)});
+ callnext({loggedin: true, email: email, ske_username: ske_username, isAdmin: (siteconfig.admins.indexOf(email)>-1)});
});
});
}
@@ -1248,4 +1337,4 @@ function getDoctype(xml){
return ret;
}
-const prohibitedDictIDs=["login", "logout", "make", "signup", "forgotpwd", "changepwd", "users", "dicts", "oneclick", "recoverpwd"];
+const prohibitedDictIDs=["login", "logout", "make", "signup", "forgotpwd", "changepwd", "users", "dicts", "oneclick", "recoverpwd","createaccount"];
diff --git a/website/package-lock.json b/website/package-lock.json
index c74d4842..65b2a559 100644
--- a/website/package-lock.json
+++ b/website/package-lock.json
@@ -48,6 +48,11 @@
"type-is": "1.6.16"
}
},
+ "buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
+ },
"busboy": {
"version": "0.2.14",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
@@ -172,6 +177,14 @@
"streamsearch": "0.1.2"
}
},
+ "ecdsa-sig-formatter": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz",
+ "integrity": "sha1-HFlQAPBKiJffuFAAiSoPTDOvhsM=",
+ "requires": {
+ "safe-buffer": "5.1.1"
+ }
+ },
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -324,6 +337,49 @@
"graceful-fs": "4.1.11"
}
},
+ "jsonwebtoken": {
+ "version": "8.2.2",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.2.2.tgz",
+ "integrity": "sha512-rFFq7ow/JpPzwgaz4IyRL9cp7f4ptjW92eZgsQyqkysLBmDjSSBhnKfQESoq0GU+qJXK/CQ0o4shgwbUPiFCdw==",
+ "requires": {
+ "jws": "3.1.5",
+ "lodash.includes": "4.3.0",
+ "lodash.isboolean": "3.0.3",
+ "lodash.isinteger": "4.0.4",
+ "lodash.isnumber": "3.0.3",
+ "lodash.isplainobject": "4.0.6",
+ "lodash.isstring": "4.0.1",
+ "lodash.once": "4.1.1",
+ "ms": "2.1.1",
+ "xtend": "4.0.1"
+ },
+ "dependencies": {
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+ }
+ }
+ },
+ "jwa": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.6.tgz",
+ "integrity": "sha512-tBO/cf++BUsJkYql/kBbJroKOgHWEigTKBAjjBEmrMGYd1QMBC74Hr4Wo2zCZw6ZrVhlJPvoMrkcOnlWR/DJfw==",
+ "requires": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.10",
+ "safe-buffer": "5.1.1"
+ }
+ },
+ "jws": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.5.tgz",
+ "integrity": "sha512-GsCSexFADNQUr8T5HPJvayTjvPIfoyJPtLQBwn5a4WZQchcrPMPMAWcC1AzJVRDKyD6ZPROPAxgv6rfHViO4uQ==",
+ "requires": {
+ "jwa": "1.1.6",
+ "safe-buffer": "5.1.1"
+ }
+ },
"libxmljs-mt": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/libxmljs-mt/-/libxmljs-mt-0.18.0.tgz",
@@ -343,6 +399,41 @@
"nan": "2.9.2"
}
},
+ "lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+ "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8="
+ },
+ "lodash.isboolean": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+ "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY="
+ },
+ "lodash.isinteger": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+ "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M="
+ },
+ "lodash.isnumber": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+ "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w="
+ },
+ "lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
+ },
+ "lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
+ },
+ "lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
+ },
"markdown": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/markdown/-/markdown-0.5.0.tgz",
@@ -428,9 +519,9 @@
"integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
},
"nodemailer": {
- "version": "4.6.4",
- "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-4.6.4.tgz",
- "integrity": "sha512-SD4uuX7NMzZ5f5m1XHDd13J4UC3SmdJk8DsmU1g6Nrs5h3x9LcXr6EBPZIqXRJ3LrF7RdklzGhZRF/TuylTcLg=="
+ "version": "4.6.5",
+ "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-4.6.5.tgz",
+ "integrity": "sha512-+bt+BgmnOXDz1uIaWXfXuTESth8UHkhtu7+X8+X2W+CHAn0AuuCyCk854qnathYQLWEC2jkpx7/pkVHcfmLKDw=="
},
"nopt": {
"version": "2.1.2",
diff --git a/website/package.json b/website/package.json
index 1073b53e..1cd8005a 100644
--- a/website/package.json
+++ b/website/package.json
@@ -4,10 +4,11 @@
"ejs": "^2.5.7",
"express": "^4.16.3",
"fs-extra": "^5.0.0",
+ "jsonwebtoken": "^8.2.2",
"libxslt": "^0.7.0",
"markdown": "^0.5.0",
"multer": "^1.3.0",
- "nodemailer": "^4.6.4",
+ "nodemailer": "^4.6.5",
"sha1": "^1.1.1",
"sqlite3": "^3.1.13",
"xmldom": "^0.1.27"
diff --git a/website/siteconfig.json.template b/website/siteconfig.json.template
index 763be84d..b0f3a749 100644
--- a/website/siteconfig.json.template
+++ b/website/siteconfig.json.template
@@ -9,6 +9,8 @@
"trackingCode": "",
"welcome": "Welcome to your Lexonomy installation.",
"mailconfig": {"host": "smtp.server.example", "port": 465,"secure": true, "from": "noreply@example.com"},
+ "sketchengineKey": null,
+ "sketchengineLoginPage": null,
"licences": {
"cc-by-4.0": {
"title": "Creative Commons Attribution 4.0 International",
diff --git a/website/views/createaccount.ejs b/website/views/createaccount.ejs
new file mode 100644
index 00000000..56f304b0
--- /dev/null
+++ b/website/views/createaccount.ejs
@@ -0,0 +1,35 @@
+
+
+
+
+
+ Create your account
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/website/views/home.ejs b/website/views/home.ejs
index 896dfc4a..9966d4ac 100644
--- a/website/views/home.ejs
+++ b/website/views/home.ejs
@@ -73,6 +73,12 @@
});
});
+ <%if(error){%>Sketch Engine login error: <%=error%>
<%}%>
+ <%if(siteconfig.sketchengineLoginPage){%>
+
+ <%}%>
<%}else{%>
<%=user.email%>
@@ -92,6 +98,16 @@
One-Click Dictionary settings
+ <%if(siteconfig.sketchengineLoginPage){%>
+
SketchEngine login
+
+ <%}%>
<%if(user.isAdmin){%>
Administration
diff --git a/website/views/signup.ejs b/website/views/signup.ejs
index 81e4dd15..fca2f693 100644
--- a/website/views/signup.ejs
+++ b/website/views/signup.ejs
@@ -20,7 +20,8 @@