From 8248665b8b9e4204d26d4ac4e73e9a616b4b12a4 Mon Sep 17 00:00:00 2001 From: Zofia Abramowska Date: Mon, 1 Dec 2014 18:47:14 +0100 Subject: [PATCH] Add AskUser cynara client and server side plugins Change-Id: Id6d30097eac100c5462c60b5bac7c41c3a39f6a7 --- CMakeLists.txt | 2 + packaging/askuser-plugins.manifest | 5 ++ packaging/askuser.spec | 14 ++++ src/CMakeLists.txt | 1 + src/common/types/SupportedTypes.h | 1 + src/plugin/CMakeLists.txt | 57 ++++++++++++++ src/plugin/client/ClientPlugin.cpp | 89 ++++++++++++++++++++++ src/plugin/service/CapacityCache.h | 126 +++++++++++++++++++++++++++++++ src/plugin/service/ServicePlugin.cpp | 141 +++++++++++++++++++++++++++++++++++ 9 files changed, 436 insertions(+) create mode 100644 packaging/askuser-plugins.manifest create mode 100644 src/plugin/CMakeLists.txt create mode 100644 src/plugin/client/ClientPlugin.cpp create mode 100644 src/plugin/service/CapacityCache.h create mode 100644 src/plugin/service/ServicePlugin.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d208e49..61ec81f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 index 0000000..a76fdba --- /dev/null +++ b/packaging/askuser-plugins.manifest @@ -0,0 +1,5 @@ + + + + + diff --git a/packaging/askuser.spec b/packaging/askuser.spec index 632cd78..fe61d25 100644 --- a/packaging/askuser.spec +++ b/packaging/askuser.spec @@ -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/* diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7387726..edb8f35 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,3 +20,4 @@ SET(ASKUSER_PATH ${PROJECT_SOURCE_DIR}/src) ADD_SUBDIRECTORY(agent) ADD_SUBDIRECTORY(common) +ADD_SUBDIRECTORY(plugin) diff --git a/src/common/types/SupportedTypes.h b/src/common/types/SupportedTypes.h index 8baf196..2776ff5 100644 --- a/src/common/types/SupportedTypes.h +++ b/src/common/types/SupportedTypes.h @@ -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 index 0000000..8e649ba --- /dev/null +++ b/src/plugin/CMakeLists.txt @@ -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 +# + +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 index 0000000..12026b7 --- /dev/null +++ b/src/plugin/client/ClientPlugin.cpp @@ -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 + * @brief Implementation of cynara client side AskUser plugin. + */ + +#include +#include +#include + +#include +#include +#include + +using namespace Cynara; + +namespace AskUser { +const std::vector clientTypes = { + SupportedTypes::Client::ALLOW_ONCE, + SupportedTypes::Client::ALLOW_PER_SESSION +}; + +class ClientPlugin : public ClientPluginInterface { +public: + const std::vector &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 index 0000000..975c216 --- /dev/null +++ b/src/plugin/service/CapacityCache.h @@ -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 + * @brief LRU cache container template declaration. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +namespace Plugin { + +template +class CapacityCache { +public: + //using KeyHasherFun = std::function; + typedef std::function KeyHasherFun; + static const std::size_t CACHE_DEFAULT_CAPACITY = 100; + + CapacityCache(const std::function &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 KeyUsageList; + typedef std::unordered_map> KeyValueMap; + + void evict(); + + std::size_t m_capacity; + + KeyHasherFun m_keyHasher; + KeyUsageList m_keyUsage; + KeyValueMap m_keyValue; +}; + +template +bool CapacityCache::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 +void CapacityCache::clear(void) { + m_keyUsage.clear(); + m_keyValue.clear(); +} + +template +void CapacityCache::evict(void) { + auto lastUsedKey = m_keyUsage.back(); + m_keyUsage.pop_back(); + + auto value_it = m_keyValue.find(lastUsedKey); + m_keyValue.erase(value_it); +} + +template +bool CapacityCache::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 index 0000000..d6f2ee0 --- /dev/null +++ b/src/plugin/service/ServicePlugin.cpp @@ -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 + * @brief Implementation of cynara server side AskUser plugin. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "CapacityCache.h" + +using namespace Cynara; + +typedef std::tuple 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 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 serviceTypes = {SupportedTypes::Service::ASK_USER}; + +class AskUserPlugin : public ServicePluginInterface { +public: + AskUserPlugin() + : m_cache(hasher) + {} + const std::vector &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 m_cache; +}; + +} // namespace AskUser + +extern "C" { +ExternalPluginInterface *create(void) { + return new AskUser::AskUserPlugin(); +} + +void destroy(ExternalPluginInterface *ptr) { + delete ptr; +} +} // extern "C" -- 2.7.4