Merge pull request +14025 from c9/ide-preview-sessions

IDE preview sessions
pull/295/merge
Fabian Jakobs 2016-06-24 09:26:06 +02:00 zatwierdzone przez GitHub
commit d1db51b557
5 zmienionych plików z 224 dodań i 80 usunięć

Wyświetl plik

@ -30,15 +30,6 @@ module.exports = function startup(options, imports, register) {
var sessionRoutes = connectModule();
connect.useSession(sessionRoutes);
sessionRoutes.use(
function(req, res, next) {
if (/^\/geckolala\//.test(req.url))
return next(new error.TooManyRequests("Rate limit exceeded"));
next();
}
);
sessionRoutes.use(Session(sessionOptions, cookie));
register(null, {

Wyświetl plik

@ -17,6 +17,7 @@ define(function(require, exports, module) {
var https = require("https");
var http = require("http");
var mime = require("mime");
var Cache = require("c9/cache");
var metrics = imports.metrics;
var parseUrl = require("url").parse;
var debug = require("debug")("preview");
@ -24,43 +25,25 @@ define(function(require, exports, module) {
var staticPrefix = imports["connect.static"].getStaticPrefix();
function getProjectSession() {
function getRole(db) {
var roleCache = new Cache(10000, 10000);
return function(req, res, next) {
var session = req.session;
req.user = req.user || { id: -1 };
var username = req.params.username;
var projectname = req.params.projectname;
var ws = req.ws = username + "/" + projectname;
if (!session.ws)
session.ws = {};
req.projectSession = session.ws[ws];
if (
!req.projectSession ||
!req.projectSession.expires ||
req.projectSession.expires <= Date.now() ||
req.projectSession.uid != req.user.id
) {
req.projectSession = session.ws[ws] = {
expires: Date.now() + 10000
if (!req.user) {
req.user = {
id: -1
};
}
next();
};
}
function getRole(db) {
return function(req, res, next) {
if (req.projectSession.role) {
req.session = {};
var key = req.params.username + "/" + req.params.projectname + ":" + req.user.id;
var wsSession = roleCache.get(key);
if (wsSession) {
req.session = wsSession;
return next();
}
db.Project.findOne({
username: req.params.username,
name: req.params.projectname
@ -70,25 +53,27 @@ define(function(require, exports, module) {
if (err) return next(err);
project.getRole(req.user, function(err, role) {
project.getRole(req.user.id, function(err, role) {
if (err) return next(err);
var wsSession = {
role: role,
pid: project.id,
uid: req.user.id,
type: project.scm
};
roleCache.set(key, wsSession);
req.session = wsSession;
if (role == db.Project.ROLE_NONE) {
if (project.isPublicPreview())
role = db.Project.ROLE_VISITOR;
else if (req.user.id == -1)
return next(new error.Unauthorized());
else
return next(new error.Forbidden("You don't have access rights to preview this workspace"));
if (!project.isPublicPreview())
return next();
wsSession.role = db.Project.ROLE_VISITOR;
}
req.projectSession.role = role;
req.projectSession.pid = project.id;
req.projectSession.uid = req.user.id;
var type = project.scm;
req.projectSession.type = type;
if (type != "docker" || project.state != db.Project.STATE_READY)
if (wsSession.type != "docker" || project.state != db.Project.STATE_READY)
return next();
project.populate("remote", function(err) {
@ -100,10 +85,8 @@ define(function(require, exports, module) {
if (err) return next(err);
if (container.state == db.Container.STATE_RUNNING)
req.projectSession.proxyUrl = "http://" + meta.host + ":9000/" + meta.cid + "/home/ubuntu/workspace";
else
req.projectSession.expires = Date.now() + 1000;
wsSession.proxyUrl = "http://" + meta.host + ":9000/" + meta.cid + "/home/ubuntu/workspace";
next();
});
} else {
@ -114,26 +97,41 @@ define(function(require, exports, module) {
});
};
}
function checkRole(db) {
return function(req, res, next) {
var role = req.session.role;
if (role == db.Project.ROLE_NONE) {
if (req.user.id == -1)
return next(new error.Unauthorized());
else
return next(new error.Forbidden("You don't have access rights to preview this workspace"));
}
return next();
};
}
function getProxyUrl(getServer) {
return function(req, res, next) {
if (req.projectSession.proxyUrl) {
req.proxyUrl = req.projectSession.proxyUrl;
if (req.session.proxyUrl) {
req.proxyUrl = req.session.proxyUrl;
return next();
}
var server = req.projectSession.vfsServer;
var server = req.session.vfsServer;
if (!server) {
server = getServer();
if (!server || !server.url)
return next(new error.ServiceUnavailable("No VFS server found"));
server = req.projectSession.vfsServer = server.internalUrl || server.url;
server = req.session.vfsServer = server.internalUrl || server.url;
}
var url = server + "/" + req.projectSession.pid + "/preview";
var url = server + "/" + req.session.pid + "/preview";
req.proxyUrl = url;
next();
};
@ -222,9 +220,6 @@ define(function(require, exports, module) {
} else if (body.indexOf("ENOENT") !== -1 || statusCode == 404) {
next(new error.NotFound("File '" + path + "' could not be found!"));
} else {
if (req.session.ws)
delete req.session.ws[req.ws];
var json;
try {
json = JSON.parse(body);
@ -365,8 +360,8 @@ define(function(require, exports, module) {
register(null, {
"preview.handler": {
getProjectSession: getProjectSession,
getRole: getRole,
checkRole: checkRole,
getProxyUrl: getProxyUrl,
proxyCall: proxyCall
}

Wyświetl plik

@ -2,7 +2,7 @@ define(function(require, exports, module) {
"use strict";
main.consumes = [
"session",
"connect",
"db",
"c9.login",
"preview.handler",
@ -14,13 +14,12 @@ define(function(require, exports, module) {
return main;
function main(options, imports, register) {
var session = imports.session;
var connect = imports.connect;
var db = imports.db;
var ensureLoggedIn = imports["c9.login"].ensureLoggedIn();
var handler = imports["preview.handler"];
var userContent = imports["user-content.redirect"];
var getVfsServers = imports["vfs.serverlist"].getServers;
var ratelimit = require("c9/ratelimit");
var frontdoor = require("frontdoor");
var error = require("http-error");
@ -38,7 +37,7 @@ define(function(require, exports, module) {
api.use(userContent.redirectPreview());
session.use(api);
connect.use(api);
api.get("/:username/:projectname/:path*", {
params: {
@ -53,8 +52,8 @@ define(function(require, exports, module) {
requestTimeout(15*60*1000),
require("./lib/middleware/sanitize-path-param"),
require("./lib/middleware/block-dot-files"),
handler.getProjectSession(),
handler.getRole(db),
handler.checkRole(db),
handler.getProxyUrl(function() {
return getVfsServers()[0] || null;
}),
@ -63,8 +62,6 @@ define(function(require, exports, module) {
api.error(function(err, req, res, next) {
if (err instanceof error.Unauthorized) {
req.logout();
delete req.session.token;
return ensureLoggedIn(req, res, next);
}
return next(err);

Wyświetl plik

@ -0,0 +1,159 @@
#!/usr/bin/env node
/*global describe it before after beforeEach afterEach */
"use strict";
"use server";
require("c9/inline-mocha")(module);
require("c9/setup_paths");
var async = require("async");
var assert = require("assert");
var nock = require("nock");
var RestClient = require("c9/rest_client");
var baseTest = require('../c9.api/base_test');
var testDb = require("../c9.db.redis/test_redis");
var setupFixtures = require("test/lib/integration/setup");
describe(__filename, function() {
var db, serverList, client;
var testUser, testProject, testRemote, testContainer;
var loggedInUser = null;
before(function(next) {
baseTest({
config: "preview",
}, function(err, services) {
db = services.db;
serverList = services["vfs.serverlist"];
services.connect.useSetup(function(req, res, next) {
req.user = loggedInUser;
next();
});
client = new RestClient("localhost", services.apiPort, {
debug: false
});
testUser = require("test/lib/integration/db/user")(db);
testProject = require("test/lib/integration/db/project")(db);
testRemote = require("test/lib/integration/db/mock-remote")(db);
testContainer = require("test/lib/integration/db/mock-container")(db);
next(err);
});
});
after(function(next) {
testDb.stop(next);
});
beforeEach(function(next) {
serverList._testSetServerList([{
url: "http://vfs.c9.dev",
internalUrl: "http://vfs.c9.dev"
}]);
testDb.reset(next);
});
describe("preview", function() {
it("Should authorize users correctly", function(next) {
var user1, user2;
var public1, private1, private2;
// user1 user2
// user2/public
// user2/private1 user1 has no access
// user2/private2 user1 is collaborator
setupFixtures(
testUser.create(),
testUser.create(),
testProject.create(),
testRemote.create({type: "docker"}),
testContainer.create(),
testProject.create(),
testRemote.create({type: "docker"}),
testContainer.create(),
testProject.create(),
testRemote.create({type: "docker"}),
testContainer.create(),
function(ctx, next) {
user1 = ctx.users[0];
user2 = ctx.users[1];
public1 = ctx.projects[0];
private1 = ctx.projects[1];
private2 = ctx.projects[2];
next();
},
function(ctx, next) {
public1.owner = user2;
public1.visibility = "public";
public1.save(next);
},
function(ctx, next) {
private1.owner = user2;
private1.visibility = "private";
private1.save(next);
},
function(ctx, next) {
private2.owner = user2;
private2.visibility = "private";
private2.save(next);
},
function(ctx, next) {
db.WorkspaceMember.create(
private2,
user1.id,
db.WorkspaceMember.ACL_R,
db.Project.ROLE_COLLABORATOR, null, next
);
},
function(fixtures, teardown) {
/*
* Logged in Role Visibility ACTION
* =================================================================
* true none public view
* false none public view
* true none private forbidden
* false none private unauthorized
* true visitor/collaborator/admin public view
* true visitor/collaborator/admin private view
*/
var expect = [
{ uid: user1.id, role: db.Project.ROLE_NONE, p: public1, code: 200 },
{ uid: -1, role: db.Project.ROLE_NONE, p: public1, code: 200 },
{ uid: user1.id, role: db.Project.ROLE_NONE, p: private1, code: 403 },
{ uid: -1, role: db.Project.ROLE_NONE, p: private1, code: 302 }, // redirect to login page
{ uid: user1.id, role: db.Project.ROLE_COLLABORATOR, p: public1, code: 200 },
{ uid: user2.id, role: db.Project.ROLE_ADMIN, p: private2, code: 200 }
];
async.eachSeries(expect, function(expect, next) {
var path = "/" + expect.p.owner.name + "/" + expect.p.name + "/";
nock('http://vfs.c9.dev')
.get("/" + expect.p.id + "/preview/")
.reply(200, []);
loggedInUser = {
id: expect.uid
};
client.get(path, function (err, res) {
assert((err && err.code) == expect.code || expect.code == 200, "Wrong return code");
next();
});
}, function(err) {
if (err) return next(err);
teardown(next);
});
}
);
});
});
});

Wyświetl plik

@ -91,8 +91,10 @@ function main(argv, config, onLoaded) {
var configs = options.argv._;
if (!configs.length)
configs = [config || DEFAULT_CONFIG];
if (options.argv.exclude && !Array.isArray(options.argv.exclude.length))
options.argv.exclude = [options.argv.exclude];
var exclude = options.argv.exclude || [];
if (typeof exclude == "string")
exclude = exclude.split(",");
var expanded = expandShortCuts(configs);
@ -120,7 +122,7 @@ function main(argv, config, onLoaded) {
function startConfigs(configs, done) {
async.each(configs, function(config, next) {
if (options.argv.exclude && options.argv.exclude.indexOf(config) > -1)
if (exclude && exclude.indexOf(config) > -1)
return next();
start(config, options, function(err, result, path) {
onLoaded && onLoaded(err, result, path);