[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