kopia lustrzana https://github.com/c9/core
Merge pull request +14025 from c9/ide-preview-sessions
IDE preview sessionspull/295/merge
commit
d1db51b557
|
@ -30,15 +30,6 @@ module.exports = function startup(options, imports, register) {
|
||||||
var sessionRoutes = connectModule();
|
var sessionRoutes = connectModule();
|
||||||
connect.useSession(sessionRoutes);
|
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));
|
sessionRoutes.use(Session(sessionOptions, cookie));
|
||||||
|
|
||||||
register(null, {
|
register(null, {
|
||||||
|
|
|
@ -17,6 +17,7 @@ define(function(require, exports, module) {
|
||||||
var https = require("https");
|
var https = require("https");
|
||||||
var http = require("http");
|
var http = require("http");
|
||||||
var mime = require("mime");
|
var mime = require("mime");
|
||||||
|
var Cache = require("c9/cache");
|
||||||
var metrics = imports.metrics;
|
var metrics = imports.metrics;
|
||||||
var parseUrl = require("url").parse;
|
var parseUrl = require("url").parse;
|
||||||
var debug = require("debug")("preview");
|
var debug = require("debug")("preview");
|
||||||
|
@ -24,43 +25,25 @@ define(function(require, exports, module) {
|
||||||
|
|
||||||
var staticPrefix = imports["connect.static"].getStaticPrefix();
|
var staticPrefix = imports["connect.static"].getStaticPrefix();
|
||||||
|
|
||||||
function getProjectSession() {
|
function getRole(db) {
|
||||||
|
var roleCache = new Cache(10000, 10000);
|
||||||
|
|
||||||
return function(req, res, next) {
|
return function(req, res, next) {
|
||||||
var session = req.session;
|
if (!req.user) {
|
||||||
|
req.user = {
|
||||||
req.user = req.user || { id: -1 };
|
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
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
req.session = {};
|
||||||
next();
|
|
||||||
};
|
var key = req.params.username + "/" + req.params.projectname + ":" + req.user.id;
|
||||||
}
|
|
||||||
|
var wsSession = roleCache.get(key);
|
||||||
function getRole(db) {
|
if (wsSession) {
|
||||||
return function(req, res, next) {
|
req.session = wsSession;
|
||||||
if (req.projectSession.role) {
|
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
db.Project.findOne({
|
db.Project.findOne({
|
||||||
username: req.params.username,
|
username: req.params.username,
|
||||||
name: req.params.projectname
|
name: req.params.projectname
|
||||||
|
@ -70,25 +53,27 @@ define(function(require, exports, module) {
|
||||||
|
|
||||||
if (err) return next(err);
|
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);
|
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 (role == db.Project.ROLE_NONE) {
|
||||||
if (project.isPublicPreview())
|
if (!project.isPublicPreview())
|
||||||
role = db.Project.ROLE_VISITOR;
|
return next();
|
||||||
else if (req.user.id == -1)
|
|
||||||
return next(new error.Unauthorized());
|
wsSession.role = db.Project.ROLE_VISITOR;
|
||||||
else
|
|
||||||
return next(new error.Forbidden("You don't have access rights to preview this workspace"));
|
|
||||||
}
|
}
|
||||||
req.projectSession.role = role;
|
|
||||||
req.projectSession.pid = project.id;
|
|
||||||
req.projectSession.uid = req.user.id;
|
|
||||||
|
|
||||||
var type = project.scm;
|
if (wsSession.type != "docker" || project.state != db.Project.STATE_READY)
|
||||||
req.projectSession.type = type;
|
|
||||||
|
|
||||||
if (type != "docker" || project.state != db.Project.STATE_READY)
|
|
||||||
return next();
|
return next();
|
||||||
|
|
||||||
project.populate("remote", function(err) {
|
project.populate("remote", function(err) {
|
||||||
|
@ -100,10 +85,8 @@ define(function(require, exports, module) {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
|
|
||||||
if (container.state == db.Container.STATE_RUNNING)
|
if (container.state == db.Container.STATE_RUNNING)
|
||||||
req.projectSession.proxyUrl = "http://" + meta.host + ":9000/" + meta.cid + "/home/ubuntu/workspace";
|
wsSession.proxyUrl = "http://" + meta.host + ":9000/" + meta.cid + "/home/ubuntu/workspace";
|
||||||
else
|
|
||||||
req.projectSession.expires = Date.now() + 1000;
|
|
||||||
|
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
} else {
|
} 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) {
|
function getProxyUrl(getServer) {
|
||||||
return function(req, res, next) {
|
return function(req, res, next) {
|
||||||
|
|
||||||
if (req.projectSession.proxyUrl) {
|
if (req.session.proxyUrl) {
|
||||||
req.proxyUrl = req.projectSession.proxyUrl;
|
req.proxyUrl = req.session.proxyUrl;
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
var server = req.projectSession.vfsServer;
|
var server = req.session.vfsServer;
|
||||||
if (!server) {
|
if (!server) {
|
||||||
server = getServer();
|
server = getServer();
|
||||||
if (!server || !server.url)
|
if (!server || !server.url)
|
||||||
return next(new error.ServiceUnavailable("No VFS server found"));
|
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;
|
req.proxyUrl = url;
|
||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
|
@ -222,9 +220,6 @@ define(function(require, exports, module) {
|
||||||
} else if (body.indexOf("ENOENT") !== -1 || statusCode == 404) {
|
} else if (body.indexOf("ENOENT") !== -1 || statusCode == 404) {
|
||||||
next(new error.NotFound("File '" + path + "' could not be found!"));
|
next(new error.NotFound("File '" + path + "' could not be found!"));
|
||||||
} else {
|
} else {
|
||||||
if (req.session.ws)
|
|
||||||
delete req.session.ws[req.ws];
|
|
||||||
|
|
||||||
var json;
|
var json;
|
||||||
try {
|
try {
|
||||||
json = JSON.parse(body);
|
json = JSON.parse(body);
|
||||||
|
@ -365,8 +360,8 @@ define(function(require, exports, module) {
|
||||||
|
|
||||||
register(null, {
|
register(null, {
|
||||||
"preview.handler": {
|
"preview.handler": {
|
||||||
getProjectSession: getProjectSession,
|
|
||||||
getRole: getRole,
|
getRole: getRole,
|
||||||
|
checkRole: checkRole,
|
||||||
getProxyUrl: getProxyUrl,
|
getProxyUrl: getProxyUrl,
|
||||||
proxyCall: proxyCall
|
proxyCall: proxyCall
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ define(function(require, exports, module) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
main.consumes = [
|
main.consumes = [
|
||||||
"session",
|
"connect",
|
||||||
"db",
|
"db",
|
||||||
"c9.login",
|
"c9.login",
|
||||||
"preview.handler",
|
"preview.handler",
|
||||||
|
@ -14,13 +14,12 @@ define(function(require, exports, module) {
|
||||||
return main;
|
return main;
|
||||||
|
|
||||||
function main(options, imports, register) {
|
function main(options, imports, register) {
|
||||||
var session = imports.session;
|
var connect = imports.connect;
|
||||||
var db = imports.db;
|
var db = imports.db;
|
||||||
var ensureLoggedIn = imports["c9.login"].ensureLoggedIn();
|
var ensureLoggedIn = imports["c9.login"].ensureLoggedIn();
|
||||||
var handler = imports["preview.handler"];
|
var handler = imports["preview.handler"];
|
||||||
var userContent = imports["user-content.redirect"];
|
var userContent = imports["user-content.redirect"];
|
||||||
var getVfsServers = imports["vfs.serverlist"].getServers;
|
var getVfsServers = imports["vfs.serverlist"].getServers;
|
||||||
var ratelimit = require("c9/ratelimit");
|
|
||||||
|
|
||||||
var frontdoor = require("frontdoor");
|
var frontdoor = require("frontdoor");
|
||||||
var error = require("http-error");
|
var error = require("http-error");
|
||||||
|
@ -38,7 +37,7 @@ define(function(require, exports, module) {
|
||||||
|
|
||||||
api.use(userContent.redirectPreview());
|
api.use(userContent.redirectPreview());
|
||||||
|
|
||||||
session.use(api);
|
connect.use(api);
|
||||||
|
|
||||||
api.get("/:username/:projectname/:path*", {
|
api.get("/:username/:projectname/:path*", {
|
||||||
params: {
|
params: {
|
||||||
|
@ -53,8 +52,8 @@ define(function(require, exports, module) {
|
||||||
requestTimeout(15*60*1000),
|
requestTimeout(15*60*1000),
|
||||||
require("./lib/middleware/sanitize-path-param"),
|
require("./lib/middleware/sanitize-path-param"),
|
||||||
require("./lib/middleware/block-dot-files"),
|
require("./lib/middleware/block-dot-files"),
|
||||||
handler.getProjectSession(),
|
|
||||||
handler.getRole(db),
|
handler.getRole(db),
|
||||||
|
handler.checkRole(db),
|
||||||
handler.getProxyUrl(function() {
|
handler.getProxyUrl(function() {
|
||||||
return getVfsServers()[0] || null;
|
return getVfsServers()[0] || null;
|
||||||
}),
|
}),
|
||||||
|
@ -63,8 +62,6 @@ define(function(require, exports, module) {
|
||||||
|
|
||||||
api.error(function(err, req, res, next) {
|
api.error(function(err, req, res, next) {
|
||||||
if (err instanceof error.Unauthorized) {
|
if (err instanceof error.Unauthorized) {
|
||||||
req.logout();
|
|
||||||
delete req.session.token;
|
|
||||||
return ensureLoggedIn(req, res, next);
|
return ensureLoggedIn(req, res, next);
|
||||||
}
|
}
|
||||||
return next(err);
|
return next(err);
|
||||||
|
|
|
@ -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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -91,8 +91,10 @@ function main(argv, config, onLoaded) {
|
||||||
var configs = options.argv._;
|
var configs = options.argv._;
|
||||||
if (!configs.length)
|
if (!configs.length)
|
||||||
configs = [config || DEFAULT_CONFIG];
|
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);
|
var expanded = expandShortCuts(configs);
|
||||||
|
|
||||||
|
@ -120,7 +122,7 @@ function main(argv, config, onLoaded) {
|
||||||
|
|
||||||
function startConfigs(configs, done) {
|
function startConfigs(configs, done) {
|
||||||
async.each(configs, function(config, next) {
|
async.each(configs, function(config, next) {
|
||||||
if (options.argv.exclude && options.argv.exclude.indexOf(config) > -1)
|
if (exclude && exclude.indexOf(config) > -1)
|
||||||
return next();
|
return next();
|
||||||
start(config, options, function(err, result, path) {
|
start(config, options, function(err, result, path) {
|
||||||
onLoaded && onLoaded(err, result, path);
|
onLoaded && onLoaded(err, result, path);
|
||||||
|
|
Ładowanie…
Reference in New Issue