Added constrains to id generator

This commit prevents id with more than one '/'. Slash can be used to
create namespaces to avoid id conflicts between multiple users.

Changed generator logic to take provided id as a name if no name was
provided.

Tested:
- Tested that id and name generates correctly
- It is no longer possible to add id with more than one '/'

Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
Change-Id: Ieef32ddb71b5a4870117aab0d624cbd46b5893e6
diff --git a/src/utils/generate_id.cpp b/src/utils/generate_id.cpp
index adb1a5b..6631e69 100644
--- a/src/utils/generate_id.cpp
+++ b/src/utils/generate_id.cpp
@@ -12,28 +12,44 @@
 static constexpr std::string_view allowedCharactersInId =
     "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_/";
 
-}
-
-void verifyIdCharacters(std::string_view triggerId)
+size_t countDigits(size_t value)
 {
-    if (triggerId.find_first_not_of(details::allowedCharactersInId) !=
-        std::string::npos)
+    size_t result = 1;
+    while (value >= 10)
     {
-        throw sdbusplus::exception::SdBusError(
-            static_cast<int>(std::errc::invalid_argument),
-            "Invalid character in id");
+        ++result;
+        value /= 10;
     }
+    return result;
 }
 
-std::string generateId(std::string_view prefix, std::string_view name,
+std::string generateId(std::string_view id, std::string_view name,
                        const std::vector<std::string>& conflictIds,
                        size_t maxLength)
 {
-    verifyIdCharacters(prefix);
+    verifyIdCharacters(id);
 
-    if (!prefix.empty() && !prefix.ends_with('/'))
+    if (id.starts_with('/'))
     {
-        return std::string(prefix);
+        id = id.substr(1);
+    }
+
+    if ((id.length() > maxLength) ||
+        (id.ends_with('/') && id.length() >= maxLength))
+    {
+        throw sdbusplus::exception::SdBusError(
+            static_cast<int>(std::errc::invalid_argument), "Id too long");
+    }
+
+    if (!id.empty() && !id.ends_with('/'))
+    {
+        if (std::find(conflictIds.begin(), conflictIds.end(), id) !=
+            conflictIds.end())
+        {
+            throw sdbusplus::exception::SdBusError(
+                static_cast<int>(std::errc::file_exists), "Duplicated id");
+        }
+        return std::string(id);
     }
 
     std::string strippedId(name);
@@ -45,7 +61,7 @@
                                       std::string_view::npos;
                        }),
         strippedId.end());
-    strippedId = std::string(prefix) + strippedId;
+    strippedId = std::string(id) + strippedId;
 
     size_t idx = 0;
     std::string tmpId = strippedId.substr(0, maxLength);
@@ -54,11 +70,65 @@
                conflictIds.end() ||
            tmpId.empty())
     {
-        tmpId = strippedId.substr(0, maxLength - std::to_string(idx).length()) +
-                std::to_string(idx);
+        size_t digitsInIdx = countDigits(idx);
+
+        if (digitsInIdx > maxLength)
+        {
+            throw sdbusplus::exception::SdBusError(
+                static_cast<int>(std::errc::file_exists),
+                "Unique indices are depleted");
+        }
+
+        tmpId =
+            strippedId.substr(0, maxLength - digitsInIdx) + std::to_string(idx);
         ++idx;
     }
+
     return tmpId;
 }
 
+} // namespace details
+
+void verifyIdCharacters(std::string_view id)
+{
+    if (id.find_first_not_of(details::allowedCharactersInId) !=
+        std::string::npos)
+    {
+        throw sdbusplus::exception::SdBusError(
+            static_cast<int>(std::errc::invalid_argument),
+            "Invalid character in id");
+    }
+
+    if (auto pos = id.find_first_of("/");
+        pos != std::string::npos && pos != id.find_last_of("/"))
+    {
+        throw sdbusplus::exception::SdBusError(
+            static_cast<int>(std::errc::invalid_argument),
+            "Too many '/' in id");
+    }
+}
+
+std::pair<std::string, std::string> generateId(
+    std::string_view id, std::string_view name, std::string_view defaultName,
+    const std::vector<std::string>& conflictIds, const size_t maxLength)
+{
+    if (name.empty() && !id.ends_with('/'))
+    {
+        name = id;
+
+        if (auto pos = name.find_last_of("/"); pos != std::string::npos)
+        {
+            name = name.substr(pos + 1);
+        }
+    }
+
+    if (name.empty())
+    {
+        name = defaultName;
+    }
+
+    return std::make_pair(details::generateId(id, name, conflictIds, maxLength),
+                          std::string{name});
+}
+
 } // namespace utils