Merge pull request #1352 from jgunderson/ssl-script
[xtuple] / node-datasource / routes / generate_oauth_key.js
1 /*jshint node:true, indent:2, curly:false, eqeqeq:true, immed:true, latedef:true, newcap:true, noarg:true,
2 regexp:true, undef:true, strict:true, trailing:true, white:true */
3 /*global X:true, _:true, SYS:true */
4
5 // https://localhost/dev/oauth/generate-key?id=7
6
7 (function () {
8   "use strict";
9
10   var ursa = require("ursa"),
11     exec = require("child_process").exec,
12     spawn = require("child_process").spawn,
13     async = require("async"),
14     path = require("path"),
15     fs = require("fs");
16
17   /**
18     Fetch the requested oauth2client model, validate the request,
19     generate a keypair whose public key will be saved with the
20     model and whose private key is returned to the browser.
21    */
22   exports.generateKey = function (req, res) {
23     var clientModel = new SYS.Oauth2client(),
24       id = req.query.id,
25       // generic error function for both the fetch and the save
26       error = function (model, err) {
27         console.log("oauth2client error ", arguments);
28         res.send({isError: true, error: err});
29       },
30       convertToP12 = function (publicKey, privateKey) {
31         var filenamePrefix = path.join(process.cwd(), "lib/private/temp_" + id),
32           publicKeyFilename = filenamePrefix + "_public_key.pem",
33           csrFilename = filenamePrefix + "_csr.pem",
34           certFilename = filenamePrefix + "_cert.pem",
35           privateKeyFilename = filenamePrefix + "_private_key.pem",
36           p12Filename = filenamePrefix + ".p12",
37           attachmentFilename = publicKey.substring(0, publicKey.indexOf("-----END")) + ".p12",
38           csrExec = "openssl req -new -key %@ -out %@".f(privateKeyFilename, csrFilename),
39           p12contents;
40
41         async.series([
42           function (callback) { fs.writeFile(publicKeyFilename, publicKey, callback); },
43           function (callback) { fs.writeFile(privateKeyFilename, privateKey, callback); },
44           function (callback) {
45             var child = exec(csrExec, callback);
46             // blow through command-line questions
47             child.stdin.setEncoding = 'utf-8';
48             child.stdin.write("\n\n\n\n\n\n\n\n\n");
49             //child.stdin.write("US\nVirginia\nNorfolk\nxTuple\n\n\n\n\n");
50             child.stdin.end();
51           },
52           function (callback) {
53             var certSpawn = spawn("openssl",
54               ["x509", "-req", "-in", csrFilename, "-signkey", privateKeyFilename, "-out", certFilename]);
55             certSpawn.on('close', function (code) {
56               callback(null, code);
57             });
58           },
59           function (callback) {
60             var child = spawn("openssl",
61               ["pkcs12", "-export", "-in", certFilename, "-inkey", privateKeyFilename, "-out", p12Filename, "-password", "pass:notasecret"]);
62
63             child.on('close', function (code) {
64               callback(null, code);
65             });
66           },
67           function (callback) {
68             fs.readFile(p12Filename, function (err, contents) {
69               p12contents = contents;
70               callback(err, contents);
71             });
72           },
73           function (callback) { fs.unlink(publicKeyFilename, callback); },
74           function (callback) { fs.unlink(privateKeyFilename, callback); },
75           function (callback) { fs.unlink(csrFilename, callback); },
76           function (callback) { fs.unlink(certFilename, callback); },
77           function (callback) { fs.unlink(p12Filename, callback); }
78         ],
79         function (err, results) {
80           if (err) {
81             res.send({isError: true, message: "Error generating p12 key: " + err.message, error: err});
82             return;
83           }
84           res.attachment(attachmentFilename);
85           res.send(new Buffer(p12contents));
86         });
87       },
88       fetchSuccess = function (model, result) {
89         var keypair = ursa.generatePrivateKey(),
90           privateKey = keypair.toPrivatePem().toString(),
91           publicKey = keypair.toPublicPem().toString(),
92           saveSuccess = function (model, result) {
93             convertToP12(publicKey, privateKey);
94           };
95
96         // Cursory validation: this should be a jwt bearer and the
97         // public key field should not have already been set.
98         if (clientModel.get("clientType" !== "jwt bearer") ||
99             clientModel.get("clientX509PubCert")) {
100           res.send({isError: true, message: "Invalid request"});
101           return;
102         }
103
104         clientModel.set("clientX509PubCert", publicKey);
105         clientModel.save(null, {
106           error: error,
107           username: req.session.passport.user.username,
108           database: req.session.passport.user.organization,
109           success: saveSuccess
110         });
111       };
112
113     clientModel.fetch({
114       id: id,
115       username: req.session.passport.user.username,
116       database: req.session.passport.user.organization,
117       error: error,
118       success: fetchSuccess
119     });
120
121   };
122 }());