Add AskUser cynara client and server side plugins 80/31180/10
authorZofia Abramowska <z.abramowska@samsung.com>
Mon, 1 Dec 2014 17:47:14 +0000 (18:47 +0100)
committerZofia Abramowska <z.abramowska@samsung.com>
Mon, 15 Dec 2014 11:04:14 +0000 (12:04 +0100)
Change-Id: Id6d30097eac100c5462c60b5bac7c41c3a39f6a7

CMakeLists.txt
packaging/askuser-plugins.manifest [new file with mode: 0644]
packaging/askuser.spec
src/CMakeLists.txt
src/common/types/SupportedTypes.h
src/plugin/CMakeLists.txt [new file with mode: 0644]
src/plugin/client/ClientPlugin.cpp [new file with mode: 0644]
src/plugin/service/CapacityCache.h [new file with mode: 0644]
src/plugin/service/ServicePlugin.cpp [new file with mode: 0644]

index d208e49..61ec81f 100644 (file)
@@ -67,6 +67,8 @@ ENDIF (CMAKE_BUILD_TYPE MATCHES "DEBUG")
 
 SET(TARGET_ASKUSER "askuser")
 SET(TARGET_ASKUSER_COMMON "askuser-common")
+SET(TARGET_PLUGIN_SERVICE "askuser-plugin-service")
+SET(TARGET_PLUGIN_CLIENT "askuser-plugin-client")
 
 ADD_SUBDIRECTORY(src)
 ADD_SUBDIRECTORY(systemd)
diff --git a/packaging/askuser-plugins.manifest b/packaging/askuser-plugins.manifest
new file mode 100644 (file)
index 0000000..a76fdba
--- /dev/null
@@ -0,0 +1,5 @@
+<manifest>
+       <request>
+               <domain name="_" />
+       </request>
+</manifest>
index 632cd78..fe61d25 100644 (file)
@@ -7,6 +7,7 @@ License:    Apache-2.0
 Source0:    %{name}-%{version}.tar.gz
 Source1001:    %{name}.manifest
 Source1002:    libaskuser-common.manifest
+Source1003:    askuser-plugins.manifest
 BuildRequires: cmake
 BuildRequires: zip
 BuildRequires: pkgconfig(libsystemd-daemon)
@@ -29,10 +30,18 @@ Summary:    Askuser common library
 %description -n libaskuser-common
 askuser common library with common functionalities
 
+%package -n askuser-plugins
+Requires:   cynara
+Summary:    Askuser commons library
+
+%description -n askuser-plugins
+askuser plugin library with cynara service and client side plugins
+
 %prep
 %setup -q
 cp -a %{SOURCE1001} .
 cp -a %{SOURCE1002} .
+cp -a %{SOURCE1003} .
 
 %build
 %if 0%{?sec_build_binary_debug_enable}
@@ -87,3 +96,8 @@ fi
 %manifest libaskuser-common.manifest
 %license LICENSE
 %{_libdir}/libaskuser-common.so*
+
+%files -n askuser-plugins
+%manifest askuser-plugins.manifest
+%license LICENSE
+%{_libdir}/cynara/plugin/*
index 7387726..edb8f35 100644 (file)
@@ -20,3 +20,4 @@ SET(ASKUSER_PATH ${PROJECT_SOURCE_DIR}/src)
 
 ADD_SUBDIRECTORY(agent)
 ADD_SUBDIRECTORY(common)
+ADD_SUBDIRECTORY(plugin)
index 8baf196..2776ff5 100644 (file)
@@ -37,6 +37,7 @@ const Cynara::PolicyType ASK_USER = 10;
 namespace Client {
 const Cynara::PolicyType ALLOW_ONCE = 11;
 const Cynara::PolicyType ALLOW_PER_SESSION = 12;
+// This one will never reach client, but will be interpreted in service plugin
 const Cynara::PolicyType ALLOW_PER_LIFE = 13;
 } //namespace Client
 
diff --git a/src/plugin/CMakeLists.txt b/src/plugin/CMakeLists.txt
new file mode 100644 (file)
index 0000000..8e649ba
--- /dev/null
@@ -0,0 +1,57 @@
+# Copyright (c) 2014 Samsung Electronics Co.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+#
+# @file        CMakeLists.txt
+# @author      Zofia Abramowska <z.abramowska@samsung.com>
+#
+
+SET(PLUGIN_PATH ${ASKUSER_PATH}/plugin)
+
+PKG_CHECK_MODULES(SERVICE_DEP
+    REQUIRED
+    cynara-plugin
+    )
+
+INCLUDE_DIRECTORIES(
+    ${SERVICE_DEP_INCLUDE_DIRS}
+    ${ASKUSER_PATH}/common
+    ${PLUGIN_PATH}/service
+    )
+
+SET(SERVICE_PLUGIN_SOURCES
+    ${PLUGIN_PATH}/service/ServicePlugin.cpp
+    )
+
+SET(CLIENT_PLUGIN_SOURCES
+    ${PLUGIN_PATH}/client/ClientPlugin.cpp
+    )
+
+ADD_DEFINITIONS("-fvisibility=default")
+
+ADD_LIBRARY(${TARGET_PLUGIN_SERVICE} SHARED ${SERVICE_PLUGIN_SOURCES})
+ADD_LIBRARY(${TARGET_PLUGIN_CLIENT} SHARED ${CLIENT_PLUGIN_SOURCES})
+
+TARGET_LINK_LIBRARIES(${TARGET_PLUGIN_SERVICE}
+    ${TARGET_ASKUSER_COMMON}
+    ${TARGET_ASKUSER_COMMON_DEPS}
+    )
+TARGET_LINK_LIBRARIES(${TARGET_PLUGIN_CLIENT}
+    ${TARGET_ASKUSER_COMMON}
+    ${TARGET_ASKUSER_COMMON_DEPS}
+    )
+
+INSTALL(TARGETS ${TARGET_PLUGIN_SERVICE}
+    DESTINATION ${LIB_INSTALL_DIR}/cynara/plugin/service/)
+INSTALL(TARGETS ${TARGET_PLUGIN_CLIENT}
+    DESTINATION ${LIB_INSTALL_DIR}/cynara/plugin/client/)
diff --git a/src/plugin/client/ClientPlugin.cpp b/src/plugin/client/ClientPlugin.cpp
new file mode 100644 (file)
index 0000000..12026b7
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License
+ */
+/**
+ * @file        ClientPlugin.cpp
+ * @author      Zofia Abramowska <z.abramowska@samsung.com>
+ * @brief       Implementation of cynara client side AskUser plugin.
+ */
+
+#include <cynara-client-plugin.h>
+#include <cynara-error.h>
+#include <vector>
+
+#include <attributes/attributes.h>
+#include <log/log.h>
+#include <types/SupportedTypes.h>
+
+using namespace Cynara;
+
+namespace AskUser {
+const std::vector<PolicyType> clientTypes = {
+        SupportedTypes::Client::ALLOW_ONCE,
+        SupportedTypes::Client::ALLOW_PER_SESSION
+};
+
+class ClientPlugin : public ClientPluginInterface {
+public:
+    const std::vector<PolicyType> &getSupportedPolicyTypes() {
+        return clientTypes;
+    }
+
+    bool isCacheable(const ClientSession &session UNUSED, const PolicyResult &result) {
+        return (result.policyType() == SupportedTypes::Client::ALLOW_PER_SESSION);
+    }
+
+    bool isUsable(const ClientSession &session,
+                  const ClientSession &prevSession,
+                  bool &updateSession,
+                  PolicyResult &result)
+    {
+        updateSession = false;
+
+        if (result.policyType() == SupportedTypes::Client::ALLOW_PER_SESSION) {
+            if (session == prevSession) {
+                return true;
+            }
+            LOGD("Previous session <" << prevSession << "> does not match current session <"
+                    << session << ">");
+            return false;
+        }
+
+        return false;
+    }
+
+    void invalidate() {}
+
+    virtual int toResult(const ClientSession &session UNUSED, PolicyResult &result) {
+        switch (result.policyType()) {
+            case SupportedTypes::Client::ALLOW_ONCE:
+            case SupportedTypes::Client::ALLOW_PER_SESSION:
+                return CYNARA_API_ACCESS_ALLOWED;
+        }
+        return CYNARA_API_ACCESS_DENIED;
+    }
+};
+
+} // namespace AskUser
+
+extern "C" {
+ExternalPluginInterface *create(void) {
+    return new AskUser::ClientPlugin();
+}
+
+void destroy(ExternalPluginInterface *ptr) {
+    delete ptr;
+}
+} // extern "C"
diff --git a/src/plugin/service/CapacityCache.h b/src/plugin/service/CapacityCache.h
new file mode 100644 (file)
index 0000000..975c216
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License
+ */
+/**
+ * @file        CapacityCache.h
+ * @author      Zofia Abramowska <z.abramowska@samsung.com>
+ * @brief       LRU cache container template declaration.
+ */
+
+#pragma once
+
+#include <iostream>
+#include <functional>
+#include <list>
+#include <string>
+#include <tuple>
+#include <unordered_map>
+
+#include <log/log.h>
+
+namespace Plugin {
+
+template<class Key, class Value>
+class CapacityCache {
+public:
+    //using KeyHasherFun = std::function<std::string(const Key &)>;
+    typedef std::function<std::string(const Key &)> KeyHasherFun;
+    static const std::size_t CACHE_DEFAULT_CAPACITY = 100;
+
+    CapacityCache(const std::function<std::string(const Key&)> &func,
+                  std::size_t capacity = CACHE_DEFAULT_CAPACITY)
+        : m_capacity(capacity),
+          m_keyHasher(func)
+    {}
+
+    bool get(const Key &key, Value &value);
+    bool update(const Key &key, const Value &value);
+    void clear();
+
+private:
+    typedef std::list<std::string> KeyUsageList;
+    typedef std::unordered_map<std::string, std::pair<Value, KeyUsageList::iterator>> KeyValueMap;
+
+    void evict();
+
+    std::size_t m_capacity;
+
+    KeyHasherFun m_keyHasher;
+    KeyUsageList m_keyUsage;
+    KeyValueMap m_keyValue;
+};
+
+template<class Key, class Value>
+bool CapacityCache<Key, Value>::get(const Key &key, Value &value) {
+    auto resultIt = m_keyValue.find(m_keyHasher(key));
+    //Do we have entry in cache?
+    if (resultIt == m_keyValue.end()) {
+        return false;
+    }
+    LOGD("Found: " << key << " with value:" << resultIt->second.first);
+
+    auto usageIt = resultIt->second.second;
+    m_keyUsage.splice(m_keyUsage.begin(), m_keyUsage, usageIt);
+
+    value = resultIt->second.first;
+    return true;
+}
+
+template<class Key, class Value>
+void CapacityCache<Key, Value>::clear(void) {
+    m_keyUsage.clear();
+    m_keyValue.clear();
+}
+
+template <class Key, class Value>
+void CapacityCache<Key, Value>::evict(void) {
+    auto lastUsedKey = m_keyUsage.back();
+    m_keyUsage.pop_back();
+
+    auto value_it = m_keyValue.find(lastUsedKey);
+    m_keyValue.erase(value_it);
+}
+
+template <class Key, class Value>
+bool CapacityCache<Key, Value>::update(const Key &key, const Value &value) {
+    if (m_capacity == 0) {
+        LOGD("Cache size is 0");
+        return false;
+    }
+    if (m_keyValue.size() == m_capacity) {
+        LOGD("Capacity [" << m_capacity << "] reached");
+        evict();
+    }
+
+    std::string cacheKey = m_keyHasher(key);
+
+    bool existed = false;
+    auto resultIt = m_keyValue.find(cacheKey);
+    if (resultIt != m_keyValue.end()) {
+        existed = true;
+        auto usageIt = resultIt->second.second;
+        m_keyUsage.splice(m_keyUsage.begin(), m_keyUsage, usageIt);
+        LOGD("Update existing entry key=<" << key << ">" << " with value=<" << value << ">");
+    } else {
+        m_keyUsage.push_front(cacheKey);
+        LOGD("Added new entry key=<" << key << ">" << " and value=<" << value << ">");
+    }
+
+    m_keyValue[cacheKey] = std::make_pair(value, m_keyUsage.begin());
+    return existed;
+}
+
+} //namespace AskUser
+
diff --git a/src/plugin/service/ServicePlugin.cpp b/src/plugin/service/ServicePlugin.cpp
new file mode 100644 (file)
index 0000000..d6f2ee0
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License
+ */
+/**
+ * @file        ServicePlugin.cpp
+ * @author      Zofia Abramowska <z.abramowska@samsung.com>
+ * @brief       Implementation of cynara server side AskUser plugin.
+ */
+
+#include <string>
+#include <tuple>
+#include <iostream>
+#include <ostream>
+#include <cynara-plugin.h>
+
+#include <types/SupportedTypes.h>
+#include <translator/Translator.h>
+
+#include "CapacityCache.h"
+
+using namespace Cynara;
+
+typedef std::tuple<std::string, std::string, std::string> Key;
+std::ostream &operator<<(std::ostream &os, const Key &key) {
+    os << "client: " << std::get<0>(key)
+       << ", user: " << std::get<1>(key)
+       << ", privilege: " << std::get<2>(key);
+    return os;
+}
+
+std::ostream &operator<<(std::ostream &os, const PolicyResult &result) {
+    os << "type: " << result.policyType()
+       << ", metadata: " << result.metadata();
+    return os;
+}
+
+namespace AskUser {
+
+std::function<std::string(const Key&)> hasher = [](const Key &key) {
+    const char separator = '\1';
+    const auto &client = std::get<0>(key);
+    const auto &user = std::get<1>(key);
+    const auto &privilege = std::get<2>(key);
+    return client + user + privilege + separator +
+            std::to_string(client.size()) + separator +
+            std::to_string(user.size()) + separator +
+            std::to_string(privilege.size());
+};
+
+const std::vector<PolicyType> serviceTypes = {SupportedTypes::Service::ASK_USER};
+
+class AskUserPlugin : public ServicePluginInterface {
+public:
+    AskUserPlugin()
+        : m_cache(hasher)
+    {}
+    const std::vector<PolicyType> &getSupportedPolicyTypes() {
+        return serviceTypes;
+    }
+    PluginStatus check(const std::string &client,
+                       const std::string &user,
+                       const std::string &privilege,
+                       PolicyResult &result,
+                       AgentType &requiredAgent,
+                       PluginData &pluginData) noexcept
+    {
+        try {
+            if (!m_cache.get(Key(client, user, privilege), result)) {
+                pluginData = Translator::Plugin::requestToData(client, user, privilege);
+                requiredAgent = AgentType(SupportedTypes::Agent::AgentType);
+                return PluginStatus::ANSWER_NOTREADY;
+            }
+
+            result = PolicyResult(PredefinedPolicyType::ALLOW);
+            return PluginStatus::ANSWER_READY;
+        } catch (const Translator::TranslateErrorException &e) {
+            LOGE("Error translating request to data : " << e.what());
+        } catch (const std::exception &e) {
+            LOGE("Failed with std exception: " << e.what());
+        } catch (...) {
+            LOGE("Failed with unknown exception: ");
+        }
+        return PluginStatus::ERROR;
+    }
+
+    PluginStatus update(const std::string &client,
+                        const std::string &user,
+                        const std::string &privilege,
+                        const PluginData &agentData,
+                        PolicyResult &result) noexcept
+    {
+        try {
+            PolicyType resultType = Translator::Plugin::dataToAnswer(agentData);
+            result = PolicyResult(resultType);
+            if (resultType == SupportedTypes::Client::ALLOW_PER_LIFE) {
+                m_cache.update(Key(client, user, privilege), PolicyResult(resultType));
+                result = PolicyResult(PredefinedPolicyType::ALLOW);
+            }
+
+            return PluginStatus::SUCCESS;
+        } catch (const Translator::TranslateErrorException &e) {
+            LOGE("Error translating data to answer : " << e.what());
+        } catch (const std::exception &e) {
+            LOGE("Failed with std exception: " << e.what());
+        } catch (...) {
+            LOGE("Failed with unknown exception: ");
+        }
+        return PluginStatus::ERROR;
+    }
+
+    void invalidate() {
+        m_cache.clear();
+    }
+
+private:
+    Plugin::CapacityCache<Key, PolicyResult> m_cache;
+};
+
+} // namespace AskUser
+
+extern "C" {
+ExternalPluginInterface *create(void) {
+    return new AskUser::AskUserPlugin();
+}
+
+void destroy(ExternalPluginInterface *ptr) {
+    delete ptr;
+}
+} // extern "C"