Created
January 7, 2019 14:13
-
-
Save jankeromnes/1581bb88b076b17c98b8fe15a514be29 to your computer and use it in GitHub Desktop.
Some old work-in-progress diff about janitor.technology
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Commits: | |
- redirect when code=&state= URL parameters have been used | |
- save access token creation date and redirect tokens | |
- expire access tokens after 30 days | |
- save machine spawn time | |
- delete any container from admin page | |
- delete container/token after a timeout (but remember across server restarts) | |
- after spawn, redirect to specific container ID in Containers page | |
commit e30a726f79b24fe71257b27babf82ee644c8e4bb | |
Author: Jan Keromnes <[email protected]> | |
Date: Fri Sep 29 21:52:38 2017 +0000 | |
deletetokens | |
diff --git a/join.js b/join.js | |
index 8cead4c..ef76a4a 100644 | |
--- a/join.js | |
+++ b/join.js | |
@@ -4,6 +4,7 @@ | |
const camp = require('camp'); | |
const http = require('http'); | |
const nodepath = require('path'); | |
+const nodeurl = require('url'); | |
const boot = require('./lib/boot'); | |
const db = require('./lib/db'); | |
@@ -128,9 +129,28 @@ function handleOAuth2Code (request, response, next) { | |
} | |
// Associate the new OAuth2 access token to the current session. | |
- // TODO: Also save the `refreshToken` when it's fully supported. | |
- oauth2Tokens[session.id] = accessToken; | |
- next(); | |
+ oauth2Tokens[session.id] = { | |
+ created: Date.now(), | |
+ accessToken, | |
+ refreshToken, | |
+ }; | |
+ | |
+ const redirectUrl = nodeurl.parse(request.url); | |
+ if (!redirectUrl.search) { | |
+ next(); | |
+ return; | |
+ } | |
+ | |
+ // Remove the used OAuth2 code and state parameters from the URL. | |
+ const oldQueryParameters = redirectUrl.search.slice(1).split('&'); | |
+ const newQueryParameters = oldQueryParameters.filter(parameter => { | |
+ return !parameter.startsWith('code=') && | |
+ !parameter.startsWith('state='); | |
+ }); | |
+ redirectUrl.search = newQueryParameters.length > 0 | |
+ ? '?' + newQueryParameters.join('&') | |
+ : null; | |
+ routes.redirect(response, nodeurl.format(redirectUrl)); | |
}); | |
} | |
@@ -138,10 +158,15 @@ function handleOAuth2Code (request, response, next) { | |
function ensureOAuth2Access (request, response, next) { | |
const { session } = request; | |
if (oauth2Tokens[session.id]) { | |
- // This session has an OAuth2 access token, so it's authenticated. | |
- // TODO: Also verify that the token is still valid, or renew it if not. | |
- next(); | |
- return; | |
+ const created = oauth2Tokens[session.id].created; | |
+ if (created && created + 1000 * 3600 * 30 > Date.now()) { | |
+ // This session has a valid OAuth2 access token, so it's authenticated. | |
+ next(); | |
+ return; | |
+ } | |
+ | |
+ // This sessions's OAuth2 access token has expired. | |
+ delete oauth2Tokens[session.id]; | |
} | |
// We can only use `http.ServerResponse`s to initiate OAuth2 authentication, | |
diff --git a/lib/hosts.js b/lib/hosts.js | |
index 080af2a..6607f98 100644 | |
--- a/lib/hosts.js | |
+++ b/lib/hosts.js | |
@@ -246,7 +246,15 @@ exports.getOAuth2Scope = function (request) { | |
return null; | |
} | |
- const { client, email, scope } = authorization; | |
+ const { client, date, email, scope } = authorization; | |
+ if (!date || Date.now() > date + 1000 * 3600 * 24 * 30) { | |
+ // The OAuth2 authorization has expired, delete it. | |
+ log('[fail] deleting expired token:', authorization); | |
+ delete oauth2tokens[tokenHash]; | |
+ db.save(); | |
+ return null; | |
+ } | |
+ | |
const hostname = exports.identify(client); | |
if (!hostname) { | |
// The authorized OAuth2 client doesn't exist anymore. | |
diff --git a/lib/machines.js b/lib/machines.js | |
index 44f7dcf..4da4a98 100644 | |
--- a/lib/machines.js | |
+++ b/lib/machines.js | |
@@ -214,6 +214,7 @@ exports.spawn = function (user, projectId, callback) { | |
machine.status = 'started'; | |
const now = Date.now(); | |
+ metrics.set(machine, 'spawned', now); | |
metrics.push(project, 'spawn-time', [ now, now - time ]); | |
db.save(); | |
diff --git a/static/js/janitor.js b/static/js/janitor.js | |
index 73469dc..c6d6a6e 100644 | |
--- a/static/js/janitor.js | |
+++ b/static/js/janitor.js | |
@@ -240,7 +240,7 @@ function getFormData (form) { | |
}, {}); | |
} | |
-// Setup editable labels. | |
+// Setup editable container labels. | |
Array.forEach(document.querySelectorAll('.editable-label'), function (label) { | |
const toggle = label.querySelector('.editable-toggle'); | |
if (!toggle) { | |
diff --git a/static/js/projects.js b/static/js/projects.js | |
index 4e3e847..628c87e 100644 | |
--- a/static/js/projects.js | |
+++ b/static/js/projects.js | |
@@ -2,21 +2,26 @@ | |
// The following code is covered by the AGPL-3.0 license. | |
// Spawn a project-specific machine when one of its links is clicked. | |
- | |
-Array.map(document.querySelectorAll('a[data-action="spawn"]'), function (link) { | |
- link.addEventListener('click', Scout.send(function (query) { | |
- query.action = link.dataset.action; | |
- query.data = { | |
- project: link.dataset.project | |
- }; | |
- query.resp = function (data) { | |
- document.location = '/containers/'; | |
- }; | |
- })); | |
+Array.forEach(document.querySelectorAll('.new-container'), function (form) { | |
+ window.setupAsyncForm(form); | |
+ form.addEventListener('submit', function (event) { | |
+ var url = '/api/hosts/' + form.dataset.host + | |
+ '/containers?project=' + form.dataset.project; | |
+ window.fetchAPI('PUT', url, null, function(error, data) { | |
+ if (error) { | |
+ window.updateFormStatus(form, 'error', String(error)); | |
+ return; | |
+ } | |
+ window.updateFormStatus(form, 'success', null); | |
+ setTimeout(function () { | |
+ document.location.href = | |
+ '/containers/#' + data.container.slice(0, 16); | |
+ }, 400); | |
+ }); | |
+ }); | |
}); | |
// Add status badges to elements with a 'data-status' attribute. | |
- | |
Array.map(document.querySelectorAll('*[data-status]'), function (element) { | |
var span = document.createElement('span'); | |
var status = element.dataset.status; | |
@@ -41,14 +46,13 @@ Array.map(document.querySelectorAll('*[data-status]'), function (element) { | |
}); | |
// Add fuzzy timestamps to elements with a 'data-timestamp' attribute. | |
- | |
Array.map(document.querySelectorAll('*[data-timestamp]'), function (element) { | |
- var date = new Date(parseInt(element.dataset.timestamp)); | |
+ var date = new Date(parseInt(element.dataset.timestamp, 10)); | |
// GMT is deprecated (see https://en.wikipedia.org/wiki/UTC). | |
element.title = date.toUTCString().replace('GMT', 'UTC'); | |
element.setAttribute('datetime', date.toISOString()); | |
// Use jQuery's live-updating timeago plugin. | |
- $(element).timeago(); | |
+ window.$(element).timeago(); | |
}); | |
diff --git a/templates/containers.html b/templates/containers.html | |
index 1d3d47f..ab00798 100644 | |
--- a/templates/containers.html | |
+++ b/templates/containers.html | |
@@ -19,7 +19,7 @@ | |
<input class="form-control" data-submit-on="blur" name="/name" placeholder="{{= project.name in xmlattr }} #{{= id in id}}" type="text" value="{{= machine.properties.name in xmlattr }}"> | |
</form> | |
<div class="editable-value"> | |
- <h4>{{= name in html}}</h4> | |
+ <h4 id="{{= machine.docker.container.slice(0,16) in xmlattr}}">{{= name in html}}</h4> | |
<span class="glyphicon glyphicon-pencil editable-toggle"></span> | |
</div> | |
</div> | |
diff --git a/templates/projects.html b/templates/projects.html | |
index 7b181c1..ef2ec77 100644 | |
--- a/templates/projects.html | |
+++ b/templates/projects.html | |
@@ -12,7 +12,7 @@ | |
<img class="project-icon" src="{{= project.icon in xmlattr}}" alt="{{= project.name in xmlattr}} Logo"> | |
<h4 class="project-title">{{= project.name in html}}</h4> | |
<div class="project-actions">{{if user then {{ | |
- <form action="/api/hosts/{{= project.docker.host in uri}}/containers?project={{= id in id}}" class="ajax-form has-feedback is-submit" data-redirect-after-success="/contributions/" method="put"> | |
+ <form class="ajax-form has-feedback is-submit new-container" data-host="{{= project.docker.host in xmlattr}}" data-project="{{= id in id}}"> | |
<button class="btn btn-primary" title="Create a new container for this project" type="submit">New Container</button> | |
</form>}} else {{ | |
<a class="btn btn-primary" href="/login">New Container</a>}}}} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment