Skip to content

Instantly share code, notes, and snippets.

@mashihua
Created June 29, 2011 02:24
Show Gist options
  • Save mashihua/1052830 to your computer and use it in GitHub Desktop.
Save mashihua/1052830 to your computer and use it in GitHub Desktop.
Cloud9 IED http_digest_auth Patch
Add http_digest_auth for Cloud9 IED.
Step:
1.git clone https://github.com/ajaxorg/cloud9.git
2.git submodule update --init --recursive
3.git apply cloud9.patch
4.bin/cloud9.sh -c config.js
5.open the url http://127.0.0.1:3000/
6.when prompt the authorization,username is admin and password is admin.
Modify login.user and login.pwd in config.js to your own authorization.Enjoy it.
diff --git a/config.js b/config.js
index 65cc622..028b386 100644
--- a/config.js
+++ b/config.js
@@ -1,5 +1,10 @@
exports.Config = {
workspace: ".",
ip: "127.0.0.1",
- port: 3000
+ port: 3000,
+ login : {
+ "user":"admin",
+ "pwd":"admin"
+ },
+ baseUrl : ''
};
\ No newline at end of file
diff --git a/server/cloud9/index.js b/server/cloud9/index.js
index 73b4632..2633008 100644
--- a/server/cloud9/index.js
+++ b/server/cloud9/index.js
@@ -59,6 +59,29 @@ exports.main = function(options) {
};
};
+ var httpdigest = require('http-digest');
+ var Url = require("url");
+ var indexRe = new RegExp("^"+(options.baseUrl || "").replace(/\/+$/, "")+"(?:\\/(?:index.html?)?)?$")
+ var users = {};
+ var httpAuth = function(){
+ return function(req, res, next){
+ if(users[req.sessionID]){
+ return next();
+ }
+ var path = Url.parse(req.url).pathname,
+ login = options.login || {};
+
+ if (path.match(indexRe)) {
+ httpdigest.http_digest_auth(req, res, login.user || '' , login.pwd || '', function(req, res){
+ users[req.sessionID] = true;
+ return next();
+ });
+ }else{
+ return next();
+ }
+ }
+ }
+
var server = Connect.createServer();
//server.use(Connect.logger());
server.use(Connect.conditionalGet());
@@ -66,6 +89,7 @@ exports.main = function(options) {
server.use(Connect.session({
key: "cloud9.sid"
}));
+ server.use(httpAuth());
server.use(ideProvider(projectDir, server));
server.use(middleware.staticProvider(Path.normalize(__dirname + "/../../support"), "/static/support"));
server.use(middleware.staticProvider(Path.normalize(__dirname + "/../../client"), "/static"));
diff --git a/support/http-digest.js b/support/http-digest.js
new file mode 100644
index 0000000..3a120b0
--- /dev/null
+++ b/support/http-digest.js
@@ -0,0 +1,191 @@
+//"use strict";
+var http = require('http');
+var crypto = require('crypto');
+
+/* constants */
+const nonce_expire_timeout = 3600000; /* server nonce expiration timeout (milliseconds) */
+const authentication_realm = "Cloud IDE Authenticate"; /* http authentication realm */
+
+/* state information */
+var nonces = {};
+
+/* nonce expiration function, to be called with setTimeout */
+function expire_nonce(nonce) {
+ delete nonces[nonce];
+}
+
+/* md5 hash function */
+function md5(str) {
+ var hash = crypto.createHash("MD5");
+ hash.update(str);
+ return hash.digest("hex");
+}
+
+/* parse the Authorization header, return a dictionary or false if an invalid header is given */
+function parse_header_string(header) {
+ var authtype = header.match(/^(\w+)\s+/);
+ if (authtype === null) {
+ return false;
+ }
+ if (authtype[1].toLowerCase() != "digest") {
+ // We currently don't support any other auth methods.
+ return false;
+ }
+ header = header.slice(authtype[1].length);
+
+ var dict = {};
+ var first = true;
+ while (header.length > 0) {
+ // eat whitespace and comma
+ if (first) {
+ first = false;
+ } else {
+ if (header[0] != ",") {
+ return false;
+ }
+ header = header.slice(1);
+ }
+ header = header.trimLeft();
+
+ // parse key
+ var key = header.match(/^\w+/);
+ if (key === null) {
+ return false;
+ }
+ key = key[0];
+ header = header.slice(key.length);
+
+ // parse equals
+ var eq = header.match(/^\s*=\s*/);
+ if (eq === null) {
+ return false;
+ }
+ header = header.slice(eq[0].length);
+
+ // parse value
+ var value;
+ if (header[0] == "\"") {
+ // quoted string
+ value = header.match(/^"([^"\\\r\n]*(?:\\.[^"\\\r\n]*)*)"/);
+ if (value === null) {
+ return false;
+ }
+ header = header.slice(value[0].length);
+ value = value[1];
+ } else {
+ // unquoted string
+ value = header.match(/^[^\s,]+/);
+ if (value === null) {
+ return false;
+ }
+ header = header.slice(value[0].length);
+ value = value[0];
+ }
+ dict[key] = value;
+
+ // eat whitespace
+ header = header.trimLeft();
+ }
+ return dict;
+}
+
+function authenticate(request, header, username, password) {
+ var authinfo = parse_header_string(header);
+ if (authinfo === false) {
+ // TODO: handle bad requests
+ return false;
+ }
+
+ // check for expiration
+ if (!(authinfo.nonce in nonces)) {
+ return false;
+ }
+
+ // calculate a1
+ var a1;
+ if (authinfo.algorithm == "MD5-sess") {
+ // TODO: implement MD5-sess
+ return false;
+ } else {
+ if (authinfo.username != username) {
+ // TODO: We currently only support a single username/password tuple
+ return false;
+ }
+ a1 = authinfo.username+":"+authentication_realm+":"+password;
+ }
+
+ // calculate a2
+ var a2;
+ if (authinfo.qop == "auth-int") {
+ // TODO: implement auth-int
+ return false;
+ } else {
+ a2 = request.method+":"+authinfo.uri;
+ }
+
+ // calculate request digest
+ var digest;
+ if (!("qop" in authinfo)) {
+ // For RFC 2069 compatibility
+ digest = md5(md5(a1)+":"+authinfo.nonce+":"+md5(a2));
+ } else {
+ if (authinfo.nc <= nonces[authinfo.nonce].count) {
+ return false;
+ }
+ nonces[authinfo.nonce].count = authinfo.nc;
+ digest = md5(md5(a1)+":"+authinfo.nonce+":"+authinfo.nc+":"+authinfo.cnonce+":"+authinfo.qop+":"+md5(a2));
+ }
+
+ if (digest == authinfo.response) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/**
+ * The http_digest_auth function authenticates the current http request and
+ * executes the callback if the user is authenticated successfully. Otherwise
+ * a 401 Unauthorized page is presented.
+ * The callback is of type function(request, response).
+ */
+var http_digest_auth = exports.http_digest_auth = function(request, response, username, password, callback) {
+ var authenticated = false;
+ if ("authorization" in request.headers) {
+ var header = request.headers.authorization;
+ authenticated = authenticate(request, header, username, password);
+ }
+ if (authenticated) {
+ callback(request, response);
+ } else {
+ // generate nonce
+ var nonce = md5(new Date().getTime()+"privstring");
+
+ nonces[nonce] = {
+ count: 0,
+ };
+ setTimeout(expire_nonce,nonce_expire_timeout,nonce);
+
+ // generate opaque (TODO: move outside)
+ var opaque = md5("hostname or something");
+
+ // create header
+ var header = "Digest realm=\""+authentication_realm+"\", qop=\"auth\", nonce=\""+nonce+"\", opaque=\""+opaque+"\"";
+ response.writeHead(401, {"WWW-Authenticate": header});
+ response.end("<!DOCTYPE html>\n<html><head><title>401 Unauthorized</title></head><body><h1>401 Unauthorized</h1><p>This page requires authorization.</p></body></html>");
+ }
+}
+
+/**
+ * The createServer method creates a web server using HTTP Digest
+ * authentication. Usage of this function is similar to the http.createServer
+ * function of the node.js standard library, but with additional parameters for
+ * the username and password.
+ * The callback is of type function(request, response).
+ */
+exports.createServer = function(username, password, callback) {
+ this.server = http.createServer(function(request, response) {
+ http_digest_auth(request, response, username, password, callback);
+ });
+ return this.server;
+};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment