[Common] Added cache for mapping privileges 06/117806/1
authorPiotr Kosko <p.kosko@samsung.com>
Tue, 7 Mar 2017 11:38:30 +0000 (12:38 +0100)
committerPiotr Kosko <p.kosko@samsung.com>
Tue, 7 Mar 2017 12:04:41 +0000 (13:04 +0100)
[Feature] Mapped privileges are stored in static map and there
  is no need to connect to database on each call. This would increase
  the performance of webapi calls, when performed repeatedly.

[Verification] Checking privileges works, checked with chrome console.
  Privilege TCT 100% passrate.

Change-Id: Ie25b636412715e2d1678c9d382d923e4be162256
Signed-off-by: Piotr Kosko <p.kosko@samsung.com>
src/common/tools.cc

index 8a6a3d3..638352f 100644 (file)
@@ -266,6 +266,13 @@ PlatformResult CheckAccess(const std::string& privilege) {
 PlatformResult CheckAccess(const std::vector<std::string>& privileges) {
   LoggerD("Enter");
 
+  // Local cache of mapped privilege strings. This routine can be called many times, especially
+  // during application launch, generating a high overhead of retrieving mapped privileges from
+  // the underlying databases. This is especially the case since the same mappings can end up
+  // being retrieved several times.
+  using MappedPrivilegeCache = std::map<std::string, std::vector<std::string>>;
+  static MappedPrivilegeCache mapped_privilege_cache;
+
   std::string api_version;
   PlatformResult res = common::tools::GetPkgApiVersion(&api_version);
   if (res.IsError()) {
@@ -283,30 +290,45 @@ PlatformResult CheckAccess(const std::vector<std::string>& privileges) {
       g_list_free(mapped_glist);
     };
 
-    input_glist = g_list_append(input_glist, (void*)input_priv.c_str());
-    int ret = privilege_manager_get_mapped_privilege_list(api_version.c_str(),
-                                                          PRVMGR_PACKAGE_TYPE_WRT,
-                                                          input_glist,
-                                                          &mapped_glist);
-    if (ret != PRVMGR_ERR_NONE) {
-      return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get mapped privilege list");
-    }
-
-    LoggerD("Mapped privileges:");
-    std::vector<std::string> mapped_vector;
-    auto push_elem = [](gpointer data, gpointer user_data) -> void {
-      if (data && user_data) {
-        std::vector<std::string>* mapped_vector =
-            static_cast<std::vector<std::string>*>(user_data);
-        char* char_data = static_cast<char*>(data);
-        mapped_vector->push_back(char_data);
-        LoggerD("mapped to: %s", char_data);
+    std::vector<std::string>* mapped_vector_ptr = nullptr;
+
+    // Check if mapped privilege is in local cache first
+    MappedPrivilegeCache::const_iterator it = mapped_privilege_cache.find(input_priv);
+    if (mapped_privilege_cache.end() == it) {
+      LoggerD("Mapped privileges - need to be fetched from database");
+      // Not in cache - retrieve from underlying databases.
+      input_glist = g_list_append(input_glist, (void*)input_priv.c_str());
+      int ret = privilege_manager_get_mapped_privilege_list(api_version.c_str(),
+                                                            PRVMGR_PACKAGE_TYPE_WRT,
+                                                            input_glist,
+                                                            &mapped_glist);
+      if (PRVMGR_ERR_NONE != ret) {
+        return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get mapped privilege list");
       }
-    };
-    g_list_foreach (mapped_glist, push_elem, &mapped_vector);
 
-    if (!AccessControl::GetInstance().CheckAccess(mapped_vector)){
-      for (const auto& mapped_priv : mapped_vector) {
+      // initialize empty cache vector
+      mapped_privilege_cache[input_priv] = std::vector<std::string>();
+      LoggerD("Mapped privileges:");
+      auto push_elem = [](gpointer data, gpointer user_data) -> void {
+        if (data && user_data) {
+          std::vector<std::string>* mapped_vector =
+              static_cast<std::vector<std::string>*>(user_data);
+          char* char_data = static_cast<char*>(data);
+          mapped_vector->push_back(char_data);
+          LoggerD("mapped to: %s", char_data);
+        }
+      };
+      // fill the vector with data
+      g_list_foreach (mapped_glist, push_elem, &mapped_privilege_cache[input_priv]);
+      mapped_vector_ptr = &mapped_privilege_cache[input_priv];
+    } else {
+      // Retrieve from local cache
+      LoggerD("Mapped privileges already in cache");
+      mapped_vector_ptr = (std::vector<std::string>*) &(it->second);
+    }
+
+    if (!AccessControl::GetInstance().CheckAccess(*mapped_vector_ptr)){
+      for (const auto& mapped_priv : *mapped_vector_ptr) {
         LoggerD("Access to privilege: %s has been denied.", mapped_priv.c_str());
       }
       return PlatformResult(ErrorCode::SECURITY_ERR, "Permission denied");
@@ -318,6 +340,12 @@ PlatformResult CheckAccess(const std::vector<std::string>& privileges) {
 PlatformResult GetPkgApiVersion(std::string* api_version) {
   LoggerD("Entered");
 
+  // Local cache of API version string.  This can be expensive to retrieve from
+  // underlying databases and this routine can be called many times during
+  // application launch.
+  static std::string cached_api_version;
+  static int cached_pid = -1;
+
   char* app_id = nullptr;
   char* pkgid = nullptr;
   char* api_ver = nullptr;
@@ -340,32 +368,40 @@ PlatformResult GetPkgApiVersion(std::string* api_version) {
   };
 
   pid_t pid = getpid();
-  int ret = app_manager_get_app_id(pid, &app_id);
-  if (ret != APP_MANAGER_ERROR_NONE) {
-    return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get app id");
-  }
+  if (cached_pid == pid) {
+    *api_version = cached_api_version;  // Retrieve from local cache
+  } else {
+    int ret = app_manager_get_app_id(pid, &app_id);
+    if (ret != APP_MANAGER_ERROR_NONE) {
+      return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get app id");
+    }
 
-  ret = app_info_create(app_id, &app_handle);
-  if (ret != APP_MANAGER_ERROR_NONE) {
-    return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get app info");
-  }
+    ret = app_info_create(app_id, &app_handle);
+    if (ret != APP_MANAGER_ERROR_NONE) {
+      return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get app info");
+    }
 
-  ret = app_info_get_package(app_handle, &pkgid);
-  if ((ret != APP_MANAGER_ERROR_NONE) || (pkgid == nullptr)) {
-    return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get pkg id");
-  }
+    ret = app_info_get_package(app_handle, &pkgid);
+    if ((ret != APP_MANAGER_ERROR_NONE) || (pkgid == nullptr)) {
+      return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get pkg id");
+    }
 
-  ret = pkgmgrinfo_pkginfo_get_usr_pkginfo(pkgid, getuid(), &pkginfo_handle);
-  if (ret != PMINFO_R_OK) {
-    return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get pkginfo_h");
-  }
+    ret = pkgmgrinfo_pkginfo_get_usr_pkginfo(pkgid, getuid(), &pkginfo_handle);
+    if (ret != PMINFO_R_OK) {
+      return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get pkginfo_h");
+    }
+
+    ret = pkgmgrinfo_pkginfo_get_api_version(pkginfo_handle, &api_ver);
+    if (ret != PMINFO_R_OK) {
+      return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get api version");
+    }
+
+    cached_api_version = api_ver;  // Save in local cache
+    cached_pid = pid;
 
-  ret = pkgmgrinfo_pkginfo_get_api_version(pkginfo_handle, &api_ver);
-  if (ret != PMINFO_R_OK) {
-    return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get api version");
+    *api_version = api_ver;
   }
 
-  *api_version = api_ver;
   return PlatformResult(ErrorCode::NO_ERROR);
 }