[Merge] lp:~thomas-voss/trust-store/add_convenience_function_for_processing_incoming_requests into lp:trust-store
Seth Arnold
seth.arnold at canonical.com
Wed Jul 16 01:57:23 UTC 2014
Actually, another question, this time about open file descriptors when the helper programs are executed.
Diff comments:
> === modified file 'CMakeLists.txt'
> --- CMakeLists.txt 2013-12-20 10:38:58 +0000
> +++ CMakeLists.txt 2014-07-15 19:50:44 +0000
> @@ -28,8 +28,10 @@
> ENDIF(CMAKE_BUILD_TYPE MATCHES [cC][oO][vV][eE][rR][aA][gG][eE])
>
> find_package(PkgConfig)
> -find_package(Boost COMPONENTS system REQUIRED)
> +find_package(Boost COMPONENTS program_options system REQUIRED)
>
> +pkg_check_modules(MIR_CLIENT mirclient REQUIRED)
> +pkg_check_modules(MIR_COMMON mircommon REQUIRED)
> pkg_check_modules(PROCESS_CPP process-cpp REQUIRED)
>
> set(TRUST_STORE_VERSION_MAJOR 0)
> @@ -40,6 +42,10 @@
>
> include_directories(
> include/
> +
> + ${MIR_CLIENT_INCLUDE_DIRS}
> + ${MIR_COMMON_INCLUDE_DIRS}
> + ${PROCESS_CPP_INCLUDE_DIRS}
> )
>
> add_subdirectory(doc)
>
> === modified file 'debian/control'
> --- debian/control 2014-06-24 13:30:26 +0000
> +++ debian/control 2014-07-15 19:50:44 +0000
> @@ -7,13 +7,19 @@
> google-mock,
> gcovr,
> graphviz,
> + libboost-program-options-dev,
> libboost-system-dev,
> libdbus-cpp-dev,
> libdbus-1-dev,
> libgtest-dev,
> + libjson-c-dev,
> libprocess-cpp-dev,
> libsqlite3-dev,
> pkg-config,
> + qt5-default,
> + qtbase5-dev,
> + qtdeclarative5-dev,
> + qtquick1-5-dev,
> Standards-Version: 3.9.5
> Section: libs
> Homepage: https://launchpad.net/trust-store
> @@ -49,6 +55,18 @@
> This package includes all the development headers and libraries for
> trust-store.
>
> +Package: trust-store-tests
> +Section: doc
> +Architecture: all
> +Depends: libtrust-store0 (= ${binary:Version}),
> + ${misc:Depends},
> +Suggests: libtrust-store-dev,
> +Description: Test files for libtrust-store0
> + Provides a common implementation of a trust store to be used by trusted
> + helpers.
> + .
> + This package includes test executables packaged for post-build execution.
> +
> Package: libtrust-store-doc
> Section: doc
> Architecture: all
>
> === modified file 'debian/libtrust-store0.install'
> --- debian/libtrust-store0.install 2013-12-18 11:58:52 +0000
> +++ debian/libtrust-store0.install 2014-07-15 19:50:44 +0000
> @@ -1,1 +1,2 @@
> usr/lib/*/lib*.so.*
> +usr/lib/*/trust-prompt
>
> === modified file 'debian/libtrust-store0.symbols'
> --- debian/libtrust-store0.symbols 2014-06-26 08:11:42 +0000
> +++ debian/libtrust-store0.symbols 2014-07-15 19:50:44 +0000
> @@ -1,29 +1,62 @@
> libtrust-store.so.0 libtrust-store0 #MINVER#
> (c++)"core::trust::create_default_store(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.0.1+14.10.20140626.1
> + (c++)"core::trust::process_trust_request(core::trust::RequestParameters const&)@Base" 0replaceme
> (c++)"core::trust::expose_store_to_bus_with_name(std::shared_ptr<core::trust::Store> const&, std::shared_ptr<core::dbus::Bus> const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.0.1+14.10.20140626.1
> (c++)"core::trust::resolve_store_on_bus_with_name(std::shared_ptr<core::dbus::Bus> const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.0.1+14.10.20140626.1
> (c++)"core::trust::expose_store_to_session_with_name(std::shared_ptr<core::trust::Store> const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.0.1+14.10.20140626.1
> (c++)"core::trust::resolve_store_in_session_with_name(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.0.1+14.10.20140626.1
> - (c++)"core::trust::Store::Query::Error::NoCurrentResult::~NoCurrentResult()@Base" 0.0.1+14.10.20140626.1
> - (c++)"core::trust::Store::Query::Error::QueryIsInErrorState::~QueryIsInErrorState()@Base" 0.0.1+14.10.20140626.1
> + (c++)"core::trust::mir::PromptProviderHelper::InvocationArguments::~InvocationArguments()@Base" 0replaceme
> + (c++)"core::trust::mir::PromptProviderHelper::exec_prompt_provider_with_arguments(core::trust::mir::PromptProviderHelper::InvocationArguments const&)@Base" 0replaceme
> + (c++)"core::trust::mir::PromptProviderHelper::PromptProviderHelper(core::trust::mir::PromptProviderHelper::CreationArguments const&)@Base" 0replaceme
> + (c++)"core::trust::mir::ConnectionVirtualTable::create_prompt_session_sync(int, void (*)(MirPromptSession*, MirPromptSessionState, void*), void*)@Base" 0replaceme
> + (c++)"core::trust::mir::ConnectionVirtualTable::ConnectionVirtualTable(MirConnection*)@Base" 0replaceme
> + (c++)"core::trust::mir::PromptSessionVirtualTable::release_sync()@Base" 0replaceme
> + (c++)"core::trust::mir::PromptSessionVirtualTable::mir_client_fd_callback(MirPromptSession*, unsigned long, int const*, void*)@Base" 0replaceme
> + (c++)"core::trust::mir::PromptSessionVirtualTable::add_prompt_provider_sync(int)@Base" 0replaceme
> + (c++)"core::trust::mir::PromptSessionVirtualTable::new_fd_for_prompt_provider()@Base" 0replaceme
> + (c++)"core::trust::mir::PromptSessionVirtualTable::PromptSessionVirtualTable(MirPromptSession*)@Base" 0replaceme
> + (c++)"core::trust::mir::create_agent_for_mir_connection(MirConnection*)@Base" 0replaceme
> + (c++)"core::trust::mir::Agent::prompt_user_for_request(int, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0replaceme
> + (c++)"core::trust::mir::Agent::on_trust_session_changed_state(MirPromptSession*, MirPromptSessionState, void*)@Base" 0replaceme
> + (c++)"core::trust::mir::Agent::translator_only_accepting_exit_status_success()@Base" 0replaceme
> + (c++)"core::trust::mir::Agent::Agent(std::shared_ptr<core::trust::mir::ConnectionVirtualTable> const&, std::shared_ptr<core::trust::mir::PromptProviderHelper> const&, std::function<core::trust::Request::Answer (core::posix::wait::Result const&)> const&)@Base" 0replaceme
> + (c++)"core::trust::mir::Agent::~Agent()@Base" 0replaceme
> + (c++)"core::trust::mir::operator==(core::trust::mir::PromptProviderHelper::InvocationArguments const&, core::trust::mir::PromptProviderHelper::InvocationArguments const&)@Base" 0replaceme
> + (c++)"core::trust::Store::Query::Errors::NoCurrentResult::~NoCurrentResult()@Base" 0replaceme
> + (c++)"core::trust::Store::Query::Errors::QueryIsInErrorState::~QueryIsInErrorState()@Base" 0replaceme
> (c++)"core::trust::Store::Errors::ErrorResettingStore::~ErrorResettingStore()@Base" 0.0.1+14.10.20140626.1
> (c++)"core::trust::operator==(core::trust::Request const&, core::trust::Request const&)@Base" 0.0.1+14.10.20140626.1
> (c++)"core::trust::operator<<(std::basic_ostream<char, std::char_traits<char> >&, core::trust::Request::Answer const&)@Base" 0.0.1+14.10.20140626.1
> (c++)"core::trust::operator<<(std::basic_ostream<char, std::char_traits<char> >&, core::trust::Request const&)@Base" 0.0.1+14.10.20140626.1
> - (c++)"typeinfo for core::trust::Store::Query::Error::NoCurrentResult at Base" 0.0.1+14.10.20140626.1
> - (c++)"typeinfo for core::trust::Store::Query::Error::QueryIsInErrorState at Base" 0.0.1+14.10.20140626.1
> + (c++)"typeinfo for core::trust::mir::PromptProviderHelper at Base" 0replaceme
> + (c++)"typeinfo for core::trust::mir::ConnectionVirtualTable at Base" 0replaceme
> + (c++)"typeinfo for core::trust::mir::PromptSessionVirtualTable at Base" 0replaceme
> + (c++)"typeinfo for core::trust::mir::Agent at Base" 0replaceme
> + (c++)"typeinfo for core::trust::Agent at Base" 0replaceme
> + (c++)"typeinfo for core::trust::Store::Query::Errors::NoCurrentResult at Base" 0replaceme
> + (c++)"typeinfo for core::trust::Store::Query::Errors::QueryIsInErrorState at Base" 0replaceme
> (c++)"typeinfo for core::trust::Store::Query at Base" 0.0.1+14.10.20140626.1
> (c++)"typeinfo for core::trust::Store::Errors::ErrorResettingStore at Base" 0.0.1+14.10.20140626.1
> (c++)"typeinfo for core::trust::Store at Base" 0.0.1+14.10.20140626.1
> (c++)"typeinfo for core::trust::Token at Base" 0.0.1+14.10.20140626.1
> - (c++)"typeinfo name for core::trust::Store::Query::Error::NoCurrentResult at Base" 0.0.1+14.10.20140626.1
> - (c++)"typeinfo name for core::trust::Store::Query::Error::QueryIsInErrorState at Base" 0.0.1+14.10.20140626.1
> + (c++)"typeinfo name for core::trust::mir::PromptProviderHelper at Base" 0replaceme
> + (c++)"typeinfo name for core::trust::mir::ConnectionVirtualTable at Base" 0replaceme
> + (c++)"typeinfo name for core::trust::mir::PromptSessionVirtualTable at Base" 0replaceme
> + (c++)"typeinfo name for core::trust::mir::Agent at Base" 0replaceme
> + (c++)"typeinfo name for core::trust::Agent at Base" 0replaceme
> + (c++)"typeinfo name for core::trust::Store::Query::Errors::NoCurrentResult at Base" 0replaceme
> + (c++)"typeinfo name for core::trust::Store::Query::Errors::QueryIsInErrorState at Base" 0replaceme
> (c++)"typeinfo name for core::trust::Store::Query at Base" 0.0.1+14.10.20140626.1
> (c++)"typeinfo name for core::trust::Store::Errors::ErrorResettingStore at Base" 0.0.1+14.10.20140626.1
> (c++)"typeinfo name for core::trust::Store at Base" 0.0.1+14.10.20140626.1
> (c++)"typeinfo name for core::trust::Token at Base" 0.0.1+14.10.20140626.1
> - (c++)"vtable for core::trust::Store::Query::Error::NoCurrentResult at Base" 0.0.1+14.10.20140626.1
> - (c++)"vtable for core::trust::Store::Query::Error::QueryIsInErrorState at Base" 0.0.1+14.10.20140626.1
> + (c++)"vtable for core::trust::mir::PromptProviderHelper at Base" 0replaceme
> + (c++)"vtable for core::trust::mir::ConnectionVirtualTable at Base" 0replaceme
> + (c++)"vtable for core::trust::mir::PromptSessionVirtualTable at Base" 0replaceme
> + (c++)"vtable for core::trust::mir::Agent at Base" 0replaceme
> + (c++)"vtable for core::trust::Agent at Base" 0replaceme
> + (c++)"vtable for core::trust::Store::Query::Errors::NoCurrentResult at Base" 0replaceme
> + (c++)"vtable for core::trust::Store::Query::Errors::QueryIsInErrorState at Base" 0replaceme
> (c++)"vtable for core::trust::Store::Query at Base" 0.0.1+14.10.20140626.1
> (c++)"vtable for core::trust::Store::Errors::ErrorResettingStore at Base" 0.0.1+14.10.20140626.1
> (c++)"vtable for core::trust::Store at Base" 0.0.1+14.10.20140626.1
>
> === added file 'debian/trust-store-tests.install'
> --- debian/trust-store-tests.install 1970-01-01 00:00:00 +0000
> +++ debian/trust-store-tests.install 2014-07-15 19:50:44 +0000
> @@ -0,0 +1,1 @@
> +usr/bin/trust-store-tests/*
>
> === added file 'include/core/trust/agent.h'
> --- include/core/trust/agent.h 1970-01-01 00:00:00 +0000
> +++ include/core/trust/agent.h 2014-07-15 19:50:44 +0000
> @@ -0,0 +1,57 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#ifndef CORE_TRUST_AGENT_H_
> +#define CORE_TRUST_AGENT_H_
> +
> +#include <core/trust/request.h>
> +#include <core/trust/visibility.h>
> +
> +namespace core
> +{
> +namespace trust
> +{
> +// Forward-declarations.
> +struct RequestParameters;
> +
> +/** @brief Abstracts user-prompting functionality. */
> +class CORE_TRUST_DLL_PUBLIC Agent
> +{
> +public:
> + /** @cond */
> + Agent() = default;
> + virtual ~Agent() = default;
> +
> + Agent(const Agent&) = delete;
> + Agent(Agent&&) = delete;
> + Agent& operator=(const Agent&) = delete;
> + Agent& operator=(Agent&&) = delete;
> + /** @endcond */
> +
> + /**
> + * @brief Presents the given request to the user, returning the user-provided answer.
> + * @param app_pid The process id of the requesting application.
> + * @param app_id The application Id of the requesting application.
> + * @param description Extended description of the trust request.
> + */
> + virtual Request::Answer prompt_user_for_request(pid_t app_pid, const std::string& app_id, const std::string& description) = 0;
> +};
> +}
> +}
> +
> +#endif // CORE_TRUST_AGENT_H_
>
> === added file 'include/core/trust/mir_agent.h'
> --- include/core/trust/mir_agent.h 1970-01-01 00:00:00 +0000
> +++ include/core/trust/mir_agent.h 2014-07-15 19:50:44 +0000
> @@ -0,0 +1,48 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#ifndef CORE_TRUST_MIR_AGENT_H_
> +#define CORE_TRUST_MIR_AGENT_H_
> +
> +#include <core/trust/visibility.h>
> +
> +#include <memory>
> +
> +// Forward declare the MirConnection type.
> +struct MirConnection;
> +
> +namespace core
> +{
> +namespace trust
> +{
> +// Forward declare the Agent interface.
> +class Agent;
> +
> +namespace mir
> +{
> +/**
> + * @brief create_agent_for_mir_connection creates a trust::Agent implementation leveraging Mir's trusted prompting API.
> + * @param connection An existing connection to a Mir instance.
> + * @throws std::logic_error if the connection object is NULL.
> + */
> +CORE_TRUST_DLL_PUBLIC std::shared_ptr<core::trust::Agent> create_agent_for_mir_connection(MirConnection* connection);
> +}
> +}
> +}
> +
> +#endif // CORE_TRUST_MIR_AGENT_H_
>
> === modified file 'include/core/trust/request.h'
> --- include/core/trust/request.h 2014-05-06 11:32:13 +0000
> +++ include/core/trust/request.h 2014-07-15 19:50:44 +0000
> @@ -24,6 +24,7 @@
> #include <cstdint>
>
> #include <chrono>
> +#include <memory>
> #include <ostream>
> #include <string>
>
> @@ -31,6 +32,10 @@
> {
> namespace trust
> {
> +// Forward declarations
> +class Agent;
> +class Store;
> +
> /**
> * @brief The Request struct encapsulates information about a trust request answered by the user.
> *
> @@ -59,7 +64,7 @@
> enum class Answer
> {
> denied, ///< Nope, I do not trust this application.
> - granted ///< Yup, I do trust this application.
> + granted, ///< Yup, I do trust this application.
> };
>
> /** The application id of the application that resulted in the request. */
> @@ -94,6 +99,77 @@
> * @return The output stream.
> */
> CORE_TRUST_DLL_PUBLIC std::ostream& operator<<(std::ostream& out, const Request& r);
> +
> +/** @brief Summarizes all parameters for processing a trust request. */
> +struct CORE_TRUST_DLL_PUBLIC RequestParameters
> +{
> + /** @brief The Agent implementation to dispatch a request to the user. */
> + std::shared_ptr<Agent> agent;
> + /** @brief The trust store to be used for caching purposes. */
> + std::shared_ptr<Store> store;
> + /** @brief The process id of the requesting application. */
> + pid_t application_pid;
> + /** @brief The id of the requesting application. */
> + std::string application_id;
> + /** @brief The service-specific feature identifier. */
> + std::uint64_t feature;
> + /** @brief An extended description that should be presented to the user on prompting. */
> + std::string description;
> +};
> +
> +/**
> + * @brief Processes an incoming trust-request by an application, tries to lookup a previous reply before
> + * issueing a prompt request via the given agent to the user. On return, the given trust-store is up-to-date.
> + *
> + * @throws std::exception To indicate that no conclusive answer could be resolved from either the store or
> + * the user. In that case, the state of the store instance passed in to the function is not altered.
> + *
> + * The following code snippet illustrates how to use the function:
> + *
> + * @code
> + * struct Service
> + * {
> + * static constexpr std::uint64_t default_feature = 0;
> + *
> + * void on_session_requested(const std::string& app_id)
> + * {
> + * core::trust::RequestParameters params
> + * {
> + * trust.agent,
> + * trust.store,
> + * app_id,
> + * default_feature,
> + * "Application " + app_id + " wants to access the example service."
> + * };
> + *
> + * switch(process_trust_request(params))
> + * {
> + * case core::trust::Request::Answer::granted:
> + * // Create session and get back to application with session credentials.
> + * break;
> + * case core::trust::Request::Answer::denied:
> + * // Deny session creation and inform application.
> + * break;
> + * }
> + * }
> + *
> + * struct
> + * {
> + * // We use Mir's trust session support to request the prompting UI.
> + * std::shared_ptr<core::trust::Agent> agent
> + * {
> + * core::trust::mir::make_agent_for_existing_connection(mir_connection)
> + * };
> + *
> + * std::shared_ptr<Store> store
> + * {
> + * core::trust::create_default_store("my.example.service");
> + * };
> + * } trust;
> + * };
> + * @endcode
> + */
> +CORE_TRUST_DLL_PUBLIC Request::Answer process_trust_request(const RequestParameters& params);
> }
> }
>
>
> === modified file 'include/core/trust/store.h'
> --- include/core/trust/store.h 2014-05-06 11:32:13 +0000
> +++ include/core/trust/store.h 2014-07-15 19:50:44 +0000
> @@ -28,6 +28,18 @@
>
> namespace core
> {
> +/**
> + * @brief Contains functionality for implementing Ubuntu's trust model.
> + *
> + * Ubuntu's trust model extends upon a strict confinement approach implemented
> + * on top of AppArmor. In this approach, applications are not trusted by default, and
> + * we assume a very negative view of the app world. That is, we assume that all apps
> + * are created with malicious intentions in mind, invading a user's privacy and wasting
> + * resources. For that, we severly limit an application's access to the system and
> + * provide trusted gates out of the confinement. These trusted gates, also called trusted helpers,
> + * ensure that the user is prompted for granting or denying trust to a specific application.
> + *
> + */
> namespace trust
> {
> /**
> @@ -36,9 +48,12 @@
> class CORE_TRUST_DLL_PUBLIC Store
> {
> public:
> + /** @brief All Store-specific error/exception types go here. */
> struct Errors
> {
> + /** @cond */
> Errors() = delete;
> + /** @endcond */
>
> /**
> * @brief Thrown if a store implementation could not access the persistence backend.
> @@ -70,9 +85,12 @@
> class Query
> {
> public:
> - struct Error
> + /** @brief All Query-specific error/exception types go here. */
> + struct Errors
> {
> - Error() = delete;
> + /** @cond */
> + Errors() = delete;
> + /** @endcond */
> /**
> * @brief Thrown if functionality of a query is accessed although the query is in error state.
> */
> @@ -165,9 +183,12 @@
> Store() = default;
> };
>
> -struct Error
> +/** @brief All core::trust-specific error/exception types go here. */
> +struct Errors
> {
> - Error() = delete;
> + /** @cond */
> + Errors() = delete;
> + /** @endcond */
>
> /**
> * @brief The ServiceNameMustNotBeEmpty is thrown if an empty service name
>
> === modified file 'src/CMakeLists.txt'
> --- src/CMakeLists.txt 2014-05-06 11:52:46 +0000
> +++ src/CMakeLists.txt 2014-07-15 19:50:44 +0000
> @@ -14,6 +14,10 @@
> #
> # Authored by: Thomas Voss <thomas.voss at canonical.com>
>
> +find_package(Qt5Core REQUIRED)
> +find_package(Qt5Qml REQUIRED)
> +find_package(Qt5Quick REQUIRED)
> +
> pkg_check_modules(DBUS_CPP dbus-cpp REQUIRED)
> pkg_check_modules(DBUS dbus-1 REQUIRED)
> pkg_check_modules(SQLITE3 sqlite3 REQUIRED)
> @@ -24,14 +28,51 @@
> ${SQLITE3_INCLUDE_DIRS}
> )
>
> +# Make sure Qt does not inject evil macros like 'signals' and 'slots'.
> +add_definitions(-DQT_NO_KEYWORDS)
> +# We need this for building the Qt-based prompt UI
> +set(CMAKE_AUTOMOC ON)
> +set(CMAKE_INCLUDE_CURRENT_DIR ON)
> +
> add_library(
> trust-store SHARED
>
> core/trust/expose.cpp
> core/trust/request.cpp
> core/trust/resolve.cpp
> +
> + # The default implementation leverages SQLite3 to persist
> + # requests.
> core/trust/impl/sqlite3/store.cpp
> -)
> +
> + # An agent-implementation leveraging Mir's trusted prompting support
> + # to prompt the user for trusting an application to access a trusted
> + # system service.
> + core/trust/mir/agent.cpp
> +)
> +
> +configure_file(
> + ${CMAKE_CURRENT_SOURCE_DIR}/core/trust/mir/config.h.in
> + ${CMAKE_CURRENT_BINARY_DIR}/core/trust/mir/config.h @ONLY)
> +
> +configure_file(
> + ${CMAKE_CURRENT_SOURCE_DIR}/core/trust/mir/prompt_config.h.in
> + ${CMAKE_CURRENT_BINARY_DIR}/core/trust/mir/prompt_config.h @ONLY)
> +
> +include_directories(${CMAKE_CURRENT_BINARY_DIR}/core/trust/mir/)
> +
> +add_executable(
> + trust-prompt
> +
> + core/trust/mir/prompt_main.cpp
> +)
> +
> +set_target_properties(
> + trust-prompt
> + PROPERTIES INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR}/core/trust/mir
> +)
> +
> +qt5_use_modules(trust-prompt Core Gui Qml Quick)
>
> target_link_libraries(
> trust-store
> @@ -40,6 +81,9 @@
>
> ${Boost_LIBRARIES}
> ${DBUS_LIBRARIES}
> + ${MIR_CLIENT_LDFLAGS}
> + ${MIR_COMMON_LDFLAGS}
> + ${PROCESS_CPP_LDFLAGS}
> ${SQLITE3_LIBRARIES}
> )
>
> @@ -63,3 +107,8 @@
> TARGETS trust-store
> LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
> )
> +
> +install(
> + TARGETS trust-prompt
> + RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}
> +)
>
> === modified file 'src/core/trust/dbus_interface.h'
> --- src/core/trust/dbus_interface.h 2013-12-21 08:05:08 +0000
> +++ src/core/trust/dbus_interface.h 2014-07-15 19:50:44 +0000
> @@ -63,17 +63,12 @@
> {
> struct Store
> {
> - static std::string& mutable_name()
> + static const std::string& name()
> {
> - static std::string s;
> + static std::string s{"com.ubuntu.trust.store"};
> return s;
> }
>
> - static const std::string& name()
> - {
> - return mutable_name();
> - }
> -
> struct Error
> {
> struct AddingRequest
>
> === modified file 'src/core/trust/expose.cpp'
> --- src/core/trust/expose.cpp 2014-05-06 11:32:13 +0000
> +++ src/core/trust/expose.cpp 2014-07-15 19:50:44 +0000
> @@ -69,13 +69,15 @@
> std::map<Key, Value> map;
> };
>
> -struct Token : public core::trust::Token, public dbus::Skeleton<core::trust::dbus::Store>
> +struct Token : public core::trust::Token
> {
> - Token(const std::shared_ptr<dbus::Bus>& bus,
> + Token(const std::string& service_name,
> + const std::shared_ptr<dbus::Bus>& bus,
> const std::shared_ptr<core::trust::Store>& store)
> - : dbus::Skeleton<core::trust::dbus::Store>(bus),
> - store(store),
> - object(access_service()->add_object_for_path(dbus::types::ObjectPath::root()))
> + : store(store),
> + bus(bus),
> + service(dbus::Service::add_service(bus, service_name)),
> + object(service->add_object_for_path(dbus::types::ObjectPath::root()))
> {
> object->install_method_handler<core::trust::dbus::Store::Add>([this](const core::dbus::Message::Ptr& msg)
> {
> @@ -97,15 +99,18 @@
> handle_remove_query(msg);
> });
>
> - worker = std::move(std::thread([this](){access_bus()->run();}));
> + worker = std::move(std::thread([this](){Token::bus->run();}));
> }
>
> ~Token()
> {
> object->uninstall_method_handler<core::trust::dbus::Store::Add>();
> object->uninstall_method_handler<core::trust::dbus::Store::Reset>();
> -
> - access_bus()->stop();
> + object->uninstall_method_handler<core::trust::dbus::Store::AddQuery>();
> + object->uninstall_method_handler<core::trust::dbus::Store::RemoveQuery>();
> +
> + bus->stop();
> +
> if (worker.joinable())
> worker.join();
> }
> @@ -125,12 +130,12 @@
> core::trust::dbus::Store::Error::AddingRequest::name(),
> e.what());
>
> - access_bus()->send(error);
> + bus->send(error);
> return;
> }
>
> auto reply = dbus::Message::make_method_return(msg);
> - access_bus()->send(reply);
> + bus->send(reply);
> }
>
> void handle_reset(const core::dbus::Message::Ptr& msg)
> @@ -145,11 +150,11 @@
> core::trust::dbus::Store::Error::ResettingStore::name(),
> e.what());
>
> - access_bus()->send(error);
> + bus->send(error);
> }
>
> auto reply = dbus::Message::make_method_return(msg);
> - access_bus()->send(reply);
> + bus->send(reply);
> }
>
> void handle_add_query(const core::dbus::Message::Ptr& msg)
> @@ -160,16 +165,16 @@
> {
> core::dbus::types::ObjectPath path{"/queries/" + std::to_string(query_counter++)};
> auto query = store->query();
> - auto object = access_service()->add_object_for_path(path);
> - auto bus = access_bus();
> - object->install_method_handler<core::trust::dbus::Store::Query::All>([bus, query](const core::dbus::Message::Ptr& msg)
> + auto object = service->add_object_for_path(path);
> +
> + object->install_method_handler<core::trust::dbus::Store::Query::All>([this, query](const core::dbus::Message::Ptr& msg)
> {
> query->all();
>
> auto reply = core::dbus::Message::make_method_return(msg);
> bus->send(reply);
> });
> - object->install_method_handler<core::trust::dbus::Store::Query::Current>([bus, query](const core::dbus::Message::Ptr& msg)
> + object->install_method_handler<core::trust::dbus::Store::Query::Current>([this, query](const core::dbus::Message::Ptr& msg)
> {
> try
> {
> @@ -177,7 +182,7 @@
> auto reply = core::dbus::Message::make_method_return(msg);
> reply->writer() << request;
> bus->send(reply);
> - } catch(const core::trust::Store::Query::Error::NoCurrentResult& e)
> + } catch(const core::trust::Store::Query::Errors::NoCurrentResult& e)
> {
> auto error = core::dbus::Message::make_error(
> msg,
> @@ -187,21 +192,21 @@
> bus->send(error);
> }
> });
> - object->install_method_handler<core::trust::dbus::Store::Query::Erase>([bus, query](const core::dbus::Message::Ptr& msg)
> + object->install_method_handler<core::trust::dbus::Store::Query::Erase>([this, query](const core::dbus::Message::Ptr& msg)
> {
> query->erase();
>
> auto reply = core::dbus::Message::make_method_return(msg);
> bus->send(reply);
> });
> - object->install_method_handler<core::trust::dbus::Store::Query::Execute>([bus, query](const core::dbus::Message::Ptr& msg)
> + object->install_method_handler<core::trust::dbus::Store::Query::Execute>([this, query](const core::dbus::Message::Ptr& msg)
> {
> query->execute();
>
> auto reply = core::dbus::Message::make_method_return(msg);
> bus->send(reply);
> });
> - object->install_method_handler<core::trust::dbus::Store::Query::ForAnswer>([bus, query](const core::dbus::Message::Ptr& msg)
> + object->install_method_handler<core::trust::dbus::Store::Query::ForAnswer>([this, query](const core::dbus::Message::Ptr& msg)
> {
> core::trust::Request::Answer a; msg->reader() >> a;
> query->for_answer(a);
> @@ -209,7 +214,7 @@
> auto reply = core::dbus::Message::make_method_return(msg);
> bus->send(reply);
> });
> - object->install_method_handler<core::trust::dbus::Store::Query::ForApplicationId>([bus, query](const core::dbus::Message::Ptr& msg)
> + object->install_method_handler<core::trust::dbus::Store::Query::ForApplicationId>([this, query](const core::dbus::Message::Ptr& msg)
> {
> std::string app_id; msg->reader() >> app_id;
> query->for_application_id(app_id);
> @@ -217,7 +222,7 @@
> auto reply = core::dbus::Message::make_method_return(msg);
> bus->send(reply);
> });
> - object->install_method_handler<core::trust::dbus::Store::Query::ForFeature>([bus, query](const core::dbus::Message::Ptr& msg)
> + object->install_method_handler<core::trust::dbus::Store::Query::ForFeature>([this, query](const core::dbus::Message::Ptr& msg)
> {
> std::uint64_t feature; msg->reader() >> feature;
> query->for_feature(feature);
> @@ -225,7 +230,7 @@
> auto reply = core::dbus::Message::make_method_return(msg);
> bus->send(reply);
> });
> - object->install_method_handler<core::trust::dbus::Store::Query::ForInterval>([bus, query](const core::dbus::Message::Ptr& msg)
> + object->install_method_handler<core::trust::dbus::Store::Query::ForInterval>([this, query](const core::dbus::Message::Ptr& msg)
> {
> std::tuple<std::int64_t, std::int64_t> interval; msg->reader() >> interval;
>
> @@ -237,14 +242,14 @@
> auto reply = core::dbus::Message::make_method_return(msg);
> bus->send(reply);
> });
> - object->install_method_handler<core::trust::dbus::Store::Query::Next>([bus, query](const core::dbus::Message::Ptr& msg)
> + object->install_method_handler<core::trust::dbus::Store::Query::Next>([this, query](const core::dbus::Message::Ptr& msg)
> {
> query->next();
>
> auto reply = core::dbus::Message::make_method_return(msg);
> bus->send(reply);
> });
> - object->install_method_handler<core::trust::dbus::Store::Query::Status>([bus, query](const core::dbus::Message::Ptr& msg)
> + object->install_method_handler<core::trust::dbus::Store::Query::Status>([this, query](const core::dbus::Message::Ptr& msg)
> {
> auto reply = core::dbus::Message::make_method_return(msg);
> reply->writer() << query->status();
> @@ -255,7 +260,7 @@
>
> auto reply = dbus::Message::make_method_return(msg);
> reply->writer() << path;
> - access_bus()->send(reply);
> + bus->send(reply);
> } catch(const std::runtime_error& e)
> {
> auto error = core::dbus::Message::make_error(
> @@ -263,7 +268,7 @@
> core::trust::dbus::Store::Error::CreatingQuery::name(),
> e.what());
>
> - access_bus()->send(error);
> + bus->send(error);
> }
> }
>
> @@ -275,17 +280,19 @@
> query_store.erase(path);
>
> auto reply = dbus::Message::make_method_return(msg);
> - access_bus()->send(reply);
> + bus->send(reply);
> } catch(...)
> {
> }
>
> auto reply = dbus::Message::make_method_return(msg);
> reply->writer();
> - access_bus()->send(reply);
> + bus->send(reply);
> }
>
> std::shared_ptr<core::trust::Store> store;
> + std::shared_ptr<dbus::Bus> bus;
> + std::shared_ptr<dbus::Service> service;
> std::shared_ptr<dbus::Object> object;
> std::thread worker;
>
> @@ -301,10 +308,17 @@
> const std::string& name)
> {
> if (name.empty())
> - throw Error::ServiceNameMustNotBeEmpty{};
> + throw Errors::ServiceNameMustNotBeEmpty{};
>
> - core::trust::dbus::Store::mutable_name() = "com.ubuntu.trust.store." + name;
> - return std::move(std::unique_ptr<core::trust::Token>(new detail::Token{bus, store}));
> + return std::move(std::unique_ptr<core::trust::Token>
> + {
> + new detail::Token
> + {
> + "com.ubuntu.trust.store." + name,
> + bus,
> + store
> + }
> + });
> }
>
> std::unique_ptr<core::trust::Token>
>
> === modified file 'src/core/trust/impl/sqlite3/store.cpp'
> --- src/core/trust/impl/sqlite3/store.cpp 2014-06-26 07:09:51 +0000
> +++ src/core/trust/impl/sqlite3/store.cpp 2014-07-15 19:50:44 +0000
> @@ -18,6 +18,8 @@
>
> #include <core/trust/store.h>
>
> +#include <core/posix/this_process.h>
> +
> #include <sqlite3.h>
>
> #include <cstring>
> @@ -32,18 +34,14 @@
> {
> std::string home()
> {
> - return std::string{::getenv("HOME")};
> + return core::posix::this_process::env::get_or_throw("HOME");
> }
>
> std::string runtime_persistent_data_dir()
> {
> - char* value = ::getenv("XDG_DATA_HOME");
> - if (!value || value[0] == '0')
> - {
> - return std::string{home() + "/.local/share"};
> - }
> -
> - return std::string{value};
> + return core::posix::this_process::env::get(
> + "XDG_DATA_HOME",
> + home() + "/.local/share");
> }
>
> struct Directory
> @@ -592,9 +590,9 @@
> {
> switch(d.status)
> {
> - case Status::error: throw Error::QueryIsInErrorState{};
> - case Status::eor: throw Error::NoCurrentResult{};
> - case Status::armed: throw Error::NoCurrentResult{};
> + case Status::error: throw Errors::QueryIsInErrorState{};
> + case Status::eor: throw Errors::NoCurrentResult{};
> + case Status::armed: throw Errors::NoCurrentResult{};
> default:
> {
> trust::Request request
> @@ -719,7 +717,7 @@
> std::shared_ptr<core::trust::Store> core::trust::create_default_store(const std::string& service_name)
> {
> if (service_name.empty())
> - throw core::trust::Error::ServiceNameMustNotBeEmpty();
> + throw core::trust::Errors::ServiceNameMustNotBeEmpty();
>
> return std::shared_ptr<trust::Store>{new sqlite::Store(service_name)};
> }
>
> === added directory 'src/core/trust/mir'
> === added file 'src/core/trust/mir/agent.cpp'
> --- src/core/trust/mir/agent.cpp 1970-01-01 00:00:00 +0000
> +++ src/core/trust/mir/agent.cpp 2014-07-15 19:50:44 +0000
> @@ -0,0 +1,268 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#include "agent.h"
> +
> +#include "prompt_main.h"
> +
> +namespace mir = core::trust::mir;
> +
> +// Invoked whenever a request for creation of pre-authenticated fds succeeds.
> +void mir::PromptSessionVirtualTable::mir_client_fd_callback(MirPromptSession */*prompt_session*/, size_t count, int const* fds, void* context)
> +{
> + if (count == 0)
> + return;
> +
> + auto ctxt = static_cast<mir::PromptSessionVirtualTable::Context*>(context);
> +
> + if (not ctxt)
> + return;
> +
> + ctxt->fd = fds[0];
> +}
> +
> +mir::PromptSessionVirtualTable::PromptSessionVirtualTable(MirPromptSession* prompt_session)
> + : prompt_session(prompt_session)
> +{
> +}
> +
> +int mir::PromptSessionVirtualTable::new_fd_for_prompt_provider()
> +{
> + static const unsigned int fd_count = 1;
> +
> + mir::PromptSessionVirtualTable::Context context;
> +
> + mir_wait_for(mir_prompt_session_new_fds_for_prompt_providers(
> + prompt_session,
> + fd_count,
> + PromptSessionVirtualTable::mir_client_fd_callback,
> + &context));
> +
> + if (context.fd == Context::invalid_fd) throw std::runtime_error
> + {
> + "Could not acquire pre-authenticated file descriptors for Mir prompt session."
> + };
> +
> + return context.fd;
> +}
> +
> +bool mir::PromptSessionVirtualTable::add_prompt_provider_sync(pid_t prompt_provider_pid)
> +{
> + return mir_prompt_session_add_prompt_provider_sync(prompt_session, prompt_provider_pid);
> +}
> +
> +void mir::PromptSessionVirtualTable::release_sync()
> +{
> + mir_prompt_session_release_sync(prompt_session);
> +}
> +
> +mir::ConnectionVirtualTable::ConnectionVirtualTable(MirConnection* connection)
> + : connection{connection}
> +{
> +}
> +
> +mir::PromptSessionVirtualTable::Ptr mir::ConnectionVirtualTable::create_prompt_session_sync(
> + // The process id of the requesting app/service
> + pid_t app_pid,
> + // Callback handling prompt session state changes.
> + mir_prompt_session_state_change_callback cb,
> + // Callback context
> + void* context)
> +{
> + return PromptSessionVirtualTable::Ptr
> + {
> + new PromptSessionVirtualTable
> + {
> + mir_connection_create_prompt_session_sync(connection, app_pid, cb, context)
> + }
> + };
> +}
> +
> +mir::PromptProviderHelper::PromptProviderHelper(
> + const mir::PromptProviderHelper::CreationArguments& args) : creation_arguments(args)
> +{
> +}
> +
> +core::posix::ChildProcess mir::PromptProviderHelper::exec_prompt_provider_with_arguments(
> + const mir::PromptProviderHelper::InvocationArguments& args)
> +{
> + static auto child_setup = []() {};
> +
> + std::vector<std::string> argv
> + {
> + "--" + std::string{core::trust::mir::cli::option_server_socket} + "=fd://" + std::to_string(args.fd),
> + "--" + std::string{core::trust::mir::cli::option_title} + "=" + args.application_id,
> + "--" + std::string{core::trust::mir::cli::option_description} + "=" + args.description
> + };
> +
> + // We just copy the environment
> + std::map<std::string, std::string> env;
> + core::posix::this_process::env::for_each([&env](const std::string& key, const std::string& value)
> + {
> + env.insert(std::make_pair(key, value));
> + });
> +
> +
> + auto result = core::posix::exec(creation_arguments.path_to_helper_executable,
What files are open at this point in the process? Are they all properly labeled close-on-exec?
> + argv,
> + env,
> + core::posix::StandardStream::empty,
> + child_setup);
> +
> + return result;
> +}
> +
> +void mir::Agent::on_trust_session_changed_state(
> + // The prompt session instance that just changed state.
> + MirPromptSession* /*prompt_provider*/,
> + // The new state of the prompt session instance.
> + MirPromptSessionState state,
> + // The context of type context.
> + void* context)
> +{
> + if (mir_prompt_session_state_stopped != state)
> + return;
> +
> + auto ctxt = static_cast<mir::Agent::OnTrustSessionStateChangedCallbackContext*>(context);
> +
> + if (not ctxt)
> + return;
> +
> + std::error_code ec;
> + // If the trust session ended (for whatever reason), we send a SIG_KILL to the
> + // prompt provider process. We hereby ensure that we never return Answer::granted
> + // unless the prompt provider cleanly exited prior to the trust session stopping.
> + ctxt->prompt_provider_process.send_signal(core::posix::Signal::sig_kill, ec);
> + // The required wait for the child process happens in prompt_user_for_request(...).
> + // TODO(tvoss): We should log ec in case of errors.
> +}
> +
> +std::function<core::trust::Request::Answer(const core::posix::wait::Result&)> mir::Agent::translator_only_accepting_exit_status_success()
> +{
> + return [](const core::posix::wait::Result& result) -> core::trust::Request::Answer
> + {
> + // We now analyze the result of the process execution.
> + if (core::posix::wait::Result::Status::exited != result.status) throw std::logic_error
> + {
> + "The prompt provider process was signaled or stopped, "
> + "unable to determine a conclusive answer from the user"
> + };
> +
> + // If the child process did not exit cleanly, we deny access to the resource.
> + if (core::posix::exit::Status::failure == result.detail.if_exited.status)
> + return core::trust::Request::Answer::denied;
> +
> + return core::trust::Request::Answer::granted;
> + };
> +}
> +
> +mir::Agent::Agent(
> + // VTable object providing access to Mir's trusted prompting functionality.
> + const mir::ConnectionVirtualTable::Ptr& connection_vtable,
> + // Exec helper for starting up prompt provider child processes with the correct setup
> + // of command line arguments and environment variables.
> + const mir::PromptProviderHelper::Ptr& exec_helper,
> + // A translator function for mapping child process exit states to trust::Request answers.
> + const std::function<core::trust::Request::Answer(const core::posix::wait::Result&)>& translator)
> + : connection_vtable(connection_vtable),
> + exec_helper(exec_helper),
> + translator(translator)
> +{
> +}
> +
> +// From core::trust::Agent:
> +core::trust::Request::Answer mir::Agent::prompt_user_for_request(pid_t app_pid, const std::string& app_id, const std::string& description)
> +{
> + // We initialize our callback context with an invalid child-process for setup
> + // purposes. Later on, once we have acquired a pre-authenticated fd for the
> + // prompt provider, we exec the actual provider in a child process and replace the
> + // instance here.
> + mir::Agent::OnTrustSessionStateChangedCallbackContext cb_context
> + {
> + core::posix::ChildProcess::invalid()
> + };
> +
> + // We ensure that the prompt session is always released cleanly, either on return or on throw.
> + struct Scope
> + {
> + ~Scope() { prompt_session->release_sync(); }
> + mir::PromptSessionVirtualTable::Ptr prompt_session;
> + } scope
> + {
> + // We setup the prompt session and wire up to our own internal callback helper.
> + connection_vtable->create_prompt_session_sync(
> + app_pid,
> + Agent::on_trust_session_changed_state,
> + &cb_context)
> + };
> +
> + // Acquire a new fd for the prompt provider.
> + auto fd = scope.prompt_session->new_fd_for_prompt_provider();
> +
> + // And prepare the actual execution in a child process.
> + mir::PromptProviderHelper::InvocationArguments args
> + {
> + fd,
> + app_id,
> + description
> + };
> +
> + // Ask the helper to fire up the prompt provider.
> + cb_context.prompt_provider_process = exec_helper->exec_prompt_provider_with_arguments(args);
> + // And subsequently wait for it to finish.
> + auto result = cb_context.prompt_provider_process.wait_for(core::posix::wait::Flags::untraced);
> +
> + return translator(result);
> +}
> +
> +bool mir::operator==(const mir::PromptProviderHelper::InvocationArguments& lhs, const mir::PromptProviderHelper::InvocationArguments& rhs)
> +{
> + return std::tie(lhs.application_id, lhs.description, lhs.fd) == std::tie(rhs.application_id, rhs.description, rhs.fd);
> +}
> +
> +#include <core/trust/mir_agent.h>
> +
> +#include "config.h"
> +
> +std::shared_ptr<core::trust::Agent> mir::create_agent_for_mir_connection(MirConnection* connection)
> +{
> + return mir::Agent::Ptr
> + {
> + new mir::Agent
> + {
> + mir::ConnectionVirtualTable::Ptr
> + {
> + new mir::ConnectionVirtualTable
> + {
> + connection
> + }
> + },
> + mir::PromptProviderHelper::Ptr
> + {
> + new mir::PromptProviderHelper
> + {
> + mir::PromptProviderHelper::CreationArguments
> + {
> + core::trust::mir::trust_prompt_executable_in_lib_dir
> + }
> + }
> + },
> + mir::Agent::translator_only_accepting_exit_status_success()
> + }
> + };
> +}
>
> === added file 'src/core/trust/mir/agent.h'
> --- src/core/trust/mir/agent.h 1970-01-01 00:00:00 +0000
> +++ src/core/trust/mir/agent.h 2014-07-15 19:50:44 +0000
> @@ -0,0 +1,200 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#ifndef CORE_TRUST_MIR_MIR_AGENT_H_
> +#define CORE_TRUST_MIR_MIR_AGENT_H_
> +
> +#include <core/trust/agent.h>
> +
> +#include <core/posix/child_process.h>
> +#include <core/posix/exec.h>
> +
> +#include <mirclient/mir_toolkit/mir_client_library.h>
> +#include <mirclient/mir_toolkit/mir_prompt_session.h>
> +
> +#include <condition_variable>
> +#include <functional>
> +#include <mutex>
> +
> +namespace core
> +{
> +namespace trust
> +{
> +namespace mir
> +{
> +// We wrap the Mir prompt session API into a struct to
> +// ease with testing and mocking.
> +struct CORE_TRUST_DLL_PUBLIC PromptSessionVirtualTable
> +{
> + // Just a convenience typedef
> + typedef std::shared_ptr<PromptSessionVirtualTable> Ptr;
> +
> + // Just a helper struct to be passed to client_fd_callbacks.
> + struct Context
> + {
> + // Marks the value of an invalid fd.
> + static constexpr const int invalid_fd{-1};
> + // The fd contained within this context instance.
> + int fd{invalid_fd};
> + };
> +
> + // Invoked whenever a request for creation of pre-authenticated fds succeeds.
> + static void mir_client_fd_callback(MirPromptSession */*prompt_session*/, size_t count, int const* fds, void* context);
> +
> + // Create a MirPromptSessionVirtualTable for a given prompt session instance.
> + // Please note that no change of ownwership is happening here. Instead, we expect
> + // the calling code to handle object lifetimes.
> + PromptSessionVirtualTable(MirPromptSession* prompt_session);
> + virtual ~PromptSessionVirtualTable() = default;
> +
> + // Requests a new, pre-authenticated fd for associating prompt providers.
> + // Returns the fd or throws std::runtime_error.
> + virtual int new_fd_for_prompt_provider();
> +
> + // Adds a prompt provider process to the prompting session, identified by its PID.
> + // Returns true if addition of the prompt provider succeeded.
> + virtual bool add_prompt_provider_sync(pid_t prompt_provider_pid);
> +
> + // Finalizes and releases the given prompt session instance.
> + virtual void release_sync();
> +
> + // The underlying prompt session instance.
> + MirPromptSession* prompt_session;
> +};
> +
> +struct CORE_TRUST_DLL_PUBLIC ConnectionVirtualTable
> +{
> + // Just a convenience typedef
> + typedef std::shared_ptr<ConnectionVirtualTable> Ptr;
> +
> + // Create a new instance of MirConnectionVirtualTable
> + // using a pre-existing connection to Mir. Please note
> + // that we do not take ownership of the MirConnection but
> + // expect the calling code to coordinate object lifetimes.
> + ConnectionVirtualTable(MirConnection* connection);
> + virtual ~ConnectionVirtualTable() = default;
> +
> + // Creates a new trusted prompt session instance synchronously.
> + virtual PromptSessionVirtualTable::Ptr create_prompt_session_sync(
> + // The process id of the requesting app/service
> + pid_t app_pid,
> + // Callback handling prompt session state changes.
> + mir_prompt_session_state_change_callback cb,
> + // Callback context
> + void* context);
> +
> + // We do not take over ownership of the connection object.
> + MirConnection* connection;
> +};
> +
> +// Abstracts common functionality required for runninging external helpers.
> +struct CORE_TRUST_DLL_PUBLIC PromptProviderHelper
> +{
> + // Just a convenience typedef.
> + typedef std::shared_ptr<PromptProviderHelper> Ptr;
> +
> + // Creation-time arguments.
> + struct CreationArguments
> + {
> + // Path to the helper executable that provides the prompting UI.
> + std::string path_to_helper_executable;
> + };
> +
> + // Invocation arguments for exec_prompt_provider_with_arguments
> + struct InvocationArguments
> + {
> + // The pre-authenticated fd that the helper
> + // should use for connecting to Mir.
> + int fd;
> + // The application id of the requesting app.
> + std::string application_id;
> + // The extended description that should be presented to the user.
> + std::string description;
> + };
> +
> + PromptProviderHelper(const CreationArguments& args);
> + virtual ~PromptProviderHelper() = default;
> +
> + // Execs the executable provided at construction time for the arguments and
> + // returns the corresponding child process.
> + virtual core::posix::ChildProcess exec_prompt_provider_with_arguments(const InvocationArguments& args);
> +
> + // We store all arguments passed at construction.
> + CreationArguments creation_arguments;
> +};
> +
> +// Implements the trust::Agent interface and dispatches calls to a helper
> +// prompt provider, tying it together with the requesting service and app
> +// by leveraging Mir's trusted session/prompting support.
> +struct CORE_TRUST_DLL_PUBLIC Agent : public core::trust::Agent
> +{
> + // Convenience typedef
> + typedef std::shared_ptr<Agent> Ptr;
> +
> + // Helper struct for injecting state into on_trust_changed_state_state callbacks.
> + // Used in prompt_user_for_request to wait for the trust session to be stopped.
> + struct OnTrustSessionStateChangedCallbackContext
> + {
> + // The process that provides the prompting UI.
> + core::posix::ChildProcess prompt_provider_process;
> + };
> +
> + // Handles state changes of trust sessions and sigkills the child process
> + // provided in context (of type OnTrustSessionStateChangedCallbackContext).
> + static void on_trust_session_changed_state(
> + // The prompt session instance that just changed state.
> + MirPromptSession* prompt_provider,
> + // The new state of the prompt session instance.
> + MirPromptSessionState state,
> + // The context of type context.
> + void* context);
> +
> + // Returns a wait result -> trust::Request::Answer translator that only returns Answer::granted if
> + // the prompt provider child process exits cleanly with status success.
> + // Throws std::logic_error if the process did not exit but was signaled.
> + static std::function<core::trust::Request::Answer(const core::posix::wait::Result&)> translator_only_accepting_exit_status_success();
> +
> + // Creates a new MirAgent instance with the given MirConnectionVirtualTable instance.
> + Agent(
> + // VTable object providing access to Mir's trusted prompting functionality.
> + const ConnectionVirtualTable::Ptr& connection_vtable,
> + // Exec helper for starting up prompt provider child processes with the correct setup
> + // of command line arguments and environment variables.
> + const PromptProviderHelper::Ptr& exec_helper,
> + // A translator function for mapping child process exit states to trust::Request answers.
> + const std::function<core::trust::Request::Answer(const core::posix::wait::Result&)>& translator);
> +
> + // From core::trust::Agent:
> + // Throws a std::logic_error if anything unforeseen happens during execution, thus
> + // indicating that no conclusive answer could be obtained from the user.
> + core::trust::Request::Answer prompt_user_for_request(pid_t app_pid, const std::string& app_id, const std::string& description) override;
> +
> + // The connection VTable used for creating trusted prompting sessions.
> + ConnectionVirtualTable::Ptr connection_vtable;
> + // Execution helper for firing up prompt provider executables.
> + PromptProviderHelper::Ptr exec_helper;
> + // Translator instance.
> + std::function<core::trust::Request::Answer(const core::posix::wait::Result&)> translator;
> +};
> +
> +CORE_TRUST_DLL_PUBLIC bool operator==(const PromptProviderHelper::InvocationArguments&, const PromptProviderHelper::InvocationArguments&);
> +}
> +}
> +}
> +
> +#endif // CORE_TRUST_MIR_MIR_AGENT_H_
>
> === added file 'src/core/trust/mir/config.h.in'
> --- src/core/trust/mir/config.h.in 1970-01-01 00:00:00 +0000
> +++ src/core/trust/mir/config.h.in 2014-07-15 19:50:44 +0000
> @@ -0,0 +1,31 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +namespace core
> +{
> +namespace trust
> +{
> +namespace mir
> +{
> +static constexpr const char* trust_prompt_executable_in_lib_dir
> +{
> + "@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/trust-prompt"
> +};
> +}
> +}
> +}
>
> === added file 'src/core/trust/mir/prompt_config.h.in'
> --- src/core/trust/mir/prompt_config.h.in 1970-01-01 00:00:00 +0000
> +++ src/core/trust/mir/prompt_config.h.in 2014-07-15 19:50:44 +0000
> @@ -0,0 +1,34 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#include <QtCore/QCoreApplication>
> +#include <QtCore/QDir>
> +
> +inline bool isRunningInstalled() {
> + static bool installed = (QCoreApplication::applicationDirPath() ==
> + QDir(("@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@")).canonicalPath());
> + return installed;
> +}
> +
> +inline QString appDirectory() {
> + if (isRunningInstalled()) {
> + return QString("@CMAKE_INSTALL_PREFIX@/@APP_DIR@/");
> + } else {
> + return QString("@CMAKE_SOURCE_DIR@/src/core/trust/mir/");
> + }
> +}
>
> === added file 'src/core/trust/mir/prompt_main.cpp'
> --- src/core/trust/mir/prompt_main.cpp 1970-01-01 00:00:00 +0000
> +++ src/core/trust/mir/prompt_main.cpp 2014-07-15 19:50:44 +0000
> @@ -0,0 +1,130 @@
> +/*
> + * Copyright (C) 2012-2013 Canonical, Ltd.
> + *
> + * Authors:
> + * Olivier Tilloy <olivier.tilloy at canonical.com>
> + * Nick Dedekind <nick.dedekind at canonical.com>
> + * Thomas Voß <thomas.voss at canonical.com>
> + *
> + * This file is part of dialer-app.
> + *
> + * dialer-app is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * dialer-app is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +// Qt
> +#include <QCommandLineParser>
> +#include <QGuiApplication>
> +
> +#include <QQuickView>
> +#include <QQuickItem>
> +#include <QQmlContext>
> +#include <QQmlEngine>
> +
> +#include "prompt_config.h"
> +#include "prompt_main.h"
> +
> +namespace core
> +{
> +namespace trust
> +{
> +namespace mir
> +{
> +class Prompt : public QGuiApplication
> +{
> + Q_OBJECT
> +public:
> + Prompt(int & argc, char ** argv)
> + : QGuiApplication(argc, argv)
> + {
> + }
> +
> +public Q_SLOTS:
> + void quit(int code)
> + {
> + exit(code);
> + }
> +};
> +}
> +}
> +}
> +
> +int main(int argc, char** argv)
> +{
> + QStringList argl;
> + for (int i = 0 ; i < argc; i++) if (argv[i]) argl << argv[i];
> +
> + QCommandLineParser parser;
> + parser.setApplicationDescription("Trusted helper prompt");
> + parser.addOption(QCommandLineOption
> + {
> + core::trust::mir::cli::option_server_socket,
> + "Path to the server socket to connect to.",
> + "socket"
> + });
> + parser.addOption(QCommandLineOption
> + {
> + core::trust::mir::cli::option_title,
> + "The title of the trust prompt.",
> + "title"
> + });
> + parser.addOption(QCommandLineOption
> + {
> + core::trust::mir::cli::option_description,
> + "An extended description for informing the user about implications of granting trust.",
> + "description"
> + });
> +
> + // Explicitly calling parse instead of process here to prevent the
> + // application exiting due to unknown command line arguments.
> + parser.parse(argl);
> +
> + // Let's validate the arguments
> + if (!parser.isSet(core::trust::mir::cli::option_title))
> + abort(); // We have to call abort here to make sure that we get signalled.
> +
> + if (parser.isSet(core::trust::mir::cli::option_server_socket))
> + ::setenv(core::trust::mir::env::option_mir_socket,
> + qPrintable(parser.value(core::trust::mir::cli::option_server_socket)), 1);
> +
> + QGuiApplication::setApplicationName("Trusted Helper Prompt");
> + core::trust::mir::Prompt app(argc, argv);
> + QQuickView* view = new QQuickView();
> + view->setResizeMode(QQuickView::SizeRootObjectToView);
> + view->setTitle(QGuiApplication::applicationName());
> +
> + // Make some properties known to the root context.
> + view->rootContext()->setContextProperty("dialog", &app);
> + // The title of the dialog.
> + view->rootContext()->setContextProperty(
> + core::trust::mir::cli::option_title,
> + parser.value(core::trust::mir::cli::option_title));
> + // The description of the dialog.
> + if (parser.isSet(core::trust::mir::cli::option_description))
> + view->rootContext()->setContextProperty(
> + core::trust::mir::cli::option_description,
> + parser.value(core::trust::mir::cli::option_description));
> +
> + // Point the engine to the right directory. Please note that
> + // the respective value changes with the installation state.
> + view->engine()->setBaseUrl(QUrl::fromLocalFile(appDirectory()));
> +
> + view->setSource(QUrl::fromLocalFile("prompt_main.qml"));
> + view->show();
> +
> + QObject::connect(view->rootObject(), SIGNAL(quit(int)), &app, SLOT(quit(int)));
> +
> + return app.exec();
> +}
> +
> +#include "prompt_main.moc"
> +
>
> === added file 'src/core/trust/mir/prompt_main.h'
> --- src/core/trust/mir/prompt_main.h 1970-01-01 00:00:00 +0000
> +++ src/core/trust/mir/prompt_main.h 2014-07-15 19:50:44 +0000
> @@ -0,0 +1,56 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#ifndef CORE_TRUST_MIR_PROMPT_MAIN_H_
> +#define CORE_TRUST_MIR_PROMPT_MAIN_H_
> +
> +namespace core
> +{
> +namespace trust
> +{
> +namespace mir
> +{
> +namespace env
> +{
> +static constexpr const char* option_mir_socket
> +{
> + "MIR_SOCKET"
> +};
> +}
> +namespace cli
> +{
> +static constexpr const char* option_server_socket
> +{
> + "mir_server_socket"
> +};
> +
> +static constexpr const char* option_title
> +{
> + "title"
> +};
> +
> +static constexpr const char* option_description
> +{
> + "description"
> +};
> +}
> +}
> +}
> +}
> +
> +#endif // CORE_TRUST_MIR_PROMPT_MAIN_H_
>
> === added file 'src/core/trust/mir/prompt_main.qml'
> --- src/core/trust/mir/prompt_main.qml 1970-01-01 00:00:00 +0000
> +++ src/core/trust/mir/prompt_main.qml 2014-07-15 19:50:44 +0000
> @@ -0,0 +1,53 @@
> +import QtQuick 2.0
> +import Ubuntu.Components 0.1
> +import QtQuick.Layouts 1.1
> +
> +Rectangle {
> + anchors.fill: parent
> +
> + signal quit(int code)
> +
> + ColumnLayout {
> + anchors.fill: parent
> + anchors.margins: units.gu(2)
> + spacing: units.gu(2)
> +
> + Label {
> + anchors.horizontalCenter: parent.horizontalCenter
> +
> + text: title
> + font.family: "Ubuntu"
> + fontSize: "large"
> +
> + maximumLineCount: 1
> + elide: Text.ElideRight
> + }
> +
> + Label {
> +
> + text: description
> + font.family: "Ubuntu"
> + fontSize: "medium"
> + Layout.maximumWidth: parent.width
> + Layout.fillHeight: true
> +
> + wrapMode: Text.WordWrap
> + }
> +
> + RowLayout {
> + spacing: units.gu(1)
> + height: units.gu(3)
> + anchors.right: parent.right
> +
> + Button {
> + text: "Deny"
> + onClicked: quit(1)
> + }
> + Button {
> + text: "Grant"
> + onClicked: quit(0)
> + }
> + }
> + }
> +}
> +
>
> === modified file 'src/core/trust/request.cpp'
> --- src/core/trust/request.cpp 2014-05-06 11:32:13 +0000
> +++ src/core/trust/request.cpp 2014-07-15 19:50:44 +0000
> @@ -18,6 +18,55 @@
>
> #include <core/trust/request.h>
>
> +#include <core/trust/agent.h>
> +#include <core/trust/store.h>
> +
> +core::trust::Request::Answer core::trust::process_trust_request(const core::trust::RequestParameters& params)
> +{
> + // We verify parameters first:
> + if (not params.agent) throw std::logic_error
> + {
> + "Cannot operate without an agent implementation."
> + };
> +
> + if (not params.store) throw std::logic_error
> + {
> + "Cannot operate without a store implementation."
> + };
> +
> + // Let's see if the store has an answer for app-id and feature.
> + auto query = params.store->query();
> +
> + // Narrow it down to the specific app and the specific feature
> + query->for_application_id(params.application_id);
> + query->for_feature(params.feature);
> +
> + query->execute();
> +
> + // We have got results and we take the most recent one as the most appropriate.
> + if (query->status() == core::trust::Store::Query::Status::has_more_results)
> + {
> + // And we are returning early.
> + return query->current().answer;
> + }
> +
> + // We do not have results available in the store, prompting the user
> + auto answer = params.agent->prompt_user_for_request(
> + params.application_pid,
> + params.application_id,
> + params.description);
> +
> + params.store->add(core::trust::Request
> + {
> + params.application_id,
> + params.feature,
> + std::chrono::system_clock::now(),
> + answer
> + });
> +
> + return answer;
> +}
> +
> bool core::trust::operator==(const core::trust::Request& lhs, const core::trust::Request& rhs)
> {
> return lhs.from == rhs.from &&
>
> === modified file 'src/core/trust/resolve.cpp'
> --- src/core/trust/resolve.cpp 2014-05-06 11:32:13 +0000
> +++ src/core/trust/resolve.cpp 2014-07-15 19:50:44 +0000
> @@ -41,15 +41,14 @@
> };
> }
>
> -struct Store :
> - public core::trust::Store,
> - public dbus::Stub<core::trust::dbus::Store>
> +struct Store : public core::trust::Store
> {
> - Store(const std::shared_ptr<core::dbus::Bus>& bus)
> - : dbus::Stub<core::trust::dbus::Store>(bus),
> - bus(bus),
> - worker{[this]() { this->bus->run(); }},
> - proxy(access_service()->object_for_path(dbus::types::ObjectPath::root()))
> + Store(const std::shared_ptr<dbus::Service>& service,
> + const std::shared_ptr<core::dbus::Bus>& bus)
> + : bus(bus),
> + worker{[this]() { Store::bus->run(); }},
> + service(service),
> + proxy(service->object_for_path(dbus::types::ObjectPath::root()))
> {
> }
>
> @@ -99,7 +98,7 @@
>
> if (result.is_error())
> {
> - throw core::trust::Store::Query::Error::NoCurrentResult{};
> + throw core::trust::Store::Query::Errors::NoCurrentResult{};
> }
>
> return result.value();
> @@ -224,7 +223,7 @@
> {
> path,
> proxy,
> - access_service()->object_for_path(path)
> + service->object_for_path(path)
> });
>
> return query;
> @@ -232,6 +231,7 @@
>
> std::shared_ptr<core::dbus::Bus> bus;
> std::thread worker;
> + std::shared_ptr<dbus::Service> service;
> std::shared_ptr<dbus::Object> proxy;
> };
> }
> @@ -242,10 +242,16 @@
> const std::string& name)
> {
> if (name.empty())
> - throw Error::ServiceNameMustNotBeEmpty{};
> + throw Errors::ServiceNameMustNotBeEmpty{};
>
> - core::trust::dbus::Store::mutable_name() = "com.ubuntu.trust.store." + name;
> - return std::shared_ptr<core::trust::Store>{new detail::Store(bus)};
> + return std::shared_ptr<core::trust::Store>
> + {
> + new detail::Store
> + {
> + core::dbus::Service::use_service(bus, "com.ubuntu.trust.store." + name),
> + bus
> + }
> + };
> }
>
> std::shared_ptr<core::trust::Store> core::trust::resolve_store_in_session_with_name(
>
> === modified file 'tests/CMakeLists.txt'
> --- tests/CMakeLists.txt 2014-05-06 11:32:13 +0000
> +++ tests/CMakeLists.txt 2014-07-15 19:50:44 +0000
> @@ -1,12 +1,11 @@
> -set (OLD_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
> -# Don't treat warnings as errors in 3rd_party/{gmock,cucumber-cpp}
> -string (REPLACE " -Werror " " " CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
> -find_package(Gtest REQUIRED)
> -include_directories(
> - ${GMOCK_INCLUDE_DIR}
> - ${GTEST_INCLUDE_DIR}
> -)
> -set (CMAKE_CXX_FLAGS ${OLD_CMAKE_CXX_FLAGS})
> +include_directories(${CMAKE_BINARY_DIR}/src)
> +
> +# Build with system gmock and embedded gtest
> +set (GMOCK_INCLUDE_DIR "/usr/include/gmock/include" CACHE PATH "gmock source include directory")
> +set (GMOCK_SOURCE_DIR "/usr/src/gmock" CACHE PATH "gmock source directory")
> +set (GTEST_INCLUDE_DIR "${GMOCK_SOURCE_DIR}/gtest/include" CACHE PATH "gtest source include directory")
> +
> +add_subdirectory(${GMOCK_SOURCE_DIR} "${CMAKE_CURRENT_BINARY_DIR}/gmock")
>
> pkg_check_modules(DBUS dbus-1)
>
> @@ -17,8 +16,11 @@
> ${CMAKE_CURRENT_BINARY_DIR}/test_data.h @ONLY)
>
> include_directories(
> + ${CMAKE_SOURCE_DIR}/src
> +
> ${CMAKE_CURRENT_BINARY_DIR}
> - ${GTEST_INCLUDE_DIRS}
> + ${GMOCK_INCLUDE_DIR}
> + ${GTEST_INCLUDE_DIR}
> ${PROCESS_CPP_INCLUDE_DIRS}
> ${DBUS_INCLUDE_DIRS}
> )
> @@ -33,12 +35,33 @@
> remote_trust_store_test.cpp
> )
>
> +add_executable(
> + request_processor_test
> + request_processor_test.cpp
> +)
> +
> +add_executable(
> + mir_agent_test
> + mir_agent_test.cpp
> +)
> +
> +# A standalone executable that validates arguments being passed in.
> +add_executable(
> + test_prompt
> + test_prompt.cpp
> +)
> +
> +target_link_libraries(test_prompt ${Boost_LIBRARIES})
> +
> target_link_libraries(
> trust_store_test
>
> trust-store
>
> - ${GTEST_BOTH_LIBRARIES}
> + gmock
> +
> + gtest
> + gtest_main
> )
>
> target_link_libraries(
> @@ -46,9 +69,46 @@
>
> trust-store
>
> - ${GTEST_BOTH_LIBRARIES}
> + gmock
> +
> + gtest
> + gtest_main
> +
> + ${PROCESS_CPP_LIBRARIES}
> +)
> +
> +target_link_libraries(
> + request_processor_test
> +
> + trust-store
> +
> + gmock
> +
> + gtest
> + gtest_main
> +
> + ${PROCESS_CPP_LIBRARIES}
> +)
> +
> +target_link_libraries(
> + mir_agent_test
> +
> + trust-store
> +
> + gmock
> +
> + gtest
> + gtest_main
> +
> ${PROCESS_CPP_LIBRARIES}
> )
>
> add_test(trust_store_test ${CMAKE_CURRENT_BINARY_DIR}/trust_store_test)
> add_test(remote_trust_store_test ${CMAKE_CURRENT_BINARY_DIR}/remote_trust_store_test)
> +add_test(request_processor_test ${CMAKE_CURRENT_BINARY_DIR}/request_processor_test)
> +add_test(mir_agent_test ${CMAKE_CURRENT_BINARY_DIR}/mir_agent_test --gtest_filter=*-*requires_mir)
> +
> +install(
> + TARGETS trust_store_test remote_trust_store_test request_processor_test mir_agent_test
> + RUNTIME DESTINATION bin/trust-store-tests
> +)
>
> === added file 'tests/mir_agent_test.cpp'
> --- tests/mir_agent_test.cpp 1970-01-01 00:00:00 +0000
> +++ tests/mir_agent_test.cpp 2014-07-15 19:50:44 +0000
> @@ -0,0 +1,430 @@
> +/*
> + * Copyright © 2013 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +// Implementation-specific header
> +#include <core/trust/mir/agent.h>
> +
> +#include <core/trust/agent.h>
> +#include <core/trust/request.h>
> +#include <core/trust/store.h>
> +
> +#include "test_data.h"
> +
> +#include <core/posix/fork.h>
> +
> +#include <gmock/gmock.h>
> +#include <gtest/gtest.h>
> +
> +#include <random>
> +#include <thread>
> +
> +namespace
> +{
> +struct MockPromptSessionVirtualTable : public core::trust::mir::PromptSessionVirtualTable
> +{
> + MockPromptSessionVirtualTable() : core::trust::mir::PromptSessionVirtualTable{nullptr}
> + {
> + }
> +
> + // Requests a new, pre-authenticated fd for associating prompt providers.
> + // Returns the fd or throws std::runtime_error.
> + MOCK_METHOD0(new_fd_for_prompt_provider, int());
> +
> + // Adds a prompt provider process to the prompting session, identified by its PID.
> + // Returns true if addition of the prompt provider succeeded.
> + MOCK_METHOD1(add_prompt_provider_sync, bool(pid_t));
> +
> + // Finalizes and releases the given prompt session instance.
> + MOCK_METHOD0(release_sync, void());
> +};
> +
> +struct MockConnectionVirtualTable : public core::trust::mir::ConnectionVirtualTable
> +{
> + MockConnectionVirtualTable() : core::trust::mir::ConnectionVirtualTable{nullptr}
> + {
> + }
> +
> + // Creates a new trusted prompt session instance synchronously.
> + MOCK_METHOD3(create_prompt_session_sync,
> + core::trust::mir::PromptSessionVirtualTable::Ptr(
> + // The process id of the requesting app/service
> + pid_t app_pid,
> + // Callback handling prompt session state changes.
> + mir_prompt_session_state_change_callback,
> + // Callback context
> + void*));
> +};
> +
> +struct MockPromptProviderHelper : public core::trust::mir::PromptProviderHelper
> +{
> + MockPromptProviderHelper(const core::trust::mir::PromptProviderHelper::CreationArguments& args)
> + : core::trust::mir::PromptProviderHelper{args}
> + {
> + using namespace ::testing;
> + ON_CALL(*this, exec_prompt_provider_with_arguments(_))
> + .WillByDefault(
> + Invoke(this, &MockPromptProviderHelper::super_exec_prompt_provider_with_arguments));
> + }
> +
> + // Execs the executable provided at construction time for the arguments and
> + // returns the corresponding child process.
> + MOCK_METHOD1(exec_prompt_provider_with_arguments,
> + core::posix::ChildProcess(
> + const core::trust::mir::PromptProviderHelper::InvocationArguments&));
> +
> + core::posix::ChildProcess super_exec_prompt_provider_with_arguments(const core::trust::mir::PromptProviderHelper::InvocationArguments& args)
> + {
> + return core::trust::mir::PromptProviderHelper::exec_prompt_provider_with_arguments(args);
> + }
> +};
> +
> +struct MockTranslator
> +{
> + MOCK_METHOD1(translate, core::trust::Request::Answer(const core::posix::wait::Result&));
> +};
> +
> +std::function<core::trust::Request::Answer(const core::posix::wait::Result&)> mock_translator_to_functor(const std::shared_ptr<MockTranslator>& ptr)
> +{
> + return [ptr](const core::posix::wait::Result& result) { return ptr->translate(result); };
> +}
> +
> +std::shared_ptr<MockPromptSessionVirtualTable> a_mocked_prompt_session_vtable()
> +{
> + return std::make_shared<testing::NiceMock<MockPromptSessionVirtualTable>>();
> +}
> +
> +std::shared_ptr<core::trust::mir::PromptProviderHelper> a_prompt_provider_calling_bin_false()
> +{
> + return std::make_shared<core::trust::mir::PromptProviderHelper>(
> + core::trust::mir::PromptProviderHelper::CreationArguments
> + {
> + "/bin/false"
> + });
> +}
> +
> +std::shared_ptr<MockPromptProviderHelper> a_mocked_prompt_provider_calling_bin_false()
> +{
> + return std::make_shared<MockPromptProviderHelper>(
> + core::trust::mir::PromptProviderHelper::CreationArguments
> + {
> + "/bin/false"
> + });
> +}
> +
> +std::shared_ptr<core::trust::mir::PromptProviderHelper> a_prompt_provider_calling_bin_true()
> +{
> + return std::make_shared<core::trust::mir::PromptProviderHelper>(
> + core::trust::mir::PromptProviderHelper::CreationArguments
> + {
> + "/bin/true"
> + });
> +}
> +}
> +
> +TEST(DefaultProcessStateTranslator, throws_for_signalled_process)
> +{
> + core::posix::wait::Result result;
> + result.status = core::posix::wait::Result::Status::signaled;
> + result.detail.if_signaled.signal = core::posix::Signal::sig_kill;
> + result.detail.if_signaled.core_dumped = true;
> +
> + auto translator = core::trust::mir::Agent::translator_only_accepting_exit_status_success();
> + EXPECT_THROW(translator(result), std::logic_error);
> +}
> +
> +TEST(DefaultProcessStateTranslator, throws_for_stopped_process)
> +{
> + core::posix::wait::Result result;
> + result.status = core::posix::wait::Result::Status::stopped;
> + result.detail.if_stopped.signal = core::posix::Signal::sig_stop;
> +
> + auto translator = core::trust::mir::Agent::translator_only_accepting_exit_status_success();
> + EXPECT_THROW(translator(result), std::logic_error);
> +}
> +
> +TEST(DefaultProcessStateTranslator, returns_denied_for_process_exiting_with_failure)
> +{
> + core::posix::wait::Result result;
> + result.status = core::posix::wait::Result::Status::exited;
> + result.detail.if_exited.status = core::posix::exit::Status::failure;
> +
> + auto translator = core::trust::mir::Agent::translator_only_accepting_exit_status_success();
> + EXPECT_EQ(core::trust::Request::Answer::denied, translator(result));
> +}
> +
> +TEST(DefaultProcessStateTranslator, returns_granted_for_process_exiting_successfully)
> +{
> + core::posix::wait::Result result;
> + result.status = core::posix::wait::Result::Status::exited;
> + result.detail.if_exited.status = core::posix::exit::Status::success;
> +
> + auto translator = core::trust::mir::Agent::translator_only_accepting_exit_status_success();
> + EXPECT_EQ(core::trust::Request::Answer::granted, translator(result));
> +}
> +
> +TEST(DefaultPromptProviderHelper, correctly_passes_arguments_to_prompt_executable)
> +{
> + core::trust::mir::PromptProviderHelper::CreationArguments cargs
> + {
> + core::trust::testing::test_prompt_executable_in_build_dir
> + };
> +
> + core::trust::mir::PromptProviderHelper::InvocationArguments iargs
> + {
> + 42,
> + "does.not.exist.application",
> + "Just an extended description"
> + };
> +
> + core::trust::mir::PromptProviderHelper helper{cargs};
> + auto child = helper.exec_prompt_provider_with_arguments(iargs);
> +
> + auto result = child.wait_for(core::posix::wait::Flags::untraced);
> +
> + EXPECT_EQ(core::posix::wait::Result::Status::exited, result.status);
> + EXPECT_EQ(core::posix::exit::Status::success, result.detail.if_exited.status);
> +}
> +
> +TEST(MirAgent, creates_prompt_session_and_execs_helper_with_preauthenticated_fd)
> +{
> + using namespace ::testing;
> +
> + const pid_t app_pid {21};
> + const std::string app_id {"does.not.exist.application"};
> + const std::string app_description {"This is just an extended description"};
> + const int pre_authenticated_fd {42};
> +
> + const core::trust::mir::PromptProviderHelper::InvocationArguments reference_invocation_args
> + {
> + pre_authenticated_fd,
> + app_id,
> + app_description
> + };
> +
> + auto connection_vtable = std::make_shared<MockConnectionVirtualTable>();
> + auto prompt_session_vtable = a_mocked_prompt_session_vtable();
> +
> + auto prompt_provider_exec_helper = a_mocked_prompt_provider_calling_bin_false();
> +
> + ON_CALL(*connection_vtable, create_prompt_session_sync(_, _, _))
> + .WillByDefault(Return(prompt_session_vtable));
> +
> + ON_CALL(*prompt_session_vtable, new_fd_for_prompt_provider())
> + .WillByDefault(Return(pre_authenticated_fd));
> +
> + ON_CALL(*prompt_session_vtable, add_prompt_provider_sync(_))
> + .WillByDefault(Return(true));
> +
> + EXPECT_CALL(*connection_vtable, create_prompt_session_sync(app_pid, _, _)).Times(1);
> + EXPECT_CALL(*prompt_session_vtable, new_fd_for_prompt_provider()).Times(1);
> + EXPECT_CALL(*prompt_provider_exec_helper,
> + exec_prompt_provider_with_arguments(
> + reference_invocation_args)).Times(1);
> +
> + core::trust::mir::Agent agent
> + {
> + connection_vtable,
> + prompt_provider_exec_helper,
> + core::trust::mir::Agent::translator_only_accepting_exit_status_success()
> + };
> +
> + EXPECT_EQ(core::trust::Request::Answer::denied, // /bin/false exits with failure.
> + agent.prompt_user_for_request(app_pid, app_id, app_description));
> +}
> +
> +TEST(MirAgent, sig_kills_prompt_provider_process_on_status_change)
> +{
> + using namespace ::testing;
> +
> + const pid_t app_pid {21};
> + const std::string app_id {"does.not.exist.application"};
> + const std::string app_description {"This is just an extended description"};
> + const int pre_authenticated_fd {42};
> +
> + auto a_spinning_process = []()
> + {
> + while (true)
> + {
> + std::this_thread::sleep_for(std::chrono::milliseconds{20});
> + }
> + return core::posix::exit::Status::success;
> + };
> +
> + auto connection_vtable = std::make_shared<MockConnectionVirtualTable>();
> + auto prompt_session_vtable = a_mocked_prompt_session_vtable();
> +
> + auto prompt_provider_helper = std::make_shared<MockPromptProviderHelper>(
> + core::trust::mir::PromptProviderHelper::CreationArguments{"/bin/false"});
> +
> + void* prompt_session_state_callback_context{nullptr};
> +
> + ON_CALL(*prompt_provider_helper, exec_prompt_provider_with_arguments(_))
> + .WillByDefault(
> + Return(
> + core::posix::fork(
> + a_spinning_process,
> + core::posix::StandardStream::empty)));
> +
> + ON_CALL(*prompt_session_vtable, new_fd_for_prompt_provider())
> + .WillByDefault(
> + Return(
> + pre_authenticated_fd));
> +
> + ON_CALL(*prompt_session_vtable, add_prompt_provider_sync(_))
> + .WillByDefault(
> + Return(
> + true));
> +
> + // An invocation results in a session being created. In addition,
> + // we store pointers to callback and context provided by the implementation
> + // for being able to later trigger the callback.
> + ON_CALL(*connection_vtable, create_prompt_session_sync(app_pid, _, _))
> + .WillByDefault(
> + DoAll(
> + SaveArg<2>(&prompt_session_state_callback_context),
> + Return(prompt_session_vtable)));
> +
> + core::trust::mir::Agent agent
> + {
> + connection_vtable,
> + prompt_provider_helper,
> + core::trust::mir::Agent::translator_only_accepting_exit_status_success()
> + };
> +
> + std::thread asynchronously_stop_the_prompting_session
> + {
> + [&prompt_session_state_callback_context]()
> + {
> + std::this_thread::sleep_for(std::chrono::seconds{1});
> +
> + core::trust::mir::Agent::on_trust_session_changed_state(
> + nullptr,
> + mir_prompt_session_state_stopped,
> + prompt_session_state_callback_context);
> + }
> + };
> +
> + // The spinning prompt provider should get signalled if the prompting session is stopped.
> + // If that does not happen, the prompt provider returns success and we would have a result
> + // granted.
> + EXPECT_THROW(agent.prompt_user_for_request(app_pid, app_id, app_description),
> + std::logic_error);
> +
> + // And some clean up.
> + if (asynchronously_stop_the_prompting_session.joinable())
> + asynchronously_stop_the_prompting_session.join();
> +}
> +
> +TEST(TrustPrompt, aborts_for_missing_title)
> +{
> + // And we pass in an empty argument vector
> + std::vector<std::string> argv;
> +
> + // We pass in the empty env
> + std::map<std::string, std::string> env;
> +
> + auto child = core::posix::exec(
> + core::trust::testing::trust_prompt_executable_in_build_dir,
> + argv,
> + env,
> + core::posix::StandardStream::empty);
> +
> + auto result = child.wait_for(core::posix::wait::Flags::untraced);
> +
> + EXPECT_EQ(core::posix::wait::Result::Status::signaled, result.status);
> + EXPECT_EQ(core::posix::Signal::sig_abrt, result.detail.if_signaled.signal);
> +}
> +
> +/***********************************************************************
> +* All tests requiring a running Mir instance go here. *
> +* They are tagged with _requires_mir and taken out of the *
> +* automatic build and test cycle. *
> +***********************************************************************/
> +
> +#include <core/trust/mir_agent.h>
> +
> +#include <core/trust/mir/config.h>
> +#include <core/trust/mir/prompt_main.h>
> +
> +namespace
> +{
> +std::map<std::string, std::string> a_copy_of_the_env()
> +{
> + std::map<std::string, std::string> result;
> + core::posix::this_process::env::for_each([&result](const std::string& key, const std::string& value)
> + {
> + result.insert(std::make_pair(key, value)) ;
> + });
> + return result;
> +}
> +
> +std::string mir_socket()
> +{
> + // We either take the XDG_RUNTIME_DIR or fall back to /tmp if XDG_RUNTIME_DIR is not set.
> + std::string dir = core::posix::this_process::env::get("XDG_RUNTIME_DIR", "/tmp");
> + return dir + "/mir_socket";
> +}
> +
> +std::string trusted_mir_socket()
> +{
> + // We either take the XDG_RUNTIME_DIR or fall back to /tmp if XDG_RUNTIME_DIR is not set.
> + std::string dir = core::posix::this_process::env::get("XDG_RUNTIME_DIR", "/tmp");
> + return dir + "/mir_socket_trusted";
> +}
> +}
> +
> +TEST(MirAgent, default_agent_works_correctly_against_running_mir_instance_requires_mir)
> +{
> + std::string pretty_function{__PRETTY_FUNCTION__};
> +
> + // We start up an application in a child process and simulate that it is requesting access
> + // to a trusted system service/resource.
> + std::vector<std::string> argv
> + {
> + "--" + std::string{core::trust::mir::cli::option_server_socket} + "=" + mir_socket(),
> + "--" + std::string{core::trust::mir::cli::option_title} + "=" + pretty_function,
> + "--" + std::string{core::trust::mir::cli::option_description} + "=" + pretty_function,
> + // We have to circumvent unity8's authentication mechanism and just provide
> + // the desktop_file_hint as part of the command line.
> + "--desktop_file_hint=/usr/share/applications/webbrowser-app.desktop"
> + };
> +
> + core::posix::ChildProcess app = core::posix::exec(
> + core::trust::mir::trust_prompt_executable_in_lib_dir,
> + argv,
> + a_copy_of_the_env(),
> + core::posix::StandardStream::empty);
> +
> + // We pretend to be a trusted helper and connect to mir via its trusted socket.
> + auto mir_connection = mir_connect_sync(mir_socket().c_str(), pretty_function.c_str());
> +
> + // Based on the mir connection, we create a prompting agent.
> + auto mir_agent = core::trust::mir::create_agent_for_mir_connection(mir_connection);
> +
> + // And issue a prompt request. As a result, the user is presented with a prompting dialog.
> + auto answer = mir_agent->prompt_user_for_request(app.pid(), pretty_function, pretty_function);
> +
> + // And we cross-check with the user:
> + std::cout << "You answered the trust prompt with: " << answer << "."
> + << "Is that correct? [y/n]:";
> +
> + char y_or_n{'n'}; std::cin >> y_or_n;
> + EXPECT_EQ('y', y_or_n);
> +
> + app.wait_for(core::posix::wait::Flags::untraced);
> +}
>
> === added file 'tests/request_processor_test.cpp'
> --- tests/request_processor_test.cpp 1970-01-01 00:00:00 +0000
> +++ tests/request_processor_test.cpp 2014-07-15 19:50:44 +0000
> @@ -0,0 +1,287 @@
> +/*
> + * Copyright © 2013 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#include <core/trust/agent.h>
> +#include <core/trust/request.h>
> +#include <core/trust/store.h>
> +
> +#include <gmock/gmock.h>
> +#include <gtest/gtest.h>
> +
> +#include <random>
> +
> +namespace
> +{
> +struct MockAgent : public core::trust::Agent
> +{
> + /**
> + * @brief Presents the given request to the user, returning the user-provided answer.
> + * @param request The trust request that a user has to answer.
> + * @param description Extended description of the trust request.
> + */
> + MOCK_METHOD3(prompt_user_for_request, core::trust::Request::Answer(pid_t, const std::string&, const std::string&));
> +};
> +
> +struct MockStore : public core::trust::Store
> +{
> + struct MockQuery : public core::trust::Store::Query
> + {
> + /** @brief Access the status of the query. */
> + MOCK_CONST_METHOD0(status, core::trust::Store::Query::Status());
> +
> + /** @brief Limit the query to a specific application Id. */
> + MOCK_METHOD1(for_application_id, void(const std::string&));
> +
> + /** @brief Limit the query to a service-specific feature. */
> + MOCK_METHOD1(for_feature, void(std::uint64_t));
> +
> + /** @brief Limit the query to the specified time interval. */
> + MOCK_METHOD2(for_interval, void(const core::trust::Request::Timestamp&, const core::trust::Request::Timestamp&));
> +
> + /** @brief Limit the query for a specific answer. */
> + MOCK_METHOD1(for_answer, void(core::trust::Request::Answer));
> +
> + /** @brief Query all stored requests. */
> + MOCK_METHOD0(all, void());
> +
> + /** @brief Execute the query against the store. */
> + MOCK_METHOD0(execute, void());
> +
> + /** @brief After successful execution, advance to the next request. */
> + MOCK_METHOD0(next, void());
> +
> + /** @brief After successful execution, erase the current element and advance to the next request. */
> + MOCK_METHOD0(erase, void());
> +
> + /** @brief Access the request the query currently points to. */
> + MOCK_METHOD0(current, core::trust::Request());
> + };
> +
> + /** @brief Resets the state of the store, implementations should discard
> + * all persistent and non-persistent state.
> + */
> + MOCK_METHOD0(reset, void());
> +
> + /** @brief Add the provided request to the store. When this function returns true,
> + * the request has been persisted by the implementation.
> + */
> + MOCK_METHOD1(add, void(const core::trust::Request&));
> +
> + /**
> + * @brief Create a query for this store.
> + */
> + MOCK_METHOD0(query, std::shared_ptr<core::trust::Store::Query>());
> +};
> +
> +pid_t the_default_pid_for_testing()
> +{
> + return 42;
> +}
> +
> +std::uint64_t the_default_feature_for_testing()
> +{
> + return 0;
> +}
> +
> +std::shared_ptr<core::trust::Agent> a_null_agent()
> +{
> + return std::shared_ptr<core::trust::Agent>{};
> +}
> +
> +std::shared_ptr<testing::NiceMock<MockAgent>> a_mocked_agent()
> +{
> + return std::make_shared<testing::NiceMock<MockAgent>>();
> +}
> +
> +std::shared_ptr<core::trust::Store> a_null_store()
> +{
> + return std::shared_ptr<core::trust::Store>{};
> +}
> +
> +std::shared_ptr<testing::NiceMock<MockStore>> a_mocked_store()
> +{
> + return std::make_shared<testing::NiceMock<MockStore>>();
> +}
> +
> +std::shared_ptr<core::trust::Store::Query> a_null_query()
> +{
> + return std::shared_ptr<core::trust::Store::Query>{};
> +}
> +
> +std::shared_ptr<testing::NiceMock<MockStore::MockQuery>> a_mocked_query()
> +{
> + return std::make_shared<testing::NiceMock<MockStore::MockQuery>>();
> +}
> +
> +core::trust::RequestParameters default_request_parameters_for_testing()
> +{
> + return core::trust::RequestParameters
> + {
> + a_null_agent(),
> + a_null_store(),
> + the_default_pid_for_testing(),
> + "this.is.just.for.testing.purposes",
> + the_default_feature_for_testing(),
> + "Someone wants to access all your credentials and steal your identity."
> + };
> +}
> +
> +core::trust::Request::Answer throw_a_dice()
> +{
> + // We seed the rng with the current time to ensure randomness across test runs.
> + static std::default_random_engine generator
> + {
> + static_cast<long unsigned int>(std::chrono::system_clock::now().time_since_epoch().count())
> + };
> + // Our dice :)
> + static std::uniform_int_distribution<int> distribution
> + {
> + 1,
> + 6
> + };
> +
> + return distribution(generator) <= 3 ?
> + core::trust::Request::Answer::denied :
> + core::trust::Request::Answer::granted;
> +}
> +
> +}
> +
> +TEST(RequestProcessing, throws_for_missing_agent_implementation)
> +{
> + auto params = default_request_parameters_for_testing();
> +
> + params.store = a_mocked_store();
> +
> + EXPECT_THROW(core::trust::process_trust_request(params), std::logic_error);
> +}
> +
> +TEST(RequestProcessing, throws_for_missing_store_implementation)
> +{
> + auto params = default_request_parameters_for_testing();
> +
> + params.agent = a_mocked_agent();
> +
> + EXPECT_THROW(core::trust::process_trust_request(params), std::logic_error);
> +}
> +
> +TEST(RequestProcessing, queries_store_for_cached_results_and_returns_cached_value)
> +{
> + using namespace ::testing;
> +
> + auto answer = throw_a_dice();
> +
> + auto params = default_request_parameters_for_testing();
> +
> + core::trust::Request request
> + {
> + params.application_id,
> + params.feature,
> + std::chrono::system_clock::now(),
> + answer
> + };
> +
> + auto mocked_agent = a_mocked_agent();
> + auto mocked_query = a_mocked_query();
> + auto mocked_store = a_mocked_store();
> +
> + ON_CALL(*mocked_query, status())
> + .WillByDefault(
> + Return(
> + core::trust::Store::Query::Status::has_more_results));
> +
> + ON_CALL(*mocked_query, current())
> + .WillByDefault(
> + Return(
> + request));
> +
> + ON_CALL(*mocked_store, query())
> + .WillByDefault(
> + Return(
> + mocked_query));
> +
> + EXPECT_CALL(*mocked_store, query()).Times(1);
> + // We expect the processor to limit the query to the respective application id
> + // and to the respective feature.
> + EXPECT_CALL(*mocked_query, for_application_id(params.application_id)).Times(1);
> + EXPECT_CALL(*mocked_query, for_feature(params.feature)).Times(1);
> + // The setup ensures that a previously stored answer is available in the store.
> + // For that, the agent should not be queried.
> + EXPECT_CALL(*mocked_agent, prompt_user_for_request(_, _, _)).Times(0);
> +
> + params.agent = mocked_agent;
> + params.store = mocked_store;
> +
> + EXPECT_EQ(answer, core::trust::process_trust_request(params));
> +}
> +
> +TEST(RequestProcessing, queries_agent_if_no_cached_results_and_returns_users_answer)
> +{
> + using namespace ::testing;
> +
> + auto answer = throw_a_dice();
> +
> + auto params = default_request_parameters_for_testing();
> +
> + core::trust::Request request
> + {
> + params.application_id,
> + params.feature,
> + std::chrono::system_clock::now(),
> + answer
> + };
> +
> + auto mocked_agent = a_mocked_agent();
> + auto mocked_query = a_mocked_query();
> + auto mocked_store = a_mocked_store();
> +
> + ON_CALL(*mocked_agent, prompt_user_for_request(params.application_pid, params.application_id, params.description))
> + .WillByDefault(
> + Return(
> + answer));
> +
> + // We return EndOfRecord for queries, and expect the request processor
> + // to subsequently ask the user for his answer.
> + ON_CALL(*mocked_query, status())
> + .WillByDefault(
> + Return(
> + core::trust::Store::Query::Status::eor));
> +
> + ON_CALL(*mocked_store, query())
> + .WillByDefault(
> + Return(
> + mocked_query));
> +
> + EXPECT_CALL(*mocked_query, current()).Times(0);
> + EXPECT_CALL(*mocked_store, query()).Times(1);
> + // We expect the processor to limit the query to the respective application id
> + // and to the respective feature.
> + EXPECT_CALL(*mocked_query, for_application_id(params.application_id)).Times(1);
> + EXPECT_CALL(*mocked_query, for_feature(params.feature)).Times(1);
> + // The setup ensures that a previously stored answer is available in the store.
> + // For that, the agent should not be queried.
> + EXPECT_CALL(*mocked_agent, prompt_user_for_request(params.application_pid, params.application_id, params.description)).Times(1);
> +
> + params.agent = mocked_agent;
> + params.store = mocked_store;
> +
> + EXPECT_EQ(answer, core::trust::process_trust_request(params));
> +}
> +
> +
> +
>
> === modified file 'tests/test_data.h.in'
> --- tests/test_data.h.in 2014-05-06 11:32:13 +0000
> +++ tests/test_data.h.in 2014-07-15 19:50:44 +0000
> @@ -23,7 +23,6 @@
> {
> namespace testing
> {
> -
> const char* session_bus_configuration_file()
> {
> return "@CMAKE_SOURCE_DIR@/data/session.conf";
> @@ -33,76 +32,20 @@
> {
> return "@CMAKE_SOURCE_DIR@/data/system.conf";
> }
> -
> -namespace com
> -{
> -namespace canonical
> -{
> -const char* user_metrics_introspection_file()
> -{
> - return "@CMAKE_CURRENT_SOURCE_DIR@/data/com.canonical.UserMetrics.xml";
> -}
> -
> -const char* url_dispatcher_introspection_file()
> -{
> - return "@CMAKE_CURRENT_SOURCE_DIR@/data/com.canonical.URLDispatcher.xml";
> -}
> -}
> -}
> -
> -namespace org
> -{
> -namespace freedesktop
> -{
> -namespace modem_manager
> -{
> -namespace modem
> -{
> -constexpr const char* cdma_introspection_file()
> -{
> - return "@CMAKE_CURRENT_SOURCE_DIR@/data/org.freedesktop.ModemManager.Modem.Cdma.xml";
> -}
> -
> -constexpr const char* firmware_introspection_file()
> -{
> - return "@CMAKE_CURRENT_SOURCE_DIR@/data/org.freedesktop.ModemManager.Modem.Firmware.xml";
> -}
> -
> -namespace gsm
> -{
> -constexpr const char* card_introspection_file()
> -{
> - return "@CMAKE_CURRENT_SOURCE_DIR@/data/org.freedesktop.ModemManager.Modem.Gsm.Card.xml";
> -}
> -
> -constexpr const char* contact_introspection_file()
> -{
> - return "@CMAKE_CURRENT_SOURCE_DIR@/data/org.freedesktop.ModemManager.Modem.Gsm.Contacts.xml";
> -}
> -
> -constexpr const char* hso_introspection_file()
> -{
> - return "@CMAKE_CURRENT_SOURCE_DIR@/data/org.freedesktop.ModemManager.Modem.Gsm.Hso.xml";
> -}
> -
> -constexpr const char* network_introspection_file()
> -{
> - return "@CMAKE_CURRENT_SOURCE_DIR@/data/org.freedesktop.ModemManager.Modem.Gsm.Network.xml";
> -}
> -
> -constexpr const char* sms_introspection_file()
> -{
> - return "@CMAKE_CURRENT_SOURCE_DIR@/data/org.freedesktop.ModemManager.Modem.Gsm.SMS.xml";
> -}
> -
> -constexpr const char* ussd_introspection_file()
> -{
> - return "@CMAKE_CURRENT_SOURCE_DIR@/data/org.freedesktop.ModemManager.Modem.Gsm.Ussd.xml";
> -}
> -}
> -}
> -}
> -}
> +}
> +namespace trust
> +{
> +namespace testing
> +{
> +static constexpr const char* trust_prompt_executable_in_build_dir
> +{
> + "@CMAKE_BINARY_DIR@/src/trust-prompt"
> +};
> +
> +static constexpr const char* test_prompt_executable_in_build_dir
> +{
> + "@CMAKE_BINARY_DIR@/tests/test_prompt"
> +};
> }
> }
> }
>
> === added file 'tests/test_prompt.cpp'
> --- tests/test_prompt.cpp 1970-01-01 00:00:00 +0000
> +++ tests/test_prompt.cpp 2014-07-15 19:50:44 +0000
> @@ -0,0 +1,49 @@
> +/*
> + * Copyright © 2013 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#include <core/trust/mir/prompt_main.h>
> +
> +#include <boost/program_options.hpp>
> +
> +int main(int argc, char** argv)
> +{
> + for (int i = 0; i < argc; i++)
> + std::cout << i << ": " << argv[i] << std::endl;
> +
> + boost::program_options::options_description options;
> + options.add_options()
> + (core::trust::mir::cli::option_server_socket, boost::program_options::value<std::string>(), "Mir server socket to connect to.")
> + (core::trust::mir::cli::option_description, boost::program_options::value<std::string>(), "Extended description of the prompt.")
> + (core::trust::mir::cli::option_title, boost::program_options::value<std::string>(), "Title of the prompt");
> +
> + boost::program_options::variables_map vm;
> + boost::program_options::store(boost::program_options::parse_command_line(argc, argv, options), vm);
> + boost::program_options::notify(vm);
> +
> + // The server-socket option is mandatory.
> + if (vm.count(core::trust::mir::cli::option_server_socket) == 0)
> + ::abort();
> + // We expect the argument to be in the format fd://%d.
> + if (vm[core::trust::mir::cli::option_server_socket].as<std::string>().find("fd://") != 0)
> + ::abort();
> + // The title option is mandatory.
> + if (vm.count(core::trust::mir::cli::option_title) == 0)
> + ::abort();
> +
> + return EXIT_SUCCESS;
> +}
>
--
https://code.launchpad.net/~thomas-voss/trust-store/add_convenience_function_for_processing_incoming_requests/+merge/226696
Your team Ubuntu Phablet Team is subscribed to branch lp:trust-store.
More information about the Ubuntu-reviews
mailing list