From fc3c0d1384b49d3149ae3b63d308d87130eb1b3a Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Fri, 25 Mar 2016 11:04:46 +0000 Subject: [PATCH] init (copied from thirdpartydatastore-sharelatex) --- libraries/access-token-encryptor/.gitignore | 62 ++++++++++++++++ .../access-token-encryptor/Gruntfile.coffee | 56 +++++++++++++++ libraries/access-token-encryptor/index.js | 2 + .../lib/coffee/AccessTokenEncryptor.coffee | 44 ++++++++++++ libraries/access-token-encryptor/package.json | 28 ++++++++ .../coffee/AccessTokenEncryptorTests.coffee | 70 +++++++++++++++++++ 6 files changed, 262 insertions(+) create mode 100644 libraries/access-token-encryptor/.gitignore create mode 100644 libraries/access-token-encryptor/Gruntfile.coffee create mode 100644 libraries/access-token-encryptor/index.js create mode 100644 libraries/access-token-encryptor/lib/coffee/AccessTokenEncryptor.coffee create mode 100644 libraries/access-token-encryptor/package.json create mode 100644 libraries/access-token-encryptor/test/unit/coffee/AccessTokenEncryptorTests.coffee diff --git a/libraries/access-token-encryptor/.gitignore b/libraries/access-token-encryptor/.gitignore new file mode 100644 index 0000000000..e9154dde4a --- /dev/null +++ b/libraries/access-token-encryptor/.gitignore @@ -0,0 +1,62 @@ +compileFolder + +Compiled source # +################### +*.com +*.class +*.dll +*.exe +*.o +*.so + +# Packages # +############ +# it's better to unpack these files and commit the raw source +# git has its own built in compression methods +*.7z +*.dmg +*.gz +*.iso +*.jar +*.rar +*.tar +*.zip + +# Logs and databases # +###################### +*.log +*.sql +*.sqlite + +# OS generated files # +###################### +.DS_Store? +ehthumbs.db +Icon? +Thumbs.db + +/node_modules/* +test/IntergrationTests/js/* +data/*/* + +app.js +app/js/* +lib/js/* +test/IntergrationTests/js/* +test/UnitTests/js/* +cookies.txt +uploads/* +public/js/editor.js +public/js/home.js +public/js/forms.js +public/js/gui.js +public/js/admin.js +public/stylesheets/mainStyle.css +public/minjs/ +test/unit/js/ +test/acceptance/js + +**.swp + +/log.json +hash_folder diff --git a/libraries/access-token-encryptor/Gruntfile.coffee b/libraries/access-token-encryptor/Gruntfile.coffee new file mode 100644 index 0000000000..ff5a6f71fa --- /dev/null +++ b/libraries/access-token-encryptor/Gruntfile.coffee @@ -0,0 +1,56 @@ +spawn = require("child_process").spawn + +module.exports = (grunt) -> + grunt.initConfig + coffee: + # app_src: + # expand: true, + # cwd: "app/coffee" + # src: ['**/*.coffee'], + # dest: 'app/js/', + # ext: '.js' + + # app: + # src: "app.coffee" + # dest: "app.js" + + unit_tests: + expand: true + cwd: "test/unit/coffee" + src: ["**/*.coffee"] + dest: "test/unit/js/" + ext: ".js" + + # acceptance_tests: + # expand: true + # cwd: "test/acceptance/coffee" + # src: ["**/*.coffee"] + # dest: "test/acceptance/js/" + # ext: ".js" + + clean: + app: ["lib/js/"] + unit_tests: ["test/unit/js"] + + mochaTest: + unit: + options: + reporter: grunt.option('reporter') or 'spec' + grep: grunt.option("grep") + src: ["test/unit/js/**/*.js"] + + grunt.loadNpmTasks 'grunt-contrib-coffee' + grunt.loadNpmTasks 'grunt-contrib-clean' + grunt.loadNpmTasks 'grunt-mocha-test' + grunt.loadNpmTasks 'grunt-execute' + grunt.loadNpmTasks 'grunt-bunyan' + + grunt.registerTask 'compile:unit_tests', ['clean:unit_tests', 'coffee:unit_tests'] + grunt.registerTask 'test:unit', ['compile:unit_tests', 'mochaTest:unit'] + + grunt.registerTask 'compile:acceptance_tests', ['clean:acceptance_tests', 'coffee:acceptance_tests'] + grunt.registerTask 'test:acceptance', ['compile:acceptance_tests', 'mochaTest:acceptance'] + + grunt.registerTask 'install', 'compile:app' + + grunt.registerTask 'default', ['run'] diff --git a/libraries/access-token-encryptor/index.js b/libraries/access-token-encryptor/index.js new file mode 100644 index 0000000000..81838a9021 --- /dev/null +++ b/libraries/access-token-encryptor/index.js @@ -0,0 +1,2 @@ +require('coffee-script'); +module.exports = require('./lib/coffee/AccessTokenEncryptor.coffee'); diff --git a/libraries/access-token-encryptor/lib/coffee/AccessTokenEncryptor.coffee b/libraries/access-token-encryptor/lib/coffee/AccessTokenEncryptor.coffee new file mode 100644 index 0000000000..be7d4eb156 --- /dev/null +++ b/libraries/access-token-encryptor/lib/coffee/AccessTokenEncryptor.coffee @@ -0,0 +1,44 @@ +crypto = require('crypto') + +settings = require('settings-sharelatex') + +cipherLabel = settings.cipherLabel +throw Error("cipherLabel must not contain a colon (:)") if cipherLabel?.match(/:/) + +cipherPassword = settings.cipherPasswords[settings.cipherLabel] +throw Error("cipherPassword not set") if not cipherPassword? +throw Error("cipherPassword too short") if cipherPassword.length < 16 + +ALGORITHM = 'aes-256-ctr' + +keyFn = (password, salt, callback)-> + return crypto.pbkdf2(password, salt, 10000, 64, callback) + +module.exports = + + encryptJson: (json, callback)-> + string = JSON.stringify(json) + salt = crypto.randomBytes(16) + keyFn cipherPassword, salt, (err, key)-> + if err? + logger.err err:err, "error getting Fn key" + return callback(err) + cipher = crypto.createCipher(ALGORITHM, key) + crypted = cipher.update(string, 'utf8', 'base64') + cipher.final('base64') + callback(null, cipherLabel + ":" + salt.toString('hex') + ":" + crypted) + + decryptToJson: (encryptedJson, callback)-> + [label, salt, cipherText] = encryptedJson.split(':', 3) + password = settings.cipherPasswords[label] + return callback(new Error("invalid password")) if not password? or password.length < 16 + keyFn password, new Buffer(salt, 'hex'), (err, key)-> + if err? + logger.err err:err, "error getting Fn key" + return callback(err) + decipher = crypto.createDecipher(ALGORITHM, key) + dec = decipher.update(cipherText, 'base64', 'utf8') + decipher.final('utf8') + try + json = JSON.parse(dec) + catch e + return callback(new Error("error decrypting token")) + callback(null, json) diff --git a/libraries/access-token-encryptor/package.json b/libraries/access-token-encryptor/package.json new file mode 100644 index 0000000000..0bef22e7e6 --- /dev/null +++ b/libraries/access-token-encryptor/package.json @@ -0,0 +1,28 @@ +{ + "name": "access-token-encryptor-sharelatex", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "Proprietary", + "dependencies": { + "coffee-script": "^1.10.0", + "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#master" + }, + "devDependencies": { + "nock": "0.15.2", + "sandboxed-module": "", + "sinon": "", + "chai": "", + "grunt-contrib-clean": "~0.5.0", + "grunt-mocha-test": "~0.10.2", + "grunt-contrib-coffee": "~0.10.1", + "grunt": "~0.4.5", + "grunt-execute": "~0.2.1", + "bunyan": "~0.22.3", + "grunt-bunyan": "~0.5.0" + } +} diff --git a/libraries/access-token-encryptor/test/unit/coffee/AccessTokenEncryptorTests.coffee b/libraries/access-token-encryptor/test/unit/coffee/AccessTokenEncryptorTests.coffee new file mode 100644 index 0000000000..a206840084 --- /dev/null +++ b/libraries/access-token-encryptor/test/unit/coffee/AccessTokenEncryptorTests.coffee @@ -0,0 +1,70 @@ +sinon = require('sinon') +chai = require('chai') +should = chai.should() +expect = chai.expect +modulePath = "../../../index.js" +SandboxedModule = require('sandboxed-module') +path = require('path') + +describe 'AccessTokenEncryptor', -> + + beforeEach -> + @testObject = {"hello":"world"} + @Encrypted = "2016.1:6e7ac79ab13a18b5749eace965ec7962:sAAYt1yQZqpvOnu6l8iUD/Y=" + @oldEncrypted = "2015.1:473a66fb5d816bc716f278ab819d88a5:+mTg7O9sgUND8pNQFG6h2GE=" + @badLabel = "xxxxxx:c7a39310056b694c:jQf+Uh5Den3JREtvc82GW5Q=" + @badKey = "2015.1:d7a39310056b694c:jQf+Uh5Den3JREtvc82GW5Q=" + @badCipherText = "2015.1:c7a39310056b694c:xQf+Uh5Den3JREtvc82GW5Q=" + @requires = requires: + "settings-sharelatex": + cipherLabel: "2016.1" + cipherPasswords: + "2016.1": "11111111111111111111111111111111111111" + "2015.1": "22222222222222222222222222222222222222" + @AccessTokenEncryptor = SandboxedModule.require modulePath, @requires + + describe "encrypt", -> + it 'should encrypt the object', (done)-> + @AccessTokenEncryptor.encryptJson @testObject, (err, encrypted)-> + expect(err).to.be.null + encrypted.should.match(/^2016.1:[0-9a-f]+:[a-zA-Z0-9=+\/]+$/) + done() + + it 'should encrypt the object differently the next time', (done)-> + @AccessTokenEncryptor.encryptJson @testObject, (err, encrypted1)=> + @AccessTokenEncryptor.encryptJson @testObject, (err, encrypted2)=> + encrypted1.should.not.equal(encrypted2) + done() + + describe "decrypt", -> + it 'should decrypt the string to get the same object', (done)-> + @AccessTokenEncryptor.encryptJson @testObject, (err, encrypted) => + expect(err).to.be.null + @AccessTokenEncryptor.decryptToJson encrypted, (err, decrypted) => + expect(err).to.be.null + expect(decrypted).to.deep.equal @testObject + done() + + it 'should decrypt an old string to get the same object', (done)-> + @AccessTokenEncryptor.decryptToJson @oldEncrypted, (err, decrypted)=> + expect(err).to.be.null + expect(decrypted).to.deep.equal @testObject + done() + + it 'should return an error when decrypting an invalid label', (done)-> + @AccessTokenEncryptor.decryptToJson @badLabel, (err, decrypted)-> + expect(err).to.be.instanceof(Error) + expect(decrypted).to.be.undefined + done() + + it 'should return an error when decrypting an invalid key', (done)-> + @AccessTokenEncryptor.decryptToJson @badKey, (err, decrypted)-> + expect(err).to.be.instanceof(Error) + expect(decrypted).to.be.undefined + done() + + it 'should return an error when decrypting an invalid ciphertext',(done)-> + @AccessTokenEncryptor.decryptToJson @badCipherText, (err, decrypted)-> + expect(err).to.be.instanceof(Error) + expect(decrypted).to.be.undefined + done()