Merge branch 'master' of github.com:grpc/grpc into cpp_auth_md_processor
diff --git a/BUILD b/BUILD
index d612d9c..bc2136d 100644
--- a/BUILD
+++ b/BUILD
@@ -719,6 +719,7 @@
"src/cpp/util/time.cc",
],
hdrs = [
+ "include/grpc++/auth_metadata_processor.h",
"include/grpc++/channel.h",
"include/grpc++/client_context.h",
"include/grpc++/completion_queue.h",
@@ -808,6 +809,7 @@
"src/cpp/util/time.cc",
],
hdrs = [
+ "include/grpc++/auth_metadata_processor.h",
"include/grpc++/channel.h",
"include/grpc++/client_context.h",
"include/grpc++/completion_queue.h",
diff --git a/Makefile b/Makefile
index 135d38c..4c4a5b2 100644
--- a/Makefile
+++ b/Makefile
@@ -4583,6 +4583,7 @@
src/cpp/util/time.cc \
PUBLIC_HEADERS_CXX += \
+ include/grpc++/auth_metadata_processor.h \
include/grpc++/channel.h \
include/grpc++/client_context.h \
include/grpc++/completion_queue.h \
@@ -4827,6 +4828,7 @@
src/cpp/util/time.cc \
PUBLIC_HEADERS_CXX += \
+ include/grpc++/auth_metadata_processor.h \
include/grpc++/channel.h \
include/grpc++/client_context.h \
include/grpc++/completion_queue.h \
diff --git a/build.json b/build.json
index a451fc3..daa0178 100644
--- a/build.json
+++ b/build.json
@@ -30,6 +30,7 @@
{
"name": "grpc++_base",
"public_headers": [
+ "include/grpc++/auth_metadata_processor.h",
"include/grpc++/channel.h",
"include/grpc++/client_context.h",
"include/grpc++/completion_queue.h",
diff --git a/include/grpc++/auth_metadata_processor.h b/include/grpc++/auth_metadata_processor.h
new file mode 100644
index 0000000..a42abef
--- /dev/null
+++ b/include/grpc++/auth_metadata_processor.h
@@ -0,0 +1,74 @@
+/*
+ *
+ * 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 GRPCXX_AUTH_METADATA_PROCESSOR_H_
+#define GRPCXX_AUTH_METADATA_PROCESSOR_H_
+
+#include <map>
+
+#include <grpc++/support/auth_context.h>
+#include <grpc++/support/status.h>
+#include <grpc++/support/string_ref.h>
+
+namespace grpc {
+
+class AuthMetadataProcessor {
+ public:
+ typedef std::multimap<grpc::string_ref, grpc::string_ref> InputMetadata;
+ typedef std::multimap<grpc::string, grpc::string_ref> OutputMetadata;
+
+ virtual ~AuthMetadataProcessor() {}
+
+ // If this method returns true, the Process function will be scheduled in
+ // a different thread from the one processing the call.
+ virtual bool IsBlocking() const { return true; }
+
+ // context is read/write: it contains the properties of the channel peer and
+ // it is the job of the Process method to augment it with properties derived
+ // from the passed-in auth_metadata.
+ // consumed_auth_metadata needs to be filled with metadata that has been
+ // consumed by the processor and will be removed from the call.
+ // response_metadata is the metadata that will be sent as part of the
+ // response.
+ // If the return value is not Status::OK, the rpc call will be aborted with
+ // the error code and error message sent back to the client.
+ virtual Status Process(const InputMetadata& auth_metadata,
+ AuthContext* context,
+ OutputMetadata* consumed_auth_metadata,
+ OutputMetadata* response_metadata) = 0;
+};
+
+} // namespace grpc
+
+#endif // GRPCXX_AUTH_METADATA_PROCESSOR_H_
+
diff --git a/include/grpc++/server_credentials.h b/include/grpc++/server_credentials.h
index 16b78c0..e006f3a 100644
--- a/include/grpc++/server_credentials.h
+++ b/include/grpc++/server_credentials.h
@@ -38,6 +38,7 @@
#include <vector>
#include <grpc++/support/config.h>
+#include <grpc++/auth_metadata_processor.h>
struct grpc_server;
@@ -49,6 +50,11 @@
public:
virtual ~ServerCredentials();
+ // This method is not thread-safe and has to be called before the server is
+ // started. The last call to this function wins.
+ virtual void SetAuthMetadataProcessor(
+ const std::shared_ptr<AuthMetadataProcessor>& processor) = 0;
+
private:
friend class ::grpc::Server;
diff --git a/include/grpc++/support/auth_context.h b/include/grpc++/support/auth_context.h
index 67e3e66..fc2701e 100644
--- a/include/grpc++/support/auth_context.h
+++ b/include/grpc++/support/auth_context.h
@@ -77,6 +77,9 @@
public:
virtual ~AuthContext() {}
+ // Returns true if the peer is authenticated.
+ virtual bool IsPeerAuthenticated() const = 0;
+
// A peer identity, in general is one or more properties (in which case they
// have the same name).
virtual std::vector<grpc::string_ref> GetPeerIdentity() const = 0;
@@ -89,6 +92,11 @@
// Iteration over all the properties.
virtual AuthPropertyIterator begin() const = 0;
virtual AuthPropertyIterator end() const = 0;
+
+ // Mutation functions: should only be used by an AuthMetadataProcessor.
+ virtual void AddProperty(const grpc::string& key,
+ const grpc::string_ref& value) = 0;
+ virtual bool SetPeerIdentityPropertyName(const grpc::string& name) = 0;
};
} // namespace grpc
diff --git a/include/grpc/grpc_security.h b/include/grpc/grpc_security.h
index 049ab3c..87bc250 100644
--- a/include/grpc/grpc_security.h
+++ b/include/grpc/grpc_security.h
@@ -275,10 +275,12 @@
typedef struct {
/* The context object is read/write: it contains the properties of the
channel peer and it is the job of the process function to augment it with
- properties derived from the passed-in metadata. */
+ properties derived from the passed-in metadata.
+ The lifetime of these objects is guaranteed until cb is invoked. */
void (*process)(void *state, grpc_auth_context *context,
- const grpc_metadata *md, size_t md_count,
+ const grpc_metadata *md, size_t num_md,
grpc_process_auth_metadata_done_cb cb, void *user_data);
+ void (*destroy)(void *state);
void *state;
} grpc_auth_metadata_processor;
diff --git a/src/core/security/credentials.c b/src/core/security/credentials.c
index 1c665f1..721e871 100644
--- a/src/core/security/credentials.c
+++ b/src/core/security/credentials.c
@@ -152,9 +152,20 @@
void grpc_server_credentials_set_auth_metadata_processor(
grpc_server_credentials *creds, grpc_auth_metadata_processor processor) {
if (creds == NULL) return;
+ if (creds->processor.destroy != NULL && creds->processor.state != NULL) {
+ creds->processor.destroy(creds->processor.state);
+ }
creds->processor = processor;
}
+void grpc_server_credentials_destroy(grpc_server_credentials *creds) {
+ if (creds == NULL) return;
+ if (creds->processor.destroy != NULL && creds->processor.state != NULL) {
+ creds->processor.destroy(creds->processor.state);
+ }
+ gpr_free(creds);
+}
+
/* -- Ssl credentials. -- */
static void ssl_destroy(grpc_credentials *creds) {
@@ -185,7 +196,7 @@
gpr_free(c->config.pem_cert_chains_sizes);
}
if (c->config.pem_root_certs != NULL) gpr_free(c->config.pem_root_certs);
- gpr_free(creds);
+ grpc_server_credentials_destroy(creds);
}
static int ssl_has_request_metadata(const grpc_credentials *creds) { return 0; }
@@ -834,7 +845,7 @@
static void fake_transport_security_server_credentials_destroy(
grpc_server_credentials *creds) {
- gpr_free(creds);
+ grpc_server_credentials_destroy(creds);
}
static int fake_transport_security_has_request_metadata(
diff --git a/src/core/security/security_context.c b/src/core/security/security_context.c
index c1b434f..95d80ba 100644
--- a/src/core/security/security_context.c
+++ b/src/core/security/security_context.c
@@ -42,19 +42,6 @@
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
-/* --- grpc_process_auth_metadata_func --- */
-
-static grpc_auth_metadata_processor server_processor = {NULL, NULL};
-
-grpc_auth_metadata_processor grpc_server_get_auth_metadata_processor(void) {
- return server_processor;
-}
-
-void grpc_server_register_auth_metadata_processor(
- grpc_auth_metadata_processor processor) {
- server_processor = processor;
-}
-
/* --- grpc_call --- */
grpc_call_error grpc_call_set_credentials(grpc_call *call,
diff --git a/src/core/security/server_auth_filter.c b/src/core/security/server_auth_filter.c
index 6e83143..b767f85 100644
--- a/src/core/security/server_auth_filter.c
+++ b/src/core/security/server_auth_filter.c
@@ -50,6 +50,7 @@
handling it. */
grpc_iomgr_closure auth_on_recv;
grpc_transport_stream_op transport_op;
+ grpc_metadata_array md;
const grpc_metadata *consumed_md;
size_t num_consumed_md;
grpc_stream_op *md_op;
@@ -90,13 +91,17 @@
call_data *calld = elem->call_data;
size_t i;
for (i = 0; i < calld->num_consumed_md; i++) {
+ const grpc_metadata *consumed_md = &calld->consumed_md[i];
/* Maybe we could do a pointer comparison but we do not have any guarantee
that the metadata processor used the same pointers for consumed_md in the
callback. */
- if (memcmp(GPR_SLICE_START_PTR(md->key->slice), calld->consumed_md[i].key,
+ if (GPR_SLICE_LENGTH(md->key->slice) != strlen(consumed_md->key) ||
+ GPR_SLICE_LENGTH(md->value->slice) != consumed_md->value_length) {
+ continue;
+ }
+ if (memcmp(GPR_SLICE_START_PTR(md->key->slice), consumed_md->key,
GPR_SLICE_LENGTH(md->key->slice)) == 0 &&
- memcmp(GPR_SLICE_START_PTR(md->value->slice),
- calld->consumed_md[i].value,
+ memcmp(GPR_SLICE_START_PTR(md->value->slice), consumed_md->value,
GPR_SLICE_LENGTH(md->value->slice)) == 0) {
return NULL; /* Delete. */
}
@@ -134,6 +139,7 @@
grpc_transport_stream_op_add_close(&calld->transport_op, status, &message);
grpc_call_next_op(elem, &calld->transport_op);
}
+ grpc_metadata_array_destroy(&calld->md);
}
static void auth_on_recv(void *user_data, int success) {
@@ -145,17 +151,15 @@
size_t nops = calld->recv_ops->nops;
grpc_stream_op *ops = calld->recv_ops->ops;
for (i = 0; i < nops; i++) {
- grpc_metadata_array md_array;
grpc_stream_op *op = &ops[i];
if (op->type != GRPC_OP_METADATA || calld->got_client_metadata) continue;
calld->got_client_metadata = 1;
if (chand->processor.process == NULL) continue;
calld->md_op = op;
- md_array = metadata_batch_to_md_array(&op->data.metadata);
+ calld->md = metadata_batch_to_md_array(&op->data.metadata);
chand->processor.process(chand->processor.state, calld->auth_context,
- md_array.metadata, md_array.count,
+ calld->md.metadata, calld->md.count,
on_md_processing_done, elem);
- grpc_metadata_array_destroy(&md_array);
return;
}
}
diff --git a/src/core/security/server_secure_chttp2.c b/src/core/security/server_secure_chttp2.c
index 8d9d036..96ca4cb 100644
--- a/src/core/security/server_secure_chttp2.c
+++ b/src/core/security/server_secure_chttp2.c
@@ -79,6 +79,9 @@
gpr_mu_unlock(&state->mu);
/* clean up */
GRPC_SECURITY_CONNECTOR_UNREF(state->sc, "server");
+ if (state->processor.state != NULL && state->processor.destroy != NULL) {
+ state->processor.destroy(state->processor.state);
+ }
gpr_free(state);
}
}
@@ -262,7 +265,13 @@
state->server = server;
state->tcp = tcp;
state->sc = sc;
+
+ /* Transfer ownership of the processor. */
state->processor = creds->processor;
+ creds->processor.state = NULL;
+ creds->processor.destroy = NULL;
+ creds->processor.process = NULL;
+
state->handshaking_tcp_endpoints = NULL;
state->is_shutdown = 0;
gpr_mu_init(&state->mu);
diff --git a/src/cpp/client/create_channel.cc b/src/cpp/client/create_channel.cc
index 1dac960..d2b2d30 100644
--- a/src/cpp/client/create_channel.cc
+++ b/src/cpp/client/create_channel.cc
@@ -51,6 +51,7 @@
std::shared_ptr<Channel> CreateCustomChannel(
const grpc::string& target, const std::shared_ptr<Credentials>& creds,
const ChannelArguments& args) {
+ GrpcLibrary init_lib; // We need to call init in case of a bad creds.
ChannelArguments cp_args = args;
std::ostringstream user_agent_prefix;
user_agent_prefix << "grpc-c++/" << grpc_version_string();
diff --git a/src/cpp/common/secure_auth_context.cc b/src/cpp/common/secure_auth_context.cc
index b18a853..8615ac8 100644
--- a/src/cpp/common/secure_auth_context.cc
+++ b/src/cpp/common/secure_auth_context.cc
@@ -37,9 +37,13 @@
namespace grpc {
-SecureAuthContext::SecureAuthContext(grpc_auth_context* ctx) : ctx_(ctx) {}
+SecureAuthContext::SecureAuthContext(grpc_auth_context* ctx,
+ bool take_ownership)
+ : ctx_(ctx), take_ownership_(take_ownership) {}
-SecureAuthContext::~SecureAuthContext() { grpc_auth_context_release(ctx_); }
+SecureAuthContext::~SecureAuthContext() {
+ if (take_ownership_) grpc_auth_context_release(ctx_);
+}
std::vector<grpc::string_ref> SecureAuthContext::GetPeerIdentity() const {
if (!ctx_) {
@@ -94,4 +98,21 @@
return AuthPropertyIterator();
}
+void SecureAuthContext::AddProperty(const grpc::string& key,
+ const grpc::string_ref& value) {
+ if (!ctx_) return;
+ grpc_auth_context_add_property(ctx_, key.c_str(), value.data(), value.size());
+}
+
+bool SecureAuthContext::SetPeerIdentityPropertyName(const grpc::string& name) {
+ if (!ctx_) return false;
+ return grpc_auth_context_set_peer_identity_property_name(ctx_,
+ name.c_str()) != 0;
+}
+
+bool SecureAuthContext::IsPeerAuthenticated() const {
+ if (!ctx_) return false;
+ return grpc_auth_context_peer_is_authenticated(ctx_) != 0;
+}
+
} // namespace grpc
diff --git a/src/cpp/common/secure_auth_context.h b/src/cpp/common/secure_auth_context.h
index 7f622b8..6edab0c 100644
--- a/src/cpp/common/secure_auth_context.h
+++ b/src/cpp/common/secure_auth_context.h
@@ -42,10 +42,12 @@
class SecureAuthContext GRPC_FINAL : public AuthContext {
public:
- SecureAuthContext(grpc_auth_context* ctx);
+ SecureAuthContext(grpc_auth_context* ctx, bool take_ownership);
~SecureAuthContext() GRPC_OVERRIDE;
+ bool IsPeerAuthenticated() const GRPC_OVERRIDE;
+
std::vector<grpc::string_ref> GetPeerIdentity() const GRPC_OVERRIDE;
grpc::string GetPeerIdentityPropertyName() const GRPC_OVERRIDE;
@@ -57,8 +59,15 @@
AuthPropertyIterator end() const GRPC_OVERRIDE;
+ void AddProperty(const grpc::string& key,
+ const grpc::string_ref& value) GRPC_OVERRIDE;
+
+ virtual bool SetPeerIdentityPropertyName(const grpc::string& name)
+ GRPC_OVERRIDE;
+
private:
grpc_auth_context* ctx_;
+ bool take_ownership_;
};
} // namespace grpc
diff --git a/src/cpp/common/secure_create_auth_context.cc b/src/cpp/common/secure_create_auth_context.cc
index f13d25a..5a907d0 100644
--- a/src/cpp/common/secure_create_auth_context.cc
+++ b/src/cpp/common/secure_create_auth_context.cc
@@ -44,7 +44,7 @@
return std::shared_ptr<const AuthContext>();
}
return std::shared_ptr<const AuthContext>(
- new SecureAuthContext(grpc_call_auth_context(call)));
+ new SecureAuthContext(grpc_call_auth_context(call), true));
}
} // namespace grpc
diff --git a/src/cpp/server/insecure_server_credentials.cc b/src/cpp/server/insecure_server_credentials.cc
index 800cd36..12807e7 100644
--- a/src/cpp/server/insecure_server_credentials.cc
+++ b/src/cpp/server/insecure_server_credentials.cc
@@ -34,6 +34,7 @@
#include <grpc++/server_credentials.h>
#include <grpc/grpc.h>
+#include <grpc/support/log.h>
namespace grpc {
namespace {
@@ -43,6 +44,11 @@
grpc_server* server) GRPC_OVERRIDE {
return grpc_server_add_insecure_http2_port(server, addr.c_str());
}
+ void SetAuthMetadataProcessor(
+ const std::shared_ptr<AuthMetadataProcessor>& processor) GRPC_OVERRIDE {
+ (void)processor;
+ GPR_ASSERT(0); // Should not be called on InsecureServerCredentials.
+ }
};
} // namespace
diff --git a/src/cpp/server/secure_server_credentials.cc b/src/cpp/server/secure_server_credentials.cc
index 5bce9ca..0e2927b 100644
--- a/src/cpp/server/secure_server_credentials.cc
+++ b/src/cpp/server/secure_server_credentials.cc
@@ -31,15 +31,94 @@
*
*/
+#include <functional>
+#include <map>
+#include <memory>
+
+
+#include "src/cpp/common/secure_auth_context.h"
#include "src/cpp/server/secure_server_credentials.h"
+#include <grpc++/auth_metadata_processor.h>
+
namespace grpc {
+void AuthMetadataProcessorAyncWrapper::Destroy(void *wrapper) {
+ auto* w = reinterpret_cast<AuthMetadataProcessorAyncWrapper*>(wrapper);
+ delete w;
+}
+
+void AuthMetadataProcessorAyncWrapper::Process(
+ void* wrapper, grpc_auth_context* context, const grpc_metadata* md,
+ size_t num_md, grpc_process_auth_metadata_done_cb cb, void* user_data) {
+ auto* w = reinterpret_cast<AuthMetadataProcessorAyncWrapper*>(wrapper);
+ if (w->processor_ == nullptr) {
+ // Early exit.
+ cb(user_data, nullptr, 0, nullptr, 0, GRPC_STATUS_OK, nullptr);
+ return;
+ }
+ if (w->processor_->IsBlocking()) {
+ w->thread_pool_->Add(
+ std::bind(&AuthMetadataProcessorAyncWrapper::InvokeProcessor, w,
+ context, md, num_md, cb, user_data));
+ } else {
+ // invoke directly.
+ w->InvokeProcessor(context, md, num_md, cb, user_data);
+ }
+}
+
+void AuthMetadataProcessorAyncWrapper::InvokeProcessor(
+ grpc_auth_context* ctx,
+ const grpc_metadata* md, size_t num_md,
+ grpc_process_auth_metadata_done_cb cb, void* user_data) {
+ AuthMetadataProcessor::InputMetadata metadata;
+ for (size_t i = 0; i < num_md; i++) {
+ metadata.insert(std::make_pair(
+ md[i].key, grpc::string_ref(md[i].value, md[i].value_length)));
+ }
+ SecureAuthContext context(ctx, false);
+ AuthMetadataProcessor::OutputMetadata consumed_metadata;
+ AuthMetadataProcessor::OutputMetadata response_metadata;
+
+ Status status = processor_->Process(metadata, &context, &consumed_metadata,
+ &response_metadata);
+
+ std::vector<grpc_metadata> consumed_md;
+ for (auto it = consumed_metadata.begin(); it != consumed_metadata.end();
+ ++it) {
+ consumed_md.push_back({it->first.c_str(),
+ it->second.data(),
+ it->second.size(),
+ 0,
+ {{nullptr, nullptr, nullptr, nullptr}}});
+ }
+ std::vector<grpc_metadata> response_md;
+ for (auto it = response_metadata.begin(); it != response_metadata.end();
+ ++it) {
+ response_md.push_back({it->first.c_str(),
+ it->second.data(),
+ it->second.size(),
+ 0,
+ {{nullptr, nullptr, nullptr, nullptr}}});
+ }
+ cb(user_data, &consumed_md[0], consumed_md.size(), &response_md[0],
+ response_md.size(), static_cast<grpc_status_code>(status.error_code()),
+ status.error_message().c_str());
+}
+
int SecureServerCredentials::AddPortToServer(const grpc::string& addr,
grpc_server* server) {
return grpc_server_add_secure_http2_port(server, addr.c_str(), creds_);
}
+void SecureServerCredentials::SetAuthMetadataProcessor(
+ const std::shared_ptr<AuthMetadataProcessor>& processor) {
+ auto *wrapper = new AuthMetadataProcessorAyncWrapper(processor);
+ grpc_server_credentials_set_auth_metadata_processor(
+ creds_, {AuthMetadataProcessorAyncWrapper::Process,
+ AuthMetadataProcessorAyncWrapper::Destroy, wrapper});
+}
+
std::shared_ptr<ServerCredentials> SslServerCredentials(
const SslServerCredentialsOptions& options) {
std::vector<grpc_ssl_pem_key_cert_pair> pem_key_cert_pairs;
diff --git a/src/cpp/server/secure_server_credentials.h b/src/cpp/server/secure_server_credentials.h
index d3d37b1..17be9e3 100644
--- a/src/cpp/server/secure_server_credentials.h
+++ b/src/cpp/server/secure_server_credentials.h
@@ -34,12 +34,36 @@
#ifndef GRPC_INTERNAL_CPP_SERVER_SECURE_SERVER_CREDENTIALS_H
#define GRPC_INTERNAL_CPP_SERVER_SECURE_SERVER_CREDENTIALS_H
+#include <memory>
+
#include <grpc++/server_credentials.h>
#include <grpc/grpc_security.h>
+#include "src/cpp/server/thread_pool_interface.h"
+
namespace grpc {
+class AuthMetadataProcessorAyncWrapper GRPC_FINAL {
+ public:
+ static void Destroy(void *wrapper);
+
+ static void Process(void* wrapper, grpc_auth_context* context,
+ const grpc_metadata* md, size_t num_md,
+ grpc_process_auth_metadata_done_cb cb, void* user_data);
+
+ AuthMetadataProcessorAyncWrapper(
+ const std::shared_ptr<AuthMetadataProcessor>& processor)
+ : thread_pool_(CreateDefaultThreadPool()), processor_(processor) {}
+
+ private:
+ void InvokeProcessor(grpc_auth_context* context, const grpc_metadata* md,
+ size_t num_md, grpc_process_auth_metadata_done_cb cb,
+ void* user_data);
+ std::unique_ptr<ThreadPoolInterface> thread_pool_;
+ std::shared_ptr<AuthMetadataProcessor> processor_;
+};
+
class SecureServerCredentials GRPC_FINAL : public ServerCredentials {
public:
explicit SecureServerCredentials(grpc_server_credentials* creds)
@@ -51,8 +75,12 @@
int AddPortToServer(const grpc::string& addr,
grpc_server* server) GRPC_OVERRIDE;
+ void SetAuthMetadataProcessor(
+ const std::shared_ptr<AuthMetadataProcessor>& processor) GRPC_OVERRIDE;
+
private:
- grpc_server_credentials* const creds_;
+ grpc_server_credentials* creds_;
+ std::unique_ptr<AuthMetadataProcessorAyncWrapper> processor_;
};
} // namespace grpc
diff --git a/test/core/end2end/fixtures/chttp2_fake_security.c b/test/core/end2end/fixtures/chttp2_fake_security.c
index b4a248f..3e64cc0 100644
--- a/test/core/end2end/fixtures/chttp2_fake_security.c
+++ b/test/core/end2end/fixtures/chttp2_fake_security.c
@@ -128,7 +128,7 @@
grpc_server_credentials *fake_ts_creds =
grpc_fake_transport_security_server_credentials_create();
if (fail_server_auth_check(server_args)) {
- grpc_auth_metadata_processor processor = {process_auth_failure, NULL};
+ grpc_auth_metadata_processor processor = {process_auth_failure, NULL, NULL};
grpc_server_credentials_set_auth_metadata_processor(fake_ts_creds,
processor);
}
diff --git a/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c b/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c
index 201d202..9193a09 100644
--- a/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c
+++ b/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c
@@ -138,7 +138,7 @@
grpc_server_credentials *ssl_creds =
grpc_ssl_server_credentials_create(NULL, &pem_cert_key_pair, 1, 0, NULL);
if (fail_server_auth_check(server_args)) {
- grpc_auth_metadata_processor processor = {process_auth_failure, NULL};
+ grpc_auth_metadata_processor processor = {process_auth_failure, NULL, NULL};
grpc_server_credentials_set_auth_metadata_processor(ssl_creds, processor);
}
chttp2_init_server_secure_fullstack(f, server_args, ssl_creds);
diff --git a/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack_with_poll.c b/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack_with_poll.c
index e7375f1..2c605d1 100644
--- a/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack_with_poll.c
+++ b/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack_with_poll.c
@@ -138,7 +138,7 @@
grpc_server_credentials *ssl_creds =
grpc_ssl_server_credentials_create(NULL, &pem_cert_key_pair, 1, 0, NULL);
if (fail_server_auth_check(server_args)) {
- grpc_auth_metadata_processor processor = {process_auth_failure, NULL};
+ grpc_auth_metadata_processor processor = {process_auth_failure, NULL, NULL};
grpc_server_credentials_set_auth_metadata_processor(ssl_creds, processor);
}
chttp2_init_server_secure_fullstack(f, server_args, ssl_creds);
diff --git a/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack_with_proxy.c b/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack_with_proxy.c
index be0dda2..8133a69 100644
--- a/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack_with_proxy.c
+++ b/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack_with_proxy.c
@@ -167,7 +167,7 @@
grpc_server_credentials *ssl_creds =
grpc_ssl_server_credentials_create(NULL, &pem_cert_key_pair, 1, 0, NULL);
if (fail_server_auth_check(server_args)) {
- grpc_auth_metadata_processor processor = {process_auth_failure, NULL};
+ grpc_auth_metadata_processor processor = {process_auth_failure, NULL, NULL};
grpc_server_credentials_set_auth_metadata_processor(ssl_creds, processor);
}
chttp2_init_server_secure_fullstack(f, server_args, ssl_creds);
diff --git a/test/core/end2end/fixtures/chttp2_simple_ssl_with_oauth2_fullstack.c b/test/core/end2end/fixtures/chttp2_simple_ssl_with_oauth2_fullstack.c
index 9a545b1..e61e276 100644
--- a/test/core/end2end/fixtures/chttp2_simple_ssl_with_oauth2_fullstack.c
+++ b/test/core/end2end/fixtures/chttp2_simple_ssl_with_oauth2_fullstack.c
@@ -67,13 +67,21 @@
return NULL;
}
+typedef struct {
+ size_t pseudo_refcount;
+} test_processor_state;
+
static void process_oauth2_success(void *state, grpc_auth_context *ctx,
const grpc_metadata *md, size_t md_count,
grpc_process_auth_metadata_done_cb cb,
void *user_data) {
const grpc_metadata *oauth2 =
find_metadata(md, md_count, "Authorization", oauth2_md);
- GPR_ASSERT(state == NULL);
+ test_processor_state *s;
+
+ GPR_ASSERT(state != NULL);
+ s = (test_processor_state *)state;
+ GPR_ASSERT(s->pseudo_refcount == 1);
GPR_ASSERT(oauth2 != NULL);
grpc_auth_context_add_cstring_property(ctx, client_identity_property_name,
client_identity);
@@ -88,7 +96,10 @@
void *user_data) {
const grpc_metadata *oauth2 =
find_metadata(md, md_count, "Authorization", oauth2_md);
- GPR_ASSERT(state == NULL);
+ test_processor_state *s;
+ GPR_ASSERT(state != NULL);
+ s = (test_processor_state *)state;
+ GPR_ASSERT(s->pseudo_refcount == 1);
GPR_ASSERT(oauth2 != NULL);
cb(user_data, oauth2, 1, NULL, 0, GRPC_STATUS_UNAUTHENTICATED, NULL);
}
@@ -171,20 +182,34 @@
return 0;
}
+static void processor_destroy(void *state) {
+ test_processor_state *s = (test_processor_state *)state;
+ GPR_ASSERT((s->pseudo_refcount--) == 1);
+ gpr_free(s);
+}
+
+static grpc_auth_metadata_processor test_processor_create(int failing) {
+ test_processor_state *s = gpr_malloc(sizeof(*s));
+ grpc_auth_metadata_processor result;
+ s->pseudo_refcount = 1;
+ result.state = s;
+ result.destroy = processor_destroy;
+ if (failing) {
+ result.process = process_oauth2_failure;
+ } else {
+ result.process = process_oauth2_success;
+ }
+ return result;
+}
+
static void chttp2_init_server_simple_ssl_secure_fullstack(
grpc_end2end_test_fixture *f, grpc_channel_args *server_args) {
grpc_ssl_pem_key_cert_pair pem_key_cert_pair = {test_server1_key,
test_server1_cert};
grpc_server_credentials *ssl_creds =
grpc_ssl_server_credentials_create(NULL, &pem_key_cert_pair, 1, 0, NULL);
- grpc_auth_metadata_processor processor;
- processor.state = NULL;
- if (fail_server_auth_check(server_args)) {
- processor.process = process_oauth2_failure;
- } else {
- processor.process = process_oauth2_success;
- }
- grpc_server_credentials_set_auth_metadata_processor(ssl_creds, processor);
+ grpc_server_credentials_set_auth_metadata_processor(
+ ssl_creds, test_processor_create(fail_server_auth_check(server_args)));
chttp2_init_server_secure_fullstack(f, server_args, ssl_creds);
}
diff --git a/test/cpp/common/secure_auth_context_test.cc b/test/cpp/common/secure_auth_context_test.cc
index 25538c1..e1f2c5e 100644
--- a/test/cpp/common/secure_auth_context_test.cc
+++ b/test/cpp/common/secure_auth_context_test.cc
@@ -50,7 +50,7 @@
// Created with nullptr
TEST_F(SecureAuthContextTest, EmptyContext) {
- SecureAuthContext context(nullptr);
+ SecureAuthContext context(nullptr, true);
EXPECT_TRUE(context.GetPeerIdentity().empty());
EXPECT_TRUE(context.GetPeerIdentityPropertyName().empty());
EXPECT_TRUE(context.FindPropertyValues("").empty());
@@ -60,12 +60,12 @@
TEST_F(SecureAuthContextTest, Properties) {
grpc_auth_context* ctx = grpc_auth_context_create(NULL);
- grpc_auth_context_add_cstring_property(ctx, "name", "chapi");
- grpc_auth_context_add_cstring_property(ctx, "name", "chapo");
- grpc_auth_context_add_cstring_property(ctx, "foo", "bar");
- EXPECT_EQ(1, grpc_auth_context_set_peer_identity_property_name(ctx, "name"));
+ SecureAuthContext context(ctx, true);
+ context.AddProperty("name", "chapi");
+ context.AddProperty("name", "chapo");
+ context.AddProperty("foo", "bar");
+ EXPECT_TRUE(context.SetPeerIdentityPropertyName("name"));
- SecureAuthContext context(ctx);
std::vector<grpc::string_ref> peer_identity = context.GetPeerIdentity();
EXPECT_EQ(2u, peer_identity.size());
EXPECT_EQ("chapi", ToString(peer_identity[0]));
@@ -78,12 +78,12 @@
TEST_F(SecureAuthContextTest, Iterators) {
grpc_auth_context* ctx = grpc_auth_context_create(NULL);
- grpc_auth_context_add_cstring_property(ctx, "name", "chapi");
- grpc_auth_context_add_cstring_property(ctx, "name", "chapo");
- grpc_auth_context_add_cstring_property(ctx, "foo", "bar");
- EXPECT_EQ(1, grpc_auth_context_set_peer_identity_property_name(ctx, "name"));
+ SecureAuthContext context(ctx, true);
+ context.AddProperty("name", "chapi");
+ context.AddProperty("name", "chapo");
+ context.AddProperty("foo", "bar");
+ EXPECT_TRUE(context.SetPeerIdentityPropertyName("name"));
- SecureAuthContext context(ctx);
AuthPropertyIterator iter = context.begin();
EXPECT_TRUE(context.end() != iter);
AuthProperty p0 = *iter;
diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc
index 5e2332c..3fc73ab 100644
--- a/test/cpp/end2end/end2end_test.cc
+++ b/test/cpp/end2end/end2end_test.cc
@@ -37,6 +37,7 @@
#include <grpc/grpc.h>
#include <grpc/support/thd.h>
#include <grpc/support/time.h>
+#include <grpc++/auth_metadata_processor.h>
#include <grpc++/channel.h>
#include <grpc++/client_context.h>
#include <grpc++/create_channel.h>
@@ -79,14 +80,23 @@
}
}
-void CheckServerAuthContext(const ServerContext* context) {
+void CheckServerAuthContext(const ServerContext* context,
+ const grpc::string& expected_client_identity) {
std::shared_ptr<const AuthContext> auth_ctx = context->auth_context();
std::vector<grpc::string_ref> ssl =
auth_ctx->FindPropertyValues("transport_security_type");
EXPECT_EQ(1u, ssl.size());
EXPECT_EQ("ssl", ToString(ssl[0]));
- EXPECT_TRUE(auth_ctx->GetPeerIdentityPropertyName().empty());
- EXPECT_TRUE(auth_ctx->GetPeerIdentity().empty());
+ if (expected_client_identity.length() == 0) {
+ EXPECT_TRUE(auth_ctx->GetPeerIdentityPropertyName().empty());
+ EXPECT_TRUE(auth_ctx->GetPeerIdentity().empty());
+ EXPECT_FALSE(auth_ctx->IsPeerAuthenticated());
+ } else {
+ auto identity = auth_ctx->GetPeerIdentity();
+ EXPECT_TRUE(auth_ctx->IsPeerAuthenticated());
+ EXPECT_EQ(1u, identity.size());
+ EXPECT_EQ(expected_client_identity, identity[0]);
+ }
}
bool CheckIsLocalhost(const grpc::string& addr) {
@@ -98,6 +108,54 @@
addr.substr(0, kIpv6.size()) == kIpv6;
}
+class TestAuthMetadataProcessor : public AuthMetadataProcessor {
+ public:
+ static const char kGoodGuy[];
+
+ TestAuthMetadataProcessor(bool is_blocking) : is_blocking_(is_blocking) {}
+
+ std::shared_ptr<Credentials> GetCompatibleClientCreds() {
+ return AccessTokenCredentials(kGoodGuy);
+ }
+ std::shared_ptr<Credentials> GetIncompatibleClientCreds() {
+ return AccessTokenCredentials("Mr Hyde");
+ }
+
+ // Interface implementation
+ bool IsBlocking() const GRPC_OVERRIDE { return is_blocking_; }
+
+ Status Process(const InputMetadata& auth_metadata, AuthContext* context,
+ OutputMetadata* consumed_auth_metadata,
+ OutputMetadata* response_metadata) GRPC_OVERRIDE {
+ EXPECT_TRUE(consumed_auth_metadata != nullptr);
+ EXPECT_TRUE(context != nullptr);
+ EXPECT_TRUE(response_metadata != nullptr);
+ auto auth_md = auth_metadata.find(GRPC_AUTHORIZATION_METADATA_KEY);
+ EXPECT_NE(auth_md, auth_metadata.end());
+ string_ref auth_md_value = auth_md->second;
+ if (auth_md_value.ends_with("Dr Jekyll")) {
+ context->AddProperty(kIdentityPropName, kGoodGuy);
+ context->SetPeerIdentityPropertyName(kIdentityPropName);
+ consumed_auth_metadata->insert(
+ std::make_pair(string(auth_md->first.data(), auth_md->first.length()),
+ auth_md->second));
+ return Status::OK;
+ } else {
+ return Status(StatusCode::UNAUTHENTICATED,
+ string("Invalid principal: ") +
+ string(auth_md_value.data(), auth_md_value.length()));
+ }
+ }
+
+ protected:
+ static const char kIdentityPropName[];
+ bool is_blocking_;
+};
+
+const char TestAuthMetadataProcessor::kGoodGuy[] = "Dr Jekyll";
+const char TestAuthMetadataProcessor::kIdentityPropName[] = "novel identity";
+
+
} // namespace
class Proxy : public ::grpc::cpp::test::util::TestService::Service {
@@ -162,8 +220,10 @@
ToString(iter->second));
}
}
- if (request->has_param() && request->param().check_auth_context()) {
- CheckServerAuthContext(context);
+ if (request->has_param() &&
+ (request->param().expected_client_identity().length() > 0 ||
+ request->param().check_auth_context())) {
+ CheckServerAuthContext(context, request->param().expected_client_identity());
}
if (request->has_param() &&
request->param().response_message_length() > 0) {
@@ -259,9 +319,18 @@
class End2endTest : public ::testing::TestWithParam<bool> {
protected:
End2endTest()
- : kMaxMessageSize_(8192), special_service_("special") {}
+ : is_server_started_(false),
+ kMaxMessageSize_(8192),
+ special_service_("special") {}
- void SetUp() GRPC_OVERRIDE {
+ void TearDown() GRPC_OVERRIDE {
+ if (is_server_started_) {
+ server_->Shutdown();
+ if (proxy_server_) proxy_server_->Shutdown();
+ }
+ }
+
+ void StartServer(const std::shared_ptr<AuthMetadataProcessor>& processor) {
int port = grpc_pick_unused_port_or_die();
server_address_ << "127.0.0.1:" << port;
// Setup server
@@ -271,22 +340,23 @@
SslServerCredentialsOptions ssl_opts;
ssl_opts.pem_root_certs = "";
ssl_opts.pem_key_cert_pairs.push_back(pkcp);
- builder.AddListeningPort(server_address_.str(),
- SslServerCredentials(ssl_opts));
+ auto server_creds = SslServerCredentials(ssl_opts);
+ server_creds->SetAuthMetadataProcessor(processor);
+ builder.AddListeningPort(server_address_.str(), server_creds);
builder.RegisterService(&service_);
builder.RegisterService("foo.test.youtube.com", &special_service_);
builder.SetMaxMessageSize(
kMaxMessageSize_); // For testing max message size.
builder.RegisterService(&dup_pkg_service_);
server_ = builder.BuildAndStart();
- }
-
- void TearDown() GRPC_OVERRIDE {
- server_->Shutdown();
- if (proxy_server_) proxy_server_->Shutdown();
+ is_server_started_ = true;
}
void ResetChannel() {
+ if (!is_server_started_) {
+ StartServer(std::shared_ptr<AuthMetadataProcessor>());
+ }
+ EXPECT_TRUE(is_server_started_);
SslCredentialsOptions ssl_opts = {test_root_cert, "", ""};
ChannelArguments args;
args.SetSslTargetNameOverride("foo.test.google.fr");
@@ -313,6 +383,7 @@
stub_ = std::move(grpc::cpp::test::util::TestService::NewStub(channel_));
}
+ bool is_server_started_;
std::shared_ptr<Channel> channel_;
std::unique_ptr<grpc::cpp::test::util::TestService::Stub> stub_;
std::unique_ptr<Server> server_;
@@ -805,6 +876,82 @@
EXPECT_TRUE(s.ok());
}
+TEST_F(End2endTest, NonBlockingAuthMetadataProcessorSuccess) {
+ auto* processor = new TestAuthMetadataProcessor(false);
+ StartServer(std::shared_ptr<AuthMetadataProcessor>(processor));
+ ResetStub(false);
+ EchoRequest request;
+ EchoResponse response;
+ ClientContext context;
+ context.set_credentials(processor->GetCompatibleClientCreds());
+ request.set_message("Hello");
+ request.mutable_param()->set_echo_metadata(true);
+ request.mutable_param()->set_expected_client_identity(
+ TestAuthMetadataProcessor::kGoodGuy);
+
+ Status s = stub_->Echo(&context, request, &response);
+ EXPECT_EQ(request.message(), response.message());
+ EXPECT_TRUE(s.ok());
+
+ // Metadata should have been consumed by the processor.
+ EXPECT_FALSE(MetadataContains(
+ context.GetServerTrailingMetadata(), GRPC_AUTHORIZATION_METADATA_KEY,
+ grpc::string("Bearer ") + TestAuthMetadataProcessor::kGoodGuy));
+}
+
+TEST_F(End2endTest, NonBlockingAuthMetadataProcessorFailure) {
+ auto* processor = new TestAuthMetadataProcessor(false);
+ StartServer(std::shared_ptr<AuthMetadataProcessor>(processor));
+ ResetStub(false);
+ EchoRequest request;
+ EchoResponse response;
+ ClientContext context;
+ context.set_credentials(processor->GetIncompatibleClientCreds());
+ request.set_message("Hello");
+
+ Status s = stub_->Echo(&context, request, &response);
+ EXPECT_FALSE(s.ok());
+ EXPECT_EQ(s.error_code(), StatusCode::UNAUTHENTICATED);
+}
+
+TEST_F(End2endTest, BlockingAuthMetadataProcessorSuccess) {
+ auto* processor = new TestAuthMetadataProcessor(true);
+ StartServer(std::shared_ptr<AuthMetadataProcessor>(processor));
+ ResetStub(false);
+ EchoRequest request;
+ EchoResponse response;
+ ClientContext context;
+ context.set_credentials(processor->GetCompatibleClientCreds());
+ request.set_message("Hello");
+ request.mutable_param()->set_echo_metadata(true);
+ request.mutable_param()->set_expected_client_identity(
+ TestAuthMetadataProcessor::kGoodGuy);
+
+ Status s = stub_->Echo(&context, request, &response);
+ EXPECT_EQ(request.message(), response.message());
+ EXPECT_TRUE(s.ok());
+
+ // Metadata should have been consumed by the processor.
+ EXPECT_FALSE(MetadataContains(
+ context.GetServerTrailingMetadata(), GRPC_AUTHORIZATION_METADATA_KEY,
+ grpc::string("Bearer ") + TestAuthMetadataProcessor::kGoodGuy));
+}
+
+TEST_F(End2endTest, BlockingAuthMetadataProcessorFailure) {
+ auto* processor = new TestAuthMetadataProcessor(true);
+ StartServer(std::shared_ptr<AuthMetadataProcessor>(processor));
+ ResetStub(false);
+ EchoRequest request;
+ EchoResponse response;
+ ClientContext context;
+ context.set_credentials(processor->GetIncompatibleClientCreds());
+ request.set_message("Hello");
+
+ Status s = stub_->Echo(&context, request, &response);
+ EXPECT_FALSE(s.ok());
+ EXPECT_EQ(s.error_code(), StatusCode::UNAUTHENTICATED);
+}
+
// Client sends 20 requests and the server returns CANCELLED status after
// reading 10 requests.
TEST_F(End2endTest, RequestStreamServerEarlyCancelTest) {
diff --git a/test/cpp/util/messages.proto b/test/cpp/util/messages.proto
index 359d1db..a022707 100644
--- a/test/cpp/util/messages.proto
+++ b/test/cpp/util/messages.proto
@@ -40,6 +40,7 @@
bool check_auth_context = 5;
int32 response_message_length = 6;
bool echo_peer = 7;
+ string expected_client_identity = 8; // will force check_auth_context.
}
message EchoRequest {
diff --git a/tools/doxygen/Doxyfile.c++ b/tools/doxygen/Doxyfile.c++
index 887f616..0cd6ddd 100644
--- a/tools/doxygen/Doxyfile.c++
+++ b/tools/doxygen/Doxyfile.c++
@@ -760,7 +760,8 @@
# spaces.
# Note: If this tag is empty the current directory is searched.
-INPUT = include/grpc++/channel.h \
+INPUT = include/grpc++/auth_metadata_processor.h \
+include/grpc++/channel.h \
include/grpc++/client_context.h \
include/grpc++/completion_queue.h \
include/grpc++/create_channel.h \
diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal
index 84b13f9..b2ebb2d 100644
--- a/tools/doxygen/Doxyfile.c++.internal
+++ b/tools/doxygen/Doxyfile.c++.internal
@@ -760,7 +760,8 @@
# spaces.
# Note: If this tag is empty the current directory is searched.
-INPUT = include/grpc++/channel.h \
+INPUT = include/grpc++/auth_metadata_processor.h \
+include/grpc++/channel.h \
include/grpc++/client_context.h \
include/grpc++/completion_queue.h \
include/grpc++/create_channel.h \
diff --git a/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json
index 0585b82..4840e24 100644
--- a/tools/run_tests/sources_and_headers.json
+++ b/tools/run_tests/sources_and_headers.json
@@ -13084,6 +13084,7 @@
"grpc"
],
"headers": [
+ "include/grpc++/auth_metadata_processor.h",
"include/grpc++/channel.h",
"include/grpc++/client_context.h",
"include/grpc++/completion_queue.h",
@@ -13135,6 +13136,7 @@
"language": "c++",
"name": "grpc++",
"src": [
+ "include/grpc++/auth_metadata_processor.h",
"include/grpc++/channel.h",
"include/grpc++/client_context.h",
"include/grpc++/completion_queue.h",
@@ -13264,6 +13266,7 @@
"grpc_unsecure"
],
"headers": [
+ "include/grpc++/auth_metadata_processor.h",
"include/grpc++/channel.h",
"include/grpc++/client_context.h",
"include/grpc++/completion_queue.h",
@@ -13312,6 +13315,7 @@
"language": "c++",
"name": "grpc++_unsecure",
"src": [
+ "include/grpc++/auth_metadata_processor.h",
"include/grpc++/channel.h",
"include/grpc++/client_context.h",
"include/grpc++/completion_queue.h",
diff --git a/vsprojects/grpc++/grpc++.vcxproj b/vsprojects/grpc++/grpc++.vcxproj
index 53930c1..0d8cf1d 100644
--- a/vsprojects/grpc++/grpc++.vcxproj
+++ b/vsprojects/grpc++/grpc++.vcxproj
@@ -213,6 +213,7 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
+ <ClInclude Include="..\..\include\grpc++\auth_metadata_processor.h" />
<ClInclude Include="..\..\include\grpc++\channel.h" />
<ClInclude Include="..\..\include\grpc++\client_context.h" />
<ClInclude Include="..\..\include\grpc++\completion_queue.h" />
diff --git a/vsprojects/grpc++/grpc++.vcxproj.filters b/vsprojects/grpc++/grpc++.vcxproj.filters
index 6bc9ed6..e38328b 100644
--- a/vsprojects/grpc++/grpc++.vcxproj.filters
+++ b/vsprojects/grpc++/grpc++.vcxproj.filters
@@ -99,6 +99,9 @@
</ClCompile>
</ItemGroup>
<ItemGroup>
+ <ClInclude Include="..\..\include\grpc++\auth_metadata_processor.h">
+ <Filter>include\grpc++</Filter>
+ </ClInclude>
<ClInclude Include="..\..\include\grpc++\channel.h">
<Filter>include\grpc++</Filter>
</ClInclude>
diff --git a/vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj b/vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj
index 12fa1b7..d5e44d6 100644
--- a/vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj
+++ b/vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj
@@ -213,6 +213,7 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
+ <ClInclude Include="..\..\include\grpc++\auth_metadata_processor.h" />
<ClInclude Include="..\..\include\grpc++\channel.h" />
<ClInclude Include="..\..\include\grpc++\client_context.h" />
<ClInclude Include="..\..\include\grpc++\completion_queue.h" />
diff --git a/vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj.filters b/vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj.filters
index 17d9d53..da7d87e 100644
--- a/vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj.filters
+++ b/vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj.filters
@@ -84,6 +84,9 @@
</ClCompile>
</ItemGroup>
<ItemGroup>
+ <ClInclude Include="..\..\include\grpc++\auth_metadata_processor.h">
+ <Filter>include\grpc++</Filter>
+ </ClInclude>
<ClInclude Include="..\..\include\grpc++\channel.h">
<Filter>include\grpc++</Filter>
</ClInclude>