Added support for Google Sign-In, optional in every way.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3187 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
parent
7b112c5376
commit
cbf54eaa17
@ -9,9 +9,10 @@
|
||||
<link type="text/css" rel="stylesheet" href="/terminal/style.css"></link>
|
||||
<link type="image/png" rel="shortcut icon" href="/terminal/favicon.png"></link>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!--HEAD-->
|
||||
</head>
|
||||
<body>
|
||||
<h1>Login</h1>
|
||||
<div id="content">$(SESSION)</div>
|
||||
<div id="content"><!--SESSION--></div>
|
||||
</body>
|
||||
</html>
|
||||
|
81
core/auth.js
81
core/auth.js
@ -8,6 +8,7 @@ var bCryptLib = require('bCrypt');
|
||||
bCrypt = new bCryptLib.bCrypt();
|
||||
|
||||
var form = require('form');
|
||||
var http = require('http');
|
||||
|
||||
File.makeDirectory("data");
|
||||
File.makeDirectory("data/auth");
|
||||
@ -108,6 +109,7 @@ function authHandler(request, response) {
|
||||
}
|
||||
} else {
|
||||
if (gAccounts[formData.name] &&
|
||||
gAccounts[formData.name].password &&
|
||||
verifyPassword(formData.password, gAccounts[formData.name].password)) {
|
||||
writeSession(session, {name: formData.name});
|
||||
if (noAdministrator()) {
|
||||
@ -140,6 +142,44 @@ function authHandler(request, response) {
|
||||
}
|
||||
contents += '<div><a href="/login/logout">Logout</a></div>\n';
|
||||
} else {
|
||||
if (gGlobalSettings && gGlobalSettings['google-signin-client_id']) {
|
||||
html = html.replace("<!--HEAD-->", `
|
||||
<script src="https://apis.google.com/js/platform.js" async defer></script>
|
||||
<meta name="google-signin-client_id" content="${gGlobalSettings['google-signin-client_id']}">
|
||||
<script>
|
||||
function onGoogleSignIn(user) {
|
||||
var token = user.getAuthResponse().id_token;
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", "/login/google");
|
||||
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||
xhr.onload = function() {
|
||||
if (xhr.status == 200) {
|
||||
var redirected = false;
|
||||
if (window.location.search.length) {
|
||||
var query = window.location.search.substring(1);
|
||||
var parts = query.split("&");
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
var part = decodeURIComponent(parts[i]);
|
||||
var key = part.substring(0, part.indexOf('='));
|
||||
var value = part.substring(part.indexOf('=') + 1);
|
||||
if (key == "return") {
|
||||
redirected = true;
|
||||
window.location.href = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!redirected) {
|
||||
window.location.path = "/";
|
||||
}
|
||||
} else {
|
||||
alert(xhr.response);
|
||||
}
|
||||
};
|
||||
xhr.send('token=' + token);
|
||||
}
|
||||
</script>
|
||||
`);
|
||||
}
|
||||
contents += '<form method="POST">\n';
|
||||
if (loginError) {
|
||||
contents += "<p>" + loginError + "</p>\n";
|
||||
@ -157,13 +197,17 @@ function authHandler(request, response) {
|
||||
contents += '<div><input id="loginButton" type="submit" name="submit" value="Login"></input></div>\n';
|
||||
contents += '</div>';
|
||||
contents += '<div id="auth_or"> - or - </div>';
|
||||
if (gGlobalSettings && gGlobalSettings['google-signin-client_id']) {
|
||||
contents += '<div class="g-signin2" data-onsuccess="onGoogleSignIn" data-scope="profile"></div>';
|
||||
contents += '<div id="auth_or"> - or - </div>';
|
||||
}
|
||||
contents += '<div id="auth_guest">\n';
|
||||
contents += '<input id="guestButton" type="submit" name="submit" value="Proceeed as Guest"></input>\n';
|
||||
contents += '</div>\n';
|
||||
contents += '</div>\n';
|
||||
contents += '</form>';
|
||||
}
|
||||
var text = html.replace("$(SESSION)", contents);
|
||||
var text = html.replace("<!--SESSION-->", contents);
|
||||
response.writeHead(200, {"Content-Type": "text/html; charset=utf-6", "Set-Cookie": cookie, "Content-Length": text.length});
|
||||
response.end(text);
|
||||
}
|
||||
@ -171,12 +215,47 @@ function authHandler(request, response) {
|
||||
removeSession(session);
|
||||
response.writeHead(303, {"Set-Cookie": "session=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT", "Location": "/login" + (request.query ? "?" + request.query : "")});
|
||||
response.end();
|
||||
} else if (request.uri == "/login/google") {
|
||||
var formData = form.decodeForm(request.query, form.decodeForm(request.body));
|
||||
return verifyGoogleToken(formData.token).then(function(user) {
|
||||
if (user && user.aud == gGlobalSettings['google-signin-client_id']) {
|
||||
session = newSession();
|
||||
var userId = user.name;
|
||||
if (gAccounts[userId] && !gAccounts[userId].google) {
|
||||
response.writeHead(500, {"Content-Type": "text/plain; charset=utf-8", "Connection": "close"});
|
||||
response.end("Account already exists and is not a Google account.");
|
||||
} else {
|
||||
if (!gAccounts[userId]) {
|
||||
gAccounts[userId] = {google: true};
|
||||
File.writeFile(kAccountsFile, JSON.stringify(gAccounts));
|
||||
if (noAdministrator()) {
|
||||
makeAdministrator(userId);
|
||||
}
|
||||
}
|
||||
|
||||
writeSession(session, {name: userId, google: true});
|
||||
|
||||
var cookie = "session=" + session + "; path=/; Max-Age=604800";
|
||||
response.writeHead(200, {"Content-Type": "text/json; charset=utf-8", "Connection": "close", "Set-Cookie": cookie});
|
||||
response.end(JSON.stringify(user));
|
||||
}
|
||||
} else {
|
||||
response.writeHead(500, {"Content-Type": "text/plain; charset=utf-8", "Connection": "close"});
|
||||
response.end();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
response.writeHead(200, {"Content-Type": "text/plain; charset=utf-8", "Connection": "close"});
|
||||
response.end("Hello, " + request.client.peerName + ".");
|
||||
}
|
||||
}
|
||||
|
||||
function verifyGoogleToken(token) {
|
||||
return http.get("https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=" + token).then(function(response) {
|
||||
return JSON.parse(response.body);
|
||||
});
|
||||
}
|
||||
|
||||
function getPermissions(session) {
|
||||
var permissions;
|
||||
var entry = readSession(session);
|
||||
|
@ -266,7 +266,15 @@ function updateLogin() {
|
||||
var a = document.createElement("a");
|
||||
if (gCredentials && gCredentials.session) {
|
||||
a.appendChild(document.createTextNode("logout " + gCredentials.session.name));
|
||||
a.setAttribute("href", "/login/logout?return=" + encodeURIComponent(url()));
|
||||
if (gCredentials.session.google) {
|
||||
gapi.load("auth2", function() {
|
||||
gapi.auth2.init();
|
||||
});
|
||||
a.setAttribute("onclick", "logoutGoogle()");
|
||||
a.setAttribute("href", "#");
|
||||
} else {
|
||||
a.setAttribute("href", "/login/logout?return=" + encodeURIComponent(url()));
|
||||
}
|
||||
} else if (window.location.href.indexOf("?guest=1") != -1) {
|
||||
window.location.href = "/login?submit=Proceed+as+Guest&return=" + encodeURIComponent(url());
|
||||
} else {
|
||||
@ -275,6 +283,12 @@ function updateLogin() {
|
||||
login.appendChild(a);
|
||||
}
|
||||
|
||||
function logoutGoogle() {
|
||||
gapi.auth2.getAuthInstance().signOut().then(function() {
|
||||
window.location.href = "/login/logout?return=" + encodeURIComponent(url());
|
||||
});
|
||||
}
|
||||
|
||||
var gOriginalInput;
|
||||
function dragHover(event) {
|
||||
event.stopPropagation();
|
||||
|
11
core/core.js
11
core/core.js
@ -279,6 +279,17 @@ function getProcess(packageOwner, packageName, key, options) {
|
||||
}
|
||||
process.eventHandlers[eventName].push(handler);
|
||||
},
|
||||
'unregister': function(eventHandle, handler) {
|
||||
if (process.eventHandlers(eventName)) {
|
||||
let index = process.eventHandlers[eventName].indexOf(handler);
|
||||
if (index != -1) {
|
||||
process.eventHandlers[eventName].splice(index, 1);
|
||||
}
|
||||
if (process.eventHandlers[eventName].length == 0) {
|
||||
delete process.eventHandlers[eventName];
|
||||
}
|
||||
}
|
||||
},
|
||||
'getUser': getUser.bind(null, process, process),
|
||||
'user': getUser(process, process),
|
||||
},
|
||||
|
61
core/http.js
Normal file
61
core/http.js
Normal file
@ -0,0 +1,61 @@
|
||||
"use strict";
|
||||
|
||||
function parseUrl(url) {
|
||||
// XXX: Hack.
|
||||
var match = url.match(new RegExp("(\\w+)://([^/]+)?(.*)"));
|
||||
return {
|
||||
protocol: match[1],
|
||||
host: match[2],
|
||||
path: match[3],
|
||||
port: match[1] == "http" ? 80 : 443,
|
||||
};
|
||||
}
|
||||
|
||||
function parseResponse(data) {
|
||||
var firstLine;
|
||||
var headers = {};
|
||||
|
||||
while (true) {
|
||||
var endLine = data.indexOf("\r\n");
|
||||
var line = data.substring(0, endLine);
|
||||
if (!firstLine) {
|
||||
firstLine = line;
|
||||
} else if (!line.length) {
|
||||
break;
|
||||
} else {
|
||||
var colon = line.indexOf(":");
|
||||
headers[line.substring(colon)] = line.substring(colon + 1);
|
||||
}
|
||||
data = data.substring(endLine + 2);
|
||||
}
|
||||
return {body: data};
|
||||
}
|
||||
|
||||
function get(url) {
|
||||
var parsed = parseUrl(url);
|
||||
return new Promise(function(resolve, reject) {
|
||||
var socket = new Socket();
|
||||
var buffer = "";
|
||||
|
||||
return socket.connect(parsed.host, parsed.port).then(function() {
|
||||
socket.read(function(data) {
|
||||
if (data) {
|
||||
buffer += data;
|
||||
} else {
|
||||
resolve(parseResponse(buffer));
|
||||
}
|
||||
});
|
||||
|
||||
if (parsed.port == 443) {
|
||||
return socket.startTls();
|
||||
}
|
||||
}).then(function() {
|
||||
socket.write(`GET ${parsed.path} HTTP/1.0\r\nHost: ${parsed.host}\r\nConnection: close\r\n\r\n`);
|
||||
socket.shutdown();
|
||||
}).catch(function(error) {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
exports.get = get;
|
@ -4,6 +4,7 @@
|
||||
<link type="text/css" rel="stylesheet" href="/terminal/style.css"></link>
|
||||
<link type="image/png" rel="shortcut icon" href="/terminal/favicon.png"></link>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!--HEAD-->
|
||||
</head>
|
||||
<body>
|
||||
<div id="body">
|
||||
|
@ -178,6 +178,11 @@ function handler(request, response, packageOwner, packageName, uri) {
|
||||
found = true;
|
||||
var data = File.readFile("core/" + kStaticFiles[i].path);
|
||||
if (kStaticFiles[i].uri == "") {
|
||||
if (gGlobalSettings && gGlobalSettings['google-signin-client_id']) {
|
||||
data = data.replace("<!--HEAD-->", `
|
||||
<script src="https://apis.google.com/js/platform.js" async defer></script>
|
||||
<meta name="google-signin-client_id" content="${gGlobalSettings['google-signin-client_id']}">`);
|
||||
}
|
||||
data = data.replace("$(VIEW_SOURCE)", "/~" + packageOwner + "/" + packageName + "/view");
|
||||
data = data.replace("$(EDIT_SOURCE)", "/~" + packageOwner + "/" + packageName + "/edit");
|
||||
} else if (kStaticFiles[i].uri == "/edit") {
|
||||
|
@ -15,7 +15,7 @@ if (core.user.credentials.permissions &&
|
||||
],
|
||||
[
|
||||
["set ", {class: "cyan", value: "key value"}],
|
||||
["Set global setting key to value."],
|
||||
["Set global setting key to value. Omit value to unset."],
|
||||
],
|
||||
[
|
||||
"permission list",
|
||||
@ -45,6 +45,7 @@ var kSimpleSettings = [
|
||||
'httpPort',
|
||||
'httpsPort',
|
||||
'index',
|
||||
'google-signin-client_id',
|
||||
];
|
||||
|
||||
function printSettings(settings) {
|
||||
@ -73,12 +74,16 @@ function onInput(input) {
|
||||
terminal.print(" ".repeat(16 - s[i].toString().length), s[i].toString(), " ", i);
|
||||
}
|
||||
});
|
||||
} else if (match = /^\s*set\s+(\w+)\s+(.*)/.exec(input)) {
|
||||
} else if (match = /^\s*set\s+(\S+)(?:\s+(.*))?/.exec(input)) {
|
||||
var key = match[1];
|
||||
var value = match[2];
|
||||
administration.getGlobalSettings().then(function(settings) {
|
||||
if (kSimpleSettings.indexOf(key) != -1) {
|
||||
settings[key] = value;
|
||||
if (value) {
|
||||
settings[key] = value;
|
||||
} else {
|
||||
delete settings[key];
|
||||
}
|
||||
administration.setGlobalSettings(settings).then(function() {
|
||||
administration.getGlobalSettings().then(printSettings);
|
||||
}).catch(function(error) {
|
||||
|
@ -100,7 +100,10 @@ core.register("onInput", function(input) {
|
||||
if (input == "new post") {
|
||||
startNewPost();
|
||||
} else if (input == "submit") {
|
||||
submitNewPost().then(renderBlog);
|
||||
submitNewPost().then(function() {
|
||||
core.unregister("onWindowMessage", onWindowMessage);
|
||||
renderBlog();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@ -131,18 +134,19 @@ function submitNewPost() {
|
||||
return gBlog.append(gNewPost);
|
||||
}
|
||||
|
||||
function startNewPost() {
|
||||
core.register("onWindowMessage", function(message) {
|
||||
gNewPost = message.message;
|
||||
terminal.cork();
|
||||
terminal.select("right");
|
||||
terminal.clear();
|
||||
terminal.print({style: "font-width: x-large", value: message.message.title});
|
||||
terminal.print(message.message.entry);
|
||||
terminal.print({command: "submit"});
|
||||
terminal.uncork();
|
||||
});
|
||||
function onWindowMessage(message) {
|
||||
gNewPost = message.message;
|
||||
terminal.cork();
|
||||
terminal.select("right");
|
||||
terminal.clear();
|
||||
terminal.print({style: "font-width: x-large", value: message.message.title});
|
||||
terminal.print(message.message.entry);
|
||||
terminal.print({command: "submit"});
|
||||
terminal.uncork();
|
||||
}
|
||||
|
||||
function startNewPost() {
|
||||
core.register("onWindowMessage", onWindowMessage);
|
||||
terminal.split([
|
||||
{
|
||||
type: "horizontal",
|
||||
|
Loading…
Reference in New Issue
Block a user