endo-core
Version:
Put some description here
336 lines (293 loc) • 11.4 kB
text/coffeescript
{afterEach, beforeEach, describe, it} = global
{expect} = require 'chai'
sinon = require 'sinon'
fs = require 'fs'
request = require 'request'
Encryption = require 'meshblu-encryption'
enableDestroy = require 'server-destroy'
shmock = require 'shmock'
MockStrategy = require '../mock-strategy'
Server = require '../..'
describe 'v2 messages', ->
beforeEach (done) ->
@serviceAuth = new Buffer('peter:i-could-eat').toString 'base64'
@privateKey = fs.readFileSync "#{__dirname}/../data/private-key.pem", 'utf8'
@HTTP_SIGNATURE_OPTIONS =
keyId: 'meshblu-webhook-key'
key: @privateKey
headers: [ 'date', 'X-MESHBLU-UUID' ]
@encryption = Encryption.fromPem @privateKey
@publicKey = @encryption.key.exportKey 'public'
@meshblu = shmock 0xd00d
enableDestroy @meshblu
@apiStrategy = new MockStrategy name: 'api'
@octobluStrategy = new MockStrategy name: 'octoblu'
@messageHandler = onMessage: sinon.stub()
@meshblu
.get 'publickey'
.reply 200, @privateKey
@meshblu
.get '/v2/whoami'
.set 'Authorization', "Basic #{@serviceAuth}"
.reply 200, {
options:
imageUrl: "http://this-is-an-image.exe"
}
@meshblu
.get '/publickey'
.reply 200, {@publicKey}
serverOptions =
logFn: ->
port: undefined,
disableLogging: true
apiStrategy: @apiStrategy
octobluStrategy: @octobluStrategy
messageHandler: @messageHandler
serviceUrl: 'http://octoblu.xxx'
deviceType: 'endo-endor'
meshbluConfig:
hostname: 'localhost'
protocol: 'http'
port: 0xd00d
uuid: 'peter'
token: 'i-could-eat'
privateKey: @privateKey
appOctobluHost: 'http://app.octoblu.mom'
userDeviceManagerUrl: 'http://manage-my.endo'
meshbluPublicKeyUri: 'http://localhost:53261/publickey'
@server = new Server serverOptions
@server.run (error) =>
return done error if error?
@serverPort = @server.address().port
done()
afterEach (done) ->
@server.stop done
afterEach (done) ->
@meshblu.destroy done
describe 'On POST /v2/messages', ->
describe 'when not signed by meshblu', ->
beforeEach (done) ->
options =
baseUrl: "http://localhost:#{@serverPort}"
headers:
'x-meshblu-uuid': 'peter'
'x-meshblu-route': JSON.stringify [
{"from": "flow-uuid", "to": "user-uuid", "type": "message.sent"}
{"from": "user-uuid", "to": "cred-uuid", "type": "message.received"}
{"from": "cred-uuid", "to": "cred-uuid", "type": "message.received"}
{"from": "cred-uuid", "to": "peter", "type": "message.received"}
]
json:
metadata:
jobType: 'hello'
data:
greeting: 'hola'
auth:
username: 'cred-uuid'
password: 'cred-token'
request.post '/v2/messages', options, (error, @response) =>
done error
it "should return a 401, because the signature doesn't exist", ->
expect(@response.statusCode).to.equal 401
describe 'when signed by meshblu, but for the wrong uuid', ->
beforeEach (done) ->
options =
httpSignature: @HTTP_SIGNATURE_OPTIONS
baseUrl: "http://localhost:#{@serverPort}"
headers:
'x-meshblu-uuid': 'Pumpkin Eater'
'x-meshblu-route': JSON.stringify [
{"from": "flow-uuid", "to": "user-uuid", "type": "message.sent"}
{"from": "user-uuid", "to": "cred-uuid", "type": "message.received"}
{"from": "cred-uuid", "to": "cred-uuid", "type": "message.received"}
{"from": "cred-uuid", "to": "peter", "type": "message.received"}
]
json:
metadata:
jobType: 'hello'
data:
greeting: 'hola'
request.post '/v2/messages', options, (error, @response) =>
done error
it "should return a 401, because that webhook is for someone else", ->
expect(@response.statusCode).to.equal 401
describe 'when signed by meshblu, for the service', ->
beforeEach 'requestOptions', ->
@requestOptions =
httpSignature: @HTTP_SIGNATURE_OPTIONS
baseUrl: "http://localhost:#{@serverPort}"
headers:
'x-meshblu-uuid': 'peter'
'x-meshblu-route': JSON.stringify [
{"from": "flow-uuid", "to": "user-uuid", "type": "message.sent"}
{"from": "user-uuid", "to": "cred-uuid", "type": "message.received"}
{"from": "cred-uuid", "to": "cred-uuid", "type": "message.received"}
{"from": "cred-uuid", "to": "peter", "type": "message.received"}
]
json:
metadata:
jobType: 'hello'
respondTo: foo: 'bar'
data:
greeting: 'hola'
describe "but the credentials device doesn't have an encrypted token for the service", ->
beforeEach 'credentials-device', ->
unencrypted =
secrets:
credentials:
secret: 'this is secret'
endo =
authorizedKey: 'some-uuid'
credentialsDeviceUuid: 'cred-uuid'
encrypted: @encryption.encrypt unencrypted
endoSignature = @encryption.sign {
authorizedKey: 'some-uuid'
credentialsDeviceUuid: 'cred-uuid'
encrypted: unencrypted
}
@meshblu
.post '/search/devices'
.set 'Authorization', "Basic #{@serviceAuth}"
.set 'x-meshblu-as', 'cred-uuid'
.send uuid: 'cred-uuid'
.reply 200, [
uuid: 'cred-uuid'
endoSignature: endoSignature
endo: endo
]
beforeEach (done) ->
request.post '/v2/messages', @requestOptions, (error, @response) =>
done error
it "should return a 422, because the credentials device is misconfigured", ->
expect(@response.statusCode).to.equal 422
describe "and the credentials device has an encrypted token", ->
beforeEach 'credentials-device', ->
unencrypted =
secrets:
credentialsDeviceToken: 'cred-token'
credentials:
secret: 'this is secret'
endo =
authorizedKey: 'some-uuid'
credentialsDeviceUuid: 'cred-uuid'
encrypted: @encryption.encrypt unencrypted
endoSignature = @encryption.sign {
authorizedKey: 'some-uuid'
credentialsDeviceUuid: 'cred-uuid'
encrypted: unencrypted
}
@meshblu
.post '/search/devices'
.set 'Authorization', "Basic #{@serviceAuth}"
.set 'x-meshblu-as', 'cred-uuid'
.send uuid: 'cred-uuid'
.reply 200, [
uuid: 'cred-uuid'
endoSignature: endoSignature
endo: endo
]
beforeEach (done) ->
credentialsDeviceAuth = new Buffer('cred-uuid:cred-token').toString 'base64'
@messageHandler.onMessage.yields null, metadata: {code: 200}, data: {whatever: 'this is a response'}
@responseHandler = @meshblu
.post '/messages'
.set 'Authorization', "Basic #{credentialsDeviceAuth}"
.set 'x-meshblu-as', 'user-uuid'
.send
devices: ['flow-uuid']
metadata:
code: 200
to: { foo: 'bar' }
data:
whatever: 'this is a response'
.reply 201
request.post '/v2/messages', @requestOptions, (error, @response) => done error
it 'should return a 201', ->
expect(@response.statusCode).to.equal 201
it 'should respond to the message via meshblu', ->
@responseHandler.done()
it 'should call the hello messageHandler with the message and auth', ->
expect(@messageHandler.onMessage).to.have.been.calledWith {
encrypted:
secrets:
credentials:
secret: 'this is secret'
metadata:
jobType: 'hello'
respondTo: foo: 'bar'
data:
greeting: 'hola'
}
describe "and we can't discover.as the credentials device", ->
beforeEach 'credentials-device', ->
unencrypted =
secrets:
credentialsDeviceToken: 'cred-token'
credentials:
secret: 'this is secret'
endo =
authorizedKey: 'some-uuid'
credentialsDeviceUuid: 'cred-uuid'
encrypted: @encryption.encrypt unencrypted
endoSignature = @encryption.sign {
authorizedKey: 'some-uuid'
credentialsDeviceUuid: 'cred-uuid'
encrypted: unencrypted
}
@meshblu
.post '/search/devices'
.set 'Authorization', "Basic #{@serviceAuth}"
.set 'x-meshblu-as', 'cred-uuid'
.send uuid: 'cred-uuid'
.reply 403
.on 'done', =>
@meshblu
.post '/search/devices'
.set 'Authorization', "Basic #{@serviceAuth}"
.set 'x-meshblu-as', 'cred-uuid'
.send uuid: 'cred-uuid'
.reply 200, [
uuid: 'cred-uuid'
endoSignature: endoSignature
endo: endo
]
@meshblu
.get '/v2/devices/cred-uuid'
.set 'Authorization', "Basic #{@serviceAuth}"
.set 'x-meshblu-as', 'cred-uuid'
.reply 403
.on 'done', =>
@meshblu
.get '/v2/devices/cred-uuid'
.set 'Authorization', "Basic #{@serviceAuth}"
.set 'x-meshblu-as', 'cred-uuid'
.reply 200,
uuid: 'cred-uuid'
endoSignature: endoSignature
endo: endo
@updateCredentialsDevice =
@meshblu
.put '/v2/devices/cred-uuid'
.set 'Authorization', "Basic #{@serviceAuth}"
.send
$addToSet:
"meshblu.whitelists.discover.as": {uuid: 'peter'}
.reply 204
beforeEach (done) ->
credentialsDeviceAuth = new Buffer('cred-uuid:cred-token').toString 'base64'
@messageHandler.onMessage.yields null, metadata: {code: 200}, data: {whatever: 'this is a response'}
@responseHandler = @meshblu
.post '/messages'
.set 'Authorization', "Basic #{credentialsDeviceAuth}"
.set 'x-meshblu-as', 'user-uuid'
.send
devices: ['flow-uuid']
metadata:
code: 200
to: { foo: 'bar' }
data:
whatever: 'this is a response'
.reply 201
request.post '/v2/messages', @requestOptions, (error, @response) => done error
it 'should update the credentials device discover.as whitelist', ->
@updateCredentialsDevice.done()