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)
--- /dev/null
+<manifest>
+ <request>
+ <domain name="_" />
+ </request>
+</manifest>
Source0: %{name}-%{version}.tar.gz
Source1001: %{name}.manifest
Source1002: libaskuser-common.manifest
+Source1003: askuser-plugins.manifest
BuildRequires: cmake
BuildRequires: zip
BuildRequires: pkgconfig(libsystemd-daemon)
%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}
%manifest libaskuser-common.manifest
%license LICENSE
%{_libdir}/libaskuser-common.so*
+
+%files -n askuser-plugins
+%manifest askuser-plugins.manifest
+%license LICENSE
+%{_libdir}/cynara/plugin/*
ADD_SUBDIRECTORY(agent)
ADD_SUBDIRECTORY(common)
+ADD_SUBDIRECTORY(plugin)
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
--- /dev/null
+# 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/)
--- /dev/null
+/*
+ * 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"
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * 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"