Skip to content

Instantly share code, notes, and snippets.

@jankeromnes
Created January 7, 2019 14:13
Show Gist options
  • Save jankeromnes/1581bb88b076b17c98b8fe15a514be29 to your computer and use it in GitHub Desktop.
Save jankeromnes/1581bb88b076b17c98b8fe15a514be29 to your computer and use it in GitHub Desktop.
Some old work-in-progress diff about janitor.technology
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