]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#1304] Finished HA service tests
authorFrancis Dupont <fdupont@isc.org>
Tue, 14 Jul 2020 11:04:12 +0000 (13:04 +0200)
committerFrancis Dupont <fdupont@isc.org>
Sat, 12 Sep 2020 08:50:34 +0000 (10:50 +0200)
src/hooks/dhcp/high_availability/ha_service.cc
src/hooks/dhcp/high_availability/tests/ha_service_unittest.cc

index 345b2abf34c10ceb01e28f4335f2fd9a9ab3deea..bda327dea16a4555390f7838a71eddff77fa5a54 100644 (file)
@@ -2156,6 +2156,8 @@ HAService::processMaintenanceCancel() {
 
 ConstElementPtr
 HAService::verifyAsyncResponse(const HttpResponsePtr& response, int& rcode) {
+    // Set the return code to error in case of early throw.
+    rcode = CONTROL_RESULT_ERROR;
     // The response must cast to JSON type.
     HttpResponseJsonPtr json_response =
         boost::dynamic_pointer_cast<HttpResponseJson>(response);
@@ -2169,9 +2171,22 @@ HAService::verifyAsyncResponse(const HttpResponsePtr& response, int& rcode) {
         isc_throw(CtrlChannelError, "no body found in the response");
     }
 
-    // Body must contain a list of responses form multiple servers.
+    // Body should contain a list of responses from multiple servers.
     if (body->getType() != Element::list) {
-        isc_throw(CtrlChannelError, "body of the response must be a list");
+        // Some control agent errors are returned as a map.
+        if (body->getType() == Element::map) {
+            ElementPtr list = Element::createList();
+            ElementPtr answer = Element::createMap();
+            answer->set(CONTROL_RESULT, Element::create(rcode));
+            ConstElementPtr text = body->get(CONTROL_TEXT);
+            if (text) {
+                answer->set(CONTROL_TEXT, text);
+            }
+            list->add(answer);
+            body = list;
+        } else {
+            isc_throw(CtrlChannelError, "body of the response must be a list");
+        }
     }
 
     // There must be at least one response.
index f8f74b215a09f626dac91b400a349e756787c969..73ad5b9e1cfc415dc1a9057994cf7d91de924a09 100644 (file)
@@ -26,6 +26,7 @@
 #include <dhcpsrv/network_state.h>
 #include <dhcpsrv/subnet_id.h>
 #include <hooks/parking_lots.h>
+#include <http/basic_auth_config.h>
 #include <http/date_time.h>
 #include <http/http_types.h>
 #include <http/listener.h>
@@ -214,7 +215,7 @@ public:
     TestHttpResponseCreator() :
         requests_(), control_result_(CONTROL_RESULT_SUCCESS),
         arguments_(), per_request_control_result_(),
-        per_request_arguments_(), request_index_() {
+        per_request_arguments_(), request_index_(), basic_auth_() {
     }
 
     /// @brief Removes all received requests.
@@ -308,6 +309,21 @@ public:
         return (HttpRequestPtr(new PostHttpRequestJson()));
     }
 
+    /// @brief Get basic HTTP authentication credentials.
+    ///
+    /// @return Basic HTTP authentication credentials and user id map.
+    const BasicHttpAuthMap& getCredentials() const {
+        return (basic_auth_.getCredentialMap());
+    }
+
+    /// @brief Add basic HTTP authentication client.
+    ///
+    /// @param user The user id to authorize.
+    /// @param password The password.
+    void addBasicAuth(const std::string& user, const std::string& password) {
+        basic_auth_.add(user, password);
+    }
+
 private:
 
     /// @brief Creates HTTP response.
@@ -340,6 +356,31 @@ private:
     /// @return Pointer to the generated HTTP OK response.
     virtual HttpResponsePtr
     createDynamicHttpResponse(const ConstHttpRequestPtr& request) {
+        // Check authentication.
+        const BasicHttpAuthMap& credentials = getCredentials();
+        if (!credentials.empty()) {
+            bool authorized = false;
+            try {
+                std::string value = request->getHeaderValue("Authorization");
+                if (value.size() < 8) {
+                    isc_throw(isc::BadValue, "header content is too short");
+                }
+                if (value.substr(0, 6) != "Basic ") {
+                    isc_throw(isc::BadValue, "no basic authentication");
+                }
+                value = value.substr(6);
+                if (credentials.count(value) != 0) {
+                    authorized = true;
+                }
+            } catch (const std::exception&) {
+                authorized = false;
+            }
+            if (!authorized) {
+                return (createStockHttpResponse(request,
+                                                HttpStatusCode::UNAUTHORIZED));
+            }
+        }
+
         // Request must always be JSON.
         ConstPostHttpRequestJsonPtr request_json =
             boost::dynamic_pointer_cast<const PostHttpRequestJson>(request);
@@ -447,6 +488,9 @@ private:
 
     /// @brief Index of the next request of the given type.
     std::map<std::string, size_t> request_index_;
+
+    /// @brief Basic HTTP authentication configuration.
+    BasicHttpAuthConfig basic_auth_;
 };
 
 /// @brief Shared pointer to the @c TestHttpResponseCreator.
@@ -532,7 +576,13 @@ public:
                                       HttpListener::RequestTimeout(REQUEST_TIMEOUT),
                                       HttpListener::IdleTimeout(IDLE_TIMEOUT))),
           leases4_(),
-          leases6_() {
+          leases6_(),
+          user1_(""),
+          password1_(""),
+          user2_(""),
+          password2_(""),
+          user3_(""),
+          password3_("") {
         MultiThreadingMgr::instance().setMode(false);
     }
 
@@ -658,6 +708,43 @@ public:
         factory3_->getResponseCreator()->setArguments("lease6-get-page", response_arguments);
     }
 
+    /// @brief Set basic HTTP authentication in a config.
+    ///
+    /// @param config Configuration to update.
+    void setBasicAuth(HAConfigPtr config) {
+        if (!config) {
+            ADD_FAILURE() << "null config";
+            return;
+        }
+        if (!user1_.empty()) {
+            HAConfig::PeerConfigPtr peer = config->getPeerConfig("server1");
+            if (!peer) {
+                ADD_FAILURE() << "null server1 config";
+                return;
+            }
+            BasicHttpAuthPtr& auth = peer->getBasicAuth();
+            auth.reset(new BasicHttpAuth(user1_, password1_));
+        }
+        if (!user2_.empty()) {
+            HAConfig::PeerConfigPtr peer = config->getPeerConfig("server2");
+            if (!peer) {
+                ADD_FAILURE() << "null server2 config";
+                return;
+            }
+            BasicHttpAuthPtr& auth = peer->getBasicAuth();
+            auth.reset(new BasicHttpAuth(user2_, password2_));
+        }
+        if (!user3_.empty()) {
+            HAConfig::PeerConfigPtr peer = config->getPeerConfig("server3");
+            if (!peer) {
+                ADD_FAILURE() << "null server3 config";
+                return;
+            }
+            BasicHttpAuthPtr& auth = peer->getBasicAuth();
+            auth.reset(new BasicHttpAuth(user3_, password3_));
+        }
+    }
+
     /// @brief Tests scenarios when lease updates are sent to a partner while
     /// the partner is online or offline.
     ///
@@ -677,6 +764,7 @@ public:
         // server 1.
         HAConfigPtr config_storage = createValidConfiguration();
         config_storage->setWaitBackupAck(wait_backup_ack);
+        setBasicAuth(config_storage);
 
         // Create parking lot where query is going to be parked and unparked.
         ParkingLotPtr parking_lot(new ParkingLot());
@@ -779,6 +867,7 @@ public:
         // server 1.
         HAConfigPtr config_storage = createValidConfiguration();
         config_storage->setWaitBackupAck(wait_backup_ack);
+        setBasicAuth(config_storage);
 
         // Create parking lot where query is going to be parked and unparked.
         ParkingLotPtr parking_lot(new ParkingLot());
@@ -871,6 +960,7 @@ public:
         // server 1.
         HAConfigPtr config_storage = createValidConfiguration();
         config_storage->setHeartbeatDelay(1000);
+        setBasicAuth(config_storage);
 
         // Create a valid static response to the heartbeat command.
         ElementPtr response_arguments = Element::createMap();
@@ -1035,7 +1125,7 @@ public:
         // Instruct the server 2 to return an error as a result of receiving a command.
         factory2_->getResponseCreator()->setControlResult(CONTROL_RESULT_ERROR);
 
-        // Start only two servers out of three. The server 3 is not running.
+        // Start HTTP servers.
         ASSERT_NO_THROW({
                 listener_->start();
                 listener2_->start();
@@ -1079,6 +1169,44 @@ public:
         EXPECT_TRUE(delete_request3);
     }
 
+    /// @brief Tests scenarios when one of the servers to which a
+    /// lease update is sent does not authorize the local server.
+    void testSndUpdatesControlResultUnauthorized() {
+        // Instruct the server 2 to require authentication.
+        factory2_->getResponseCreator()->addBasicAuth("foo", "bar");
+
+        // Start HTTP servers.
+        ASSERT_NO_THROW({
+                listener_->start();
+                listener2_->start();
+                listener3_->start();
+        });
+
+        testSendLeaseUpdates([] {
+            ADD_FAILURE() << "unpark function called but expected that "
+                "the packet is dropped";
+        }, false, 1);
+
+        // The updates should be sent to server 2 and this server should
+        // return error code.
+        EXPECT_TRUE(factory2_->getResponseCreator()->getReceivedRequests().empty());
+
+        // Lease updates should be successfully sent to server3.
+        EXPECT_EQ(2, factory3_->getResponseCreator()->getReceivedRequests().size());
+
+        // Check that the server 3 has received lease4-update command.
+        auto update_request3 =
+            factory3_->getResponseCreator()->findRequest("lease4-update",
+                                                         "192.1.2.3");
+        EXPECT_TRUE(update_request3);
+
+        // Check that the server 3 has received lease4-del command.
+        auto delete_request3 =
+            factory3_->getResponseCreator()->findRequest("lease4-del",
+                                                         "192.2.3.4");
+        EXPECT_TRUE(delete_request3);
+    }
+
     /// @brief Tests scenarios when one of the servers to which
     /// updates are sent is offline.
     void testSendUpdatesBackupServerOffline() {
@@ -1249,7 +1377,7 @@ public:
         // Instruct the server 2 to return an error as a result of receiving a command.
         factory2_->getResponseCreator()->setControlResult(CONTROL_RESULT_ERROR);
 
-        // Start only two servers out of three. The server 3 is not running.
+        // Start HTTP servers.
         ASSERT_NO_THROW({
                 listener_->start();
                 listener2_->start();
@@ -1272,6 +1400,28 @@ public:
         EXPECT_TRUE(update_request2);
     }
 
+    /// @brief Tests scenarios when one of the servers to which a
+    /// lease update is sent does not authorize the local server.
+    void testSendUpdatesControlResultUnauthorized6() {
+        // Instruct the server 2 to require authentication.
+        factory2_->getResponseCreator()->addBasicAuth("foo", "bar");
+
+        // Start HTTP servers.
+        ASSERT_NO_THROW({
+                listener_->start();
+                listener2_->start();
+                listener3_->start();
+        });
+
+        testSendLeaseUpdates6([] {
+            ADD_FAILURE() << "unpark function called but expected that "
+                "the packet is dropped";
+        }, false, 1);
+
+        // The updates should be sent to server 2 and this server should return error code.
+        EXPECT_TRUE(factory2_->getResponseCreator()->getReceivedRequests().empty());
+    }
+
     /// @brief These tests verify that the server accepts the response
     /// to the lease6-bulk-apply command including
     /// failed-deleted-leases and failed-leases parameters.
@@ -1365,6 +1515,7 @@ public:
         // Create HA configuration for 3 servers. This server is
         // server 1.
         HAConfigPtr config_storage = createValidConfiguration();
+        setBasicAuth(config_storage);
 
         // Leases are fetched in pages, so the lease4-get-page should be
         // sent multiple times. The server is configured to return leases
@@ -1413,6 +1564,7 @@ public:
         // Create HA configuration for 3 servers. This server is
         // server 1.
         HAConfigPtr config_storage = createValidConfiguration();
+        setBasicAuth(config_storage);
 
         // Leases are fetched in pages, so the lease6-get-page should be
         // sent multiple times. The server is configured to return leases
@@ -1466,6 +1618,24 @@ public:
 
     /// @brief IPv6 leases to be used in the tests.
     std::vector<Lease6Ptr> leases6_;
+
+    /// @brief Basic HTTP authentication user id for server1.
+    std::string user1_;
+
+    /// @brief Basic HTTP authentication password for server1.
+    std::string password1_;
+
+    /// @brief Basic HTTP authentication user id for server2.
+    std::string user2_;
+
+    /// @brief Basic HTTP authentication password for server2.
+    std::string password2_;
+
+    /// @brief Basic HTTP authentication user id for server3.
+    std::string user3_;
+
+    /// @brief Basic HTTP authentication password for server3.
+    std::string password3_;
 };
 
 // Test that server performs load balancing and assigns appropriate classes
@@ -1627,6 +1797,33 @@ TEST_F(HAServiceTest, sendSuccessfulUpdatesMultiThreading) {
     testSendSuccessfulUpdates();
 }
 
+// Test scenario when all lease updates are sent successfully.
+TEST_F(HAServiceTest, sendSuccessfulUpdatesAuthorized) {
+    // Update config to provide authentication.
+    user2_ = "foo";
+    password2_ = "bar";
+    user3_ = "test";
+    password3_ = "1234";
+    // Instruct servers to require authentication.
+    factory2_->getResponseCreator()->addBasicAuth("foo", "bar");
+    factory3_->getResponseCreator()->addBasicAuth("test", "1234");
+    testSendSuccessfulUpdates();
+}
+
+// Test scenario when all lease updates are sent successfully.
+TEST_F(HAServiceTest, sendSuccessfulUpdatesAuthorizedMultiThreading) {
+    // Update config to provide authentication.
+    user2_ = "foo";
+    password2_ = "bar";
+    user3_ = "test";
+    password3_ = "1234";
+    // Instruct servers to require authentication.
+    factory2_->getResponseCreator()->addBasicAuth("foo", "bar");
+    factory3_->getResponseCreator()->addBasicAuth("test", "1234");
+    MultiThreadingMgr::instance().setMode(true);
+    testSendSuccessfulUpdates();
+}
+
 // Test scenario when lease updates are not sent to the failover peer.
 TEST_F(HAServiceTest, sendUpdatesPartnerDown) {
     testSendUpdatesPartnerDown();
@@ -1673,6 +1870,19 @@ TEST_F(HAServiceTest, sendUpdatesControlResultErrorMultiThreading) {
     testSndUpdatesControlResultError();
 }
 
+// Test scenario when one of the servers to which a lease update is sent
+// requires not provided authentication.
+TEST_F(HAServiceTest, sendUpdatesControlResultUnauthorized) {
+    testSndUpdatesControlResultUnauthorized();
+}
+
+// Test scenario when one of the servers to which a lease update is sent
+// requires not provided authentication.
+TEST_F(HAServiceTest, sendUpdatesControlResultUnauthorizedMultiThreading) {
+    MultiThreadingMgr::instance().setMode(true);
+    testSndUpdatesControlResultUnauthorized();
+}
+
 // Test scenario when all lease updates are sent successfully.
 TEST_F(HAServiceTest, sendSuccessfulUpdates6) {
     testSendSuccessfulUpdates6();
@@ -1683,6 +1893,34 @@ TEST_F(HAServiceTest, sendSuccessfulUpdates6MultiThreading) {
     MultiThreadingMgr::instance().setMode(true);
     testSendSuccessfulUpdates6();
 }
+
+// Test scenario when all lease updates are sent successfully.
+TEST_F(HAServiceTest, sendSuccessfulUpdates6Authorized) {
+    // Update config to provide authentication.
+    user2_ = "foo";
+    password2_ = "bar";
+    user3_ = "test";
+    password3_ = "1234";
+    // Instruct servers to require authentication.
+    factory2_->getResponseCreator()->addBasicAuth("foo", "bar");
+    factory3_->getResponseCreator()->addBasicAuth("test", "1234");
+    testSendSuccessfulUpdates6();
+}
+
+// Test scenario when all lease updates are sent successfully.
+TEST_F(HAServiceTest, sendSuccessfulUpdates6AuthorizedMultiThreading) {
+    // Update config to provide authentication.
+    user2_ = "foo";
+    password2_ = "bar";
+    user3_ = "test";
+    password3_ = "1234";
+    // Instruct servers to require authentication.
+    factory2_->getResponseCreator()->addBasicAuth("foo", "bar");
+    factory3_->getResponseCreator()->addBasicAuth("test", "1234");
+    MultiThreadingMgr::instance().setMode(true);
+    testSendSuccessfulUpdates6();
+}
+
 // Test scenario when lease updates are not sent to the failover peer.
 TEST_F(HAServiceTest, sendUpdatesPartnerDown6) {
     testSendUpdatesPartnerDown6();
@@ -1729,6 +1967,19 @@ TEST_F(HAServiceTest, sendUpdatesControlResultError6MultiThreading) {
     testSendUpdatesControlResultError6();
 }
 
+// Test scenario when one of the servers to which a lease update is sent
+// requires not provided authentication.
+TEST_F(HAServiceTest, sendUpdatesControlResultUnauthorized6) {
+    testSendUpdatesControlResultUnauthorized6();
+}
+
+// Test scenario when one of the servers to which a lease update is sent
+// requires not provided authentication.
+TEST_F(HAServiceTest, sendUpdatesControlResultUnauthorized6MultiThreading) {
+    MultiThreadingMgr::instance().setMode(true);
+    testSendUpdatesControlResultUnauthorized6();
+}
+
 // This test verifies that the server accepts the response to the lease6-bulk-apply
 // command including failed-deleted-leases and failed-leases parameters.
 TEST_F(HAServiceTest, sendUpdatesFailedLeases6) {
@@ -1870,6 +2121,32 @@ TEST_F(HAServiceTest, recurringHeartbeat) {
     EXPECT_GE(factory2_->getResponseCreator()->getReceivedRequests().size(), 0);
 }
 
+// This test verifies that the heartbeat is periodically sent to the
+// other server.
+TEST_F(HAServiceTest, recurringHeartbeatAuthorized) {
+    // Update config to provide authentication.
+    user2_ = "foo";
+    password2_ = "bar";
+    user3_ = "test";
+    password3_ = "1234";
+    // Instruct servers to require authentication.
+    factory2_->getResponseCreator()->addBasicAuth("foo", "bar");
+    factory3_->getResponseCreator()->addBasicAuth("test", "1234");
+    // Start the servers.
+    ASSERT_NO_THROW({
+        listener_->start();
+        listener2_->start();
+        listener3_->start();
+    });
+
+    // All servers are configured to return success and all servers are online.
+    // The heartbeat should be successful (as indicated by the 'true' argument).
+    ASSERT_NO_FATAL_FAILURE(testRecurringHeartbeat(CONTROL_RESULT_SUCCESS, true));
+
+    // Server 2 should have received the heartbeat
+    EXPECT_GE(factory2_->getResponseCreator()->getReceivedRequests().size(), 0);
+}
+
 // This test verifies that the heartbeat is considered being unsuccessful if the
 // partner is offline.
 TEST_F(HAServiceTest, recurringHeartbeatServerOffline) {
@@ -1905,6 +2182,28 @@ TEST_F(HAServiceTest, recurringHeartbeatControlResultError) {
     EXPECT_EQ(1, factory2_->getResponseCreator()->getReceivedRequests().size());
 }
 
+// This test verifies that the heartbeat is considered being unsuccessful if
+// the partner requires not provided authentication.
+TEST_F(HAServiceTest, recurringHeartbeatControlResultUnauthorized) {
+    // Instruct servers to require authentication.
+    factory2_->getResponseCreator()->addBasicAuth("foo", "bar");
+    factory3_->getResponseCreator()->addBasicAuth("test", "1234");
+
+    // Start the servers.
+    ASSERT_NO_THROW({
+        listener_->start();
+        listener2_->start();
+        listener3_->start();
+    });
+
+    // Run the actual test. The servers return a control error and it is expected
+    // that the state is not poked.
+    ASSERT_NO_FATAL_FAILURE(testRecurringHeartbeat(CONTROL_RESULT_ERROR, false));
+
+    // Server 2 should have received the heartbeat.
+    EXPECT_TRUE(factory2_->getResponseCreator()->getReceivedRequests().empty());
+}
+
 // This test verifies that IPv4 leases can be fetched from the peer and inserted
 // or updated in the local lease database.
 TEST_F(HAServiceTest, asyncSyncLeases) {
@@ -1946,6 +2245,7 @@ TEST_F(HAServiceTest, asyncSyncLeases) {
 
     // Create HA configuration.
     HAConfigPtr config_storage = createValidConfiguration();
+    setBasicAuth(config_storage);
 
     // Convert leases to the JSON format, the same as used by the lease_cmds
     // hook library. Configure our test HTTP servers to return those
@@ -2023,79 +2323,33 @@ TEST_F(HAServiceTest, asyncSyncLeases) {
     }
 }
 
-// Test that there is no exception thrown during leases synchronization
-// when server returns a wrong answer.
-TEST_F(HAServiceTest, asyncSyncLeasesWrongAnswer) {
+// This test verifies that IPv4 leases can be fetched from the peer and inserted
+// or updated in the local lease database.
+TEST_F(HAServiceTest, asyncSyncLeasesAuthorized) {
+    // Update config to provide authentication.
+    user2_ = "foo";
+    password2_ = "bar";
+    user3_ = "test";
+    password3_ = "1234";
+
+    // Instruct servers to require authentication.
+    factory2_->getResponseCreator()->addBasicAuth("foo", "bar");
+    factory3_->getResponseCreator()->addBasicAuth("test", "1234");
+
     // Create lease manager.
     ASSERT_NO_THROW(LeaseMgrFactory::create("universe=4 type=memfile persist=false"));
 
     // Create IPv4 leases which will be fetched from the other server.
     ASSERT_NO_THROW(generateTestLeases4());
 
-    // Create HA configuration.
-    HAConfigPtr config_storage = createValidConfiguration();
-    // Setting the heartbeat delay to 0 disables the recurring heartbeat.
-    // We just want to synchronize leases and not send the heartbeat.
-    config_storage->setHeartbeatDelay(0);
-
-    // Set empty response. This should cause the HA service to log an
-    // error but not crash.
-    ElementPtr response_arguments = Element::createMap();
-
-    factory2_->getResponseCreator()->setArguments(response_arguments);
-    factory3_->getResponseCreator()->setArguments(response_arguments);
-
-    // Start the servers.
-    ASSERT_NO_THROW({
-        listener_->start();
-        listener2_->start();
-        listener3_->start();
-    });
-
-    TestHAService service(io_service_, network_state_, config_storage);
-
-    // Start fetching leases asynchronously.
-    ASSERT_NO_THROW(service.asyncSyncLeases());
-
-    // Run IO service to actually perform the transaction.
-    ASSERT_NO_THROW(runIOService(1000));
-}
-
-// Test that there is no exception thrown during leases synchronization
-// when servers are offline.
-TEST_F(HAServiceTest, asyncSyncLeasesServerOffline) {
-    // Create HA configuration.
-    HAConfigPtr config_storage = createValidConfiguration();
-    // Setting the heartbeat delay to 0 disables the recurring heartbeat.
-    // We just want to synchronize leases and not send the heartbeat.
-    config_storage->setHeartbeatDelay(0);
-
-    TestHAService service(io_service_, network_state_, config_storage);
-
-    // Start fetching leases asynchronously.
-    ASSERT_NO_THROW(service.asyncSyncLeases());
-
-    // Run IO service for 1 second.
-    ASSERT_NO_THROW(runIOService(1000));
-}
-
-// This test verifies that IPv6 leases can be fetched from the peer and inserted
-// or updated in the local lease database.
-TEST_F(HAServiceTest, asyncSyncLeases6) {
-    // Create lease manager.
-    ASSERT_NO_THROW(LeaseMgrFactory::create("universe=6 type=memfile persist=false"));
-
-    // Create IPv6 leases which will be fetched from the other server.
-    ASSERT_NO_THROW(generateTestLeases6());
-
-    for (size_t i = 0; i < leases6_.size(); ++i) {
+    for (size_t i = 0; i < leases4_.size(); ++i) {
         // For every even lease index we add this lease to the database to exercise
         // the scenario when a lease is already in the database and may be updated
         // by the lease synchronization procedure.
         if ((i % 2) == 0) {
             // Add a copy of the lease to make sure that by modifying the lease
             // contents we don't affect the lease in the database.
-            Lease6Ptr lease_to_add(new Lease6(*leases6_[i]));
+            Lease4Ptr lease_to_add(new Lease4(*leases4_[i]));
             // Modify valid lifetime of the lease in the database so we can
             // later use this value to verify if the lease has been updated.
             --lease_to_add->valid_lft_;
@@ -2107,19 +2361,20 @@ TEST_F(HAServiceTest, asyncSyncLeases6) {
     // of synchrnonization process because cltt is checked and the lease is
     // updated if the cltt of the fetched lease is later than the cltt of the
     // existing lease.
-    ++leases6_[0]->cltt_;
+    ++leases4_[0]->cltt_;
 
     // For the second lease, set the wrong subnet identifier. This should be
     // rejected and this lease shouldn't be inserted into the database.
     // Other leases should be inserted/updated just fine.
-    ++leases6_[1]->subnet_id_ = 0;
+    ++leases4_[1]->subnet_id_ = 0;
 
     // Modify the partner's lease cltt so it is earlier than the local lease.
     // Therfore, this lease update should be rejected.
-    --leases6_[2]->cltt_;
+    --leases4_[2]->cltt_;
 
     // Create HA configuration.
     HAConfigPtr config_storage = createValidConfiguration();
+    setBasicAuth(config_storage);
 
     // Convert leases to the JSON format, the same as used by the lease_cmds
     // hook library. Configure our test HTTP servers to return those
@@ -2127,9 +2382,9 @@ TEST_F(HAServiceTest, asyncSyncLeases6) {
     ElementPtr response_arguments = Element::createMap();
 
     // Leases are fetched in pages, so the lease4-get-page should be
-    // sent multiple times. We need to configure the server side to
-    // return leases in 3-element chunks.
-    createPagedSyncResponses6();
+    // sent multiple times. The server is configured to return leases
+    // in 3-element chunks.
+    createPagedSyncResponses4();
 
     // Start the servers.
     ASSERT_NO_THROW({
@@ -2138,8 +2393,7 @@ TEST_F(HAServiceTest, asyncSyncLeases6) {
         listener3_->start();
     });
 
-    TestHAService service(io_service_, network_state_, config_storage,
-                          HAServerType::DHCPv6);
+    TestHAService service(io_service_, network_state_, config_storage);
     // Setting the heartbeat delay to 0 disables the recurring heartbeat.
     // We just want to synchronize leases and not send the heartbeat.
     config_storage->setHeartbeatDelay(0);
@@ -2152,30 +2406,373 @@ TEST_F(HAServiceTest, asyncSyncLeases6) {
         // Stop running the IO service if we see a lease in the lease
         // database which is expected to be inserted as a result of lease
         // syncing.
-        return (!LeaseMgrFactory::instance().getLeases6(SubnetID(10)).empty());
+        return (!LeaseMgrFactory::instance().getLeases4(SubnetID(10)).empty());
     }));
 
     // Check if all leases have been stored in the local database.
-    for (size_t i = 0; i < leases6_.size(); ++i) {
+    for (size_t i = 0; i < leases4_.size(); ++i) {
         if (i == 1) {
             // This lease was purposely malformed and thus shouldn't be
             // inserted into the database.
-            EXPECT_FALSE(LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
-                                                               leases6_[i]->addr_))
-                << "lease " << leases6_[i]->addr_.toText()
+            EXPECT_FALSE(LeaseMgrFactory::instance().getLease4(leases4_[i]->addr_))
+                << "lease " << leases4_[i]->addr_.toText()
                 << " was inserted into the database, but it shouldn't";
+
         } else {
-            // Other leases should be inserted/updated.
-            Lease6Ptr existing_lease = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
-                                                                             leases6_[i]->addr_);
-            ASSERT_TRUE(existing_lease) << "lease " << leases6_[i]->addr_.toText()
+            // All other leases should be in the database.
+            Lease4Ptr existing_lease = LeaseMgrFactory::instance().getLease4(leases4_[i]->addr_);
+            ASSERT_TRUE(existing_lease) << "lease " << leases4_[i]->addr_.toText()
                                         << " not in the lease database";
-
+            // The lease with #2 returned by the partner is older than its local instance.
+            // The local server should reject this lease.
             if (i == 2) {
                 // The existing lease should have unmodified timestamp because the
                 // update is expected to be rejected. Same for valid lifetime.
-                EXPECT_LT(leases6_[i]->cltt_, existing_lease->cltt_);
-                EXPECT_NE(leases6_[i]->valid_lft_, existing_lease->valid_lft_);
+                EXPECT_LT(leases4_[i]->cltt_, existing_lease->cltt_);
+                EXPECT_NE(leases4_[i]->valid_lft_, existing_lease->valid_lft_);
+
+            } else {
+                // All other leases should have the same cltt.
+                EXPECT_EQ(leases4_[i]->cltt_, existing_lease->cltt_);
+
+                // Leases with even indexes were added to the database with modified
+                // valid lifetime. Thus the local copy of each such lease should have
+                // this modified valid lifetime. The lease #0 should be updated from
+                // the partner because of the partner's cltt was set to later time.
+                if ((i != 0) && (i % 2) == 0) {
+                    EXPECT_EQ(leases4_[i]->valid_lft_ - 1, existing_lease->valid_lft_);
+
+                } else {
+                    // All other leases should have been fetched from the partner and
+                    // inserted with no change.
+                    EXPECT_EQ(leases4_[i]->valid_lft_, existing_lease->valid_lft_);
+                }
+            }
+        }
+    }
+}
+
+// Test that there is no exception thrown during leases synchronization
+// when server returns a wrong answer.
+TEST_F(HAServiceTest, asyncSyncLeasesWrongAnswer) {
+    // Create lease manager.
+    ASSERT_NO_THROW(LeaseMgrFactory::create("universe=4 type=memfile persist=false"));
+
+    // Create IPv4 leases which will be fetched from the other server.
+    ASSERT_NO_THROW(generateTestLeases4());
+
+    // Create HA configuration.
+    HAConfigPtr config_storage = createValidConfiguration();
+    // Setting the heartbeat delay to 0 disables the recurring heartbeat.
+    // We just want to synchronize leases and not send the heartbeat.
+    config_storage->setHeartbeatDelay(0);
+
+    // Set empty response. This should cause the HA service to log an
+    // error but not crash.
+    ElementPtr response_arguments = Element::createMap();
+
+    factory2_->getResponseCreator()->setArguments(response_arguments);
+    factory3_->getResponseCreator()->setArguments(response_arguments);
+
+    // Start the servers.
+    ASSERT_NO_THROW({
+        listener_->start();
+        listener2_->start();
+        listener3_->start();
+    });
+
+    TestHAService service(io_service_, network_state_, config_storage);
+
+    // Start fetching leases asynchronously.
+    ASSERT_NO_THROW(service.asyncSyncLeases());
+
+    // Run IO service to actually perform the transaction.
+    ASSERT_NO_THROW(runIOService(1000));
+}
+
+// Test that there is no exception thrown during leases synchronization
+// when servers are offline.
+TEST_F(HAServiceTest, asyncSyncLeasesServerOffline) {
+    // Create HA configuration.
+    HAConfigPtr config_storage = createValidConfiguration();
+    // Setting the heartbeat delay to 0 disables the recurring heartbeat.
+    // We just want to synchronize leases and not send the heartbeat.
+    config_storage->setHeartbeatDelay(0);
+
+    TestHAService service(io_service_, network_state_, config_storage);
+
+    // Start fetching leases asynchronously.
+    ASSERT_NO_THROW(service.asyncSyncLeases());
+
+    // Run IO service for 1 second.
+    ASSERT_NO_THROW(runIOService(1000));
+}
+
+// Test that there is no exception thrown during leases synchronization
+// when servers require not provided authentication.
+TEST_F(HAServiceTest, asyncSyncLeasesServerUnauthorized) {
+    // Instruct servers to require authentication.
+    factory2_->getResponseCreator()->addBasicAuth("foo", "bar");
+    factory3_->getResponseCreator()->addBasicAuth("test", "1234");
+
+    // Create lease manager.
+    ASSERT_NO_THROW(LeaseMgrFactory::create("universe=4 type=memfile persist=false"));
+
+    // Create IPv4 leases which will be fetched from the other server.
+    ASSERT_NO_THROW(generateTestLeases4());
+
+    // Create HA configuration.
+    HAConfigPtr config_storage = createValidConfiguration();
+    setBasicAuth(config_storage);
+
+    // Setting the heartbeat delay to 0 disables the recurring heartbeat.
+    // We just want to synchronize leases and not send the heartbeat.
+    config_storage->setHeartbeatDelay(0);
+
+    // Start the servers.
+    ASSERT_NO_THROW({
+        listener_->start();
+        listener2_->start();
+        listener3_->start();
+    });
+
+    TestHAService service(io_service_, network_state_, config_storage);
+
+    // Start fetching leases asynchronously.
+    ASSERT_NO_THROW(service.asyncSyncLeases());
+
+    // Run IO service for 1 second.
+    ASSERT_NO_THROW(runIOService(1000));
+}
+
+// This test verifies that IPv6 leases can be fetched from the peer and inserted
+// or updated in the local lease database.
+TEST_F(HAServiceTest, asyncSyncLeases6) {
+    // Create lease manager.
+    ASSERT_NO_THROW(LeaseMgrFactory::create("universe=6 type=memfile persist=false"));
+
+    // Create IPv6 leases which will be fetched from the other server.
+    ASSERT_NO_THROW(generateTestLeases6());
+
+    for (size_t i = 0; i < leases6_.size(); ++i) {
+        // For every even lease index we add this lease to the database to exercise
+        // the scenario when a lease is already in the database and may be updated
+        // by the lease synchronization procedure.
+        if ((i % 2) == 0) {
+            // Add a copy of the lease to make sure that by modifying the lease
+            // contents we don't affect the lease in the database.
+            Lease6Ptr lease_to_add(new Lease6(*leases6_[i]));
+            // Modify valid lifetime of the lease in the database so we can
+            // later use this value to verify if the lease has been updated.
+            --lease_to_add->valid_lft_;
+            LeaseMgrFactory::instance().addLease(lease_to_add);
+        }
+    }
+
+    // Modify cltt of the first lease. This lease should be updated as a result
+    // of synchrnonization process because cltt is checked and the lease is
+    // updated if the cltt of the fetched lease is later than the cltt of the
+    // existing lease.
+    ++leases6_[0]->cltt_;
+
+    // For the second lease, set the wrong subnet identifier. This should be
+    // rejected and this lease shouldn't be inserted into the database.
+    // Other leases should be inserted/updated just fine.
+    ++leases6_[1]->subnet_id_ = 0;
+
+    // Modify the partner's lease cltt so it is earlier than the local lease.
+    // Therfore, this lease update should be rejected.
+    --leases6_[2]->cltt_;
+
+    // Create HA configuration.
+    HAConfigPtr config_storage = createValidConfiguration();
+    setBasicAuth(config_storage);
+
+    // Convert leases to the JSON format, the same as used by the lease_cmds
+    // hook library. Configure our test HTTP servers to return those
+    // leases in this format.
+    ElementPtr response_arguments = Element::createMap();
+
+    // Leases are fetched in pages, so the lease4-get-page should be
+    // sent multiple times. We need to configure the server side to
+    // return leases in 3-element chunks.
+    createPagedSyncResponses6();
+
+    // Start the servers.
+    ASSERT_NO_THROW({
+        listener_->start();
+        listener2_->start();
+        listener3_->start();
+    });
+
+    TestHAService service(io_service_, network_state_, config_storage,
+                          HAServerType::DHCPv6);
+    // Setting the heartbeat delay to 0 disables the recurring heartbeat.
+    // We just want to synchronize leases and not send the heartbeat.
+    config_storage->setHeartbeatDelay(0);
+
+    // Start fetching leases asynchronously.
+    ASSERT_NO_THROW(service.asyncSyncLeases());
+
+    // Run IO service to actually perform the transaction.
+    ASSERT_NO_THROW(runIOService(TEST_TIMEOUT, []() {
+        // Stop running the IO service if we see a lease in the lease
+        // database which is expected to be inserted as a result of lease
+        // syncing.
+        return (!LeaseMgrFactory::instance().getLeases6(SubnetID(10)).empty());
+    }));
+
+    // Check if all leases have been stored in the local database.
+    for (size_t i = 0; i < leases6_.size(); ++i) {
+        if (i == 1) {
+            // This lease was purposely malformed and thus shouldn't be
+            // inserted into the database.
+            EXPECT_FALSE(LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+                                                               leases6_[i]->addr_))
+                << "lease " << leases6_[i]->addr_.toText()
+                << " was inserted into the database, but it shouldn't";
+        } else {
+            // Other leases should be inserted/updated.
+            Lease6Ptr existing_lease = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+                                                                             leases6_[i]->addr_);
+            ASSERT_TRUE(existing_lease) << "lease " << leases6_[i]->addr_.toText()
+                                        << " not in the lease database";
+
+            if (i == 2) {
+                // The existing lease should have unmodified timestamp because the
+                // update is expected to be rejected. Same for valid lifetime.
+                EXPECT_LT(leases6_[i]->cltt_, existing_lease->cltt_);
+                EXPECT_NE(leases6_[i]->valid_lft_, existing_lease->valid_lft_);
+
+            } else {
+                // All other leases should have the same cltt.
+                EXPECT_EQ(leases6_[i]->cltt_, existing_lease->cltt_);
+
+                // Leases with even indexes were added to the database with modified
+                // valid lifetime. Thus the local copy of each such lease should have
+                // this modified valid lifetime. The lease #0 should be updated from
+                // the partner because of the partner's cltt was set to later time.
+                if ((i != 0) && (i % 2) == 0) {
+                    EXPECT_EQ(leases6_[i]->valid_lft_ - 1, existing_lease->valid_lft_);
+
+                } else {
+                    // All other leases should have been fetched from the partner and
+                    // inserted with no change.
+                    EXPECT_EQ(leases6_[i]->valid_lft_, existing_lease->valid_lft_);
+                }
+            }
+        }
+    }
+}
+
+// This test verifies that IPv6 leases can be fetched from the peer and inserted
+// or updated in the local lease database.
+TEST_F(HAServiceTest, asyncSyncLeases6Authorized) {
+    // Update config to provide authentication.
+    user2_ = "foo";
+    password2_ = "bar";
+    user3_ = "test";
+    password3_ = "1234";
+
+    // Instruct servers to require authentication.
+    factory2_->getResponseCreator()->addBasicAuth("foo", "bar");
+    factory3_->getResponseCreator()->addBasicAuth("test", "1234");
+
+    // Create lease manager.
+    ASSERT_NO_THROW(LeaseMgrFactory::create("universe=6 type=memfile persist=false"));
+
+    // Create IPv6 leases which will be fetched from the other server.
+    ASSERT_NO_THROW(generateTestLeases6());
+
+    for (size_t i = 0; i < leases6_.size(); ++i) {
+        // For every even lease index we add this lease to the database to exercise
+        // the scenario when a lease is already in the database and may be updated
+        // by the lease synchronization procedure.
+        if ((i % 2) == 0) {
+            // Add a copy of the lease to make sure that by modifying the lease
+            // contents we don't affect the lease in the database.
+            Lease6Ptr lease_to_add(new Lease6(*leases6_[i]));
+            // Modify valid lifetime of the lease in the database so we can
+            // later use this value to verify if the lease has been updated.
+            --lease_to_add->valid_lft_;
+            LeaseMgrFactory::instance().addLease(lease_to_add);
+        }
+    }
+
+    // Modify cltt of the first lease. This lease should be updated as a result
+    // of synchrnonization process because cltt is checked and the lease is
+    // updated if the cltt of the fetched lease is later than the cltt of the
+    // existing lease.
+    ++leases6_[0]->cltt_;
+
+    // For the second lease, set the wrong subnet identifier. This should be
+    // rejected and this lease shouldn't be inserted into the database.
+    // Other leases should be inserted/updated just fine.
+    ++leases6_[1]->subnet_id_ = 0;
+
+    // Modify the partner's lease cltt so it is earlier than the local lease.
+    // Therfore, this lease update should be rejected.
+    --leases6_[2]->cltt_;
+
+    // Create HA configuration.
+    HAConfigPtr config_storage = createValidConfiguration();
+    setBasicAuth(config_storage);
+
+    // Convert leases to the JSON format, the same as used by the lease_cmds
+    // hook library. Configure our test HTTP servers to return those
+    // leases in this format.
+    ElementPtr response_arguments = Element::createMap();
+
+    // Leases are fetched in pages, so the lease4-get-page should be
+    // sent multiple times. We need to configure the server side to
+    // return leases in 3-element chunks.
+    createPagedSyncResponses6();
+
+    // Start the servers.
+    ASSERT_NO_THROW({
+        listener_->start();
+        listener2_->start();
+        listener3_->start();
+    });
+
+    TestHAService service(io_service_, network_state_, config_storage,
+                          HAServerType::DHCPv6);
+    // Setting the heartbeat delay to 0 disables the recurring heartbeat.
+    // We just want to synchronize leases and not send the heartbeat.
+    config_storage->setHeartbeatDelay(0);
+
+    // Start fetching leases asynchronously.
+    ASSERT_NO_THROW(service.asyncSyncLeases());
+
+    // Run IO service to actually perform the transaction.
+    ASSERT_NO_THROW(runIOService(TEST_TIMEOUT, []() {
+        // Stop running the IO service if we see a lease in the lease
+        // database which is expected to be inserted as a result of lease
+        // syncing.
+        return (!LeaseMgrFactory::instance().getLeases6(SubnetID(10)).empty());
+    }));
+
+    // Check if all leases have been stored in the local database.
+    for (size_t i = 0; i < leases6_.size(); ++i) {
+        if (i == 1) {
+            // This lease was purposely malformed and thus shouldn't be
+            // inserted into the database.
+            EXPECT_FALSE(LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+                                                               leases6_[i]->addr_))
+                << "lease " << leases6_[i]->addr_.toText()
+                << " was inserted into the database, but it shouldn't";
+        } else {
+            // Other leases should be inserted/updated.
+            Lease6Ptr existing_lease = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+                                                                             leases6_[i]->addr_);
+            ASSERT_TRUE(existing_lease) << "lease " << leases6_[i]->addr_.toText()
+                                        << " not in the lease database";
+
+            if (i == 2) {
+                // The existing lease should have unmodified timestamp because the
+                // update is expected to be rejected. Same for valid lifetime.
+                EXPECT_LT(leases6_[i]->cltt_, existing_lease->cltt_);
+                EXPECT_NE(leases6_[i]->valid_lft_, existing_lease->valid_lft_);
 
             } else {
                 // All other leases should have the same cltt.
@@ -2256,6 +2853,44 @@ TEST_F(HAServiceTest, asyncSyncLeases6ServerOffline) {
     ASSERT_NO_THROW(runIOService(1000));
 }
 
+// Test that there is no exception thrown during IPv6 leases synchronization
+// when server requires not provided authentication.
+TEST_F(HAServiceTest, asyncSyncLeases6Unauthorized) {
+    // Instruct servers to require authentication.
+    factory2_->getResponseCreator()->addBasicAuth("foo", "bar");
+    factory3_->getResponseCreator()->addBasicAuth("test", "1234");
+
+    // Create lease manager.
+    ASSERT_NO_THROW(LeaseMgrFactory::create("universe=6 type=memfile persist=false"));
+
+    // Create IPv6 leases which will be fetched from the other server.
+    ASSERT_NO_THROW(generateTestLeases6());
+
+    // Create HA configuration.
+    HAConfigPtr config_storage = createValidConfiguration();
+    setBasicAuth(config_storage);
+
+    // Setting the heartbeat delay to 0 disables the recurring heartbeat.
+    // We just want to synchronize leases and not send the heartbeat.
+    config_storage->setHeartbeatDelay(0);
+
+    // Start the servers.
+    ASSERT_NO_THROW({
+        listener_->start();
+        listener2_->start();
+        listener3_->start();
+    });
+
+    TestHAService service(io_service_, network_state_, config_storage,
+                          HAServerType::DHCPv6);
+
+    // Start fetching leases asynchronously.
+    ASSERT_NO_THROW(service.asyncSyncLeases());
+
+    // Run IO service to actually perform the transaction.
+    ASSERT_NO_THROW(runIOService(1000));
+}
+
 // This test verifies that the ha-sync command is processed successfully for the
 // DHCPv4 server.
 TEST_F(HAServiceTest, processSynchronize4) {
@@ -2283,6 +2918,42 @@ TEST_F(HAServiceTest, processSynchronize4) {
     EXPECT_TRUE(factory2_->getResponseCreator()->findRequest("dhcp-enable",""));
 }
 
+// This test verifies that the ha-sync command is processed successfully for the
+// DHCPv4 server.
+TEST_F(HAServiceTest, processSynchronize4Authorized) {
+    // Update config to provide authentication.
+    user2_ = "foo";
+    password2_ = "bar";
+    user3_ = "test";
+    password3_ = "1234";
+
+    // Instruct servers to require authentication.
+    factory2_->getResponseCreator()->addBasicAuth("foo", "bar");
+    factory3_->getResponseCreator()->addBasicAuth("test", "1234");
+
+    // Run HAService::processSynchronize and gather a response.
+    ConstElementPtr rsp;
+    runProcessSynchronize4(rsp);
+
+    // The response should indicate success.
+    ASSERT_TRUE(rsp);
+    checkAnswer(rsp, CONTROL_RESULT_SUCCESS, "Lease database synchronization"
+                " complete.");
+
+    // All leases should have been inserted into the database.
+    for (size_t i = 0; i < leases4_.size(); ++i) {
+        Lease4Ptr existing_lease = LeaseMgrFactory::instance().getLease4(leases4_[i]->addr_);
+        ASSERT_TRUE(existing_lease) << "lease " << leases4_[i]->addr_.toText()
+                                    << " not in the lease database";
+    }
+
+    // The following commands should have been sent to the server2: dhcp-disable,
+    // lease4-get-page and dhcp-enable.
+    EXPECT_TRUE(factory2_->getResponseCreator()->findRequest("dhcp-disable","20"));
+    EXPECT_TRUE(factory2_->getResponseCreator()->findRequest("lease4-get-page",""));
+    EXPECT_TRUE(factory2_->getResponseCreator()->findRequest("dhcp-enable",""));
+}
+
 // This test verifies that an error is reported when sending a dhcp-disable
 // command causes an error.
 TEST_F(HAServiceTest, processSynchronizeDisableError) {
@@ -2305,6 +2976,24 @@ TEST_F(HAServiceTest, processSynchronizeDisableError) {
     EXPECT_FALSE(factory2_->getResponseCreator()->findRequest("dhcp-enable",""));
 }
 
+// This test verifies that an error is reported when sending any not
+// authenticated command causes a not authorized error.
+TEST_F(HAServiceTest, processSynchronizeUnauthorized) {
+    // Instruct server2 to require authentication.
+    factory2_->getResponseCreator()->addBasicAuth("foo", "bar");
+
+    // Run HAService::processSynchronize and gather a response.
+    ConstElementPtr rsp;
+    runProcessSynchronize4(rsp);
+
+    // The response should indicate an error
+    ASSERT_TRUE(rsp);
+    checkAnswer(rsp, CONTROL_RESULT_ERROR);
+
+    // The server2 should only receive dhcp-disable commands.
+    EXPECT_TRUE(factory2_->getResponseCreator()->getReceivedRequests().empty());
+}
+
 // This test verifies that an error is reported when sending a lease4-get-page
 // command causes an error.
 TEST_F(HAServiceTest, processSynchronizeLease4GetPageError) {
@@ -2376,6 +3065,43 @@ TEST_F(HAServiceTest, processSynchronize6) {
     EXPECT_TRUE(factory2_->getResponseCreator()->findRequest("dhcp-enable",""));
 }
 
+// This test verifies that the ha-sync command is processed successfully for the
+// DHCPv6 server.
+TEST_F(HAServiceTest, processSynchronize6Authorized) {
+    // Update config to provide authentication.
+    user2_ = "foo";
+    password2_ = "bar";
+    user3_ = "test";
+    password3_ = "1234";
+
+    // Instruct servers to require authentication.
+    factory2_->getResponseCreator()->addBasicAuth("foo", "bar");
+    factory3_->getResponseCreator()->addBasicAuth("test", "1234");
+
+    // Run HAService::processSynchronize and gather a response.
+    ConstElementPtr rsp;
+    runProcessSynchronize6(rsp);
+
+    // The response should indicate success.
+    ASSERT_TRUE(rsp);
+    checkAnswer(rsp, CONTROL_RESULT_SUCCESS, "Lease database synchronization"
+                " complete.");
+
+    // All leases should have been inserted into the database.
+    for (size_t i = 0; i < leases6_.size(); ++i) {
+        Lease6Ptr existing_lease = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+                                                                         leases6_[i]->addr_);
+        ASSERT_TRUE(existing_lease) << "lease " << leases6_[i]->addr_.toText()
+                                    << " not in the lease database";
+    }
+
+    // The following commands should have been sent to the server2: dhcp-disable,
+    // lease6-get-page and dhcp-enable.
+    EXPECT_TRUE(factory2_->getResponseCreator()->findRequest("dhcp-disable","20"));
+    EXPECT_TRUE(factory2_->getResponseCreator()->findRequest("lease6-get-page",""));
+    EXPECT_TRUE(factory2_->getResponseCreator()->findRequest("dhcp-enable",""));
+}
+
 // This test verifies that an error is reported when sending a dhcp-disable
 // command causes an error.
 TEST_F(HAServiceTest, processSynchronize6DisableError) {
@@ -2398,6 +3124,24 @@ TEST_F(HAServiceTest, processSynchronize6DisableError) {
     EXPECT_FALSE(factory2_->getResponseCreator()->findRequest("dhcp-enable",""));
 }
 
+// This test verifies that an error is reported when sending any not
+// authenticated command causes a not authorized error.
+TEST_F(HAServiceTest, processSynchronize6Unauthorized) {
+    // Instruct server2 to require authentication.
+    factory2_->getResponseCreator()->addBasicAuth("foo", "bar");
+
+    // Run HAService::processSynchronize and gather a response.
+    ConstElementPtr rsp;
+    runProcessSynchronize6(rsp);
+
+    // The response should indicate an error
+    ASSERT_TRUE(rsp);
+    checkAnswer(rsp, CONTROL_RESULT_ERROR);
+
+    // The server2 should only receive dhcp-disable commands.
+    EXPECT_TRUE(factory2_->getResponseCreator()->getReceivedRequests().empty());
+}
+
 // This test verifies that an error is reported when sending a lease6-get-page
 // command causes an error.
 TEST_F(HAServiceTest, processSynchronizeLease6GetPageError) {
@@ -2442,9 +3186,55 @@ TEST_F(HAServiceTest, processSynchronize6EnableError) {
 }
 
 // This test verifies that the DHCPv4 service can be disabled on the remote server.
-TEST_F(HAServiceTest, asyncDisableDHCPService4) {
+TEST_F(HAServiceTest, asyncDisableDHCPService4) {
+    // Create HA configuration.
+    HAConfigPtr config_storage = createValidConfiguration();
+    setBasicAuth(config_storage);
+
+    // Start the servers.
+    ASSERT_NO_THROW({
+        listener_->start();
+        listener2_->start();
+        listener3_->start();
+    });
+
+    TestHAService service(io_service_, network_state_, config_storage);
+
+    // Send dhcp-disable command with max-period of 10 seconds.
+    // When the transaction is finished, the IO service gets stopped.
+    ASSERT_NO_THROW(service.asyncDisableDHCPService("server3", 10,
+                                                    [this](const bool success,
+                                                           const std::string& error_message) {
+        EXPECT_TRUE(success);
+        EXPECT_TRUE(error_message.empty());
+        io_service_->stop();
+    }));
+
+    // Run IO service to actually perform the transaction.
+    ASSERT_NO_THROW(runIOService(TEST_TIMEOUT));
+
+    // The second server should not receive the command.
+    EXPECT_FALSE(factory2_->getResponseCreator()->findRequest("dhcp-disable","10"));
+    // The third server should receive the dhcp-disable command with the max-period
+    // value of 10.
+    EXPECT_TRUE(factory3_->getResponseCreator()->findRequest("dhcp-disable","10"));
+}
+
+// This test verifies that the DHCPv4 service can be disabled on the remote server.
+TEST_F(HAServiceTest, asyncDisableDHCPService4Authorized) {
+    // Update config to provide authentication.
+    user2_ = "foo";
+    password2_ = "bar";
+    user3_ = "test";
+    password3_ = "1234";
+
+    // Instruct servers to require authentication.
+    factory2_->getResponseCreator()->addBasicAuth("foo", "bar");
+    factory3_->getResponseCreator()->addBasicAuth("test", "1234");
+
     // Create HA configuration.
     HAConfigPtr config_storage = createValidConfiguration();
+    setBasicAuth(config_storage);
 
     // Start the servers.
     ASSERT_NO_THROW({
@@ -2531,10 +3321,88 @@ TEST_F(HAServiceTest, asyncDisableDHCPService4ControlResultError) {
     ASSERT_NO_THROW(runIOService(TEST_TIMEOUT));
 }
 
+// This test verifies that an error is returned when the remote server
+// requires not provided authentication.
+TEST_F(HAServiceTest, asyncDisableDHCPService4ControlResultUnauthorized) {
+    // Instruct servers to require authentication.
+    factory2_->getResponseCreator()->addBasicAuth("foo", "bar");
+    factory3_->getResponseCreator()->addBasicAuth("test", "1234");
+
+    // Create HA configuration.
+    HAConfigPtr config_storage = createValidConfiguration();
+
+    // Start the servers.
+    ASSERT_NO_THROW({
+        listener_->start();
+        listener2_->start();
+        listener3_->start();
+    });
+
+    TestHAService service(io_service_, network_state_, config_storage);
+
+    // Send dhcp-disable command with max-period of 10 seconds.
+    // When the transaction is finished, the IO service gets stopped.
+    ASSERT_NO_THROW(service.asyncDisableDHCPService("server3", 10,
+                                                    [this](const bool success,
+                                                           const std::string& error_message) {
+        EXPECT_FALSE(success);
+        EXPECT_FALSE(error_message.empty());
+        io_service_->stop();
+    }));
+
+    // Run IO service to actually perform the transaction.
+    ASSERT_NO_THROW(runIOService(TEST_TIMEOUT));
+}
+
 // This test verifies that the DHCPv4 service can be enabled on the remote server.
 TEST_F(HAServiceTest, asyncEnableDHCPService4) {
     // Create HA configuration.
     HAConfigPtr config_storage = createValidConfiguration();
+    setBasicAuth(config_storage);
+
+    // Start the servers.
+    ASSERT_NO_THROW({
+        listener_->start();
+        listener2_->start();
+        listener3_->start();
+    });
+
+    TestHAService service(io_service_, network_state_, config_storage);
+
+    // Send dhcp-enable command. When the transaction is finished,
+    // the IO service gets stopped.
+    ASSERT_NO_THROW(service.asyncEnableDHCPService("server2",
+                                                   [this](const bool success,
+                                                          const std::string& error_message) {
+        EXPECT_TRUE(success);
+        EXPECT_TRUE(error_message.empty());
+        io_service_->stop();
+    }));
+
+    // Run IO service to actually perform the transaction.
+    ASSERT_NO_THROW(runIOService(TEST_TIMEOUT));
+
+    // The second server should receive the dhcp-enable.
+    EXPECT_TRUE(factory2_->getResponseCreator()->findRequest("dhcp-enable",""));
+    // The third server should not receive the command.
+    EXPECT_FALSE(factory3_->getResponseCreator()->findRequest("dhcp-enable",""));
+}
+
+// This test verifies that the DHCPv4 service can be enabled on the remote server.
+TEST_F(HAServiceTest, asyncEnableDHCPService4Authorized) {
+    // Update config to provide authentication.
+    user2_ = "foo";
+    password2_ = "bar";
+    user3_ = "test";
+    password3_ = "1234";
+
+    // Instruct servers to require authentication.
+    factory2_->getResponseCreator()->addBasicAuth("foo", "bar");
+    factory3_->getResponseCreator()->addBasicAuth("test", "1234");
+
+    // Create HA configuration.
+    HAConfigPtr config_storage = createValidConfiguration();
+    setBasicAuth(config_storage);
 
     // Start the servers.
     ASSERT_NO_THROW({
@@ -2620,6 +3488,39 @@ TEST_F(HAServiceTest, asyncEnableDHCPService4ControlResultError) {
     ASSERT_NO_THROW(runIOService(TEST_TIMEOUT));
 }
 
+// This test verifies that an error is returned when the remote server
+// requires not provided authentication.
+TEST_F(HAServiceTest, asyncEnableDHCPService4ControlResultUnauthorized) {
+    // Instruct servers to require authentication.
+    factory2_->getResponseCreator()->addBasicAuth("foo", "bar");
+    factory3_->getResponseCreator()->addBasicAuth("test", "1234");
+
+    // Create HA configuration.
+    HAConfigPtr config_storage = createValidConfiguration();
+
+    // Start the servers.
+    ASSERT_NO_THROW({
+        listener_->start();
+        listener2_->start();
+        listener3_->start();
+    });
+
+    TestHAService service(io_service_, network_state_, config_storage);
+
+    // Send dhcp-enable command. When the transaction is finished,
+    // the IO service gets stopped.
+    ASSERT_NO_THROW(service.asyncEnableDHCPService("server2",
+                                                   [this](const bool success,
+                                                          const std::string& error_message) {
+        EXPECT_FALSE(success);
+        EXPECT_FALSE(error_message.empty());
+        io_service_->stop();
+    }));
+
+    // Run IO service to actually perform the transaction.
+    ASSERT_NO_THROW(runIOService(TEST_TIMEOUT));
+}
+
 // This test verifies that the "ha-scopes" command is processed correctly.
 TEST_F(HAServiceTest, processScopes) {
     // Create HA configuration.
@@ -2785,6 +3686,59 @@ TEST_F(HAServiceTest, processMaintenanceStartSuccess) {
     // Create HA configuration for 3 servers. This server is
     // server 1.
     HAConfigPtr config_storage = createValidConfiguration();
+    setBasicAuth(config_storage);
+
+    // Start the servers.
+    ASSERT_NO_THROW({
+        listener_->start();
+        listener2_->start();
+    });
+
+    HAService service(io_service_, network_state_, config_storage);
+
+    // The tested function is synchronous, so we need to run server side IO service
+    // in background to not block the main thread.
+    auto thread = runIOServiceInThread();
+
+    // Process ha-maintenance-start command.
+    ConstElementPtr rsp;
+    ASSERT_NO_THROW(rsp = service.processMaintenanceStart());
+
+    // Stop the IO service. This should cause the thread to terminate.
+    io_service_->stop();
+    thread->join();
+    io_service_->get_io_service().reset();
+    io_service_->poll();
+
+    // The partner of our server is online and should have responded with
+    // the success status. Therefore, this server should have transitioned
+    // to the partner-in-maintenance state.
+    ASSERT_TRUE(rsp);
+    checkAnswer(rsp, CONTROL_RESULT_SUCCESS, "Server is now in the partner-in-maintenance state"
+                " and its partner is in-maintenance state. The partner can be now safely"
+                " shut down.");
+
+    EXPECT_EQ(HA_PARTNER_IN_MAINTENANCE_ST, service.getCurrState());
+}
+
+// This test verifies the case when the server receiving the ha-maintenance-start
+// command successfully transitions to the partner-in-maintenance state and its
+// partner transitions to the in-maintenance state.
+TEST_F(HAServiceTest, processMaintenanceStartSuccessAuthorized) {
+    // Update config to provide authentication.
+    user2_ = "foo";
+    password2_ = "bar";
+    user3_ = "test";
+    password3_ = "1234";
+
+    // Instruct servers to require authentication.
+    factory2_->getResponseCreator()->addBasicAuth("foo", "bar");
+    factory3_->getResponseCreator()->addBasicAuth("test", "1234");
+
+    // Create HA configuration for 3 servers. This server is
+    // server 1.
+    HAConfigPtr config_storage = createValidConfiguration();
+    setBasicAuth(config_storage);
 
     // Start the servers.
     ASSERT_NO_THROW({
@@ -2903,6 +3857,48 @@ TEST_F(HAServiceTest, processMaintenanceStartPartnerError) {
     EXPECT_EQ(HA_PARTNER_DOWN_ST, service.getCurrState());
 }
 
+// This test verifies the case when the server is receiving
+// ha-maintenance-start command and tries to notify the partner
+// which requires not provided authentication.
+TEST_F(HAServiceTest, processMaintenanceStartPartnerUnauthorized) {
+    // Instruct servers to require authentication.
+    factory2_->getResponseCreator()->addBasicAuth("foo", "bar");
+    factory3_->getResponseCreator()->addBasicAuth("test", "1234");
+
+    // Create HA configuration for 3 servers. This server is
+    // server 1.
+    HAConfigPtr config_storage = createValidConfiguration();
+
+    // Start the servers.
+    ASSERT_NO_THROW({
+        listener_->start();
+        listener2_->start();
+    });
+
+    HAService service(io_service_, network_state_, config_storage);
+
+    // The tested function is synchronous, so we need to run server side IO service
+    // in background to not block the main thread.
+    auto thread = runIOServiceInThread();
+
+    // Process ha-maintenance-start command.
+    ConstElementPtr rsp;
+    ASSERT_NO_THROW(rsp = service.processMaintenanceStart());
+
+    // Stop the IO service. This should cause the thread to terminate.
+    io_service_->stop();
+    thread->join();
+    io_service_->get_io_service().reset();
+    io_service_->poll();
+
+    ASSERT_TRUE(rsp);
+    checkAnswer(rsp, CONTROL_RESULT_SUCCESS,
+                "Server is now in the partner-down state as its"
+                " partner appears to be offline for maintenance.");
+
+    EXPECT_EQ(HA_PARTNER_DOWN_ST, service.getCurrState());
+}
+
 // This test verifies the case when the server is receiving
 // ha-maintenance-start command and tries to notify the partner
 // which returns a special result indicating that it can't enter
@@ -2956,6 +3952,58 @@ TEST_F(HAServiceTest, processMaintenanceCancelSuccess) {
     // Create HA configuration for 3 servers. This server is
     // server 1.
     HAConfigPtr config_storage = createValidConfiguration();
+    setBasicAuth(config_storage);
+
+    // Start the servers.
+    ASSERT_NO_THROW({
+        listener_->start();
+        listener2_->start();
+    });
+
+    TestHAService service(io_service_, network_state_, config_storage);
+
+    ASSERT_NO_THROW(service.verboseTransition(HA_PARTNER_IN_MAINTENANCE_ST));
+
+    // The tested function is synchronous, so we need to run server side IO service
+    // in background to not block the main thread.
+    auto thread = runIOServiceInThread();
+
+    // Process ha-maintenance-cancel command.
+    ConstElementPtr rsp;
+    ASSERT_NO_THROW(rsp = service.processMaintenanceCancel());
+
+    // Stop the IO service. This should cause the thread to terminate.
+    io_service_->stop();
+    thread->join();
+    io_service_->get_io_service().reset();
+    io_service_->poll();
+
+    // The partner of our server is online and should have responded with
+    // the success status. Therefore, this server should have transitioned
+    // to the partner-in-maintenance state.
+    ASSERT_TRUE(rsp);
+    checkAnswer(rsp, CONTROL_RESULT_SUCCESS, "Server maintenance successfully canceled.");
+
+    EXPECT_EQ(HA_WAITING_ST, service.getCurrState());
+}
+
+// This test verifies the case when the server receiving the ha-maintenance-cancel
+// command successfully transitions out of the partner-in-maintenance state.
+TEST_F(HAServiceTest, processMaintenanceCancelSuccessAuthorized) {
+    // Update config to provide authentication.
+    user2_ = "foo";
+    password2_ = "bar";
+    user3_ = "test";
+    password3_ = "1234";
+
+    // Instruct servers to require authentication.
+    factory2_->getResponseCreator()->addBasicAuth("foo", "bar");
+    factory3_->getResponseCreator()->addBasicAuth("test", "1234");
+
+    // Create HA configuration for 3 servers. This server is
+    // server 1.
+    HAConfigPtr config_storage = createValidConfiguration();
+    setBasicAuth(config_storage);
 
     // Start the servers.
     ASSERT_NO_THROW({
@@ -3035,6 +4083,52 @@ TEST_F(HAServiceTest, processMaintenanceCancelPartnerError) {
     EXPECT_EQ(HA_PARTNER_IN_MAINTENANCE_ST, service.getCurrState());
 }
 
+// This test verifies that the maintenance is not canceled in case the
+// partner requires not provided authentication.
+TEST_F(HAServiceTest, processMaintenanceCancelPartnerUnauthorized) {
+    // Instruct servers to require authentication.
+    factory2_->getResponseCreator()->addBasicAuth("foo", "bar");
+    factory3_->getResponseCreator()->addBasicAuth("test", "1234");
+
+    // Create HA configuration for 3 servers. This server is
+    // server 1.
+    HAConfigPtr config_storage = createValidConfiguration();
+
+    // Start the servers.
+    ASSERT_NO_THROW({
+        listener_->start();
+        listener2_->start();
+    });
+
+    TestHAService service(io_service_, network_state_, config_storage);
+
+    ASSERT_NO_THROW(service.verboseTransition(HA_PARTNER_IN_MAINTENANCE_ST));
+
+    // The tested function is synchronous, so we need to run server side IO service
+    // in background to not block the main thread.
+    auto thread = runIOServiceInThread();
+
+    // Process ha-maintenance-cancel command.
+    ConstElementPtr rsp;
+    ASSERT_NO_THROW(rsp = service.processMaintenanceCancel());
+
+    // Stop the IO service. This should cause the thread to terminate.
+    io_service_->stop();
+    thread->join();
+    io_service_->get_io_service().reset();
+    io_service_->poll();
+
+    // The partner should have responded with an error.
+    ASSERT_TRUE(rsp);
+    checkAnswer(rsp, CONTROL_RESULT_ERROR,
+                "Unable to cancel maintenance. The partner server responded"
+                " with the following message to the ha-maintenance-notify"
+                " commmand: Unauthorized, error code 1.");
+
+    // The state of this server should not change.
+    EXPECT_EQ(HA_PARTNER_IN_MAINTENANCE_ST, service.getCurrState());
+}
+
 
 /// @brief HA partner to the server under test.
 ///