Browse Source

Implement basic approach of credentials handling

- Add abstract credentials classes
- Implement SslCredentials and integrate them with Http2Channel
- Update addressbook example with credentials usage
TODO: Implement tests
Alexey Edelev 5 years ago
parent
commit
a1d1b4363d

+ 1 - 0
examples/addressbook/CMakeLists.txt

@@ -75,6 +75,7 @@ set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/globalenums.h PROPERTIES
 include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src/protobuf ${CMAKE_SOURCE_DIR}/src/grpc)
 
 file(GLOB SOURCES main.cpp addressbookengine.cpp universallistmodel.cpp universallistmodelbase.cpp)
+file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/../addressbookserver/cert.pem DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
 
 set(addressbook "addressbook_example")
 add_executable(${addressbook} ${SOURCES} ${GENERATED_SOURCES} resources.qrc)

+ 25 - 1
examples/addressbook/addressbookengine.cpp

@@ -26,16 +26,40 @@
 #include "addressbookengine.h"
 #include "addressbookclient.h"
 #include <http2channel.h>
+#include <insecurecredentials.h>
+#include <sslcredentials.h>
 
 #include <QDebug>
+#include <QFile>
+#include <QSslConfiguration>
+#include <QCryptographicHash>
 
 using namespace qtprotobuf::examples;
 
+class AuthCredentials : public qtprotobuf::CallCredentials
+{
+public:
+    AuthCredentials(const QString &userName, const QString &password) {
+        callCredentials_p = [userName, password]() { return CredentialMap{{QLatin1String("user-name"), QVariant::fromValue(userName)},
+                            {QLatin1String("user-password"), QVariant::fromValue(password)}}; };
+    }
+};
+
 AddressBookEngine::AddressBookEngine() : QObject()
   , m_client(new AddressBookClient)
   , m_contacts(new ContactsListModel({}, this))
 {
-    std::shared_ptr<qtprotobuf::AbstractChannel> channel(new qtprotobuf::Http2Channel("localhost", 65001));
+    //Prepare ssl configuration
+    QSslConfiguration conf = QSslConfiguration::defaultConfiguration();
+    QFile certFile("cert.pem");
+    certFile.open(QIODevice::ReadOnly);
+    QByteArray cert = certFile.readAll();
+    conf.setCaCertificates({QSslCertificate(cert)});
+    conf.setProtocol(QSsl::TlsV1_2);
+    conf.setAllowedNextProtocols({QSslConfiguration::ALPNProtocolHTTP2});
+
+    std::shared_ptr<qtprotobuf::AbstractChannel> channel(new qtprotobuf::Http2Channel("localhost", 65001, qtprotobuf::SslCredentials(conf) |
+                                                                                      AuthCredentials("authorizedUser", QCryptographicHash::hash("test", QCryptographicHash::Md5).toHex())));
     m_client->attachChannel(channel);
     m_client->subscribeContactsUpdates(ListFrame());
     connect(m_client, &AddressBookClient::contactsUpdated, this, [this](const Contacts &contacts) {

+ 2 - 0
examples/addressbookserver/CMakeLists.txt

@@ -20,3 +20,5 @@ if(WIN32)
 elseif(UNIX)
     target_link_libraries(${ADDRESSBOOK_SERVER} protobuf grpc grpc++)
 endif()
+
+file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/cert.pem ${CMAKE_CURRENT_SOURCE_DIR}/key.pem DESTINATION ${CMAKE_CURRENT_BINARY_DIR})

+ 34 - 0
examples/addressbookserver/cert.pem

@@ -0,0 +1,34 @@
+-----BEGIN CERTIFICATE-----
+MIIF6zCCA9OgAwIBAgIJAMPfLBc9ERZGMA0GCSqGSIb3DQEBCwUAMIGLMQswCQYD
+VQQGEwJERTEPMA0GA1UECAwGQmVybGluMQ8wDQYDVQQHDAZCZXJsaW4xEzARBgNV
+BAoMClF0UHJvdG9idWYxDDAKBgNVBAsMA1JuRDESMBAGA1UEAwwJbG9jYWxob3N0
+MSMwIQYJKoZIhvcNAQkBFhRxdHByb3RvYnVmQGdtYWlsLmNvbTAeFw0xOTA0MjMx
+NTI5MzNaFw0yMDA0MjIxNTI5MzNaMIGLMQswCQYDVQQGEwJERTEPMA0GA1UECAwG
+QmVybGluMQ8wDQYDVQQHDAZCZXJsaW4xEzARBgNVBAoMClF0UHJvdG9idWYxDDAK
+BgNVBAsMA1JuRDESMBAGA1UEAwwJbG9jYWxob3N0MSMwIQYJKoZIhvcNAQkBFhRx
+dHByb3RvYnVmQGdtYWlsLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
+ggIBANCB7zdc6v+gsYCsGIYsT3iyGPgvFG7cJJWnQO9iW6Tn/kBcreYuCdVSSODK
+/NtPaA1r8j9FCDvLkzewNQ2Idv08/oKBYfOaVtQvh1cn7ktr1usiXtYv16cimeKk
+8iYbiczkZOah2xq+ivm9+05WkYTzcSjBpXg19894024GHd7oRV9G5MCr760k8YLM
+ALnoPpTl0yfs5cEcTybqvZFZNqkHDX2ziEbcF/mVcxcyEsmenbX6MI0easg2qZeq
+Sb7AW7tIMVoWUxDkUIor4vogbgU2IljAjzn0i+fPncB41TU5IiIU35vO67kFBXyI
+Ms3LpN1+Siz5HYHjwaWl0ecebuT83kP33VNc1ULkKJG5UbZUYfgZjUwZPgFFzVzQ
+cif0mhYj9s5Nmn6Q/twxeIXOIZAQdLOq625Wwx8bh+mGaV5mtw3wtXJjbceGa1z/
+Pnni4x2B7IfSiOzGLZZNRRHIjUskeONHUHn8YBrLLg5RT+tvPnGDVPh0cKEH2P4F
+cNRPmaqg75siFoJ+m3DlFM952tRcfzJumgbUfEnrL8bxTZPYVoq22qCDm3UGNW8n
+pthv8Y2hemv6V9lF050z2vpL2DtU9RmtkBWx7ipPUdsEOm4e0rdOCk7zo8IAiWMQ
+6o1L23IPTsqaEpqk3b7tT+5dtLkpeomAT8Hz91ChWUb6jrTVAgMBAAGjUDBOMB0G
+A1UdDgQWBBQIb2RjqkeMsUjH9QJ5BYnAtJ/vojAfBgNVHSMEGDAWgBQIb2RjqkeM
+sUjH9QJ5BYnAtJ/vojAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQDG
+2x+I9C+4XFCpg9KVA+BBi6+7yi4ulH0yv7W3hNWBV2MxTpvVN6RIt8xI1sPFXc2T
+g2wCIAIPGIPNDe8hO3gcfIIe059P+J3ZNjybUg5p6CR9O7kqgqV/nL4gVRuBJeYq
+6YLr1lKL9r3Zdbkq1tcaCaj816zdXVky0us72XP28/xJc96crgKzDI69vESy5jq6
+QT/HoBwYiSaWXNgb0zJzc//e0upXSLeTkYizAJ5OGkQ/MQYE7gDvtPlGVhQ0rnl/
+FLsiZJokuxtLOTvYJ8Gynjz3QwbClN/bmUbOgD0fqW6BEZyZSJ4zCz0BJnwg46gc
+IN+p6vi50MG4ZcJcnMl/3tAxt2RxHNLi0j21NSLyFj60gK+vL3/zzkdYF9ZxX1L+
+dqhTqioCVrV96rJIQbz6JrbFhUCdyEHYG7yi6LxHTUex33XouhGAfZ0lri5wWZq8
+0Rx8PEZ7SbjARtnvA7uXIAfgD+n3oqnkg9IDPH8PbdRcJrAdje3O2c1+OzTAC8ni
+czaCWG748gfZPe0JpENP7P56RTh9avj4sHISCx4r+sh0lruLp2JZPr9qtw09uWlq
+Mn58bcnDu9RjVfPXk43s9WfJC9XII+JoNcW3iJDSHxQb9XLvwR9ntWhkVvOKenPP
+b00kWr6FfId6fiOmUww5WkW+Wtt4XOYsypaN+DskeA==
+-----END CERTIFICATE-----

+ 52 - 0
examples/addressbookserver/key.pem

@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+0mVZG9D69wbsZefVDJii9NLvWThGog==
+-----END PRIVATE KEY-----

+ 31 - 1
examples/addressbookserver/main.cpp

@@ -1,4 +1,6 @@
 #include <iostream>
+#include <fstream>
+#include <sstream>
 #include <vector>
 #include <grpc++/grpc++.h>
 #include "addressbook.pb.h"
@@ -47,6 +49,22 @@ public:
 
     ::grpc::Status addContact(::grpc::ServerContext* context, const Contact* request, Contacts* response) override
     {
+        bool isUserOk = false;
+        bool isPasswordOk = false;
+        for (auto it = context->client_metadata().begin(); it != context->client_metadata().end(); ++it) {
+            if ((*it).first == std::string("user-name") && (*it).second == "authorizedUser") {
+                isUserOk = true;
+            }
+            if ((*it).first == std::string("user-password") && (*it).second == "098f6bcd4621d373cade4e832627b4f6") {
+                isPasswordOk = true;
+            }
+            std::cout << (*it).first << (*it).second << std::endl;
+        }
+
+        if (!isUserOk || !isPasswordOk) {
+            return ::grpc::Status(::grpc::StatusCode::UNAUTHENTICATED, grpc::string("Invalid user or password"));
+        }
+
         std::cout << "addContact called" << std::endl;
         Contact* newContact = m_contacts.add_list();
         *newContact = *request;
@@ -98,8 +116,20 @@ int main(int argc, char *argv[])
     std::string server_address("localhost:65001");
     AddressBookService service;
 
+    std::ifstream tfile("cert.pem");
+    std::stringstream cert;
+    cert << tfile.rdbuf();
+    tfile.close();
+
+    tfile.open("key.pem");
+    std::stringstream key;
+    key << tfile.rdbuf();
+    tfile.close();
+
     grpc::ServerBuilder builder;
-    builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
+    grpc::SslServerCredentialsOptions opts(GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE);
+    opts.pem_key_cert_pairs.push_back({key.str(), cert.str()});
+    builder.AddListeningPort(server_address, grpc::SslServerCredentials(opts));
     builder.RegisterService(&service);
     std::unique_ptr<grpc::ServerCompletionQueue> cq = builder.AddCompletionQueue();
     std::unique_ptr<grpc::Server> server(builder.BuildAndStart());

+ 8 - 2
src/grpc/CMakeLists.txt

@@ -12,12 +12,18 @@ endif()
 file(GLOB SOURCES asyncreply.cpp
     abstractchannel.cpp
     http2channel.cpp
-    abstractclient.cpp)
+    abstractclient.cpp
+    abstractcredentials.cpp
+    sslcredentials.cpp
+    insecurecredentials.cpp)
 
 file(GLOB HEADERS asyncreply.h
     abstractchannel.h
     http2channel.h
-    abstractclient.h)
+    abstractclient.h
+    abstractcredentials.h
+    sslcredentials.h
+    insecurecredentials.h)
 
 set(GRPC_SUPPORT_LIBRARY_TARGET qtgrpc)
 add_library(${GRPC_SUPPORT_LIBRARY_TARGET} ${SOURCES})

+ 26 - 0
src/grpc/abstractcredentials.cpp

@@ -0,0 +1,26 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of qtprotobuf project https://git.semlanik.org/semlanik/qtprotobuf
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "abstractcredentials.h"

+ 137 - 0
src/grpc/abstractcredentials.h

@@ -0,0 +1,137 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of qtprotobuf project https://git.semlanik.org/semlanik/qtprotobuf
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#pragma once
+
+#include <QString>
+#include <QHash>
+#include <QVariant>
+
+#include <functional>
+
+namespace qtprotobuf {
+
+class CallCredentials;
+class ChannelCredentials;
+
+class AbstractCredentials
+{
+public:
+    template<typename Call, typename Channel,
+             typename std::enable_if_t<std::is_base_of<qtprotobuf::CallCredentials, Call>::value
+                                       && std::is_base_of<qtprotobuf::ChannelCredentials, Channel>::value, int> = 0>
+    AbstractCredentials(const Call &call, const Channel &channel);
+
+    template<typename Call,
+             typename std::enable_if_t<std::is_base_of<qtprotobuf::CallCredentials, Call>::value, int> = 0>
+    AbstractCredentials(const Call &call);
+
+    template<typename Channel,
+             typename std::enable_if_t<std::is_base_of<qtprotobuf::ChannelCredentials, Channel>::value, int> = 0>
+    AbstractCredentials(const Channel &channel);
+
+    using CredentialMap = QHash<QLatin1String, QVariant>;
+
+    CredentialMap callCredentials() {
+        if (callCredentials_p) {
+            return callCredentials_p();
+        }
+        return CredentialMap();
+    }
+
+    CredentialMap channelCredentials() {
+        if (channelCredentials_p) {
+            return channelCredentials_p();
+        }
+        return CredentialMap();
+    }
+protected:
+    AbstractCredentials() = default;
+
+    using CredentialsFunction = std::function<CredentialMap(void)>;
+    CredentialsFunction callCredentials_p;
+    CredentialsFunction channelCredentials_p;
+};
+
+class CallCredentials : public AbstractCredentials
+{
+protected:
+    CallCredentials() {
+        channelCredentials_p = [](){ return CredentialMap(); };
+    }
+
+private:
+    using AbstractCredentials::channelCredentials_p;
+};
+
+class ChannelCredentials : public AbstractCredentials
+{
+protected:
+    ChannelCredentials() {
+        callCredentials_p = [](){ return CredentialMap(); };
+    }
+private:
+    using AbstractCredentials::callCredentials_p;
+};
+
+template<typename Call, typename Channel,
+         typename std::enable_if_t<std::is_base_of<qtprotobuf::CallCredentials, Call>::value
+                                   && std::is_base_of<qtprotobuf::ChannelCredentials, Channel>::value, int> = 0>
+AbstractCredentials::AbstractCredentials(const Call &call, const Channel &channel)
+{
+    callCredentials_p = call.callCredentials_p;
+    channelCredentials_p = channel.channelCredentials_p;
+}
+
+template<typename Call,
+         typename std::enable_if_t<std::is_base_of<qtprotobuf::CallCredentials, Call>::value, int> = 0>
+AbstractCredentials::AbstractCredentials(const Call &call)
+{
+    callCredentials_p = call.callCredentials_p;
+}
+
+template<typename Channel,
+         typename std::enable_if_t<std::is_base_of<qtprotobuf::ChannelCredentials, Channel>::value, int> = 0>
+AbstractCredentials::AbstractCredentials(const Channel &channel)
+{
+    channelCredentials_p = channel.channelCredentials_p;
+}
+}
+
+template<typename Call, typename Channel,
+         typename std::enable_if_t<std::is_base_of<qtprotobuf::CallCredentials, Call>::value
+                                   && std::is_base_of<qtprotobuf::ChannelCredentials, Channel>::value, int> = 0>
+qtprotobuf::AbstractCredentials operator|(const Call &call, const Channel &channel)
+{
+    return qtprotobuf::AbstractCredentials(call, channel);
+}
+
+template<typename Call, typename Channel,
+         typename std::enable_if_t<std::is_base_of<qtprotobuf::CallCredentials, Call>::value
+                                   && std::is_base_of<qtprotobuf::ChannelCredentials, Channel>::value, int> = 0>
+qtprotobuf::AbstractCredentials operator|(const Channel &channel, const Call &call)
+{
+    return qtprotobuf::AbstractCredentials(call, channel);
+}

+ 24 - 5
src/grpc/http2channel.cpp

@@ -35,8 +35,10 @@
 #include <QEventLoop>
 #include <QTimer>
 #include <QtEndian>
+
 #include "asyncreply.h"
 #include "abstractclient.h"
+#include "abstractcredentials.h"
 
 #include <unordered_map>
 
@@ -90,17 +92,23 @@ namespace qtprotobuf {
 struct Http2ChannelPrivate {
     QUrl url;
     QNetworkAccessManager nm;
+    AbstractCredentials credentials;
+    QSslConfiguration sslConfig;
     QNetworkReply* post(const QString &method, const QString &service, const QByteArray &args, bool stream = false) {
         QUrl callUrl = url;
         callUrl.setPath("/" + service + "/" + method);
 
         qProtoDebug() << "Service call url: " << callUrl;
-
         QNetworkRequest request(callUrl);
         request.setHeader(QNetworkRequest::ContentTypeHeader, "application/grpc");
         request.setRawHeader(GrpcAcceptEncodingHeader, "identity,deflate,gzip");
         request.setRawHeader(AcceptEncodingHeader, "identity,gzip");
         request.setRawHeader(TEHeader, "trailers");
+        request.setSslConfiguration(sslConfig);
+        AbstractCredentials::CredentialMap callCredentials = credentials.callCredentials();
+        for (auto i = callCredentials.begin(); i != callCredentials.end(); ++i) {
+            request.setRawHeader(i.key().data(), i.value().toString().toUtf8());
+        }
 
         request.setAttribute(QNetworkRequest::Http2DirectAttribute, true);
 
@@ -144,14 +152,25 @@ struct Http2ChannelPrivate {
         //Message size doesn't matter for now
         return networkReply->readAll().mid(5);
     }
+
+    Http2ChannelPrivate(const AbstractCredentials &credentials_) : credentials(credentials_) {
+        if (credentials.channelCredentials().contains(QLatin1String("sslConfig"))) {
+            sslConfig = credentials.channelCredentials().value(QLatin1String("sslConfig")).value<QSslConfiguration>();
+        }
+
+        if (sslConfig.isNull()) {
+            url.setScheme("http");
+        } else {
+            url.setScheme("https");
+        }
+    }
 };
 
 }
 
-Http2Channel::Http2Channel(const QString &addr, quint16 port) : AbstractChannel()
-  , d(new Http2ChannelPrivate)
+Http2Channel::Http2Channel(const QString &addr, quint16 port, const AbstractCredentials &credentials) : AbstractChannel()
+  , d(new Http2ChannelPrivate(credentials))
 {
-    d->url.setScheme("http");
     d->url.setHost(addr, QUrl::StrictMode);
     d->url.setPort(port);
 }
@@ -178,7 +197,7 @@ AbstractChannel::StatusCodes Http2Channel::call(const QString &method, const QSt
     StatusCodes grpcStatus = StatusCodes::Unknown;
     ret = d->processReply(networkReply, grpcStatus);
 
-    qProtoDebug() << __func__ << "RECV: " << ret.toHex();
+    qProtoDebug() << __func__ << "RECV: " << ret.toHex() << "grpcStatus" << grpcStatus;
     return grpcStatus;
 }
 

+ 2 - 1
src/grpc/http2channel.h

@@ -33,11 +33,12 @@
 namespace qtprotobuf {
 
 struct Http2ChannelPrivate;
+class AbstractCredentials;
 
 class Http2Channel final : public AbstractChannel
 {
 public:
-    Http2Channel(const QString &addr, quint16 port);
+    Http2Channel(const QString &addr, quint16 port, const AbstractCredentials &credentials);
     ~Http2Channel();
 
     StatusCodes call(const QString &method, const QString &service, const QByteArray &args, QByteArray &ret) override;

+ 26 - 0
src/grpc/insecurecredentials.cpp

@@ -0,0 +1,26 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of qtprotobuf project https://git.semlanik.org/semlanik/qtprotobuf
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "insecurecredentials.h"

+ 40 - 0
src/grpc/insecurecredentials.h

@@ -0,0 +1,40 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of qtprotobuf project https://git.semlanik.org/semlanik/qtprotobuf
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#pragma once
+
+#include "abstractcredentials.h"
+
+namespace qtprotobuf {
+
+class InsecureCredentials : public ChannelCredentials
+{
+public:
+    InsecureCredentials() {
+        channelCredentials_p = []() { return AbstractCredentials::CredentialMap(); };
+    }
+};
+
+}

+ 26 - 0
src/grpc/sslcredentials.cpp

@@ -0,0 +1,26 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of qtprotobuf project https://git.semlanik.org/semlanik/qtprotobuf
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "sslcredentials.h"

+ 44 - 0
src/grpc/sslcredentials.h

@@ -0,0 +1,44 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of qtprotobuf project https://git.semlanik.org/semlanik/qtprotobuf
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#pragma once
+
+#include "abstractcredentials.h"
+
+#include <QSslConfiguration>
+
+namespace qtprotobuf {
+
+class SslCredentials : public ChannelCredentials
+{
+public:
+    SslCredentials(const QSslConfiguration &configuation) {
+        CredentialMap credentials = CredentialMap{{QLatin1String("sslConfig"),
+                QVariant::fromValue<QSslConfiguration>(configuation)}};
+        channelCredentials_p = [credentials]() { return credentials; };
+    }
+};
+
+}

+ 7 - 6
tests/test_grpc/clienttest.cpp

@@ -26,6 +26,7 @@
 #include "testserviceclient.h"
 #include "http2channel.h"
 #include "qtprotobuf.h"
+#include "insecurecredentials.h"
 
 #include <QTimer>
 
@@ -60,7 +61,7 @@ TEST_F(ClientTest, StringEchoTest)
     int argc = 0;
     QCoreApplication app(argc, nullptr);
     TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<Http2Channel>("localhost", 50051));
+    testClient.attachChannel(std::make_shared<Http2Channel>("localhost", 50051, InsecureCredentials()));
     SimpleStringMessage result;
     SimpleStringMessage request;
     request.setTestFieldString("Hello beach!");
@@ -73,7 +74,7 @@ TEST_F(ClientTest, StringEchoAsyncTest)
     int argc = 0;
     QCoreApplication app(argc, nullptr);
     TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<Http2Channel>("localhost", 50051));
+    testClient.attachChannel(std::make_shared<Http2Channel>("localhost", 50051, InsecureCredentials()));
     SimpleStringMessage result;
     SimpleStringMessage request;
     request.setTestFieldString("Hello beach!");
@@ -96,7 +97,7 @@ TEST_F(ClientTest, StringEchoAsync2Test)
     int argc = 0;
     QCoreApplication app(argc, nullptr);
     TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<Http2Channel>("localhost", 50051));
+    testClient.attachChannel(std::make_shared<Http2Channel>("localhost", 50051, InsecureCredentials()));
     SimpleStringMessage result;
     SimpleStringMessage request;
     request.setTestFieldString("Hello beach!");
@@ -117,7 +118,7 @@ TEST_F(ClientTest, StringEchoAsyncAbortTest)
     int argc = 0;
     QCoreApplication app(argc, nullptr);
     TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<Http2Channel>("localhost", 50051));
+    testClient.attachChannel(std::make_shared<Http2Channel>("localhost", 50051, InsecureCredentials()));
     SimpleStringMessage result;
     SimpleStringMessage request;
     request.setTestFieldString("sleep");
@@ -172,7 +173,7 @@ TEST_F(ClientTest, StringEchoStreamTest)
     int argc = 0;
     QCoreApplication app(argc, nullptr);
     TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<Http2Channel>("localhost", 50051));
+    testClient.attachChannel(std::make_shared<Http2Channel>("localhost", 50051, InsecureCredentials()));
     SimpleStringMessage result;
     SimpleStringMessage request;
     request.setTestFieldString("Stream");
@@ -205,7 +206,7 @@ TEST_F(ClientTest, StringEchoStreamTestRetUpdates)
     int argc = 0;
     QCoreApplication app(argc, nullptr);
     TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<Http2Channel>("localhost", 50051));
+    testClient.attachChannel(std::make_shared<Http2Channel>("localhost", 50051, InsecureCredentials()));
     SimpleStringMessage result;
     SimpleStringMessage request;
     request.setTestFieldString("Stream");