shill: Initial support for loading and saving of services.
BUG=chromium-os:17255
TEST=unit tests
Change-Id: Ibf2721f72bfd44afbe6be9ac2cce0a2e978465eb
Reviewed-on: http://gerrit.chromium.org/gerrit/3954
Reviewed-by: Chris Masone <cmasone@chromium.org>
Tested-by: Darin Petkov <petkov@chromium.org>
diff --git a/mock_store.h b/mock_store.h
new file mode 100644
index 0000000..5e79a90
--- /dev/null
+++ b/mock_store.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHILL_MOCK_STORE_
+#define SHILL_MOCK_STORE_
+
+#include <gmock/gmock.h>
+
+#include "shill/store_interface.h"
+
+namespace shill {
+
+class MockStore : public StoreInterface {
+ public:
+ MOCK_METHOD0(Open, bool());
+ MOCK_METHOD0(Close, bool());
+ MOCK_METHOD0(GetGroups, std::set<std::string>());
+ MOCK_METHOD1(ContainsGroup, bool(const std::string &group));
+ MOCK_METHOD2(DeleteKey,
+ bool(const std::string &group, const std::string &key));
+ MOCK_METHOD1(DeleteGroup, bool(const std::string &group));
+ MOCK_METHOD3(GetString, bool(const std::string &group,
+ const std::string &key,
+ std::string *value));
+ MOCK_METHOD3(SetString, bool(const std::string &group,
+ const std::string &key,
+ const std::string &value));
+ MOCK_METHOD3(GetBool, bool(const std::string &group,
+ const std::string &key,
+ bool *value));
+ MOCK_METHOD3(SetBool, bool(const std::string &group,
+ const std::string &key,
+ bool value));
+ MOCK_METHOD3(GetInt, bool(const std::string &group,
+ const std::string &key,
+ int *value));
+ MOCK_METHOD3(SetInt, bool(const std::string &group,
+ const std::string &key,
+ int value));
+ MOCK_METHOD3(GetStringList, bool(const std::string &group,
+ const std::string &key,
+ std::vector<std::string> *value));
+ MOCK_METHOD3(SetStringList, bool(const std::string &group,
+ const std::string &key,
+ const std::vector<std::string> &value));
+ MOCK_METHOD3(GetCryptedString, bool(const std::string &group,
+ const std::string &key,
+ std::string *value));
+ MOCK_METHOD3(SetCryptedString, bool(const std::string &group,
+ const std::string &key,
+ const std::string &value));
+};
+
+} // namespace shill
+
+#endif // SHILL_MOCK_STORE_
diff --git a/service.cc b/service.cc
index 0ce233d..395af35 100644
--- a/service.cc
+++ b/service.cc
@@ -21,21 +21,51 @@
#include "shill/property_accessor.h"
#include "shill/refptr_types.h"
#include "shill/service_dbus_adaptor.h"
+#include "shill/store_interface.h"
using std::map;
using std::string;
using std::vector;
namespace shill {
+
+const char Service::kCheckPortalAuto[] = "auto";
+const char Service::kCheckPortalFalse[] = "false";
+const char Service::kCheckPortalTrue[] = "true";
+
+const char Service::kStorageAutoConnect[] = "AutoConnect";
+const char Service::kStorageCheckPortal[] = "CheckPortal";
+const char Service::kStorageEapAnonymousIdentity[] = "EAP.AnonymousIdentity";
+const char Service::kStorageEapCACert[] = "EAP.CACert";
+const char Service::kStorageEapCACertID[] = "EAP.CACertID";
+const char Service::kStorageEapCertID[] = "EAP.CertID";
+const char Service::kStorageEapClientCert[] = "EAP.ClientCert";
+const char Service::kStorageEapEap[] = "EAP.EAP";
+const char Service::kStorageEapIdentity[] = "EAP.Identity";
+const char Service::kStorageEapInnerEap[] = "EAP.InnerEAP";
+const char Service::kStorageEapKeyID[] = "EAP.KeyID";
+const char Service::kStorageEapKeyManagement[] = "EAP.KeyMgmt";
+const char Service::kStorageEapPIN[] = "EAP.PIN";
+const char Service::kStorageEapPassword[] = "EAP.Password";
+const char Service::kStorageEapPrivateKey[] = "EAP.PrivateKey";
+const char Service::kStorageEapPrivateKeyPassword[] = "EAP.PrivateKeyPassword";
+const char Service::kStorageEapUseSystemCAs[] = "EAP.UseSystemCAs";
+const char Service::kStorageFavorite[] = "Favorite";
+const char Service::kStorageName[] = "Name";
+const char Service::kStoragePriority[] = "Priority";
+const char Service::kStorageProxyConfig[] = "ProxyConfig";
+const char Service::kStorageSaveCredentials[] = "SaveCredentials";
+
Service::Service(ControlInterface *control_interface,
EventDispatcher *dispatcher,
const ProfileRefPtr &profile,
const string& name)
: auto_connect_(false),
+ check_portal_(kCheckPortalAuto),
connectable_(false),
favorite_(false),
- priority_(0),
- save_credentials_(false),
+ priority_(kPriorityNone),
+ save_credentials_(true),
profile_(profile),
dispatcher_(dispatcher),
name_(name),
@@ -118,6 +148,10 @@
return adaptor_->GetRpcIdentifier();
}
+string Service::GetStorageIdentifier() {
+ return UniqueName();
+}
+
void Service::HelpRegisterDerivedBool(const string &name,
bool(Service::*get)(void),
bool(Service::*set)(const bool&)) {
@@ -134,4 +168,149 @@
StringAccessor(new CustomAccessor<Service, string>(this, get, set)));
}
+void Service::SaveString(StoreInterface *storage,
+ const string &key,
+ const string &value,
+ bool crypted,
+ bool save) {
+ if (value.empty() || !save) {
+ storage->DeleteKey(GetStorageIdentifier(), key);
+ return;
+ }
+ if (crypted) {
+ storage->SetCryptedString(GetStorageIdentifier(), key, value);
+ return;
+ }
+ storage->SetString(GetStorageIdentifier(), key, value);
+}
+
+void Service::LoadEapCredentials(StoreInterface *storage) {
+ const string id = GetStorageIdentifier();
+ storage->GetCryptedString(id, kStorageEapIdentity, &eap_.identity);
+ storage->GetString(id, kStorageEapEap, &eap_.eap);
+ storage->GetString(id, kStorageEapInnerEap, &eap_.inner_eap);
+ storage->GetCryptedString(id,
+ kStorageEapAnonymousIdentity,
+ &eap_.anonymous_identity);
+ storage->GetString(id, kStorageEapClientCert, &eap_.client_cert);
+ storage->GetString(id, kStorageEapCertID, &eap_.cert_id);
+ storage->GetString(id, kStorageEapPrivateKey, &eap_.private_key);
+ storage->GetCryptedString(id,
+ kStorageEapPrivateKeyPassword,
+ &eap_.private_key_password);
+ storage->GetString(id, kStorageEapKeyID, &eap_.key_id);
+ storage->GetString(id, kStorageEapCACert, &eap_.ca_cert);
+ storage->GetString(id, kStorageEapCACertID, &eap_.ca_cert_id);
+ storage->GetBool(id, kStorageEapUseSystemCAs, &eap_.use_system_cas);
+ storage->GetString(id, kStorageEapPIN, &eap_.pin);
+ storage->GetCryptedString(id, kStorageEapPassword, &eap_.password);
+ storage->GetString(id, kStorageEapKeyManagement, &eap_.key_management);
+}
+
+void Service::SaveEapCredentials(StoreInterface *storage) {
+ bool save = save_credentials_;
+ SaveString(storage, kStorageEapIdentity, eap_.identity, true, save);
+ SaveString(storage, kStorageEapEap, eap_.eap, false, true);
+ SaveString(storage, kStorageEapInnerEap, eap_.inner_eap, false, true);
+ SaveString(storage,
+ kStorageEapAnonymousIdentity,
+ eap_.anonymous_identity,
+ true,
+ save);
+ SaveString(storage, kStorageEapClientCert, eap_.client_cert, false, save);
+ SaveString(storage, kStorageEapCertID, eap_.cert_id, false, save);
+ SaveString(storage, kStorageEapPrivateKey, eap_.private_key, false, save);
+ SaveString(storage,
+ kStorageEapPrivateKeyPassword,
+ eap_.private_key_password,
+ true,
+ save);
+ SaveString(storage, kStorageEapKeyID, eap_.key_id, false, save);
+ SaveString(storage, kStorageEapCACert, eap_.ca_cert, false, true);
+ SaveString(storage, kStorageEapCACertID, eap_.ca_cert_id, false, true);
+ storage->SetBool(GetStorageIdentifier(),
+ kStorageEapUseSystemCAs,
+ eap_.use_system_cas);
+ SaveString(storage, kStorageEapPIN, eap_.pin, false, save);
+ SaveString(storage, kStorageEapPassword, eap_.password, true, save);
+ SaveString(storage,
+ kStorageEapKeyManagement,
+ eap_.key_management,
+ false,
+ true);
+}
+
+bool Service::Load(StoreInterface *storage) {
+ const string id = GetStorageIdentifier();
+ if (!storage->ContainsGroup(id)) {
+ LOG(WARNING) << "Service is not available in the persistent store: " << id;
+ return false;
+ }
+ storage->GetBool(id, kStorageAutoConnect, &auto_connect_);
+ storage->GetString(id, kStorageCheckPortal, &check_portal_);
+ storage->GetBool(id, kStorageFavorite, &favorite_);
+ storage->GetInt(id, kStoragePriority, &priority_);
+ storage->GetString(id, kStorageProxyConfig, &proxy_config_);
+ storage->GetBool(id, kStorageSaveCredentials, &save_credentials_);
+
+ LoadEapCredentials(storage);
+
+ // TODO(petkov): Load these:
+
+ // "Name"
+ // "WiFi.HiddenSSID"
+ // "SSID"
+ // "Failure"
+ // "Modified"
+ // "LastAttempt"
+ // WiFiService: "Passphrase"
+ // "APN"
+ // "LastGoodAPN"
+
+ return true;
+}
+
+bool Service::Save(StoreInterface *storage) {
+ const string id = GetStorageIdentifier();
+
+ // TODO(petkov): We could choose to simplify the saving code by removing most
+ // conditionals thus saving even default values.
+ if (favorite_) {
+ storage->SetBool(id, kStorageAutoConnect, auto_connect_);
+ }
+ if (check_portal_ == kCheckPortalAuto) {
+ storage->DeleteKey(id, kStorageCheckPortal);
+ } else {
+ storage->SetString(id, kStorageCheckPortal, check_portal_);
+ }
+ storage->SetBool(id, kStorageFavorite, favorite_);
+ storage->SetString(id, kStorageName, name_);
+ SaveString(storage, kStorageProxyConfig, proxy_config_, false, true);
+ if (priority_ != kPriorityNone) {
+ storage->SetInt(id, kStoragePriority, priority_);
+ } else {
+ storage->DeleteKey(id, kStoragePriority);
+ }
+ if (save_credentials_) {
+ storage->DeleteKey(id, kStorageSaveCredentials);
+ } else {
+ storage->SetBool(id, kStorageSaveCredentials, false);
+ }
+
+ SaveEapCredentials(storage);
+
+ // TODO(petkov): Save these:
+
+ // "WiFi.HiddenSSID"
+ // "SSID"
+ // "Failure"
+ // "Modified"
+ // "LastAttempt"
+ // WiFiService: "Passphrase"
+ // "APN"
+ // "LastGoodAPN"
+
+ return true;
+}
+
} // namespace shill
diff --git a/service.h b/service.h
index f6f6418..f22e646 100644
--- a/service.h
+++ b/service.h
@@ -11,6 +11,7 @@
#include <base/memory/ref_counted.h>
#include <base/memory/scoped_ptr.h>
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
#include "shill/accessor_interface.h"
#include "shill/property_store.h"
@@ -25,6 +26,7 @@
class Error;
class EventDispatcher;
class ServiceAdaptorInterface;
+class StoreInterface;
// A Service is a uniquely named entity, which the system can
// connect in order to begin sending and receiving network traffic.
@@ -34,6 +36,10 @@
// becomes populated over time.
class Service : public base::RefCounted<Service> {
public:
+ static const char kCheckPortalAuto[];
+ static const char kCheckPortalFalse[];
+ static const char kCheckPortalTrue[];
+
enum ConnectFailure {
kServiceFailureUnknown,
kServiceFailureActivationFailure,
@@ -80,23 +86,35 @@
const ProfileRefPtr &profile,
const std::string& name);
virtual ~Service();
+
virtual void Connect() = 0;
virtual void Disconnect() = 0;
virtual bool IsActive() { return false; }
- // Returns a string that is guaranteed to uniquely identify this
- // Service instance.
- virtual const std::string &UniqueName() { return name_; }
+ // Returns a string that is guaranteed to uniquely identify this Service
+ // instance.
+ const std::string &UniqueName() { return name_; }
std::string GetRpcIdentifier();
+ // Returns the unique persistent storage identifier for the service.
+ std::string GetStorageIdentifier();
+
+ // Loads the service from persistent |storage|. Returns true on success.
+ virtual bool Load(StoreInterface *storage);
+
+ // Saves the service to persistent |storage|. Returns true on success.
+ virtual bool Save(StoreInterface *storage);
+
bool auto_connect() const { return auto_connect_; }
void set_auto_connect(bool connect) { auto_connect_ = connect; }
PropertyStore *store() { return &store_; }
protected:
+ static const int kPriorityNone = 0;
+
virtual std::string CalculateState() = 0;
void HelpRegisterDerivedBool(const std::string &name,
@@ -106,6 +124,18 @@
std::string(Service::*get)(void),
bool(Service::*set)(const std::string&));
+ // Assigns |value| to |key| in |storage| if |value| is non-empty and |save| is
+ // true. Otherwise, removes |key| from |storage|. If |crypted| is true, the
+ // value is encrypted.
+ void SaveString(StoreInterface *storage,
+ const std::string &key,
+ const std::string &value,
+ bool crypted,
+ bool save);
+
+ void LoadEapCredentials(StoreInterface *storage);
+ void SaveEapCredentials(StoreInterface *storage);
+
// Properties
bool auto_connect_;
std::string check_portal_;
@@ -123,6 +153,37 @@
EventDispatcher *dispatcher_;
private:
+ friend class ServiceAdaptorInterface;
+ FRIEND_TEST(ServiceTest, Constructor);
+ FRIEND_TEST(ServiceTest, Save);
+ FRIEND_TEST(ServiceTest, SaveString);
+ FRIEND_TEST(ServiceTest, SaveStringCrypted);
+ FRIEND_TEST(ServiceTest, SaveStringDontSave);
+ FRIEND_TEST(ServiceTest, SaveStringEmpty);
+
+ static const char kStorageAutoConnect[];
+ static const char kStorageCheckPortal[];
+ static const char kStorageEapAnonymousIdentity[];
+ static const char kStorageEapCACert[];
+ static const char kStorageEapCACertID[];
+ static const char kStorageEapCertID[];
+ static const char kStorageEapClientCert[];
+ static const char kStorageEapEap[];
+ static const char kStorageEapIdentity[];
+ static const char kStorageEapInnerEap[];
+ static const char kStorageEapKeyID[];
+ static const char kStorageEapKeyManagement[];
+ static const char kStorageEapPIN[];
+ static const char kStorageEapPassword[];
+ static const char kStorageEapPrivateKey[];
+ static const char kStorageEapPrivateKeyPassword[];
+ static const char kStorageEapUseSystemCAs[];
+ static const char kStorageFavorite[];
+ static const char kStorageName[];
+ static const char kStoragePriority[];
+ static const char kStorageProxyConfig[];
+ static const char kStorageSaveCredentials[];
+
virtual std::string GetDeviceRpcId() = 0;
std::string GetProfileRpcId() {
@@ -136,7 +197,6 @@
Connection *connection_;
scoped_ptr<ServiceAdaptorInterface> adaptor_;
- friend class ServiceAdaptorInterface;
DISALLOW_COPY_AND_ASSIGN(Service);
};
diff --git a/service_unittest.cc b/service_unittest.cc
index e47bd2e..9b5b023 100644
--- a/service_unittest.cc
+++ b/service_unittest.cc
@@ -20,16 +20,19 @@
#include "shill/mock_control.h"
#include "shill/mock_profile.h"
#include "shill/mock_service.h"
+#include "shill/mock_store.h"
#include "shill/property_store_unittest.h"
#include "shill/shill_event.h"
using std::map;
using std::string;
using std::vector;
-using ::testing::_;
-using ::testing::NiceMock;
-using ::testing::Return;
-using ::testing::Test;
+using testing::_;
+using testing::AtLeast;
+using testing::NiceMock;
+using testing::Return;
+using testing::StrictMock;
+using testing::Test;
namespace shill {
@@ -58,6 +61,11 @@
const char ServiceTest::kProfileName[] = "profile";
+TEST_F(ServiceTest, Constructor) {
+ EXPECT_TRUE(service_->save_credentials_);
+ EXPECT_EQ(Service::kCheckPortalAuto, service_->check_portal_);
+}
+
TEST_F(ServiceTest, GetProperties) {
EXPECT_CALL(*service_.get(), CalculateState()).WillRepeatedly(Return(""));
EXPECT_CALL(*service_.get(), GetDeviceRpcId())
@@ -185,4 +193,71 @@
}
}
+TEST_F(ServiceTest, GetStorageIdentifier) {
+ EXPECT_EQ(kMockServiceName, service_->GetStorageIdentifier());
+}
+
+TEST_F(ServiceTest, Load) {
+ NiceMock<MockStore> storage;
+ const string id = kMockServiceName;
+ EXPECT_CALL(storage, ContainsGroup(id)).WillOnce(Return(true));
+ EXPECT_CALL(storage, GetString(id, _, _))
+ .Times(AtLeast(1))
+ .WillRepeatedly(Return(true));
+ EXPECT_TRUE(service_->Load(&storage));
+}
+
+TEST_F(ServiceTest, LoadFail) {
+ StrictMock<MockStore> storage;
+ const string id = kMockServiceName;
+ EXPECT_CALL(storage, ContainsGroup(kMockServiceName)).WillOnce(Return(false));
+ EXPECT_FALSE(service_->Load(&storage));
+}
+
+TEST_F(ServiceTest, SaveString) {
+ MockStore storage;
+ static const char kKey[] = "test-key";
+ static const char kData[] = "test-data";
+ EXPECT_CALL(storage, SetString(kMockServiceName, kKey, kData))
+ .WillOnce(Return(true));
+ service_->SaveString(&storage, kKey, kData, false, true);
+}
+
+TEST_F(ServiceTest, SaveStringCrypted) {
+ MockStore storage;
+ static const char kKey[] = "test-key";
+ static const char kData[] = "test-data";
+ EXPECT_CALL(storage, SetCryptedString(kMockServiceName, kKey, kData))
+ .WillOnce(Return(true));
+ service_->SaveString(&storage, kKey, kData, true, true);
+}
+
+TEST_F(ServiceTest, SaveStringDontSave) {
+ MockStore storage;
+ static const char kKey[] = "test-key";
+ EXPECT_CALL(storage, DeleteKey(kMockServiceName, kKey))
+ .WillOnce(Return(true));
+ service_->SaveString(&storage, kKey, "data", false, false);
+}
+
+TEST_F(ServiceTest, SaveStringEmpty) {
+ MockStore storage;
+ static const char kKey[] = "test-key";
+ EXPECT_CALL(storage, DeleteKey(kMockServiceName, kKey))
+ .WillOnce(Return(true));
+ service_->SaveString(&storage, kKey, "", true, true);
+}
+
+TEST_F(ServiceTest, Save) {
+ NiceMock<MockStore> storage;
+ const string id = kMockServiceName;
+ EXPECT_CALL(storage, SetString(id, _, _))
+ .Times(AtLeast(1))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(storage, DeleteKey(id, _))
+ .Times(AtLeast(1))
+ .WillRepeatedly(Return(true));
+ EXPECT_TRUE(service_->Save(&storage));
+}
+
} // namespace shill