Update the callback logic for handle the wauthn_cb_update_linked_data 97/309697/9
authorYonggoo Kang <ygace.kang@samsung.com>
Mon, 15 Apr 2024 16:21:04 +0000 (01:21 +0900)
committerYonggoo Kang <ygace.kang@samsung.com>
Tue, 16 Apr 2024 10:19:07 +0000 (19:19 +0900)
 - Add wauthn_cb_update_linked_data related logic and unittests
 - Fix bug on the __checkValidity() about valid range
 - Move UnsetBusy() into the Response Callback
 - Minor fix about naming
 - Ready for manual test with the added callback

Change-Id: I31794d5e6c9036f142ae8aaf0b58552e59c98546

15 files changed:
srcs/client/client-request.h
srcs/client/client.cpp
srcs/common/serialization.cpp
srcs/common/utils.cpp
srcs/server/request-ga.h
srcs/server/request-mc.h
srcs/server/request.h
srcs/server/service.cpp
srcs/server/service.h
tests/client-request-test.cpp
tests/manual/man_tests.cpp
tests/serialization-test.cpp
tests/test-common.cpp
tests/test-common.h
tests/webauthn-client-test.cpp

index c7f1057..e02fe4a 100644 (file)
@@ -46,19 +46,24 @@ public:
     {
     }
 
-    int getStatus() const
+    int GetStatus() const
     {
         return m_status;
     }
 
-    bool failed() const
+    bool Failed() const
     {
-        return m_status != WAUTHN_ERROR_NONE;
+        return m_status < WAUTHN_ERROR_NONE;
     }
 
-    GenericClientRequest &send()
+    bool Incompleted() const
     {
-        if (failed())
+        return m_status == WAUTHN_ERROR_NONE_AND_WAIT;
+    }
+
+    GenericClientRequest &Send()
+    {
+        if (Failed())
         {
             m_status = WAUTHN_ERROR_INVALID_STATE;
             return *this;
@@ -71,25 +76,25 @@ public:
         }
         m_sent = true;
         m_status = m_conn->send(m_buffer);
-        if (failed())
+        if (Failed())
             LogError("Error in send. Error code: " << m_status);
         return *this;
     }
 
-    template <typename... T> GenericClientRequest &send(const T&... args)
+    template <typename... T> GenericClientRequest &Send(const T&... args)
     {
-        if (failed())
+        if (Failed())
         {
             m_status = WAUTHN_ERROR_INVALID_STATE;
             return *this;
         }
         Serialization::Serialize(m_buffer, args...);
-        return send();
+        return Send();
     }
 
-    GenericClientRequest &recv()
+    GenericClientRequest &Recv()
     {
-        if (failed())
+        if (Failed())
         {
             m_status = WAUTHN_ERROR_INVALID_STATE;
             return *this;
@@ -101,37 +106,37 @@ public:
             return *this;
         }
         m_status = m_conn->recv(m_buffer);
-        if (failed())
+        if (Failed())
             LogError("Error in recv: " << wauthn_error_to_string(m_status));
         else
             Deserialization::Deserialize(m_buffer, m_status);
         return *this;
     }
 
-    template <typename... T> GenericClientRequest &recv(T&... args)
+    template <typename... T> GenericClientRequest &Recv(T&... args)
     {
-        if (failed())
+        if (Failed())
         {
             m_status = WAUTHN_ERROR_INVALID_STATE;
             return *this;
         }
-        recv();
-        if (failed())
+        Recv();
+        if (Failed())
             LogError("Error in recv: " << wauthn_error_to_string(m_status));
         else
             Deserialization::Deserialize(m_buffer, args...);
         return *this;
     }
 
-    template <typename... T> GenericClientRequest &recv(T&&... args)
+    template <typename... T> GenericClientRequest &Recv(T&&... args)
     {
-        if (failed())
+        if (Failed())
         {
             m_status = WAUTHN_ERROR_INVALID_STATE;
             return *this;
         }
-        recv();
-        if (failed())
+        Recv();
+        if (Failed())
         {
             LogError("Error in recv: " << wauthn_error_to_string(m_status));
         }
@@ -139,29 +144,29 @@ public:
         return *this;
     }
 
-    template <typename... T> GenericClientRequest &sendRequest(const T&... args)
+    template <typename... T> GenericClientRequest &SendRequest(const T&... args)
     {
-        if (failed())
+        if (Failed())
         {
             m_status = WAUTHN_ERROR_INVALID_STATE;
             return *this;
         }
         Serialization::Serialize(m_buffer, args...);
-        if (send().failed())
+        if (Send().Failed())
             return *this;
-        return recv();
+        return Recv();
     }
 
-    GenericClientRequest &sendRequest()
+    GenericClientRequest &SendRequest()
     {
-        if (failed())
+        if (Failed())
         {
             m_status = WAUTHN_ERROR_INVALID_STATE;
             return *this;
         }
-        if (send().failed())
+        if (Send().Failed())
             return *this;
-        return recv();
+        return Recv();
     }
 
     virtual int Init() = 0;
@@ -218,24 +223,39 @@ template <typename A, typename B>
 void cb_worker(std::shared_ptr<GenericClientRequest> request, A &callbacks, B cred)
 {
     int ret = try_catch([&]() -> int {
-        if (callbacks == nullptr || callbacks->response_callback == nullptr){
-            LogError("Invalid callback parameter");
-            return WAUTHN_ERROR_INVALID_PARAMETER;
-        }
         if (callbacks->qrcode_callback == nullptr)
             LogDebug("There is no qrcode_callback");
         else{ //callbacks->qrcode_callback != nullptr
             std::string qr_code;
-            if (request->recv(qr_code).failed())
+            if (request->Recv(qr_code).Failed())
                 LogError("Error on receive qrcode");
             LogDebug("Received qr_contents: " << qr_code);
             callbacks->qrcode_callback(qr_code.c_str(), callbacks->user_data);
         }
-        if(request->recv(&cred).failed())
-            LogError("Error on receive response");
+        request->Recv(&cred);
+        callbacks->response_callback(cred, wauthn_error_e(request->GetStatus()),
+                                     callbacks->user_data);
+        if(request->Failed())
+        {
+            LogError("Error on receive response: " << request->GetStatus());
+            return WAUTHN_ERROR_INVALID_STATE;
+        }
+
+        wauthn_hybrid_linked_data_s *linked_data = nullptr;
+
+        while (request->Recv(&linked_data).Incompleted())
+        {
+            callbacks->linked_data_callback(linked_data, wauthn_error_e(request->GetStatus()),
+                                            callbacks->user_data);
+            LogDebug("More update linked_data can be received. Waiting to recv()");
+        }
+        LogDebug("There is no more updated linked_data");
+        callbacks->linked_data_callback(linked_data, wauthn_error_e(request->GetStatus()),
+                                        callbacks->user_data);
         return WAUTHN_ERROR_NONE;
     });
-    callbacks->response_callback(cred, wauthn_error_e(ret), callbacks->user_data);
+    if (ret != WAUTHN_ERROR_NONE)
+        LogError("Error on handling callbacks");
 }
 
 template <typename T>
@@ -255,11 +275,11 @@ int wauthn_process(const wauthn_client_data_s *client_data,
         if (ret != WAUTHN_ERROR_NONE)
             return ret;
 
-        if (request->sendRequest(client_data, options).failed())
-            return request->getStatus();
-        LogDebug("Response: " << wauthn_error_to_string(request->getStatus()));
+        if (request->SendRequest(client_data, options).Failed())
+            return request->GetStatus();
+        LogDebug("Response: " << wauthn_error_to_string(request->GetStatus()));
 
-        typename T::PubKeyCred *cred = NULL;
+        typename T::PubKeyCred *cred = nullptr;
         std::thread worker([request, callbacks, cred]{cb_worker(std::move(request), callbacks, cred);});
         worker.detach();
         return WAUTHN_ERROR_NONE;
index 671ff37..4061824 100644 (file)
@@ -44,8 +44,9 @@ int wauthn_cancel()
     return try_catch([&]() -> int {
         ClientRequest request(WebAuthnCall::CANCEL);
         request.Init();
-        if (request.sendRequest().failed())
-            LogError("Error on cancel request, Response: " << wauthn_error_to_string(request.getStatus()));
-        return request.getStatus();
+        if (request.SendRequest().Failed())
+            LogError("Error on cancel request, Response: "
+                     << wauthn_error_to_string(request.GetStatus()));
+        return request.GetStatus();
     });
 }
index 32ec092..2f44dcf 100644 (file)
@@ -106,7 +106,7 @@ void WAuthnCtypeSerializer::deserialize(IStream& stream, const char** data) {
 
 // For wauthn_error_e
 void __checkValidity(const wauthn_error_e data) {
-    if (data > WAUTHN_ERROR_NONE || data < WAUTHN_ERROR_CANCELLED)
+    if (data > WAUTHN_ERROR_NONE_AND_WAIT || data < WAUTHN_ERROR_TIMEOUT)
         ThrowMsg(SerializationException::InvalidStreamData,
             "Invalid value for wauthn_error_e: value=" << data);
 }
index 80b783b..712bec7 100644 (file)
@@ -43,12 +43,14 @@ void checkParameters(const wauthn_pubkey_cred_request_options_s *options)
 }
 void checkParameters(wauthn_mc_callbacks_s *callbacks)
 {
-    if (callbacks == nullptr || callbacks->response_callback == nullptr)
+    if (callbacks == nullptr || callbacks->response_callback == nullptr
+        || callbacks->linked_data_callback == nullptr)
         ThrowMsg(ClientException::InvalidParameter, "Invalid callback");
 }
 void checkParameters(wauthn_ga_callbacks_s *callbacks)
 {
-    if (callbacks == nullptr || callbacks->response_callback == nullptr)
+    if (callbacks == nullptr || callbacks->response_callback == nullptr
+        || callbacks->linked_data_callback == nullptr)
         ThrowMsg(ClientException::InvalidParameter, "Invalid callback");
 }
 void checkParameters(wauthn_hybrid_linked_data_s *linked_device,
index 78a700b..fdecb2c 100644 (file)
@@ -40,6 +40,7 @@ public:
             MessageBuffer buffer(userData->service->GetSocketManager()->newMessage());
             Serialization::Serialize(buffer, result, pubkey_cred);
             userData->service->GetSocketManager()->Write(userData->connectionID, std::move(buffer));
+            userData->service->UnsetBusy();
         }
         else
         {
index 7af5fb7..d925c04 100644 (file)
@@ -40,6 +40,7 @@ public:
             MessageBuffer buffer(userData->service->GetSocketManager()->newMessage());
             Serialization::Serialize(buffer, result, pubkey_cred);
             userData->service->GetSocketManager()->Write(userData->connectionID, std::move(buffer));
+            userData->service->UnsetBusy();
         }
         else
         {
index b67eff5..ce06924 100644 (file)
@@ -33,6 +33,21 @@ public:
             LogError("Cannot write qr_contents to client");
     }
 
+    static void UpdateLinkedDataCallback(const wauthn_hybrid_linked_data_s *linked_data,
+                                         wauthn_error_e result,
+                                         void *user_data)
+    {
+        user_data_s *userData = GetUserData(user_data);
+        if (userData != nullptr)
+        {
+            MessageBuffer buffer(userData->service->GetSocketManager()->newMessage());
+            Serialization::Serialize(buffer, result, linked_data);
+            userData->service->GetSocketManager()->Write(userData->connectionID, std::move(buffer));
+        }
+        else
+            LogError("Cannot write updated linked_data to client");
+    }
+
 protected:
     static user_data_s *GetUserData(void *user_data)
     {
index e4ec284..9fc3999 100644 (file)
@@ -83,7 +83,8 @@ void GenericService::Worker(SocketManager::ConnectionID connectionID,
     typename T::Callbacks callbacks;
     callbacks.qrcode_callback = &(Request::QRCallback);
     callbacks.response_callback = &(T::ResponseCallback);
-    //checkParameters(clientData, options, &callbacks);
+    callbacks.linked_data_callback = &(Request::UpdateLinkedDataCallback);
+
     if (options->linked_device != nullptr)  // The qrcode_callback should not be called.
     {
         LogDebug("Adjust qrcode_callback to nullptr");
@@ -108,7 +109,6 @@ void GenericService::Worker(SocketManager::ConnectionID connectionID,
     });
     if (ret != WAUTHN_ERROR_NONE)
         LogError("Unhandled Error: " << wauthn_error_to_string(ret));
-    UnsetBusy();
 }
 
 template <typename T>
index 3f49e37..e5aba54 100644 (file)
@@ -69,6 +69,11 @@ public:
      */
     SocketManager *GetSocketManager();
 
+    /**
+     * Unset to server is not busy
+     */
+    void UnsetBusy();
+
     virtual SocketManager::ServiceDescription GetServiceDescription() = 0;
 
 private:
@@ -117,11 +122,6 @@ private:
      */
     int GetCredentials(SocketManager::ConnectionID connectionID, Cred *creds);
 
-    /**
-     * Unset to server is not busy
-     */
-    void UnsetBusy();
-
     bool PluginEnabled();
 
     int HandleNotSupported(Event &&msg);
index 1a2a526..d2b6333 100644 (file)
@@ -90,6 +90,22 @@ void wah_make_credential(const wauthn_client_data_s *client_data,
     sleep(1);
     if (callbacks != nullptr && callbacks->response_callback != nullptr)
         callbacks->response_callback(nullptr, WAUTHN_ERROR_NONE, callbacks->user_data);
+
+    if (callbacks != nullptr && callbacks->linked_data_callback != nullptr)
+    {
+        int callTimes = g_target_times_update_linked_data;
+        std::cout << "Calling linked_data_callback for " << callTimes << " times.." <<
+            std::endl;
+        while (callTimes > 1)
+        {
+            sleep(1);
+            callbacks->linked_data_callback(nullptr, WAUTHN_ERROR_NONE_AND_WAIT,
+                                            callbacks->user_data);
+            callTimes -= 1;
+        }
+        sleep(1);
+        callbacks->linked_data_callback(nullptr, WAUTHN_ERROR_NONE, callbacks->user_data);
+    }
 }
 
 void wah_get_assertion(const wauthn_client_data_s *client_data,
@@ -105,6 +121,22 @@ void wah_get_assertion(const wauthn_client_data_s *client_data,
     sleep(1);
     if (callbacks != nullptr && callbacks->response_callback != nullptr)
         callbacks->response_callback(nullptr, WAUTHN_ERROR_NONE, callbacks->user_data);
+
+    if (callbacks != nullptr && callbacks->linked_data_callback != nullptr)
+    {
+        int callTimes = g_target_times_update_linked_data;
+        std::cout << "Calling linked_data_callback for " << callTimes << " times.." <<
+            std::endl;
+        while (callTimes > 1)
+        {
+            sleep(1);
+            callbacks->linked_data_callback(nullptr, WAUTHN_ERROR_NONE_AND_WAIT,
+                                            callbacks->user_data);
+            callTimes -= 1;
+        }
+        sleep(1);
+        callbacks->linked_data_callback(nullptr, WAUTHN_ERROR_NONE, callbacks->user_data);
+    }
 }
 
 int wah_cancel()
@@ -220,9 +252,9 @@ TEST_F(ClientRequestTest, send_failed_N)
     try{
         TestInvalidClientRequest invalidRequest(WebAuthnCall::MAKE_CREDENTIAL);
         EXPECT_EQ(invalidRequest.Init(), WAUTHN_ERROR_SOCKET);
-        EXPECT_EQ(invalidRequest.getStatus(), WAUTHN_ERROR_SOCKET);
-        invalidRequest.send();
-        EXPECT_EQ(invalidRequest.getStatus(), WAUTHN_ERROR_INVALID_STATE);
+        EXPECT_EQ(invalidRequest.GetStatus(), WAUTHN_ERROR_SOCKET);
+        invalidRequest.Send();
+        EXPECT_EQ(invalidRequest.GetStatus(), WAUTHN_ERROR_INVALID_STATE);
     } catch (...) {
         ret = -1;
     }
@@ -243,17 +275,30 @@ TEST_F(ClientRequestTest, request_to_not_supported_server_N)
             callbacks = (wauthn_mc_callbacks_s*) calloc(1, sizeof(wauthn_mc_callbacks_s));
             callbacks->qrcode_callback = test_cb_display_qrcode;
             callbacks->response_callback = test_cb_mc_response;
+            callbacks->linked_data_callback = test_cb_update_linked_data;
             test_user_data_s *userdata = (test_user_data_s*) calloc(1, sizeof(test_user_data_s));
             userdata->test_data = (char*)("user data");
             userdata->test_int = 999;
+            userdata->called_qr = false;
+            userdata->called_response = false;
+            g_target_times_update_linked_data = 1;
+            userdata->times_update_linked_data = 0;
             callbacks->user_data = userdata;
             retVal = wauthn_process<TestClientRequestMC>(&TestCommonData::clientData,
                 &TestCommonData::pubkeyCredCreationOptions, callbacks);
 
             EXPECT_EQ(retVal, WAUTHN_ERROR_NOT_SUPPORTED)
-                    << "[wauthn_process<TestClientRequestMC>] failed. "
-                    << "retVal=" << wauthn_error_to_string(ret) << std::endl;
+                << "[wauthn_process<TestClientRequestMC>] failed. "
+                << "retVal=" << wauthn_error_to_string(ret) << std::endl;
             sleep(1);
+            EXPECT_EQ(userdata->called_qr, false)
+                << "The QR callback should not be called by cancel" << std::endl;
+            EXPECT_EQ(userdata->called_response, false)
+                << "The response callback should not be called by cancel" << std::endl;
+            EXPECT_EQ(0, userdata->times_update_linked_data)
+                << "The update_linked_data callback should not be called by cancel"
+                << std::endl;
+
             free(userdata);
             free(callbacks);
         }
@@ -266,7 +311,7 @@ TEST_F(ClientRequestTest, request_to_not_supported_server_N)
     EXPECT_EQ(ret, 0);
 }
 
-TEST_F(ClientRequestTest, MC_without_QR_callback_P)
+TEST_F(ClientRequestTest, MC_with_LD_and_not_call_QR_callback_P)
 {
     int ret = 1;
     WA::SocketManager manager;
@@ -280,21 +325,35 @@ TEST_F(ClientRequestTest, MC_without_QR_callback_P)
             callbacks = (wauthn_mc_callbacks_s*) calloc(1, sizeof(wauthn_mc_callbacks_s));
             callbacks->qrcode_callback = test_cb_display_qrcode;
             callbacks->response_callback = test_cb_mc_response;
+            callbacks->linked_data_callback = test_cb_update_linked_data;
             test_user_data_s *userdata = (test_user_data_s*) calloc(1, sizeof(test_user_data_s));
             userdata->test_data = (char*)("user data");
             userdata->test_int = 999;
+            userdata->called_qr = false;
+            userdata->called_response = false;
+            g_target_times_update_linked_data = 1;
+            userdata->times_update_linked_data = 0;
             callbacks->user_data = userdata;
             retVal = wauthn_process<TestClientRequestMC>(&TestCommonData::clientData,
                 &TestCommonData::pubkeyCredCreationOptions, callbacks);
 
             EXPECT_EQ(retVal, WAUTHN_ERROR_NONE)
-                    << "[wauthn_process<TestClientRequestMC>] failed. "
-                    << "retVal=" << wauthn_error_to_string(ret) << std::endl;
-            sleep(5);
+                << "[wauthn_process<TestClientRequestMC>] failed. "
+                << "retVal=" << wauthn_error_to_string(ret) << std::endl;
+
+            // Waiting for called the callbacks
+            sleep(g_target_times_update_linked_data + 5);
+            EXPECT_EQ(userdata->called_qr, false)
+                << "The QR callback should not be called" << std::endl;
+            EXPECT_EQ(userdata->called_response, true)
+                << "The response callback should be called" << std::endl;
+            EXPECT_EQ(g_target_times_update_linked_data,
+                      userdata->times_update_linked_data)
+                << "The update_linked_data callback should be called for setted times"
+                << std::endl;
             free(userdata);
             free(callbacks);
         }
-        sleep(1);
         ret = 0;
     } catch (...) {
         std::cout << "Error in starting service, unknown exception occurred" << std::endl;
@@ -303,7 +362,7 @@ TEST_F(ClientRequestTest, MC_without_QR_callback_P)
     EXPECT_EQ(ret, 0);
 }
 
-TEST_F(ClientRequestTest, MC_with_QR_callback_P)
+TEST_F(ClientRequestTest, MC_without_LD_and_call_QR_callback_P)
 {
     int ret = 1;
     WA::SocketManager manager;
@@ -317,17 +376,33 @@ TEST_F(ClientRequestTest, MC_with_QR_callback_P)
             callbacks = (wauthn_mc_callbacks_s*) calloc(1, sizeof(wauthn_mc_callbacks_s));
             callbacks->qrcode_callback = test_cb_display_qrcode;
             callbacks->response_callback = test_cb_mc_response;
+            callbacks->linked_data_callback = test_cb_update_linked_data;
             test_user_data_s *userdata = (test_user_data_s*) calloc(1, sizeof(test_user_data_s));
             userdata->test_data = (char*)("user data");
             userdata->test_int = 999;
+            userdata->called_qr = false;
+            userdata->called_response = false;
+            g_target_times_update_linked_data = 1;
+            userdata->times_update_linked_data = 0;
             callbacks->user_data = userdata;
             retVal = wauthn_process<TestClientRequestMC>(&TestCommonData::clientData,
-                &TestCommonData::pubkeyCredCreationOptionsWithQR, callbacks);
+                &TestCommonData::pubkeyCredCreationOptionsNoLD, callbacks);
 
             EXPECT_EQ(retVal, WAUTHN_ERROR_NONE)
-                    << "[wauthn_process<TestClientRequestMC>] failed. "
-                    << "retVal=" << wauthn_error_to_string(ret) << std::endl;
-            sleep(5);
+                << "[wauthn_process<TestClientRequestMC>] failed. "
+                << "retVal=" << wauthn_error_to_string(ret) << std::endl;
+
+            // Waiting for called the callbacks
+            sleep(g_target_times_update_linked_data + 5);
+            EXPECT_EQ(userdata->called_qr, true)
+                << "The QR callback should be called" << std::endl;
+            EXPECT_EQ(userdata->called_response, true)
+                << "The response callback should be called" << std::endl;
+            EXPECT_EQ(g_target_times_update_linked_data,
+                      userdata->times_update_linked_data)
+                << "The update_linked_data callback should be called for setted times"
+                << std::endl;
+
             free(userdata);
             free(callbacks);
         }
@@ -341,7 +416,7 @@ TEST_F(ClientRequestTest, MC_with_QR_callback_P)
 }
 
 
-TEST_F(ClientRequestTest, GA_without_QR_callback_P)
+TEST_F(ClientRequestTest, GA_with_LD_and_not_call_QR_callback_P)
 {
     int ret = 1;
     WA::SocketManager manager;
@@ -355,17 +430,33 @@ TEST_F(ClientRequestTest, GA_without_QR_callback_P)
             callbacks = (wauthn_ga_callbacks_s*) calloc(1, sizeof(wauthn_ga_callbacks_s));
             callbacks->qrcode_callback = test_cb_display_qrcode;
             callbacks->response_callback = test_cb_ga_response;
+            callbacks->linked_data_callback = test_cb_update_linked_data;
             test_user_data_s *userdata = (test_user_data_s*) calloc(1, sizeof(test_user_data_s));
             userdata->test_data = (char*)("user data");
             userdata->test_int = 999;
+            userdata->called_qr = false;
+            userdata->called_response = false;
+            g_target_times_update_linked_data = 1;
+            userdata->times_update_linked_data = 0;
             callbacks->user_data = userdata;
             retVal = wauthn_process<TestClientRequestGA>(&TestCommonData::clientData,
                 &TestCommonData::pubkeyCredRequestOptions, callbacks);
 
             EXPECT_EQ(retVal, WAUTHN_ERROR_NONE)
-                    << "[wauthn_process<TestClientRequestGA>] failed. "
-                    << "retVal=" << wauthn_error_to_string(ret) << std::endl;
-            sleep(5);
+                << "[wauthn_process<TestClientRequestGA>] failed. "
+                << "retVal=" << wauthn_error_to_string(ret) << std::endl;
+
+            // Waiting for called the callbacks
+            sleep(g_target_times_update_linked_data + 5);
+            EXPECT_EQ(userdata->called_qr, false)
+                << "The QR callback should not be called" << std::endl;
+            EXPECT_EQ(userdata->called_response, true)
+                << "The response callback should be called" << std::endl;
+            EXPECT_EQ(g_target_times_update_linked_data,
+                      userdata->times_update_linked_data)
+                << "The update_linked_data callback should be called for setted times"
+                << std::endl;
+
             free(userdata);
             free(callbacks);
         }
@@ -378,7 +469,7 @@ TEST_F(ClientRequestTest, GA_without_QR_callback_P)
     EXPECT_EQ(ret, 0);
 }
 
-TEST_F(ClientRequestTest, GA_with_QR_callback_P)
+TEST_F(ClientRequestTest, GA_without_LD_and_call_QR_callback_P)
 {
     int ret = 1;
     WA::SocketManager manager;
@@ -392,17 +483,33 @@ TEST_F(ClientRequestTest, GA_with_QR_callback_P)
             callbacks = (wauthn_ga_callbacks_s*) calloc(1, sizeof(wauthn_ga_callbacks_s));
             callbacks->qrcode_callback = test_cb_display_qrcode;
             callbacks->response_callback = test_cb_ga_response;
+            callbacks->linked_data_callback = test_cb_update_linked_data;
             test_user_data_s *userdata = (test_user_data_s*) calloc(1, sizeof(test_user_data_s));
             userdata->test_data = (char*)("user data");
             userdata->test_int = 999;
+            userdata->called_qr = false;
+            userdata->called_response = false;
+            g_target_times_update_linked_data = 1;
+            userdata->times_update_linked_data = 0;
             callbacks->user_data = userdata;
             retVal = wauthn_process<TestClientRequestGA>(&TestCommonData::clientData,
-                &TestCommonData::pubkeyCredRequestOptionsWithQR, callbacks);
+                &TestCommonData::pubkeyCredRequestOptionsNoLD, callbacks);
 
             EXPECT_EQ(retVal, WAUTHN_ERROR_NONE)
-                    << "[wauthn_process<TestClientRequestGA>] failed. "
-                    << "retVal=" << wauthn_error_to_string(ret) << std::endl;
-            sleep(5);
+                << "[wauthn_process<TestClientRequestGA>] failed. "
+                << "retVal=" << wauthn_error_to_string(ret) << std::endl;
+
+            // Waiting for called the callbacks
+            sleep(g_target_times_update_linked_data + 5);
+            EXPECT_EQ(userdata->called_qr, true)
+                << "The QR callback should be called" << std::endl;
+            EXPECT_EQ(userdata->called_response, true)
+                << "The response callback should be called" << std::endl;
+            EXPECT_EQ(g_target_times_update_linked_data,
+                      userdata->times_update_linked_data)
+                << "The update_linked_data callback should be called for setted times"
+                << std::endl;
+
             free(userdata);
             free(callbacks);
         }
@@ -429,12 +536,17 @@ TEST_F(ClientRequestTest, not_allowed_N)
             mc_callbacks = (wauthn_mc_callbacks_s*) calloc(1, sizeof(wauthn_mc_callbacks_s));
             mc_callbacks->qrcode_callback = test_cb_display_qrcode;
             mc_callbacks->response_callback = test_cb_mc_response;
+            mc_callbacks->linked_data_callback = test_cb_update_linked_data;
             test_user_data_s *mc_userdata = (test_user_data_s*) calloc(1, sizeof(test_user_data_s));
             mc_userdata->test_data = (char*)("user data");
             mc_userdata->test_int = 999;
+            mc_userdata->called_qr = false;
+            mc_userdata->called_response = false;
+            g_target_times_update_linked_data = 1;
+            mc_userdata->times_update_linked_data = 0;
             mc_callbacks->user_data = mc_userdata;
             retVal = wauthn_process<TestClientRequestMC>(&TestCommonData::clientData,
-                &TestCommonData::pubkeyCredCreationOptions, mc_callbacks);
+                &TestCommonData::pubkeyCredCreationOptionsNoLD, mc_callbacks);
 
             EXPECT_EQ(retVal, WAUTHN_ERROR_NONE)
                 << "[wauthn_process<TestClientRequestMC>] failed. "
@@ -446,9 +558,13 @@ TEST_F(ClientRequestTest, not_allowed_N)
             ga_callbacks = (wauthn_ga_callbacks_s*) calloc(1, sizeof(wauthn_ga_callbacks_s));
             ga_callbacks->qrcode_callback = test_cb_display_qrcode;
             ga_callbacks->response_callback = test_cb_ga_response;
+            ga_callbacks->linked_data_callback = test_cb_update_linked_data;
             test_user_data_s *ga_userdata = (test_user_data_s*) calloc(1, sizeof(test_user_data_s));
             ga_userdata->test_data = (char*)("user data");
             ga_userdata->test_int = 999;
+            ga_userdata->called_qr = false;
+            ga_userdata->called_response = false;
+            ga_userdata->times_update_linked_data = 0;
             ga_callbacks->user_data = ga_userdata;
             retVal = wauthn_process<TestClientRequestGA>(&TestCommonData::clientData,
                 &TestCommonData::pubkeyCredRequestOptions, ga_callbacks);
@@ -457,7 +573,25 @@ TEST_F(ClientRequestTest, not_allowed_N)
                 << "[wauthn_process<TestClientRequestGA>] failed. "
                 << "ret=" << wauthn_error_to_string(retVal) << std::endl;
 
-            sleep(5);
+            // Waiting for called the callbacks
+            sleep(g_target_times_update_linked_data + 5);
+            EXPECT_EQ(mc_userdata->called_qr, true)
+                << "The QR callback for MC should be called" << std::endl;
+            EXPECT_EQ(mc_userdata->called_response, true)
+                << "The response callback for MC should be called" << std::endl;
+            EXPECT_EQ(g_target_times_update_linked_data,
+                      mc_userdata->times_update_linked_data)
+                << "The update_linked_data callback for MC should be called for setted times"
+                << std::endl;
+
+            EXPECT_EQ(ga_userdata->called_qr, false)
+                << "The QR callback for GA should not be called by not allowed" << std::endl;
+            EXPECT_EQ(ga_userdata->called_response, false)
+                << "The response callback for GA should not be called by not allowed" << std::endl;
+            EXPECT_EQ(0, ga_userdata->times_update_linked_data)
+                << "The update_linked_data callback for GA should not be called by not allowed"
+                << std::endl;
+
             free(mc_userdata);
             free(ga_userdata);
             free(mc_callbacks);
@@ -472,7 +606,6 @@ TEST_F(ClientRequestTest, not_allowed_N)
     EXPECT_EQ(ret, 0);
 }
 
-
 TEST_F(ClientRequestTest, cancel_P)
 {
     int ret = 1;
@@ -487,9 +620,14 @@ TEST_F(ClientRequestTest, cancel_P)
             mc_callbacks = (wauthn_mc_callbacks_s*) calloc(1, sizeof(wauthn_mc_callbacks_s));
             mc_callbacks->qrcode_callback = test_cb_display_qrcode;
             mc_callbacks->response_callback = test_cb_mc_response;
+            mc_callbacks->linked_data_callback = test_cb_update_linked_data;
             test_user_data_s *mc_userdata = (test_user_data_s*) calloc(1, sizeof(test_user_data_s));
             mc_userdata->test_data = (char*)("user data");
             mc_userdata->test_int = 999;
+            mc_userdata->called_qr = false;
+            mc_userdata->called_response = false;
+            g_target_times_update_linked_data = 1;
+            mc_userdata->times_update_linked_data = 0;
             mc_callbacks->user_data = mc_userdata;
             retVal = wauthn_process<TestClientRequestMC>(&TestCommonData::clientData,
                 &TestCommonData::pubkeyCredCreationOptions, mc_callbacks);
@@ -498,18 +636,80 @@ TEST_F(ClientRequestTest, cancel_P)
                 << "[wauthn_process<TestClientRequestMC>] failed. "
                 << "retVal=" << wauthn_error_to_string(ret) << std::endl;
 
-            sleep(1);
             TestClientRequest request(WebAuthnCall::CANCEL);
             request.Init();
-            if (request.sendRequest().failed())
+            if (request.SendRequest().Failed())
                 LogError("Error on cancel request, Response: "
-                    << wauthn_error_to_string(request.getStatus()));
-            retVal = request.getStatus();
+                    << wauthn_error_to_string(request.GetStatus()));
+            retVal = request.GetStatus();
 
             EXPECT_EQ(retVal, WAUTHN_ERROR_NONE)
                 << "[wauthn_cancel] failed. "
                 << "ret=" << wauthn_error_to_string(retVal) << std::endl;
-            sleep(5);
+
+            EXPECT_EQ(mc_userdata->called_qr, false)
+                << "The QR callback should not be called by cancel" << std::endl;
+            EXPECT_EQ(mc_userdata->called_response, false)
+                << "The response callback should not be called by cancel" << std::endl;
+            EXPECT_EQ(0, mc_userdata->times_update_linked_data)
+                << "The update_linked_data callback should not be called by cancel"
+                << std::endl;
+
+            // Waiting for called the callbacks
+            sleep(g_target_times_update_linked_data + 5);
+            free(mc_userdata);
+            free(mc_callbacks);
+        }
+        ret = 0;
+    } catch (...) {
+        std::cout << "Error in starting service, unknown exception occurred" << std::endl;
+        ret = -1;
+    }
+    EXPECT_EQ(ret, 0);
+}
+
+TEST_F(ClientRequestTest, MC_multiple_update_linked_data_P)
+{
+    int ret = 1;
+    WA::SocketManager manager;
+    try{
+        auto service = std::make_unique<WA::TestService>(std::make_shared<TestDLLoader>());
+        manager.RegisterSocketService(std::move(service));
+        SocketManagerLoop loop(manager);
+        {
+            int retVal = WAUTHN_ERROR_NONE;
+            wauthn_mc_callbacks_s *mc_callbacks = nullptr;
+            mc_callbacks = (wauthn_mc_callbacks_s*) calloc(1, sizeof(wauthn_mc_callbacks_s));
+            mc_callbacks->qrcode_callback = test_cb_display_qrcode;
+            mc_callbacks->response_callback = test_cb_mc_response;
+            mc_callbacks->linked_data_callback = test_cb_update_linked_data;
+            test_user_data_s *mc_userdata = (test_user_data_s*) calloc(1, sizeof(test_user_data_s));
+            mc_userdata->test_data = (char*)("user data");
+            mc_userdata->test_int = 999;
+            mc_userdata->called_qr = false;
+            mc_userdata->called_response = false;
+            g_target_times_update_linked_data = 5;
+            mc_userdata->times_update_linked_data = 0;
+            mc_callbacks->user_data = mc_userdata;
+            retVal = wauthn_process<TestClientRequestMC>(&TestCommonData::clientData,
+                &TestCommonData::pubkeyCredCreationOptionsNoLD, mc_callbacks);
+
+            EXPECT_EQ(retVal, WAUTHN_ERROR_NONE)
+                << "[wauthn_process<TestClientRequestMC>] failed. "
+                << "retVal=" << wauthn_error_to_string(ret) << std::endl;
+
+            // Waiting for called the callbacks
+            sleep(g_target_times_update_linked_data + 5);
+
+            EXPECT_EQ(mc_userdata->called_qr, true)
+                << "The QR callback should be called" << std::endl;
+            EXPECT_EQ(mc_userdata->called_response, true)
+                << "The response callback should be called" << std::endl;
+            EXPECT_EQ(g_target_times_update_linked_data,
+                      mc_userdata->times_update_linked_data)
+                << "The update_linked_data callback should be called for setted times"
+                << std::endl;
+
             free(mc_userdata);
             free(mc_callbacks);
         }
@@ -522,4 +722,58 @@ TEST_F(ClientRequestTest, cancel_P)
     EXPECT_EQ(ret, 0);
 }
 
+TEST_F(ClientRequestTest, GA_multiple_update_linked_data_P)
+{
+    int ret = 1;
+    WA::SocketManager manager;
+    try{
+        auto service = std::make_unique<WA::TestService>(std::make_shared<TestDLLoader>());
+        manager.RegisterSocketService(std::move(service));
+        SocketManagerLoop loop(manager);
+        {
+            int retVal = WAUTHN_ERROR_NONE;
+            wauthn_ga_callbacks_s *ga_callbacks = nullptr;
+            ga_callbacks = (wauthn_ga_callbacks_s*) calloc(1, sizeof(wauthn_ga_callbacks_s));
+            ga_callbacks->qrcode_callback = test_cb_display_qrcode;
+            ga_callbacks->response_callback = test_cb_ga_response;
+            ga_callbacks->linked_data_callback = test_cb_update_linked_data;
+            test_user_data_s *ga_userdata = (test_user_data_s*) calloc(1, sizeof(test_user_data_s));
+            ga_userdata->test_data = (char*)("user data");
+            ga_userdata->test_int = 999;
+            ga_userdata->called_qr = false;
+            ga_userdata->called_response = false;
+            g_target_times_update_linked_data = 5;
+            ga_userdata->times_update_linked_data = 0;
+            ga_callbacks->user_data = ga_userdata;
+            retVal = wauthn_process<TestClientRequestGA>(&TestCommonData::clientData,
+                &TestCommonData::pubkeyCredRequestOptionsNoLD, ga_callbacks);
+
+            EXPECT_EQ(retVal, WAUTHN_ERROR_NONE)
+                << "[wauthn_process<TestClientRequestGA>] failed. "
+                << "retVal=" << wauthn_error_to_string(ret) << std::endl;
+
+            // Waiting for called the callbacks
+            sleep(g_target_times_update_linked_data + 5);
+
+            EXPECT_EQ(ga_userdata->called_qr, true)
+                << "The QR callback should be called" << std::endl;
+            EXPECT_EQ(ga_userdata->called_response, true)
+                << "The response callback should be called" << std::endl;
+            EXPECT_EQ(g_target_times_update_linked_data,
+                      ga_userdata->times_update_linked_data)
+                << "The update_linked_data callback should be called for setted times"
+                << std::endl;
+
+            free(ga_userdata);
+            free(ga_callbacks);
+        }
+        sleep(1);
+        ret = 0;
+    } catch (...) {
+        std::cout << "Error in starting service, unknown exception occurred" << std::endl;
+        ret = -1;
+    }
+    EXPECT_EQ(ret, 0);
+}
+
 } // namespace WebAuthn
index d1d4cb6..94cb51f 100644 (file)
@@ -114,11 +114,14 @@ void DisplayQRCallback(const char *qr_contents, void *data)
     auto testContents = static_cast<TestContents *>(data);
     if (testContents->path.empty() || !qr_contents) {
         std::cout << "qrcode_callback failed" << std::endl;
-        std::cout << "QRcode path: " << testContents->path << std::endl;
-        std::cout << "qr_contents: " << qr_contents << std::endl;
         testContents->status = false;
         return;
     }
+    else
+    {
+        std::cout << "QRcode path: " << testContents->path << std::endl;
+        std::cout << "qr_contents: " << qr_contents << std::endl;
+    }
     if (testContents->negative)
         GenerateAndDisplayQR("https://tinyurl.com/5x2bcwjy", *testContents);
     else
@@ -177,7 +180,13 @@ void GACallback(const wauthn_pubkey_credential_assertion_s *pubkey_cred,
     }
     testContents->status = 2;
 }
-
+/* TBD
+void UpdateLinkedDataCallback(const wauthn_hybrid_linked_data_s *linked_data,
+    wauthn_error_e result,
+    void *user_data)
+{
+}
+*/
 bool Test(struct TestContents &testContents)
 {
     /* MakeCredential */
@@ -227,6 +236,7 @@ bool Test(struct TestContents &testContents)
     wauthn_mc_callbacks_s mcCallbacks;
     mcCallbacks.qrcode_callback = DisplayQRCallback;
     mcCallbacks.response_callback = MCCallback;
+    // TODO: Add UpdateLinkedDataCallback with the webauthn-ble's update
     mcCallbacks.user_data = &testContents;
 
     int ret = wauthn_make_credential(&clientData, &mcOptions, &mcCallbacks);
@@ -275,6 +285,7 @@ bool Test(struct TestContents &testContents)
     wauthn_ga_callbacks_s gaCallbacks;
     gaCallbacks.qrcode_callback = DisplayQRCallback;
     gaCallbacks.response_callback = GACallback;
+    // TODO: Add UpdateLinkedDataCallback with the webauthn-ble's update
     gaCallbacks.user_data = &testContents;
 
     ret = wauthn_get_assertion(&clientData, &gaOptions, &gaCallbacks);
@@ -301,8 +312,21 @@ int main(int argc, char *argv[])
     TestContents testContents = {0, "/tmp/webauthn-qrcode.png",
                                  {}, {}, WAUTHN_TRANSPORT_NONE};
 
-    if (argc > 1 && std::string_view("-n") == argv[1])
-        testContents.negative = true;
+    if (argc == 2)
+    {
+        if (std::string_view("-n") == argv[1])
+            testContents.negative = true;
+        else
+        {
+            std::cout << "only -\"n\" option is supported" << std::endl;
+            return true;
+        }
+    }
+    else if (argc > 2)
+    {
+        std::cout << "Invalid parameters" << std::endl;
+        return true;
+    }
 
     int err = TurnBluetoothOn();
     if (err != BT_ERROR_NONE) {
@@ -318,7 +342,7 @@ int main(int argc, char *argv[])
         ret = true;
     }
 
-    if (system("killall /usr/apps/org.tizen.image-viewer_common/bin/image-viewer")) {
+    if (system("/usr/bin/killall /usr/apps/org.tizen.image-viewer_common/bin/image-viewer")) {
         std::cout << "Cannot close org.tizen.image-viewer\n";
         ret = true;
     }
index 5a8d2d1..14a7fac 100644 (file)
@@ -163,7 +163,8 @@ void __testUnsignedCharPtr(unsigned char *data, size_t data_size)
     WAuthnCtypeSerializer::deserialize(buffer, &deserialized, &deserialized_len);
 
     EXPECT_EQ(data_size, deserialized_len);
-    if (deserialized_len != 0) { // If deserialized_len is zero, memcmp is guaranteed to return zero
+    if (data_size != 0 && deserialized_len != 0) {
+        // If deserialized_len is zero, memcmp is guaranteed to return zero
         EXPECT_EQ(memcmp(data, deserialized, deserialized_len), 0);
     }
     else {
@@ -264,22 +265,24 @@ bool __compareWAuthErrorE(wauthn_error_e expected, wauthn_error_e actual)
 TEST_F(WAuthnSerializationTest, wauthn_error_e_P)
 {
     __testSerialization(WAUTHN_ERROR_NONE, __compareWAuthErrorE);
+    __testSerialization(WAUTHN_ERROR_NONE_AND_WAIT, __compareWAuthErrorE);
     __testSerialization(WAUTHN_ERROR_NOT_ALLOWED, __compareWAuthErrorE);
     __testSerialization(WAUTHN_ERROR_ENCODING_FAILED, __compareWAuthErrorE);
     __testSerialization(WAUTHN_ERROR_CANCELLED, __compareWAuthErrorE);
+    __testSerialization(WAUTHN_ERROR_TIMEOUT, __compareWAuthErrorE);
 }
 TEST_F(WAuthnSerializationTest, wauthn_error_e_N1)
 {// serialize: invalid wauthn_error_e
-    wauthn_error_e data1 = static_cast<wauthn_error_e>(WAUTHN_ERROR_NONE + 1);
+    wauthn_error_e data1 = static_cast<wauthn_error_e>(WAUTHN_ERROR_NONE_AND_WAIT + 1);
     __testSerializeForInvalidMember(data1);
-    wauthn_error_e data2 = static_cast<wauthn_error_e>(WAUTHN_ERROR_CANCELLED - 1);
+    wauthn_error_e data2 = static_cast<wauthn_error_e>(WAUTHN_ERROR_TIMEOUT - 1);
     __testSerializeForInvalidMember(data2);
 }
 TEST_F(WAuthnSerializationTest, wauthn_error_e_N2)
 {// deserialize: invalid wauthn_error_e
     MessageBuffer buffer;
     wauthn_error_e deserialized;
-    __fillBufferWithTestCommonData(buffer, WAUTHN_ERROR_NONE + 1, nullptr, 0);
+    __fillBufferWithTestCommonData(buffer, WAUTHN_ERROR_NONE_AND_WAIT + 1, nullptr, 0);
     try {
         WAuthnCtypeSerializer::deserialize(buffer, &deserialized);
         EXPECT_TRUE(false);
@@ -287,7 +290,7 @@ TEST_F(WAuthnSerializationTest, wauthn_error_e_N2)
     }
 
     MessageBuffer buffer1;
-    __fillBufferWithTestCommonData(buffer1, WAUTHN_ERROR_CANCELLED - 1, nullptr, 0);
+    __fillBufferWithTestCommonData(buffer1, WAUTHN_ERROR_TIMEOUT - 1, nullptr, 0);
     try {
         WAuthnCtypeSerializer::deserialize(buffer1, &deserialized);
         EXPECT_TRUE(false);
index 533d8e6..fbafa91 100644 (file)
@@ -156,7 +156,7 @@ namespace TestCommonData {
     wauthn_pubkey_cred_creation_options_s pubkeyCredCreationOptions = {&rpEntity, &userEntity,
             &pubkeyCredParams2, timeout, &pubkeyCredDescriptors2, &authenticatorSelCri, &pubkeyCredHints2,
             attestation, &attestationFormats1, &authenticationExts2, &hybridLinkedData};
-    wauthn_pubkey_cred_creation_options_s pubkeyCredCreationOptionsWithQR = {&rpEntity, &userEntity,
+    wauthn_pubkey_cred_creation_options_s pubkeyCredCreationOptionsNoLD = {&rpEntity, &userEntity,
             &pubkeyCredParams2, timeout, &pubkeyCredDescriptors2, &authenticatorSelCri, &pubkeyCredHints2,
             attestation, &attestationFormats1, &authenticationExts2, nullptr};
     wauthn_pubkey_cred_creation_options_s emptyPubkeyCredCreationOptions = {nullptr, nullptr,
@@ -166,7 +166,7 @@ namespace TestCommonData {
     wauthn_pubkey_cred_request_options_s pubkeyCredRequestOptions = {timeout, const_cast<char *>(rpId),
             &pubkeyCredDescriptors2, user_verification, &pubkeyCredHints2, attestation, &attestationFormats1,
             &authenticationExts2, &hybridLinkedData};
-    wauthn_pubkey_cred_request_options_s pubkeyCredRequestOptionsWithQR = {timeout, const_cast<char *>(rpId),
+    wauthn_pubkey_cred_request_options_s pubkeyCredRequestOptionsNoLD = {timeout, const_cast<char *>(rpId),
             &pubkeyCredDescriptors2, user_verification, &pubkeyCredHints2, attestation, &attestationFormats1,
             &authenticationExts2, nullptr};
     wauthn_pubkey_cred_request_options_s emptyPubkeyCredRequestOptions = {0, nullptr,
index b6511fd..fe9d3d7 100644 (file)
@@ -37,25 +37,31 @@ typedef struct _test_user_data_s {
     int test_int;
     bool called_qr;
     bool called_response;
+    int times_update_linked_data;
 } test_user_data_s;
+inline int g_target_times_update_linked_data;
 
 inline void _print_user_data(void *user_data)
 {
-    std::cout << "user_data is not null, data=" <<
-        (static_cast<test_user_data_s*>(user_data))->test_data <<
-        ", int=" << (static_cast<test_user_data_s*>(user_data))->test_int << std::endl;
+    if (user_data == nullptr)
+        std::cout << "user_data is null" << std::endl;
+    else{
+        test_user_data_s *userData = static_cast<test_user_data_s*>(user_data);
+
+        std::cout << "user_data is not null, data=" <<
+            userData->test_data << ", int=" << userData->test_int <<
+            ", qr=" << userData->called_qr << ", response=" <<
+            ", target update times=" << g_target_times_update_linked_data <<
+            ", update times=" << userData->times_update_linked_data << std::endl;
+    }
 }
 
 inline void test_cb_display_qrcode(const char *qr_contents, void *user_data)
 {
     std::cout << "QR Code: " << std::string(qr_contents) << std::endl;
-    if (user_data == nullptr)
-        std::cout << "user_data is null" << std::endl;
-    else
-    {
-        _print_user_data(user_data);
+    _print_user_data(user_data);
+    if (user_data != nullptr)
         (static_cast<test_user_data_s*>(user_data))->called_qr = true;
-    }
 }
 
 inline void test_cb_mc_response(
@@ -70,13 +76,9 @@ inline void test_cb_mc_response(
         std::cout << "pubkey_cred is not null: " <<
             pubkey_cred->type << ", " <<
             pubkey_cred->authenticator_attachment << std::endl;
-    if (user_data == nullptr)
-        std::cout << "user_data is null" << std::endl;
-    else
-    {
-        _print_user_data(user_data);
+    _print_user_data(user_data);
+    if (user_data != nullptr)
         (static_cast<test_user_data_s*>(user_data))->called_response = true;
-    }
 }
 
 inline void test_cb_ga_response(
@@ -89,13 +91,23 @@ inline void test_cb_ga_response(
         std::cout << "pubkey_cred is null" << std::endl;
     else
         std::cout << "pubkey_cred is not null" << std::endl;
-    if (user_data == nullptr)
-        std::cout << "user_data is null" << std::endl;
-    else
-    {
-        _print_user_data(user_data);
+    _print_user_data(user_data);
+    if (user_data != nullptr)
         (static_cast<test_user_data_s*>(user_data))->called_response = true;
-    }
+}
+
+inline void test_cb_update_linked_data(
+    const wauthn_hybrid_linked_data_s *linked_data,
+    wauthn_error_e result,
+    void *user_data)
+{
+    std::cout << "update_linked_data, result: " << wauthn_error_to_string(result) << std::endl;
+    if (linked_data == nullptr)
+        std::cout << "linked_data is null" << std::endl;
+    else
+        std::cout << "linked_data is not null" << std::endl;
+    (static_cast<test_user_data_s*>(user_data))->times_update_linked_data += 1;
+    _print_user_data(user_data);
 }
 
 namespace TestCommonData {
@@ -216,12 +228,12 @@ namespace TestCommonData {
     extern unsigned long timeout;
     extern wauthn_attestation_pref_e attestation;
     extern wauthn_pubkey_cred_creation_options_s pubkeyCredCreationOptions;
-    extern wauthn_pubkey_cred_creation_options_s pubkeyCredCreationOptionsWithQR;
+    extern wauthn_pubkey_cred_creation_options_s pubkeyCredCreationOptionsNoLD;
     extern wauthn_pubkey_cred_creation_options_s emptyPubkeyCredCreationOptions;
 
     extern const char *rpId;
     extern wauthn_pubkey_cred_request_options_s pubkeyCredRequestOptions;
-    extern wauthn_pubkey_cred_request_options_s pubkeyCredRequestOptionsWithQR;
+    extern wauthn_pubkey_cred_request_options_s pubkeyCredRequestOptionsNoLD;
     extern wauthn_pubkey_cred_request_options_s emptyPubkeyCredRequestOptions;
 
     extern wauthn_pubkey_credential_attestation_s pubkeyCredentialAttestation;
index 723df4a..6da25d7 100644 (file)
@@ -47,6 +47,7 @@ TEST_F(WebAuthnTest, invalid_client_data_N)
     mc_callbacks = (wauthn_mc_callbacks_s*) calloc(1, sizeof(wauthn_mc_callbacks_s));
     mc_callbacks->qrcode_callback = test_cb_display_qrcode;
     mc_callbacks->response_callback = test_cb_mc_response;
+    mc_callbacks->linked_data_callback = test_cb_update_linked_data;
     ret = wauthn_make_credential(nullptr,
                                  &TestCommonData::pubkeyCredCreationOptions,
                                  mc_callbacks);
@@ -60,6 +61,7 @@ TEST_F(WebAuthnTest, invalid_client_data_N)
     ga_callbacks = (wauthn_ga_callbacks_s*) calloc(1, sizeof(wauthn_ga_callbacks_s));
     ga_callbacks->qrcode_callback = test_cb_display_qrcode;
     ga_callbacks->response_callback = test_cb_ga_response;
+    ga_callbacks->linked_data_callback = test_cb_update_linked_data;
     ret = wauthn_get_assertion(nullptr,
                                &TestCommonData::pubkeyCredRequestOptions,
                                ga_callbacks);
@@ -80,6 +82,7 @@ TEST_F(WebAuthnTest, invalid_options_N)
     mc_callbacks = (wauthn_mc_callbacks_s*) calloc(1, sizeof(wauthn_mc_callbacks_s));
     mc_callbacks->qrcode_callback = test_cb_display_qrcode;
     mc_callbacks->response_callback = test_cb_mc_response;
+    mc_callbacks->linked_data_callback = test_cb_update_linked_data;
     ret = wauthn_make_credential(&TestCommonData::clientData,
                                  nullptr,
                                  mc_callbacks);
@@ -93,6 +96,7 @@ TEST_F(WebAuthnTest, invalid_options_N)
     ga_callbacks = (wauthn_ga_callbacks_s*) calloc(1, sizeof(wauthn_ga_callbacks_s));
     ga_callbacks->qrcode_callback = test_cb_display_qrcode;
     ga_callbacks->response_callback = test_cb_ga_response;
+    ga_callbacks->linked_data_callback = test_cb_update_linked_data;
     ret = wauthn_get_assertion(&TestCommonData::clientData,
                                nullptr,
                                ga_callbacks);
@@ -120,6 +124,17 @@ TEST_F(WebAuthnTest, invalid_callbacks_N)
     mc_callbacks = (wauthn_mc_callbacks_s*) calloc(1, sizeof(wauthn_mc_callbacks_s));
     mc_callbacks->qrcode_callback = test_cb_display_qrcode;
     mc_callbacks->response_callback = nullptr;
+    mc_callbacks->linked_data_callback = test_cb_update_linked_data;
+    ret = wauthn_make_credential(&TestCommonData::clientData,
+                                 &TestCommonData::pubkeyCredCreationOptions,
+                                 mc_callbacks);
+    EXPECT_EQ(ret, WAUTHN_ERROR_INVALID_PARAMETER)
+        << "[invalid_callbacks_N] failed. "
+        << "ret=" << wauthn_error_to_string(ret) << std::endl;
+
+    ret = WAUTHN_ERROR_NONE;
+    mc_callbacks->response_callback = test_cb_mc_response;
+    mc_callbacks->linked_data_callback = nullptr;
     ret = wauthn_make_credential(&TestCommonData::clientData,
                                  &TestCommonData::pubkeyCredCreationOptions,
                                  mc_callbacks);
@@ -140,6 +155,17 @@ TEST_F(WebAuthnTest, invalid_callbacks_N)
     ga_callbacks = (wauthn_ga_callbacks_s*) calloc(1, sizeof(wauthn_ga_callbacks_s));
     ga_callbacks->qrcode_callback = test_cb_display_qrcode;
     ga_callbacks->response_callback = nullptr;
+    ga_callbacks->linked_data_callback = test_cb_update_linked_data;
+    ret = wauthn_get_assertion(&TestCommonData::clientData,
+                               &TestCommonData::pubkeyCredRequestOptions,
+                               ga_callbacks);
+    EXPECT_EQ(ret, WAUTHN_ERROR_INVALID_PARAMETER)
+        << "[invalid_callbacks_N] failed. "
+        << "ret=" << wauthn_error_to_string(ret) << std::endl;
+
+    ret = WAUTHN_ERROR_NONE;
+    ga_callbacks->response_callback = test_cb_ga_response;
+    ga_callbacks->linked_data_callback = nullptr;
     ret = wauthn_get_assertion(&TestCommonData::clientData,
                                &TestCommonData::pubkeyCredRequestOptions,
                                ga_callbacks);
@@ -157,8 +183,9 @@ TEST_F(WebAuthnTest, miss_qr_callback_without_linked_data_N)
     mc_callbacks = (wauthn_mc_callbacks_s*) calloc(1, sizeof(wauthn_mc_callbacks_s));
     mc_callbacks->qrcode_callback = nullptr;
     mc_callbacks->response_callback = test_cb_mc_response;
+    mc_callbacks->linked_data_callback = test_cb_update_linked_data;
     ret = wauthn_make_credential(&TestCommonData::clientData,
-                                 &TestCommonData::pubkeyCredCreationOptionsWithQR,
+                                 &TestCommonData::pubkeyCredCreationOptionsNoLD,
                                  mc_callbacks);
     EXPECT_EQ(ret, WAUTHN_ERROR_INVALID_PARAMETER)
         << "[miss_qr_callback_without_linked_data_N] failed. "
@@ -169,8 +196,9 @@ TEST_F(WebAuthnTest, miss_qr_callback_without_linked_data_N)
     ga_callbacks = (wauthn_ga_callbacks_s*) calloc(1, sizeof(wauthn_ga_callbacks_s));
     ga_callbacks->qrcode_callback = nullptr;
     ga_callbacks->response_callback = test_cb_ga_response;
+    ga_callbacks->linked_data_callback = test_cb_update_linked_data;
     ret = wauthn_get_assertion(&TestCommonData::clientData,
-                               &TestCommonData::pubkeyCredRequestOptionsWithQR,
+                               &TestCommonData::pubkeyCredRequestOptionsNoLD,
                                nullptr);
     EXPECT_EQ(ret, WAUTHN_ERROR_INVALID_PARAMETER)
         << "[miss_qr_callback_without_linked_data_N] failed. "