mirror of https://github.com/grpc/grpc.git
Merge pull request #3558 from murgatroid99/node_auth_plugin
Add Node Auth plugin codepull/3547/head
commit
77556c36bb
22 changed files with 1138 additions and 378 deletions
@ -0,0 +1,259 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#include <node.h> |
||||
|
||||
#include "grpc/grpc.h" |
||||
#include "grpc/grpc_security.h" |
||||
#include "grpc/support/log.h" |
||||
#include "call_credentials.h" |
||||
#include "call.h" |
||||
|
||||
namespace grpc { |
||||
namespace node { |
||||
|
||||
using Nan::Callback; |
||||
using Nan::EscapableHandleScope; |
||||
using Nan::HandleScope; |
||||
using Nan::Maybe; |
||||
using Nan::MaybeLocal; |
||||
using Nan::ObjectWrap; |
||||
using Nan::Persistent; |
||||
using Nan::Utf8String; |
||||
|
||||
using v8::Exception; |
||||
using v8::External; |
||||
using v8::Function; |
||||
using v8::FunctionTemplate; |
||||
using v8::Integer; |
||||
using v8::Local; |
||||
using v8::Object; |
||||
using v8::ObjectTemplate; |
||||
using v8::Value; |
||||
|
||||
Nan::Callback *CallCredentials::constructor; |
||||
Persistent<FunctionTemplate> CallCredentials::fun_tpl; |
||||
|
||||
CallCredentials::CallCredentials(grpc_credentials *credentials) |
||||
: wrapped_credentials(credentials) {} |
||||
|
||||
CallCredentials::~CallCredentials() { |
||||
grpc_credentials_release(wrapped_credentials); |
||||
} |
||||
|
||||
void CallCredentials::Init(Local<Object> exports) { |
||||
HandleScope scope; |
||||
Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New); |
||||
tpl->SetClassName(Nan::New("CallCredentials").ToLocalChecked()); |
||||
tpl->InstanceTemplate()->SetInternalFieldCount(1); |
||||
Nan::SetPrototypeMethod(tpl, "compose", Compose); |
||||
fun_tpl.Reset(tpl); |
||||
Local<Function> ctr = Nan::GetFunction(tpl).ToLocalChecked(); |
||||
Nan::Set(ctr, Nan::New("createFromPlugin").ToLocalChecked(), |
||||
Nan::GetFunction( |
||||
Nan::New<FunctionTemplate>(CreateFromPlugin)).ToLocalChecked()); |
||||
Nan::Set(exports, Nan::New("CallCredentials").ToLocalChecked(), ctr); |
||||
constructor = new Nan::Callback(ctr); |
||||
} |
||||
|
||||
bool CallCredentials::HasInstance(Local<Value> val) { |
||||
HandleScope scope; |
||||
return Nan::New(fun_tpl)->HasInstance(val); |
||||
} |
||||
|
||||
Local<Value> CallCredentials::WrapStruct(grpc_credentials *credentials) { |
||||
EscapableHandleScope scope; |
||||
const int argc = 1; |
||||
if (credentials == NULL) { |
||||
return scope.Escape(Nan::Null()); |
||||
} |
||||
Local<Value> argv[argc] = { |
||||
Nan::New<External>(reinterpret_cast<void *>(credentials))}; |
||||
MaybeLocal<Object> maybe_instance = Nan::NewInstance( |
||||
constructor->GetFunction(), argc, argv); |
||||
if (maybe_instance.IsEmpty()) { |
||||
return scope.Escape(Nan::Null()); |
||||
} else { |
||||
return scope.Escape(maybe_instance.ToLocalChecked()); |
||||
} |
||||
} |
||||
|
||||
grpc_credentials *CallCredentials::GetWrappedCredentials() { |
||||
return wrapped_credentials; |
||||
} |
||||
|
||||
NAN_METHOD(CallCredentials::New) { |
||||
if (info.IsConstructCall()) { |
||||
if (!info[0]->IsExternal()) { |
||||
return Nan::ThrowTypeError( |
||||
"CallCredentials can only be created with the provided functions"); |
||||
} |
||||
Local<External> ext = info[0].As<External>(); |
||||
grpc_credentials *creds_value = |
||||
reinterpret_cast<grpc_credentials *>(ext->Value()); |
||||
CallCredentials *credentials = new CallCredentials(creds_value); |
||||
credentials->Wrap(info.This()); |
||||
info.GetReturnValue().Set(info.This()); |
||||
return; |
||||
} else { |
||||
const int argc = 1; |
||||
Local<Value> argv[argc] = {info[0]}; |
||||
MaybeLocal<Object> maybe_instance = constructor->GetFunction()->NewInstance( |
||||
argc, argv); |
||||
if (maybe_instance.IsEmpty()) { |
||||
// There's probably a pending exception
|
||||
return; |
||||
} else { |
||||
info.GetReturnValue().Set(maybe_instance.ToLocalChecked()); |
||||
} |
||||
} |
||||
} |
||||
|
||||
NAN_METHOD(CallCredentials::Compose) { |
||||
if (!CallCredentials::HasInstance(info.This())) { |
||||
return Nan::ThrowTypeError( |
||||
"compose can only be called on CallCredentials objects"); |
||||
} |
||||
if (!CallCredentials::HasInstance(info[0])) { |
||||
return Nan::ThrowTypeError( |
||||
"compose's first argument must be a CallCredentials object"); |
||||
} |
||||
CallCredentials *self = ObjectWrap::Unwrap<CallCredentials>(info.This()); |
||||
CallCredentials *other = ObjectWrap::Unwrap<CallCredentials>( |
||||
Nan::To<Object>(info[0]).ToLocalChecked()); |
||||
grpc_credentials *creds = grpc_composite_credentials_create( |
||||
self->wrapped_credentials, other->wrapped_credentials, NULL); |
||||
info.GetReturnValue().Set(WrapStruct(creds)); |
||||
} |
||||
|
||||
|
||||
|
||||
NAN_METHOD(CallCredentials::CreateFromPlugin) { |
||||
if (!info[0]->IsFunction()) { |
||||
return Nan::ThrowTypeError( |
||||
"createFromPlugin's argument must be a function"); |
||||
} |
||||
grpc_metadata_credentials_plugin plugin; |
||||
plugin_state *state = new plugin_state; |
||||
state->callback = new Nan::Callback(info[0].As<Function>()); |
||||
plugin.get_metadata = plugin_get_metadata; |
||||
plugin.destroy = plugin_destroy_state; |
||||
plugin.state = reinterpret_cast<void*>(state); |
||||
grpc_credentials *creds = grpc_metadata_credentials_create_from_plugin(plugin, |
||||
NULL); |
||||
info.GetReturnValue().Set(WrapStruct(creds)); |
||||
} |
||||
|
||||
NAN_METHOD(PluginCallback) { |
||||
// Arguments: status code, error details, metadata
|
||||
if (!info[0]->IsUint32()) { |
||||
return Nan::ThrowTypeError( |
||||
"The callback's first argument must be a status code"); |
||||
} |
||||
if (!info[1]->IsString()) { |
||||
return Nan::ThrowTypeError( |
||||
"The callback's second argument must be a string"); |
||||
} |
||||
if (!info[2]->IsObject()) { |
||||
return Nan::ThrowTypeError( |
||||
"The callback's third argument must be an object"); |
||||
} |
||||
shared_ptr<Resources> resources(new Resources); |
||||
grpc_status_code code = static_cast<grpc_status_code>( |
||||
Nan::To<uint32_t>(info[0]).FromJust()); |
||||
char *details = *Utf8String(info[1]); |
||||
grpc_metadata_array array; |
||||
if (!CreateMetadataArray(Nan::To<Object>(info[2]).ToLocalChecked(), |
||||
&array, resources)){ |
||||
return Nan::ThrowError("Failed to parse metadata"); |
||||
} |
||||
grpc_credentials_plugin_metadata_cb cb = |
||||
reinterpret_cast<grpc_credentials_plugin_metadata_cb>( |
||||
Nan::Get(info.Callee(), |
||||
Nan::New("cb").ToLocalChecked() |
||||
).ToLocalChecked().As<External>()->Value()); |
||||
void *user_data = |
||||
Nan::Get(info.Callee(), |
||||
Nan::New("user_data").ToLocalChecked() |
||||
).ToLocalChecked().As<External>()->Value(); |
||||
cb(user_data, array.metadata, array.count, code, details); |
||||
} |
||||
|
||||
NAUV_WORK_CB(SendPluginCallback) { |
||||
Nan::HandleScope scope; |
||||
plugin_callback_data *data = reinterpret_cast<plugin_callback_data*>( |
||||
async->data); |
||||
// Attach cb and user_data to plugin_callback so that it can access them later
|
||||
v8::Local<v8::Function> plugin_callback = Nan::GetFunction( |
||||
Nan::New<v8::FunctionTemplate>(PluginCallback)).ToLocalChecked(); |
||||
Nan::Set(plugin_callback, Nan::New("cb").ToLocalChecked(), |
||||
Nan::New<v8::External>(reinterpret_cast<void*>(data->cb))); |
||||
Nan::Set(plugin_callback, Nan::New("user_data").ToLocalChecked(), |
||||
Nan::New<v8::External>(data->user_data)); |
||||
const int argc = 2; |
||||
v8::Local<v8::Value> argv[argc] = { |
||||
Nan::New(data->service_url).ToLocalChecked(), |
||||
plugin_callback |
||||
}; |
||||
Nan::Callback *callback = data->state->callback; |
||||
callback->Call(argc, argv); |
||||
delete data; |
||||
uv_unref((uv_handle_t *)async); |
||||
uv_close((uv_handle_t *)async, (uv_close_cb)free); |
||||
} |
||||
|
||||
void plugin_get_metadata(void *state, const char *service_url, |
||||
grpc_credentials_plugin_metadata_cb cb, |
||||
void *user_data) { |
||||
uv_async_t *async = static_cast<uv_async_t*>(malloc(sizeof(uv_async_t))); |
||||
uv_async_init(uv_default_loop(), |
||||
async, |
||||
SendPluginCallback); |
||||
plugin_callback_data *data = new plugin_callback_data; |
||||
data->state = reinterpret_cast<plugin_state*>(state); |
||||
data->service_url = service_url; |
||||
data->cb = cb; |
||||
data->user_data = user_data; |
||||
async->data = data; |
||||
/* libuv says that it will coalesce calls to uv_async_send. If there is ever a
|
||||
* problem with a callback not getting called, that is probably the reason */ |
||||
uv_async_send(async); |
||||
} |
||||
|
||||
void plugin_destroy_state(void *ptr) { |
||||
plugin_state *state = reinterpret_cast<plugin_state *>(ptr); |
||||
delete state->callback; |
||||
} |
||||
|
||||
} // namespace node
|
||||
} // namespace grpc
|
@ -0,0 +1,100 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef GRPC_NODE_CALL_CREDENTIALS_H_ |
||||
#define GRPC_NODE_CALL_CREDENTIALS_H_ |
||||
|
||||
#include <node.h> |
||||
#include <nan.h> |
||||
#include "grpc/grpc_security.h" |
||||
|
||||
namespace grpc { |
||||
namespace node { |
||||
|
||||
class CallCredentials : public Nan::ObjectWrap { |
||||
public: |
||||
static void Init(v8::Local<v8::Object> exports); |
||||
static bool HasInstance(v8::Local<v8::Value> val); |
||||
/* Wrap a grpc_credentials struct in a javascript object */ |
||||
static v8::Local<v8::Value> WrapStruct(grpc_credentials *credentials); |
||||
|
||||
/* Returns the grpc_credentials struct that this object wraps */ |
||||
grpc_credentials *GetWrappedCredentials(); |
||||
|
||||
private: |
||||
explicit CallCredentials(grpc_credentials *credentials); |
||||
~CallCredentials(); |
||||
|
||||
// Prevent copying
|
||||
CallCredentials(const CallCredentials &); |
||||
CallCredentials &operator=(const CallCredentials &); |
||||
|
||||
static NAN_METHOD(New); |
||||
static NAN_METHOD(CreateSsl); |
||||
static NAN_METHOD(CreateFromPlugin); |
||||
|
||||
static NAN_METHOD(Compose); |
||||
static Nan::Callback *constructor; |
||||
// Used for typechecking instances of this javascript class
|
||||
static Nan::Persistent<v8::FunctionTemplate> fun_tpl; |
||||
|
||||
grpc_credentials *wrapped_credentials; |
||||
}; |
||||
|
||||
/* Auth metadata plugin functionality */ |
||||
|
||||
typedef struct plugin_state { |
||||
Nan::Callback *callback; |
||||
} plugin_state; |
||||
|
||||
typedef struct plugin_callback_data { |
||||
plugin_state *state; |
||||
const char *service_url; |
||||
grpc_credentials_plugin_metadata_cb cb; |
||||
void *user_data; |
||||
} plugin_callback_data; |
||||
|
||||
void plugin_get_metadata(void *state, const char *service_url, |
||||
grpc_credentials_plugin_metadata_cb cb, |
||||
void *user_data); |
||||
|
||||
void plugin_destroy_state(void *state); |
||||
|
||||
NAN_METHOD(PluginCallback); |
||||
|
||||
NAUV_WORK_CB(SendPluginCallback); |
||||
|
||||
} // namespace node
|
||||
} // namepsace grpc
|
||||
|
||||
#endif // GRPC_NODE_CALL_CREDENTIALS_H_
|
@ -0,0 +1,163 @@ |
||||
/* |
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
/** |
||||
* Credentials module |
||||
* |
||||
* This module contains factory methods for two different credential types: |
||||
* CallCredentials and ChannelCredentials. ChannelCredentials are things like |
||||
* SSL credentials that can be used to secure a connection, and are used to |
||||
* construct a Client object. CallCredentials genrally modify metadata, so they |
||||
* can be attached to an individual method call. |
||||
* |
||||
* CallCredentials can be composed with other CallCredentials to create |
||||
* CallCredentials. ChannelCredentials can be composed with CallCredentials |
||||
* to create ChannelCredentials. No combined credential can have more than |
||||
* one ChannelCredentials. |
||||
* |
||||
* For example, to create a client secured with SSL that uses Google |
||||
* default application credentials to authenticate: |
||||
* |
||||
* var channel_creds = credentials.createSsl(root_certs); |
||||
* (new GoogleAuth()).getApplicationDefault(function(err, credential) { |
||||
* var call_creds = credentials.createFromGoogleCredential(credential); |
||||
* var combined_creds = credentials.combineChannelCredentials( |
||||
* channel_creds, call_creds); |
||||
* var client = new Client(address, combined_creds); |
||||
* }); |
||||
* |
||||
* @module |
||||
*/ |
||||
|
||||
'use strict'; |
||||
|
||||
var grpc = require('bindings')('grpc_node.node'); |
||||
|
||||
var CallCredentials = grpc.CallCredentials; |
||||
|
||||
var ChannelCredentials = grpc.ChannelCredentials; |
||||
|
||||
var Metadata = require('./metadata.js'); |
||||
|
||||
/** |
||||
* Create an SSL Credentials object. If using a client-side certificate, both |
||||
* the second and third arguments must be passed. |
||||
* @param {Buffer} root_certs The root certificate data |
||||
* @param {Buffer=} private_key The client certificate private key, if |
||||
* applicable |
||||
* @param {Buffer=} cert_chain The client certificate cert chain, if applicable |
||||
* @return {ChannelCredentials} The SSL Credentials object |
||||
*/ |
||||
exports.createSsl = ChannelCredentials.createSsl; |
||||
|
||||
/** |
||||
* Create a gRPC credentials object from a metadata generation function. This |
||||
* function gets the service URL and a callback as parameters. The error |
||||
* passed to the callback can optionally have a 'code' value attached to it, |
||||
* which corresponds to a status code that this library uses. |
||||
* @param {function(String, function(Error, Metadata))} metadata_generator The |
||||
* function that generates metadata |
||||
* @return {CallCredentials} The credentials object |
||||
*/ |
||||
exports.createFromMetadataGenerator = function(metadata_generator) { |
||||
return CallCredentials.createFromPlugin(function(service_url, callback) { |
||||
metadata_generator(service_url, function(error, metadata) { |
||||
var code = grpc.status.OK; |
||||
var message = ''; |
||||
if (error) { |
||||
message = error.message; |
||||
if (error.hasOwnProperty('code')) { |
||||
code = error.code; |
||||
} |
||||
} |
||||
callback(code, message, metadata._getCoreRepresentation()); |
||||
}); |
||||
}); |
||||
}; |
||||
|
||||
/** |
||||
* Create a gRPC credential from a Google credential object. |
||||
* @param {Object} google_credential The Google credential object to use |
||||
* @return {CallCredentials} The resulting credentials object |
||||
*/ |
||||
exports.createFromGoogleCredential = function(google_credential) { |
||||
return exports.createFromMetadataGenerator(function(service_url, callback) { |
||||
google_credential.getRequestMetadata(service_url, function(err, header) { |
||||
if (err) { |
||||
callback(err); |
||||
return; |
||||
} |
||||
var metadata = new Metadata(); |
||||
metadata.add('authorization', header.Authorization); |
||||
callback(null, metadata); |
||||
}); |
||||
}); |
||||
}; |
||||
|
||||
/** |
||||
* Combine a ChannelCredentials with any number of CallCredentials into a single |
||||
* ChannelCredentials object. |
||||
* @param {ChannelCredentials} channel_credential The ChannelCredentials to |
||||
* start with |
||||
* @param {...CallCredentials} credentials The CallCredentials to compose |
||||
* @return ChannelCredentials A credentials object that combines all of the |
||||
* input credentials |
||||
*/ |
||||
exports.combineChannelCredentials = function(channel_credential) { |
||||
var current = channel_credential; |
||||
for (var i = 1; i < arguments.length; i++) { |
||||
current = current.compose(arguments[i]); |
||||
} |
||||
return current; |
||||
}; |
||||
|
||||
/** |
||||
* Combine any number of CallCredentials into a single CallCredentials object |
||||
* @param {...CallCredentials} credentials the CallCredentials to compose |
||||
* @return CallCredentials A credentials object that combines all of the input |
||||
* credentials |
||||
*/ |
||||
exports.combineCallCredentials = function() { |
||||
var current = arguments[0]; |
||||
for (var i = 1; i < arguments.length; i++) { |
||||
current = current.compose(arguments[i]); |
||||
} |
||||
return current; |
||||
} |
||||
|
||||
/** |
||||
* Create an insecure credentials object. This is used to create a channel that |
||||
* does not use SSL. This cannot be composed with anything. |
||||
* @return {ChannelCredentials} The insecure credentials object |
||||
*/ |
||||
exports.createInsecure = ChannelCredentials.createInsecure; |
@ -0,0 +1,243 @@ |
||||
/* |
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
'use strict'; |
||||
|
||||
var assert = require('assert'); |
||||
var fs = require('fs'); |
||||
var path = require('path'); |
||||
|
||||
var grpc = require('..'); |
||||
|
||||
/** |
||||
* This is used for testing functions with multiple asynchronous calls that |
||||
* can happen in different orders. This should be passed the number of async |
||||
* function invocations that can occur last, and each of those should call this |
||||
* function's return value |
||||
* @param {function()} done The function that should be called when a test is |
||||
* complete. |
||||
* @param {number} count The number of calls to the resulting function if the |
||||
* test passes. |
||||
* @return {function()} The function that should be called at the end of each |
||||
* sequence of asynchronous functions. |
||||
*/ |
||||
function multiDone(done, count) { |
||||
return function() { |
||||
count -= 1; |
||||
if (count <= 0) { |
||||
done(); |
||||
} |
||||
}; |
||||
} |
||||
|
||||
describe('client credentials', function() { |
||||
var Client; |
||||
var server; |
||||
var port; |
||||
var client_ssl_creds; |
||||
var client_options = {}; |
||||
before(function() { |
||||
var proto = grpc.load(__dirname + '/test_service.proto'); |
||||
server = new grpc.Server(); |
||||
server.addProtoService(proto.TestService.service, { |
||||
unary: function(call, cb) { |
||||
call.sendMetadata(call.metadata); |
||||
cb(null, {}); |
||||
}, |
||||
clientStream: function(stream, cb){ |
||||
stream.on('data', function(data) {}); |
||||
stream.on('end', function() { |
||||
stream.sendMetadata(stream.metadata); |
||||
cb(null, {}); |
||||
}); |
||||
}, |
||||
serverStream: function(stream) { |
||||
stream.sendMetadata(stream.metadata); |
||||
stream.end(); |
||||
}, |
||||
bidiStream: function(stream) { |
||||
stream.on('data', function(data) {}); |
||||
stream.on('end', function() { |
||||
stream.sendMetadata(stream.metadata); |
||||
stream.end(); |
||||
}); |
||||
} |
||||
}); |
||||
var key_path = path.join(__dirname, './data/server1.key'); |
||||
var pem_path = path.join(__dirname, './data/server1.pem'); |
||||
var key_data = fs.readFileSync(key_path); |
||||
var pem_data = fs.readFileSync(pem_path); |
||||
var creds = grpc.ServerCredentials.createSsl(null, |
||||
[{private_key: key_data, |
||||
cert_chain: pem_data}]); |
||||
//creds = grpc.ServerCredentials.createInsecure();
|
||||
port = server.bind('localhost:0', creds); |
||||
server.start(); |
||||
|
||||
Client = proto.TestService; |
||||
var ca_path = path.join(__dirname, '../test/data/ca.pem'); |
||||
var ca_data = fs.readFileSync(ca_path); |
||||
client_ssl_creds = grpc.credentials.createSsl(ca_data); |
||||
var host_override = 'foo.test.google.fr'; |
||||
client_options['grpc.ssl_target_name_override'] = host_override; |
||||
client_options['grpc.default_authority'] = host_override; |
||||
}); |
||||
after(function() { |
||||
server.forceShutdown(); |
||||
}); |
||||
it('Should accept SSL creds for a client', function(done) { |
||||
var client = new Client('localhost:' + port, client_ssl_creds, |
||||
client_options); |
||||
client.unary({}, function(err, data) { |
||||
assert.ifError(err); |
||||
done(); |
||||
}); |
||||
}); |
||||
it('Should update metadata with SSL creds', function(done) { |
||||
var metadataUpdater = function(service_url, callback) { |
||||
var metadata = new grpc.Metadata(); |
||||
metadata.set('plugin_key', 'plugin_value'); |
||||
callback(null, metadata); |
||||
}; |
||||
var creds = grpc.credentials.createFromMetadataGenerator(metadataUpdater); |
||||
var combined_creds = grpc.credentials.combineCredentials(client_ssl_creds, |
||||
creds); |
||||
var client = new Client('localhost:' + port, combined_creds, |
||||
client_options); |
||||
var call = client.unary({}, function(err, data) { |
||||
assert.ifError(err); |
||||
}); |
||||
call.on('metadata', function(metadata) { |
||||
assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); |
||||
done(); |
||||
}); |
||||
}); |
||||
it('Should update metadata for two simultaneous calls', function(done) { |
||||
done = multiDone(done, 2); |
||||
var metadataUpdater = function(service_url, callback) { |
||||
var metadata = new grpc.Metadata(); |
||||
metadata.set('plugin_key', 'plugin_value'); |
||||
callback(null, metadata); |
||||
}; |
||||
var creds = grpc.credentials.createFromMetadataGenerator(metadataUpdater); |
||||
var combined_creds = grpc.credentials.combineCredentials(client_ssl_creds, |
||||
creds); |
||||
var client = new Client('localhost:' + port, combined_creds, |
||||
client_options); |
||||
var call = client.unary({}, function(err, data) { |
||||
assert.ifError(err); |
||||
}); |
||||
call.on('metadata', function(metadata) { |
||||
assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); |
||||
done(); |
||||
}); |
||||
var call2 = client.unary({}, function(err, data) { |
||||
assert.ifError(err); |
||||
}); |
||||
call2.on('metadata', function(metadata) { |
||||
assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); |
||||
done(); |
||||
}); |
||||
}); |
||||
describe('Per-rpc creds', function() { |
||||
var client; |
||||
var updater_creds; |
||||
before(function() { |
||||
client = new Client('localhost:' + port, client_ssl_creds, |
||||
client_options); |
||||
var metadataUpdater = function(service_url, callback) { |
||||
var metadata = new grpc.Metadata(); |
||||
metadata.set('plugin_key', 'plugin_value'); |
||||
callback(null, metadata); |
||||
}; |
||||
updater_creds = grpc.credentials.createFromMetadataGenerator( |
||||
metadataUpdater); |
||||
}); |
||||
it('Should update metadata on a unary call', function(done) { |
||||
var call = client.unary({}, function(err, data) { |
||||
assert.ifError(err); |
||||
}, null, {credentials: updater_creds}); |
||||
call.on('metadata', function(metadata) { |
||||
assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); |
||||
done(); |
||||
}); |
||||
}); |
||||
it('should update metadata on a client streaming call', function(done) { |
||||
var call = client.clientStream(function(err, data) { |
||||
assert.ifError(err); |
||||
}, null, {credentials: updater_creds}); |
||||
call.on('metadata', function(metadata) { |
||||
assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); |
||||
done(); |
||||
}); |
||||
call.end(); |
||||
}); |
||||
it('should update metadata on a server streaming call', function(done) { |
||||
var call = client.serverStream({}, null, {credentials: updater_creds}); |
||||
call.on('data', function() {}); |
||||
call.on('metadata', function(metadata) { |
||||
assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); |
||||
done(); |
||||
}); |
||||
}); |
||||
it('should update metadata on a bidi streaming call', function(done) { |
||||
var call = client.bidiStream(null, {credentials: updater_creds}); |
||||
call.on('data', function() {}); |
||||
call.on('metadata', function(metadata) { |
||||
assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); |
||||
done(); |
||||
}); |
||||
call.end(); |
||||
}); |
||||
it('should be able to use multiple plugin credentials', function(done) { |
||||
var altMetadataUpdater = function(service_url, callback) { |
||||
var metadata = new grpc.Metadata(); |
||||
metadata.set('other_plugin_key', 'other_plugin_value'); |
||||
callback(null, metadata); |
||||
}; |
||||
var alt_updater_creds = grpc.credentials.createFromMetadataGenerator( |
||||
altMetadataUpdater); |
||||
var combined_updater = grpc.credentials.combineCallCredentials( |
||||
updater_creds, alt_updater_creds); |
||||
var call = client.unary({}, function(err, data) { |
||||
assert.ifError(err); |
||||
}, null, {credentials: updater_creds}); |
||||
call.on('metadata', function(metadata) { |
||||
assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); |
||||
assert.deepEqual(metadata.get('other_plugin_key'), |
||||
['other_plugin_value']); |
||||
done(); |
||||
}); |
||||
}); |
||||
}); |
||||
}); |
Loading…
Reference in new issue