Refactor SDK Core to keep all users from all backends in memory
All users for all backend are kept in memory.
Added unit tests.
Closes: MAASMOB-63
diff --git a/src/mpin_sdk.cpp b/src/mpin_sdk.cpp
index 65608aa..41d34b3 100644
--- a/src/mpin_sdk.cpp
+++ b/src/mpin_sdk.cpp
@@ -121,11 +121,21 @@
{
}
+String User::GetKey() const
+{
+ return String().Format("%s@%s", m_id.c_str(), m_backend.c_str());
+}
+
const String& User::GetId() const
{
return m_id;
}
+const String& User::GetBackend() const
+{
+ return m_backend;
+}
+
const String& User::GetDeviceName() const
{
return m_deviceName;
@@ -161,6 +171,11 @@
m_timePermitCache.Set(timePermit, date);
}
+void User::SetBackend(const String& backend)
+{
+ m_backend = backend;
+}
+
void User::SetStartedRegistration(const String& mpinIdHex, const String& regOTT)
{
m_state = STARTED_REGISTRATION;
@@ -193,8 +208,9 @@
m_state = BLOCKED;
}
-Status User::RestoreState(const String& stateString, const String& mpinIdHex, const String& regOTT)
+Status User::RestoreState(const String& stateString, const String& mpinIdHex, const String& regOTT, const String& backend)
{
+ SetBackend(backend);
SetStartedRegistration(mpinIdHex, regOTT);
State state = StringToState(stateString);
@@ -637,6 +653,12 @@
return Status(Status::FLOW_ERROR, String("CRYPTO_TEE crypto type is currently not supported"));
}
+ Status s = LoadUsersFromStorage();
+ if(s != Status::OK)
+ {
+ return s;
+ }
+
m_state = INITIALIZED;
String backend = config.Get(CONFIG_BACKEND);
@@ -738,12 +760,6 @@
return s;
}
- s = LoadUsersFromStorage();
- if(s != Status::OK)
- {
- return s;
- }
-
m_state = BACKEND_SET;
return Status(Status::OK);
}
@@ -828,7 +844,8 @@
bool userIsNew = (user->GetState() == User::INVALID);
if(userIsNew)
{
- AddUser(user);
+ user->SetBackend(MakeBackendKey(m_RPAServer));
+ m_users[user->GetKey()] = user;
}
String mpinIdHex = response.GetJsonData().GetStringParam("mpinId");
@@ -837,7 +854,7 @@
if(userIsNew || userDataChanged)
{
- user->SetStartedRegistration(mpinIdHex, regOTT);
+ user->SetStartedRegistration(mpinIdHex, regOTT);
writeUsersToStorage = true;
}
@@ -1374,92 +1391,9 @@
return Status::OK;
}
-void MPinSDK::DeleteUser(UserPtr user)
-{
- DeleteUser(user, m_RPAServer);
-}
-
-void MPinSDK::DeleteUser(INOUT UserPtr user, const String& backend)
-{
- if(MakeBackendKey(backend) == MakeBackendKey(m_RPAServer))
- {
- DeleteUser(user, m_RPAServer, m_users);
- }
- else
- {
- UsersMap usersMap;
- LoadUsersFromStorage(backend, usersMap);
- DeleteUser(user, backend, usersMap);
- }
-}
-
-void MPinSDK::DeleteUser(INOUT UserPtr user, const String& backend, UsersMap& usersMap)
-{
- UsersMap::iterator i = usersMap.find(user->GetId());
- //if(i == m_users.end() || user != i->second)
- // TODO: Get back to the full user check after the SDK is refactored to store full user list (for all backends)
- if(i == m_users.end())
- {
- return;
- }
-
- m_crypto->DeleteRegOTT(i->second->GetMPinId());
- m_crypto->DeleteToken(i->second->GetMPinId());
- i->second->Invalidate();
- m_logoutData.erase(i->second);
- usersMap.erase(i);
- WriteUsersToStorage(backend, usersMap);
-}
-
-Status MPinSDK::ListUsers(std::vector<UserPtr>& users) const
-{
- Status s = CheckIfBackendIsSet();
- if(s != Status::OK)
- {
- return s;
- }
-
- ListUsers(users, m_users);
- return Status::OK;
-}
-
-Status MPinSDK::ListUsers(OUT std::vector<UserPtr>& users, const String& backend) const
-{
- Status s = CheckIfIsInitialized();
- if(s != Status::OK)
- {
- return s;
- }
-
- UsersMap usersMap;
- s = LoadUsersFromStorage(backend, usersMap);
- if(s != Status::OK)
- {
- return s;
- }
-
- ListUsers(users, usersMap);
- return Status::OK;
-}
-
-void MPinSDK::ListUsers(OUT std::vector<UserPtr>& users, const UsersMap& usersMap) const
-{
- users.clear();
- users.reserve(usersMap.size());
- for(UsersMap::const_iterator i = usersMap.begin(); i != usersMap.end(); ++i)
- {
- users.push_back(i->second);
- }
-}
-
-void MPinSDK::AddUser(UserPtr user)
-{
- m_users[user->GetId()] = user;
-}
-
Status MPinSDK::CheckUserState(UserPtr user, User::State expectedState)
{
- UsersMap::iterator i = m_users.find(user->GetId());
+ UsersMap::iterator i = m_users.find(user->GetKey());
if(expectedState == User::INVALID)
{
if(i != m_users.end())
@@ -1483,7 +1417,12 @@
if(user != i->second)
{
- return Status(Status::FLOW_ERROR, String().Format("Different user with the '%s' id was previously added", user->GetId().c_str()));
+ return Status(Status::FLOW_ERROR, String().Format("Different user object with the '%s' id was previously added", user->GetId().c_str()));
+ }
+
+ if(user->GetBackend() != MakeBackendKey(m_RPAServer))
+ {
+ return Status(Status::FLOW_ERROR, String().Format("User '%s' is registered within a different backend than the current one", user->GetId().c_str()));
}
if(user->GetState() != expectedState)
@@ -1495,172 +1434,66 @@
return Status(Status::OK);
}
-String MPinSDK::MakeBackendKey(const String& backendServer) const
+void MPinSDK::DeleteUser(UserPtr user)
{
- String backend = backendServer;
- backend.ReplaceAll("https://", "");
- backend.ReplaceAll("http://", "");
- backend.TrimRight("/");
- return backend;
+ Status s = CheckIfIsInitialized();
+ if(s != Status::OK)
+ {
+ return;
+ }
+
+ UsersMap::iterator i = m_users.find(user->GetKey());
+ if(i == m_users.end() || user != i->second)
+ {
+ return;
+ }
+
+ m_crypto->DeleteRegOTT(i->second->GetMPinId());
+ m_crypto->DeleteToken(i->second->GetMPinId());
+ i->second->Invalidate();
+ m_logoutData.erase(i->second);
+ m_users.erase(i);
+ WriteUsersToStorage();
}
-Status MPinSDK::WriteUsersToStorage() const
+Status MPinSDK::ListUsers(OUT std::vector<UserPtr>& users, const String& backend) const
{
- return WriteUsersToStorage(m_RPAServer, m_users);
-}
+ Status s = CheckIfIsInitialized();
+ if(s != Status::OK)
+ {
+ return s;
+ }
-Status MPinSDK::WriteUsersToStorage(const String& backendServer, const UsersMap& usersMap) const
-{
- IStorage* storage = m_context->GetStorage(IStorage::NONSECURE);
- String data;
- storage->GetData(data);
- data.Trim();
-
- try
- {
- json::Object allBackendsObject;
- if(!data.empty())
+ users.clear();
+ users.reserve(m_users.size());
+
+ String backendKey = MakeBackendKey(backend);
+
+ for(UsersMap::const_iterator i = m_users.begin(); i != m_users.end(); ++i)
+ {
+ if(backendKey.empty() || backendKey == i->second->GetBackend())
{
- std::istringstream strIn(data);
- json::Reader::Read(allBackendsObject, strIn);
+ users.push_back(i->second);
}
-
- String backend = MakeBackendKey(backendServer);
-
- json::Object& rootObject = (json::Object&) allBackendsObject[backend];
- rootObject.Clear();
-
- for (UsersMap::const_iterator i = usersMap.begin(); i != usersMap.end(); ++i)
- {
- UserPtr user = i->second;
-
- json::Object timePermitCacheObject;
- timePermitCacheObject["date"] = json::Number(user->GetTimePermitCache().GetDate());
- timePermitCacheObject["timePermit"] = json::String(util::HexEncode(user->GetTimePermitCache().GetTimePermit()));
-
- json::Object userObject;
- userObject["timePermitCache"] = timePermitCacheObject;
-
- if(!user->GetDeviceName().empty())
- {
- userObject["deviceName"] = json::String(user->GetDeviceName());
- }
-
- userObject["state"] = json::String(user->GetStateString());
-
- rootObject[user->GetMPinIdHex()] = userObject;
-
- Status s;
- switch(user->GetState())
- {
- case User::STARTED_REGISTRATION:
- case User::ACTIVATED:
- s = m_crypto->SaveRegOTT(user->GetMPinId(), user->GetRegOTT());
- break;
- case User::REGISTERED:
- s = m_crypto->DeleteRegOTT(user->GetMPinId());
- break;
- default:
- break;
- }
- if(s != Status::OK)
- {
- return s;
- }
- }
-
- std::stringstream strOut;
- json::Writer::Write(allBackendsObject, strOut);
- storage->SetData(strOut.str());
-
- }
- catch(const json::Exception& e)
- {
- return Status(Status::STORAGE_ERROR, e.what());
}
-
- return Status(Status::OK);
+
+ return Status::OK;
}
-Status MPinSDK::LoadUsersFromStorage()
+Status MPinSDK::ListUsers(OUT std::vector<UserPtr>& users) const
{
- ClearUsers();
- return LoadUsersFromStorage(m_RPAServer, m_users);
+ Status s = CheckIfBackendIsSet();
+ if(s != Status::OK)
+ {
+ return s;
+ }
+
+ return ListUsers(users, m_RPAServer);
}
-Status MPinSDK::LoadUsersFromStorage(const String& backendServer, UsersMap& usersMap) const
+Status MPinSDK::ListAllUsers(OUT std::vector<UserPtr>& users) const
{
- IStorage* storage = m_context->GetStorage(IStorage::NONSECURE);
- String data;
- storage->GetData(data);
- data.Trim();
- if(data.empty())
- {
- return Status(Status::OK);
- }
-
- try
- {
- json::Object allBackendsObject;
- std::istringstream str(data);
- json::Reader::Read(allBackendsObject, str);
-
- String backend = MakeBackendKey(backendServer);
-
- json::Object::const_iterator i = allBackendsObject.Find(backend);
- if(i == allBackendsObject.End())
- {
- return Status(Status::OK);
- }
-
- const json::Object& rootObject = (const json::Object&) i->element;
- for(i = rootObject.Begin(); i != rootObject.End(); ++i)
- {
- const String& mpinIdHex = i->name;
- String mpinId = util::HexDecode(mpinIdHex);
- util::JsonObject mpinIdJson;
- if(!mpinIdJson.Parse(mpinId.c_str()))
- {
- return Status(Status::STORAGE_ERROR, String().Format("Failed to parse mpinId json: '%s'", mpinId.c_str()));
- }
- const json::Object& userObject = (const json::Object&) i->element;
- const std::string& id = ((const json::String&) mpinIdJson["userID"]).Value();
- std::string deviceName;
- json::Object::const_iterator dni = userObject.Find("deviceName");
- if(dni != userObject.End())
- {
- deviceName = ((const json::String&) dni->element).Value();
- }
-
- String regOTT;
- Status s = m_crypto->LoadRegOTT(mpinId, regOTT);
- if(s != Status::OK)
- {
- return s;
- }
-
- UserPtr user = MakeNewUser(id, deviceName);
- s = user->RestoreState(((const json::String&) userObject["state"]).Value(), mpinIdHex, regOTT);
- if(s != Status::OK)
- {
- return s;
- }
-
- const json::Object& timePermitCacheObject = (const json::Object&) userObject["timePermitCache"];
- int date = (int) ((const json::Number&) timePermitCacheObject["date"]).Value();
- const String& timePermit = util::HexDecode(((const json::String&) timePermitCacheObject["timePermit"]).Value());
-
- user->CacheTimePermit(timePermit, date);
-
- usersMap[id] = user;
- }
- }
- catch(const json::Exception& e)
- {
- return Status(Status::STORAGE_ERROR, e.what());
- }
-
- return Status(Status::OK);
+ return ListUsers(users, "");
}
Status MPinSDK::ListBackends(OUT std::vector<String>& backends) const
@@ -1671,9 +1504,10 @@
return s;
}
- IStorage* storage = m_context->GetStorage(IStorage::NONSECURE);
- String data;
- storage->GetData(data);
+ backends.clear();
+
+ String data;
+ m_context->GetStorage(IStorage::NONSECURE)->GetData(data);
data.Trim();
if(data.empty())
{
@@ -1699,9 +1533,141 @@
return Status::OK;
}
-const char * MPinSDK::GetVersion()
+String MPinSDK::MakeBackendKey(const String& backendServer) const
{
- return MPIN_SDK_V2_VERSION;
+ String backend = backendServer;
+ backend.ReplaceAll("https://", "");
+ backend.ReplaceAll("http://", "");
+ backend.TrimRight("/");
+ return backend;
+}
+
+Status MPinSDK::WriteUsersToStorage() const
+{
+ try
+ {
+ json::Object rootObject;
+ for (UsersMap::const_iterator i = m_users.begin(); i != m_users.end(); ++i)
+ {
+ UserPtr user = i->second;
+
+ json::Object timePermitCacheObject;
+ timePermitCacheObject["date"] = json::Number(user->GetTimePermitCache().GetDate());
+ timePermitCacheObject["timePermit"] = json::String(util::HexEncode(user->GetTimePermitCache().GetTimePermit()));
+
+ json::Object userObject;
+ userObject["timePermitCache"] = timePermitCacheObject;
+
+ if(!user->GetDeviceName().empty())
+ {
+ userObject["deviceName"] = json::String(user->GetDeviceName());
+ }
+
+ userObject["state"] = json::String(user->GetStateString());
+
+ ((json::Object&) rootObject[user->GetBackend()])[user->GetMPinIdHex()] = userObject;
+
+ Status s;
+ switch(user->GetState())
+ {
+ case User::STARTED_REGISTRATION:
+ case User::ACTIVATED:
+ s = m_crypto->SaveRegOTT(user->GetMPinId(), user->GetRegOTT());
+ break;
+ case User::REGISTERED:
+ s = m_crypto->DeleteRegOTT(user->GetMPinId());
+ break;
+ default:
+ break;
+ }
+ if(s != Status::OK)
+ {
+ return s;
+ }
+ }
+
+ std::stringstream strOut;
+ json::Writer::Write(rootObject, strOut);
+ m_context->GetStorage(IStorage::NONSECURE)->SetData(strOut.str());
+ }
+ catch(const json::Exception& e)
+ {
+ return Status(Status::STORAGE_ERROR, e.what());
+ }
+
+ return Status::OK;
+}
+
+Status MPinSDK::LoadUsersFromStorage()
+{
+ ClearUsers();
+
+ String data;
+ m_context->GetStorage(IStorage::NONSECURE)->GetData(data);
+ data.Trim();
+ if(data.empty())
+ {
+ return Status(Status::OK);
+ }
+
+ try
+ {
+ json::Object rootObject;
+ std::istringstream str(data);
+ json::Reader::Read(rootObject, str);
+
+ for(json::Object::const_iterator backendsIter = rootObject.Begin(); backendsIter != rootObject.End(); ++backendsIter)
+ {
+ const json::Object& backendObject = (const json::Object&) backendsIter->element;
+
+ for(json::Object::const_iterator usersIter = backendObject.Begin(); usersIter != backendObject.End(); ++usersIter)
+ {
+ const String& mpinIdHex = usersIter->name;
+ String mpinId = util::HexDecode(mpinIdHex);
+ util::JsonObject mpinIdJson;
+ if(!mpinIdJson.Parse(mpinId.c_str()))
+ {
+ return Status(Status::STORAGE_ERROR, String().Format("Failed to parse mpinId json: '%s'", mpinId.c_str()));
+ }
+ const json::Object& userObject = (const json::Object&) usersIter->element;
+ const std::string& id = ((const json::String&) mpinIdJson["userID"]).Value();
+ std::string deviceName;
+ json::Object::const_iterator dni = userObject.Find("deviceName");
+ if(dni != userObject.End())
+ {
+ deviceName = ((const json::String&) dni->element).Value();
+ }
+
+ String regOTT;
+ Status s = m_crypto->LoadRegOTT(mpinId, regOTT);
+ if(s != Status::OK)
+ {
+ return s;
+ }
+
+ UserPtr user = MakeNewUser(id, deviceName);
+ s = user->RestoreState(((const json::String&) userObject["state"]).Value(), mpinIdHex, regOTT, backendsIter->name);
+ if(s != Status::OK)
+ {
+ return s;
+ }
+
+ const json::Object& timePermitCacheObject = (const json::Object&) userObject["timePermitCache"];
+ int date = (int) ((const json::Number&) timePermitCacheObject["date"]).Value();
+ const String& timePermit = util::HexDecode(((const json::String&) timePermitCacheObject["timePermit"]).Value());
+
+ user->CacheTimePermit(timePermit, date);
+
+ m_users[user->GetKey()] = user;
+ }
+ }
+ }
+ catch(const json::Exception& e)
+ {
+ return Status(Status::STORAGE_ERROR, e.what());
+ }
+
+ return Status::OK;
}
bool MPinSDK::CanLogout(UserPtr user)
@@ -1773,3 +1739,8 @@
m_clientSettings[key].Accept(sv);
return sv.GetData();
}
+
+const char * MPinSDK::GetVersion()
+{
+ return MPIN_SDK_V2_VERSION;
+}
diff --git a/src/mpin_sdk.h b/src/mpin_sdk.h
index bd7a2f1..77ca6f6 100644
--- a/src/mpin_sdk.h
+++ b/src/mpin_sdk.h
@@ -173,29 +173,33 @@
};
const String& GetId() const;
+ const String& GetBackend() const;
const String& GetMPinId() const;
State GetState() const;
private:
friend class MPinSDK;
User(const String& id, const String& deviceName);
+ String GetKey() const;
const String& GetDeviceName() const;
const String& GetMPinIdHex() const;
const String& GetRegOTT() const;
const TimePermitCache& GetTimePermitCache() const;
void CacheTimePermit(const String& timePermit, int date);
+ void SetBackend(const String& backend);
void SetStartedRegistration(const String& mpinIdHex, const String& regOTT);
void SetActivated();
void SetRegistered();
void Invalidate();
void Block();
- Status RestoreState(const String& stateString, const String& mpinIdHex, const String& regOTT);
+ Status RestoreState(const String& stateString, const String& mpinIdHex, const String& regOTT, const String& backend);
String GetStateString() const;
static String StateToString(State state);
static State StringToState(const String& stateString);
private:
String m_id;
+ String m_backend;
String m_deviceName;
State m_state;
String m_mpinId;
@@ -256,14 +260,14 @@
Status GetSessionDetails(const String& accessCode, OUT SessionDetails& sessionDetails);
void DeleteUser(INOUT UserPtr user);
- void DeleteUser(INOUT UserPtr user, const String& backend);
- Status ListUsers(OUT std::vector<UserPtr>& users) const;
Status ListUsers(OUT std::vector<UserPtr>& users, const String& backend) const;
+ Status ListUsers(OUT std::vector<UserPtr>& users) const;
+ Status ListAllUsers(OUT std::vector<UserPtr>& users) const;
Status ListBackends(OUT std::vector<String>& backends) const;
- const char * GetVersion();
bool CanLogout(IN UserPtr user);
bool Logout(IN UserPtr user);
String GetClientParam(const String& key);
+ const char * GetVersion();
static const char *CONFIG_BACKEND;
static const char *CONFIG_RPS_PREFIX;
@@ -361,15 +365,10 @@
Status GetCertivoxTimePermitShare(INOUT UserPtr user, const util::JsonObject& cutomerTimePermitData, OUT String& resultTimePermit);
bool ValidateAccessNumber(const String& accessNumber);
bool ValidateAccessNumberChecksum(const String& accessNumber);
- void AddUser(IN UserPtr user);
Status CheckUserState(IN UserPtr user, User::State expectedState);
- void DeleteUser(INOUT UserPtr user, const String& backend, UsersMap& usersMap);
String MakeBackendKey(const String& backendServer) const;
Status WriteUsersToStorage() const;
- Status WriteUsersToStorage(const String& backendServer, const UsersMap& usersMap) const;
Status LoadUsersFromStorage();
- Status LoadUsersFromStorage(const String& backendServer, UsersMap& usersMap) const;
- void ListUsers(OUT std::vector<UserPtr>& users, const UsersMap& usersMap) const;
static const char *DEFAULT_RPS_PREFIX;
static const int AN_WITH_CHECKSUM_LEN = 7;
diff --git a/src/version.h b/src/version.h
index a9b268d..1ae5af5 100644
--- a/src/version.h
+++ b/src/version.h
@@ -25,6 +25,6 @@
#define _MPIN_SDK_VERSION_H_
#define MPIN_SDK_VERSION "1.0.0"
-#define MPIN_SDK_V2_VERSION "2.1.0"
+#define MPIN_SDK_V2_VERSION "2.2.0"
#endif // _MPIN_SDK_VERSION_H_
diff --git a/tests/unit_tests.cpp b/tests/unit_tests.cpp
index 055d7ee..f963195 100644
--- a/tests/unit_tests.cpp
+++ b/tests/unit_tests.cpp
@@ -191,10 +191,14 @@
}
users.clear();
+ sdk.ListAllUsers(users);
+ BOOST_CHECK_EQUAL(users.size(), 1);
+
+ users.clear();
sdk.ListUsers(users, backend);
BOOST_CHECK_EQUAL(users.size(), 1);
- sdk.DeleteUser(users[0], backend);
+ sdk.DeleteUser(users[0]);
users.clear();
sdk.ListUsers(users, backend);
BOOST_CHECK(users.empty());