Merge lp:~thomas-voss/trust-store/add_convenience_function_for_processing_incoming_requests into lp:trust-store

Proposed by Thomas Voß
Status: Merged
Approved by: Thomas Voß
Approved revision: 54
Merged at revision: 19
Proposed branch: lp:~thomas-voss/trust-store/add_convenience_function_for_processing_incoming_requests
Merge into: lp:trust-store
Diff against target: 2822 lines (+2097/-171)
26 files modified
CMakeLists.txt (+7/-1)
debian/control (+19/-0)
debian/libtrust-store0.install (+2/-0)
debian/libtrust-store0.symbols (+41/-8)
debian/trust-store-tests.install (+1/-0)
include/core/trust/agent.h (+57/-0)
include/core/trust/mir_agent.h (+48/-0)
include/core/trust/request.h (+78/-1)
include/core/trust/store.h (+25/-4)
src/CMakeLists.txt (+62/-1)
src/core/trust/dbus_interface.h (+2/-7)
src/core/trust/expose.cpp (+46/-32)
src/core/trust/impl/sqlite3/store.cpp (+10/-12)
src/core/trust/mir/agent.cpp (+267/-0)
src/core/trust/mir/agent.h (+196/-0)
src/core/trust/mir/config.h.in (+41/-0)
src/core/trust/mir/prompt_config.h.in (+39/-0)
src/core/trust/mir/prompt_main.cpp (+191/-0)
src/core/trust/mir/prompt_main.h (+65/-0)
src/core/trust/mir/prompt_main.qml (+53/-0)
src/core/trust/request.cpp (+49/-0)
src/core/trust/resolve.cpp (+19/-13)
tests/CMakeLists.txt (+66/-12)
tests/mir_agent_test.cpp (+420/-0)
tests/request_processor_test.cpp (+287/-0)
tests/test_data.h.in (+6/-80)
To merge this branch: bzr merge lp:~thomas-voss/trust-store/add_convenience_function_for_processing_incoming_requests
Reviewer Review Type Date Requested Status
Seth Arnold (community) Approve
PS Jenkins bot continuous-integration Needs Fixing
Marcus Tomlinson (community) Approve
Review via email: mp+226696@code.launchpad.net

Commit message

Provide an agent implementation that leverages Mir's trusted prompting API.
Introduce a default prompt provider using Qt/QML.
Provide a test harness around the core::trust::mir::Agent implementation and the prompt provider.
Add a convenience function for processing incoming requests.

Description of the change

Provide an agent implementation that leverages Mir's trusted prompting API.
Introduce a default prompt provider using Qt/QML.
Provide a test harness around the core::trust::mir::Agent implementation and the prompt provider.
Add a convenience function for processing incoming requests.

To post a comment you must log in.
19. By Thomas Voß

Add another set of tests.

20. By Thomas Voß

Make sure that trust-prompt executable is installed with libtrust-store0.

21. By Thomas Voß

Adjust symbols file.

22. By Thomas Voß

Replace manual env-var checking.
Add missing build dependencies.

23. By Thomas Voß

Add build dependencies on qt.

24. By Thomas Voß

Add build-dependency on boost program options.

25. By Thomas Voß

Replace process with parse to prevent unexpected exits.

26. By Thomas Voß

Get rid of compile-time service name deduction.

27. By Thomas Voß

Temporarily alter the acceptance test to connect to mir's untrusted socket.

28. By Thomas Voß

Add missing build dependency on libjson-c-dev.

Revision history for this message
Seth Arnold (seth-arnold) wrote :

Looks good to me, one comment inline. Thanks

Revision history for this message
Marcus Tomlinson (marcustomlinson) wrote :

Ok, so there's a fair amount of code here, I hope I haven't missed anything major.
Here are a few observations:

35 + qt5-default,
36 + qtbase5-dev,

AFAIK qtbase5-dev is included in qt5-default

202 + * @param app_id The application Id of the requesting application.

OCD I know, but the everywhere else you've used "id" instead of "Id" (Should be "application id").

320 + * issueing a prompt request via the given agent to the user. On return, the given trust-store is up-to-date.

Should be: "issuing a prompt..."

334 + * core::trust::RequestParameters params
335 + * {
336 + * trust.agent,
337 + * trust.store,
338 + * app_id,
339 + * default_feature,
340 + * "Application " + app_id + " wants to access the example service."
341 + * };

You're missing "app_pid" (3rd param) between "trust.store" and "app_id".

362 + * std::shared_ptr<Store> store

Should be "std::shared_ptr<core::trust::Store> store"
(as it is for "core::trust::Agent" before this)

389 + * resources. For that, we severly limit an application's access to the system and

Should be "we severely limit..."

716 + static auto child_setup = []() {};

() not needed. Could just be: "static auto child_setup = []{};"
Just a personal style thing I guess.

854 +std::shared_ptr<core::trust::Agent> mir::create_agent_for_mir_connection(MirConnection* connection)

The implementation of this method (although impressively nested) is really hard to read. Just saying...

990 +// Abstracts common functionality required for runninging external helpers.

"runninging" ;)

1154 +inline QString appDirectory() {
1155 + if (isRunningInstalled()) {
1156 + return QString("@CMAKE_INSTALL_PREFIX@/@APP_DIR@/");

Is this not supposed to return "@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@" as is validated in isRunningInstalled()? (I'm probably just confused here)

1174 + * This file is part of dialer-app.

Really? You have 3 references to "diaper-app" in this header comment.

1255 + if (!parser.isSet(core::trust::mir::cli::option_title))
1256 + abort(); // We have to call abort here to make sure that we get signalled.

This app (trust-prompt) just aborts if the arguments are incorrect. Could you add a -h / --help handler to display the intended usage.

Also, when I run trust-prompt, the width of the window that appears seems to be too short (the left side of the "Deny" button is cut off a bit).

1879 + auto a_spinning_process = []()

Again, could just be: "auto a_spinning_process = []"

review: Needs Fixing
Revision history for this message
Seth Arnold (seth-arnold) wrote :

Actually, another question, this time about open file descriptors when the helper programs are executed.

29. By Thomas Voß

Fix some minor issues.

Revision history for this message
Marcus Tomlinson (marcustomlinson) wrote :

545 + static const std::string& name()

const std::string? Should we not rather use static constexpr const char*?

review: Needs Fixing
30. By Thomas Voß

Adjust implementation of isRunningInstalled to correctly check for LIB_DIR as opposed to BIN_DIR.

31. By Thomas Voß

Add missing return.

32. By Thomas Voß

Remove obsolete synchronous addition of prompt provider pids.

33. By Thomas Voß

Fix installation of QML file for default trust prompt.

34. By Thomas Voß

Adjust installation of prompt_main.qml.

35. By Thomas Voß

Adjust naming of prompt application helper.

36. By Thomas Voß

Make extra sure that MIR_SOCKET is set.

37. By Thomas Voß

Correctly format helper invocation.

38. By Thomas Voß

Switch to boost::program_options for cli processing trust-prompt.

39. By Thomas Voß

Fix crash at exit.

40. By Thomas Voß

Make sure that the event loop is cleaned up correctly.

41. By Thomas Voß

Refactor main prompt.

42. By Thomas Voß

Install custom message handler.

Revision history for this message
Marcus Tomlinson (marcustomlinson) wrote :

> 545 + static const std::string& name()
>
> const std::string? Should we not rather use static constexpr const char*?

Ah, I see this name() trait is somewhat of a standard amongst your projects. Sorry, ignore that one.

Revision history for this message
Marcus Tomlinson (marcustomlinson) wrote :

Symbols need updating again.

review: Needs Fixing
43. By Thomas Voß

Address reviewer comments.

44. By Thomas Voß

Remove symbol.

Revision history for this message
Thomas Voß (thomas-voss) wrote :
Download full text (3.2 KiB)

> Ok, so there's a fair amount of code here, I hope I haven't missed anything
> major.
> Here are a few observations:
>
> 35 + qt5-default,
> 36 + qtbase5-dev,
>
> AFAIK qtbase5-dev is included in qt5-default
>

Hmmm, seems to be the usual approach, though.

> 202 + * @param app_id The application Id of the requesting
> application.
>
> OCD I know, but the everywhere else you've used "id" instead of "Id" (Should
> be "application id").
>

Fixed.

> 320 + * issueing a prompt request via the given agent to the user. On
> return, the given trust-store is up-to-date.
>
> Should be: "issuing a prompt..."
>

Fixed.

> 334 + * core::trust::RequestParameters params
> 335 + * {
> 336 + * trust.agent,
> 337 + * trust.store,
> 338 + * app_id,
> 339 + * default_feature,
> 340 + * "Application " + app_id + " wants to access the
> example service."
> 341 + * };
>
> You're missing "app_pid" (3rd param) between "trust.store" and "app_id".
>

Fixed.

> 362 + * std::shared_ptr<Store> store
>
> Should be "std::shared_ptr<core::trust::Store> store"
> (as it is for "core::trust::Agent" before this)
>
> 389 + * resources. For that, we severly limit an application's access to
> the system and
>
> Should be "we severely limit..."
>

Fixed.

> 716 + static auto child_setup = []() {};
>
> () not needed. Could just be: "static auto child_setup = []{};"
> Just a personal style thing I guess.
>

Yup, personal style :) I like mine better :)

> 854 +std::shared_ptr<core::trust::Agent>
> mir::create_agent_for_mir_connection(MirConnection* connection)
>
> The implementation of this method (although impressively nested) is really
> hard to read. Just saying...
>

Adjusted.

> 990 +// Abstracts common functionality required for runninging external
> helpers.
>
> "runninging" ;)
>

Fixed.

> 1154 +inline QString appDirectory() {
> 1155 + if (isRunningInstalled()) {
> 1156 + return QString("@CMAKE_INSTALL_PREFIX@/@APP_DIR@/");
>
> Is this not supposed to return "@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@"
> as is validated in isRunningInstalled()? (I'm probably just confused here)
>

Fixed.

> 1174 + * This file is part of dialer-app.
>
> Really? You have 3 references to "diaper-app" in this header comment.
>

Fixed, like the typo, though :)

> 1255 + if (!parser.isSet(core::trust::mir::cli::option_title))
> 1256 + abort(); // We have to call abort here to make sure that we
> get signalled.
>
> This app (trust-prompt) just aborts if the arguments are incorrect. Could you
> add a -h / --help handler to display the intended usage.
>

Hmmm, it's actually not meant for public consumption :) So I would like to keep its chattiness to a minimum.

> Also, when I run trust-prompt, the width of the window that appears seems to
> be too short (the left side of the "Deny" button is cut off a bit).
>

Yup, known and working on phone.

> 1879 + auto a_spinning_process = []()
>
> Again, could just be: "auto a_spinning_process ...

Read more...

Revision history for this message
Thomas Voß (thomas-voss) wrote :

> Actually, another question, this time about open file descriptors when the
> helper programs are executed.

Right now, helper programs are by definition trusted by the helper and usually run unconfined. However, in a future iteration, whenever we start to allow confined applications to be added to trust sessions, we will likely leverage upstart to fire up the respective application. In that case, we would share the pre-authenticated mir socket via different means and would not share any fd with the helper app.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
45. By Thomas Voß

Adjust symbol files to account for architecture differences.

46. By Thomas Voß

i376 really should be i386.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
47. By Thomas Voß

Enable more architectures.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
48. By Thomas Voß

Adjust architecture.

49. By Thomas Voß

[ thomas-voss ]
* Remove obsolete data/session.conf and data/system.conf files. Adjust
  directory creation default mode to 0755. (LP: #1338587)
[ Ubuntu daily release ]
* New rebuild forced

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
50. By Thomas Voß

Remove obsolete include.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
51. By Thomas Voß

Re-enable test_data.h.in for capturing paths used during mir_agent_tests.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
52. By Thomas Voß

Add a small sleep, evil but necessary.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
53. By Thomas Voß

Clean up application on test end.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Marcus Tomlinson (marcustomlinson) wrote :

So I think this all looks pretty good, here are just a few more observations:

565 + static std::string s{"com.ubuntu.trust.store"};

I know it doesn't really matter but the "const" is missing here.
Should be: "static const std::string s{"com.ubuntu.trust.store"};"

1493 + (core::trust::mir::cli::option_title, boost::program_options::value<std::string>(), "Title of the prompt");

Missing a full-stop at the end of the string (doesn't match the others).

1530 + default:
1531 + break;

Redundant "break;"

2514 +std::shared_ptr<core::trust::Store::Query> a_null_query()

2052 +std::function<core::trust::Request::Answer(const core::posix::wait::Result&)> mock_translator_to_functor(const std::shared_ptr<MockTranslator>& ptr)

2062 +std::shared_ptr<core::trust::mir::PromptProviderHelper> a_prompt_provider_calling_bin_false()

2080 +std::shared_ptr<core::trust::mir::PromptProviderHelper> a_prompt_provider_calling_bin_true()

The above methods don't appear in your tests.

1426 === added file 'src/core/trust/mir/prompt_main.cpp'
2786 === added file 'tests/test_prompt.cpp'

These 2 files are pretty much doing the same thing. I get how one is a test version of the other but is it not a maintenance nightmare waiting to happen? If the app is used for internal use only, why not just check for something like a --testargs argument when running testing.

review: Needs Fixing
54. By Thomas Voß

Remove test_prompt executable and fold functionality into the trust-prompt executable.

Revision history for this message
Thomas Voß (thomas-voss) wrote :

> So I think this all looks pretty good, here are just a few more observations:
>
> 565 + static std::string s{"com.ubuntu.trust.store"};
>
> I know it doesn't really matter but the "const" is missing here.
> Should be: "static const std::string s{"com.ubuntu.trust.store"};"
>

Fixed.

> 1493 + (core::trust::mir::cli::option_title,
> boost::program_options::value<std::string>(), "Title of the prompt");
>
> Missing a full-stop at the end of the string (doesn't match the others).
>

Fixed.

> 1530 + default:
> 1531 + break;
>
> Redundant "break;"

But the compiler needs it :)
>
> 2514 +std::shared_ptr<core::trust::Store::Query> a_null_query()
>

Keeping it for future convenience.

> 2052 +std::function<core::trust::Request::Answer(const
> core::posix::wait::Result&)> mock_translator_to_functor(const
> std::shared_ptr<MockTranslator>& ptr)
>

Removed.

> 2062 +std::shared_ptr<core::trust::mir::PromptProviderHelper>
> a_prompt_provider_calling_bin_false()
>

Removed.

> 2080 +std::shared_ptr<core::trust::mir::PromptProviderHelper>
> a_prompt_provider_calling_bin_true()
>

Removed.

> The above methods don't appear in your tests.
>
> 1426 === added file 'src/core/trust/mir/prompt_main.cpp'
> 2786 === added file 'tests/test_prompt.cpp'
>
> These 2 files are pretty much doing the same thing. I get how one is a test
> version of the other but is it not a maintenance nightmare waiting to happen?
> If the app is used for internal use only, why not just check for something
> like a --testargs argument when running testing.

Fixed.

Revision history for this message
Marcus Tomlinson (marcustomlinson) wrote :

> > 1530 + default:
> > 1531 + break;
> >
> > Redundant "break;"
>
> But the compiler needs it :)

Oh, haha, why did I think it wasn't needed? Or, I mean, I was just testing you ;)

Anyways, LGTM!

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Seth Arnold (seth-arnold) wrote :

Thanks for the responses. Looks good to me.

review: Approve
55. By Thomas Voß

Add missing build dependency on libmirclient-dev.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2013-12-20 10:38:58 +0000
3+++ CMakeLists.txt 2014-07-17 18:44:56 +0000
4@@ -28,8 +28,10 @@
5 ENDIF(CMAKE_BUILD_TYPE MATCHES [cC][oO][vV][eE][rR][aA][gG][eE])
6
7 find_package(PkgConfig)
8-find_package(Boost COMPONENTS system REQUIRED)
9+find_package(Boost COMPONENTS program_options system REQUIRED)
10
11+pkg_check_modules(MIR_CLIENT mirclient REQUIRED)
12+pkg_check_modules(MIR_COMMON mircommon REQUIRED)
13 pkg_check_modules(PROCESS_CPP process-cpp REQUIRED)
14
15 set(TRUST_STORE_VERSION_MAJOR 0)
16@@ -40,6 +42,10 @@
17
18 include_directories(
19 include/
20+
21+ ${MIR_CLIENT_INCLUDE_DIRS}
22+ ${MIR_COMMON_INCLUDE_DIRS}
23+ ${PROCESS_CPP_INCLUDE_DIRS}
24 )
25
26 add_subdirectory(doc)
27
28=== modified file 'debian/control'
29--- debian/control 2014-06-24 13:30:26 +0000
30+++ debian/control 2014-07-17 18:44:56 +0000
31@@ -7,13 +7,20 @@
32 google-mock,
33 gcovr,
34 graphviz,
35+ libboost-program-options-dev,
36 libboost-system-dev,
37 libdbus-cpp-dev,
38 libdbus-1-dev,
39 libgtest-dev,
40+ libjson-c-dev,
41+ libmirclient-dev,
42 libprocess-cpp-dev,
43 libsqlite3-dev,
44 pkg-config,
45+ qt5-default,
46+ qtbase5-dev,
47+ qtdeclarative5-dev,
48+ qtquick1-5-dev,
49 Standards-Version: 3.9.5
50 Section: libs
51 Homepage: https://launchpad.net/trust-store
52@@ -49,6 +56,18 @@
53 This package includes all the development headers and libraries for
54 trust-store.
55
56+Package: trust-store-tests
57+Section: libdevel
58+Architecture: any
59+Depends: libtrust-store0 (= ${binary:Version}),
60+ ${misc:Depends},
61+Suggests: libtrust-store-dev,
62+Description: Test files for libtrust-store0
63+ Provides a common implementation of a trust store to be used by trusted
64+ helpers.
65+ .
66+ This package includes test executables packaged for post-build execution.
67+
68 Package: libtrust-store-doc
69 Section: doc
70 Architecture: all
71
72=== modified file 'debian/libtrust-store0.install'
73--- debian/libtrust-store0.install 2013-12-18 11:58:52 +0000
74+++ debian/libtrust-store0.install 2014-07-17 18:44:56 +0000
75@@ -1,1 +1,3 @@
76 usr/lib/*/lib*.so.*
77+usr/lib/*/trust-prompt
78+usr/share/core/trust/mir/*
79
80=== modified file 'debian/libtrust-store0.symbols'
81--- debian/libtrust-store0.symbols 2014-06-26 08:11:42 +0000
82+++ debian/libtrust-store0.symbols 2014-07-17 18:44:56 +0000
83@@ -1,29 +1,62 @@
84 libtrust-store.so.0 libtrust-store0 #MINVER#
85 (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
86+ (c++)"core::trust::process_trust_request(core::trust::RequestParameters const&)@Base" 0replaceme
87 (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
88 (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
89 (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
90 (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
91- (c++)"core::trust::Store::Query::Error::NoCurrentResult::~NoCurrentResult()@Base" 0.0.1+14.10.20140626.1
92- (c++)"core::trust::Store::Query::Error::QueryIsInErrorState::~QueryIsInErrorState()@Base" 0.0.1+14.10.20140626.1
93+ (c++)"core::trust::mir::PromptProviderHelper::InvocationArguments::~InvocationArguments()@Base" 0replaceme
94+ (c++)"core::trust::mir::PromptProviderHelper::exec_prompt_provider_with_arguments(core::trust::mir::PromptProviderHelper::InvocationArguments const&)@Base" 0replaceme
95+ (c++)"core::trust::mir::PromptProviderHelper::PromptProviderHelper(core::trust::mir::PromptProviderHelper::CreationArguments const&)@Base" 0replaceme
96+ (c++)"core::trust::mir::ConnectionVirtualTable::create_prompt_session_sync(int, void (*)(MirPromptSession*, MirPromptSessionState, void*), void*)@Base" 0replaceme
97+ (c++)"core::trust::mir::ConnectionVirtualTable::ConnectionVirtualTable(MirConnection*)@Base" 0replaceme
98+ (c++)"core::trust::mir::PromptSessionVirtualTable::release_sync()@Base" 0replaceme
99+ (c++|arch=amd64 armhf64 ppc64el)"core::trust::mir::PromptSessionVirtualTable::mir_client_fd_callback(MirPromptSession*, unsigned long, int const*, void*)@Base" 0replaceme
100+ (c++|arch=armhf i386 powerpc)"core::trust::mir::PromptSessionVirtualTable::mir_client_fd_callback(MirPromptSession*, unsigned int, int const*, void*)@Base" 0replaceme
101+ (c++)"core::trust::mir::PromptSessionVirtualTable::new_fd_for_prompt_provider()@Base" 0replaceme
102+ (c++)"core::trust::mir::PromptSessionVirtualTable::PromptSessionVirtualTable(MirPromptSession*)@Base" 0replaceme
103+ (c++)"core::trust::mir::create_agent_for_mir_connection(MirConnection*)@Base" 0replaceme
104+ (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
105+ (c++)"core::trust::mir::Agent::on_trust_session_changed_state(MirPromptSession*, MirPromptSessionState, void*)@Base" 0replaceme
106+ (c++)"core::trust::mir::Agent::translator_only_accepting_exit_status_success()@Base" 0replaceme
107+ (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
108+ (c++)"core::trust::mir::Agent::~Agent()@Base" 0replaceme
109+ (c++)"core::trust::mir::operator==(core::trust::mir::PromptProviderHelper::InvocationArguments const&, core::trust::mir::PromptProviderHelper::InvocationArguments const&)@Base" 0replaceme
110+ (c++)"core::trust::Store::Query::Errors::NoCurrentResult::~NoCurrentResult()@Base" 0replaceme
111+ (c++)"core::trust::Store::Query::Errors::QueryIsInErrorState::~QueryIsInErrorState()@Base" 0replaceme
112 (c++)"core::trust::Store::Errors::ErrorResettingStore::~ErrorResettingStore()@Base" 0.0.1+14.10.20140626.1
113 (c++)"core::trust::operator==(core::trust::Request const&, core::trust::Request const&)@Base" 0.0.1+14.10.20140626.1
114 (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
115 (c++)"core::trust::operator<<(std::basic_ostream<char, std::char_traits<char> >&, core::trust::Request const&)@Base" 0.0.1+14.10.20140626.1
116- (c++)"typeinfo for core::trust::Store::Query::Error::NoCurrentResult@Base" 0.0.1+14.10.20140626.1
117- (c++)"typeinfo for core::trust::Store::Query::Error::QueryIsInErrorState@Base" 0.0.1+14.10.20140626.1
118+ (c++)"typeinfo for core::trust::mir::PromptProviderHelper@Base" 0replaceme
119+ (c++)"typeinfo for core::trust::mir::ConnectionVirtualTable@Base" 0replaceme
120+ (c++)"typeinfo for core::trust::mir::PromptSessionVirtualTable@Base" 0replaceme
121+ (c++)"typeinfo for core::trust::mir::Agent@Base" 0replaceme
122+ (c++)"typeinfo for core::trust::Agent@Base" 0replaceme
123+ (c++)"typeinfo for core::trust::Store::Query::Errors::NoCurrentResult@Base" 0replaceme
124+ (c++)"typeinfo for core::trust::Store::Query::Errors::QueryIsInErrorState@Base" 0replaceme
125 (c++)"typeinfo for core::trust::Store::Query@Base" 0.0.1+14.10.20140626.1
126 (c++)"typeinfo for core::trust::Store::Errors::ErrorResettingStore@Base" 0.0.1+14.10.20140626.1
127 (c++)"typeinfo for core::trust::Store@Base" 0.0.1+14.10.20140626.1
128 (c++)"typeinfo for core::trust::Token@Base" 0.0.1+14.10.20140626.1
129- (c++)"typeinfo name for core::trust::Store::Query::Error::NoCurrentResult@Base" 0.0.1+14.10.20140626.1
130- (c++)"typeinfo name for core::trust::Store::Query::Error::QueryIsInErrorState@Base" 0.0.1+14.10.20140626.1
131+ (c++)"typeinfo name for core::trust::mir::PromptProviderHelper@Base" 0replaceme
132+ (c++)"typeinfo name for core::trust::mir::ConnectionVirtualTable@Base" 0replaceme
133+ (c++)"typeinfo name for core::trust::mir::PromptSessionVirtualTable@Base" 0replaceme
134+ (c++)"typeinfo name for core::trust::mir::Agent@Base" 0replaceme
135+ (c++)"typeinfo name for core::trust::Agent@Base" 0replaceme
136+ (c++)"typeinfo name for core::trust::Store::Query::Errors::NoCurrentResult@Base" 0replaceme
137+ (c++)"typeinfo name for core::trust::Store::Query::Errors::QueryIsInErrorState@Base" 0replaceme
138 (c++)"typeinfo name for core::trust::Store::Query@Base" 0.0.1+14.10.20140626.1
139 (c++)"typeinfo name for core::trust::Store::Errors::ErrorResettingStore@Base" 0.0.1+14.10.20140626.1
140 (c++)"typeinfo name for core::trust::Store@Base" 0.0.1+14.10.20140626.1
141 (c++)"typeinfo name for core::trust::Token@Base" 0.0.1+14.10.20140626.1
142- (c++)"vtable for core::trust::Store::Query::Error::NoCurrentResult@Base" 0.0.1+14.10.20140626.1
143- (c++)"vtable for core::trust::Store::Query::Error::QueryIsInErrorState@Base" 0.0.1+14.10.20140626.1
144+ (c++)"vtable for core::trust::mir::PromptProviderHelper@Base" 0replaceme
145+ (c++)"vtable for core::trust::mir::ConnectionVirtualTable@Base" 0replaceme
146+ (c++)"vtable for core::trust::mir::PromptSessionVirtualTable@Base" 0replaceme
147+ (c++)"vtable for core::trust::mir::Agent@Base" 0replaceme
148+ (c++)"vtable for core::trust::Agent@Base" 0replaceme
149+ (c++)"vtable for core::trust::Store::Query::Errors::NoCurrentResult@Base" 0replaceme
150+ (c++)"vtable for core::trust::Store::Query::Errors::QueryIsInErrorState@Base" 0replaceme
151 (c++)"vtable for core::trust::Store::Query@Base" 0.0.1+14.10.20140626.1
152 (c++)"vtable for core::trust::Store::Errors::ErrorResettingStore@Base" 0.0.1+14.10.20140626.1
153 (c++)"vtable for core::trust::Store@Base" 0.0.1+14.10.20140626.1
154
155=== added file 'debian/trust-store-tests.install'
156--- debian/trust-store-tests.install 1970-01-01 00:00:00 +0000
157+++ debian/trust-store-tests.install 2014-07-17 18:44:56 +0000
158@@ -0,0 +1,1 @@
159+usr/bin/trust-store-tests/*
160
161=== added file 'include/core/trust/agent.h'
162--- include/core/trust/agent.h 1970-01-01 00:00:00 +0000
163+++ include/core/trust/agent.h 2014-07-17 18:44:56 +0000
164@@ -0,0 +1,57 @@
165+/*
166+ * Copyright © 2014 Canonical Ltd.
167+ *
168+ * This program is free software: you can redistribute it and/or modify it
169+ * under the terms of the GNU Lesser General Public License version 3,
170+ * as published by the Free Software Foundation.
171+ *
172+ * This program is distributed in the hope that it will be useful,
173+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
174+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
175+ * GNU Lesser General Public License for more details.
176+ *
177+ * You should have received a copy of the GNU Lesser General Public License
178+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
179+ *
180+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
181+ */
182+
183+#ifndef CORE_TRUST_AGENT_H_
184+#define CORE_TRUST_AGENT_H_
185+
186+#include <core/trust/request.h>
187+#include <core/trust/visibility.h>
188+
189+namespace core
190+{
191+namespace trust
192+{
193+// Forward-declarations.
194+struct RequestParameters;
195+
196+/** @brief Abstracts user-prompting functionality. */
197+class CORE_TRUST_DLL_PUBLIC Agent
198+{
199+public:
200+ /** @cond */
201+ Agent() = default;
202+ virtual ~Agent() = default;
203+
204+ Agent(const Agent&) = delete;
205+ Agent(Agent&&) = delete;
206+ Agent& operator=(const Agent&) = delete;
207+ Agent& operator=(Agent&&) = delete;
208+ /** @endcond */
209+
210+ /**
211+ * @brief Presents the given request to the user, returning the user-provided answer.
212+ * @param app_pid The process id of the requesting application.
213+ * @param app_id The application id of the requesting application.
214+ * @param description Extended description of the trust request.
215+ */
216+ virtual Request::Answer prompt_user_for_request(pid_t app_pid, const std::string& app_id, const std::string& description) = 0;
217+};
218+}
219+}
220+
221+#endif // CORE_TRUST_AGENT_H_
222
223=== added file 'include/core/trust/mir_agent.h'
224--- include/core/trust/mir_agent.h 1970-01-01 00:00:00 +0000
225+++ include/core/trust/mir_agent.h 2014-07-17 18:44:56 +0000
226@@ -0,0 +1,48 @@
227+/*
228+ * Copyright © 2014 Canonical Ltd.
229+ *
230+ * This program is free software: you can redistribute it and/or modify it
231+ * under the terms of the GNU Lesser General Public License version 3,
232+ * as published by the Free Software Foundation.
233+ *
234+ * This program is distributed in the hope that it will be useful,
235+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
236+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
237+ * GNU Lesser General Public License for more details.
238+ *
239+ * You should have received a copy of the GNU Lesser General Public License
240+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
241+ *
242+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
243+ */
244+
245+#ifndef CORE_TRUST_MIR_AGENT_H_
246+#define CORE_TRUST_MIR_AGENT_H_
247+
248+#include <core/trust/visibility.h>
249+
250+#include <memory>
251+
252+// Forward declare the MirConnection type.
253+struct MirConnection;
254+
255+namespace core
256+{
257+namespace trust
258+{
259+// Forward declare the Agent interface.
260+class Agent;
261+
262+namespace mir
263+{
264+/**
265+ * @brief create_agent_for_mir_connection creates a trust::Agent implementation leveraging Mir's trusted prompting API.
266+ * @param connection An existing connection to a Mir instance.
267+ * @throws std::logic_error if the connection object is NULL.
268+ */
269+CORE_TRUST_DLL_PUBLIC std::shared_ptr<core::trust::Agent> create_agent_for_mir_connection(MirConnection* connection);
270+}
271+}
272+}
273+
274+#endif // CORE_TRUST_MIR_AGENT_H_
275
276=== modified file 'include/core/trust/request.h'
277--- include/core/trust/request.h 2014-05-06 11:32:13 +0000
278+++ include/core/trust/request.h 2014-07-17 18:44:56 +0000
279@@ -24,6 +24,7 @@
280 #include <cstdint>
281
282 #include <chrono>
283+#include <memory>
284 #include <ostream>
285 #include <string>
286
287@@ -31,6 +32,10 @@
288 {
289 namespace trust
290 {
291+// Forward declarations
292+class Agent;
293+class Store;
294+
295 /**
296 * @brief The Request struct encapsulates information about a trust request answered by the user.
297 *
298@@ -59,7 +64,7 @@
299 enum class Answer
300 {
301 denied, ///< Nope, I do not trust this application.
302- granted ///< Yup, I do trust this application.
303+ granted, ///< Yup, I do trust this application.
304 };
305
306 /** The application id of the application that resulted in the request. */
307@@ -94,6 +99,78 @@
308 * @return The output stream.
309 */
310 CORE_TRUST_DLL_PUBLIC std::ostream& operator<<(std::ostream& out, const Request& r);
311+
312+/** @brief Summarizes all parameters for processing a trust request. */
313+struct CORE_TRUST_DLL_PUBLIC RequestParameters
314+{
315+ /** @brief The Agent implementation to dispatch a request to the user. */
316+ std::shared_ptr<Agent> agent;
317+ /** @brief The trust store to be used for caching purposes. */
318+ std::shared_ptr<Store> store;
319+ /** @brief The process id of the requesting application. */
320+ pid_t application_pid;
321+ /** @brief The id of the requesting application. */
322+ std::string application_id;
323+ /** @brief The service-specific feature identifier. */
324+ std::uint64_t feature;
325+ /** @brief An extended description that should be presented to the user on prompting. */
326+ std::string description;
327+};
328+
329+/**
330+ * @brief Processes an incoming trust-request by an application, tries to lookup a previous reply before
331+ * issuing a prompt request via the given agent to the user. On return, the given trust-store is up-to-date.
332+ *
333+ * @throws std::exception To indicate that no conclusive answer could be resolved from either the store or
334+ * the user. In that case, the state of the store instance passed in to the function is not altered.
335+ *
336+ * The following code snippet illustrates how to use the function:
337+ *
338+ * @code
339+ * struct Service
340+ * {
341+ * static constexpr std::uint64_t default_feature = 0;
342+ *
343+ * void on_session_requested(pid_t app_pid, const std::string& app_id)
344+ * {
345+ * core::trust::RequestParameters params
346+ * {
347+ * trust.agent,
348+ * trust.store,
349+ * app_pid,
350+ * app_id,
351+ * default_feature,
352+ * "Application " + app_id + " wants to access the example service."
353+ * };
354+ *
355+ * switch(process_trust_request(params))
356+ * {
357+ * case core::trust::Request::Answer::granted:
358+ * // Create session and get back to application with session credentials.
359+ * break;
360+ * case core::trust::Request::Answer::denied:
361+ * // Deny session creation and inform application.
362+ * break;
363+ * }
364+ * }
365+ *
366+ * struct
367+ * {
368+ * // We use Mir's trust session support to request the prompting UI.
369+ * std::shared_ptr<core::trust::Agent> agent
370+ * {
371+ * core::trust::mir::make_agent_for_existing_connection(mir_connection)
372+ * };
373+ *
374+ * std::shared_ptr<core::trust::Store> store
375+ * {
376+ * core::trust::create_default_store("my.example.service");
377+ * };
378+ * } trust;
379+ * };
380+ * @endcode
381+ */
382+CORE_TRUST_DLL_PUBLIC Request::Answer process_trust_request(const RequestParameters& params);
383 }
384 }
385
386
387=== modified file 'include/core/trust/store.h'
388--- include/core/trust/store.h 2014-05-06 11:32:13 +0000
389+++ include/core/trust/store.h 2014-07-17 18:44:56 +0000
390@@ -28,6 +28,18 @@
391
392 namespace core
393 {
394+/**
395+ * @brief Contains functionality for implementing Ubuntu's trust model.
396+ *
397+ * Ubuntu's trust model extends upon a strict confinement approach implemented
398+ * on top of AppArmor. In this approach, applications are not trusted by default, and
399+ * we assume a very negative view of the app world. That is, we assume that all apps
400+ * are created with malicious intentions in mind, invading a user's privacy and wasting
401+ * resources. For that, we severely limit an application's access to the system and
402+ * provide trusted gates out of the confinement. These trusted gates, also called trusted helpers,
403+ * ensure that the user is prompted for granting or denying trust to a specific application.
404+ *
405+ */
406 namespace trust
407 {
408 /**
409@@ -36,9 +48,12 @@
410 class CORE_TRUST_DLL_PUBLIC Store
411 {
412 public:
413+ /** @brief All Store-specific error/exception types go here. */
414 struct Errors
415 {
416+ /** @cond */
417 Errors() = delete;
418+ /** @endcond */
419
420 /**
421 * @brief Thrown if a store implementation could not access the persistence backend.
422@@ -70,9 +85,12 @@
423 class Query
424 {
425 public:
426- struct Error
427+ /** @brief All Query-specific error/exception types go here. */
428+ struct Errors
429 {
430- Error() = delete;
431+ /** @cond */
432+ Errors() = delete;
433+ /** @endcond */
434 /**
435 * @brief Thrown if functionality of a query is accessed although the query is in error state.
436 */
437@@ -165,9 +183,12 @@
438 Store() = default;
439 };
440
441-struct Error
442+/** @brief All core::trust-specific error/exception types go here. */
443+struct Errors
444 {
445- Error() = delete;
446+ /** @cond */
447+ Errors() = delete;
448+ /** @endcond */
449
450 /**
451 * @brief The ServiceNameMustNotBeEmpty is thrown if an empty service name
452
453=== modified file 'src/CMakeLists.txt'
454--- src/CMakeLists.txt 2014-05-06 11:52:46 +0000
455+++ src/CMakeLists.txt 2014-07-17 18:44:56 +0000
456@@ -14,6 +14,10 @@
457 #
458 # Authored by: Thomas Voss <thomas.voss@canonical.com>
459
460+find_package(Qt5Core REQUIRED)
461+find_package(Qt5Qml REQUIRED)
462+find_package(Qt5Quick REQUIRED)
463+
464 pkg_check_modules(DBUS_CPP dbus-cpp REQUIRED)
465 pkg_check_modules(DBUS dbus-1 REQUIRED)
466 pkg_check_modules(SQLITE3 sqlite3 REQUIRED)
467@@ -24,14 +28,51 @@
468 ${SQLITE3_INCLUDE_DIRS}
469 )
470
471+# Make sure Qt does not inject evil macros like 'signals' and 'slots'.
472+add_definitions(-DQT_NO_KEYWORDS)
473+# We need this for building the Qt-based prompt UI
474+set(CMAKE_AUTOMOC ON)
475+set(CMAKE_INCLUDE_CURRENT_DIR ON)
476+
477 add_library(
478 trust-store SHARED
479
480 core/trust/expose.cpp
481 core/trust/request.cpp
482 core/trust/resolve.cpp
483+
484+ # The default implementation leverages SQLite3 to persist
485+ # requests.
486 core/trust/impl/sqlite3/store.cpp
487-)
488+
489+ # An agent-implementation leveraging Mir's trusted prompting support
490+ # to prompt the user for trusting an application to access a trusted
491+ # system service.
492+ core/trust/mir/agent.cpp
493+)
494+
495+configure_file(
496+ ${CMAKE_CURRENT_SOURCE_DIR}/core/trust/mir/config.h.in
497+ ${CMAKE_CURRENT_BINARY_DIR}/core/trust/mir/config.h @ONLY)
498+
499+configure_file(
500+ ${CMAKE_CURRENT_SOURCE_DIR}/core/trust/mir/prompt_config.h.in
501+ ${CMAKE_CURRENT_BINARY_DIR}/core/trust/mir/prompt_config.h @ONLY)
502+
503+include_directories(${CMAKE_CURRENT_BINARY_DIR}/core/trust/mir/)
504+
505+add_executable(
506+ trust-prompt
507+
508+ core/trust/mir/prompt_main.cpp
509+)
510+
511+set_target_properties(
512+ trust-prompt
513+ PROPERTIES INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR}/core/trust/mir
514+)
515+
516+qt5_use_modules(trust-prompt Core Gui Qml Quick)
517
518 target_link_libraries(
519 trust-store
520@@ -40,9 +81,19 @@
521
522 ${Boost_LIBRARIES}
523 ${DBUS_LIBRARIES}
524+ ${MIR_CLIENT_LDFLAGS}
525+ ${MIR_COMMON_LDFLAGS}
526+ ${PROCESS_CPP_LDFLAGS}
527 ${SQLITE3_LIBRARIES}
528 )
529
530+target_link_libraries(
531+ trust-prompt
532+
533+ ${Boost_LIBRARIES}
534+ ${PROCESS_CPP_LDFLAGS}
535+)
536+
537 # We compile with all symbols visible by default. For the shipping library, we strip
538 # out all symbols that are not in core::dbus::*
539 set(symbol_map "${CMAKE_SOURCE_DIR}/symbols.map")
540@@ -63,3 +114,13 @@
541 TARGETS trust-store
542 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
543 )
544+
545+install(
546+ TARGETS trust-prompt
547+ RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}
548+)
549+
550+install(
551+ FILES core/trust/mir/prompt_main.qml
552+ DESTINATION ${CMAKE_INSTALL_DATADIR}/core/trust/mir
553+)
554
555=== modified file 'src/core/trust/dbus_interface.h'
556--- src/core/trust/dbus_interface.h 2013-12-21 08:05:08 +0000
557+++ src/core/trust/dbus_interface.h 2014-07-17 18:44:56 +0000
558@@ -63,17 +63,12 @@
559 {
560 struct Store
561 {
562- static std::string& mutable_name()
563+ static const std::string& name()
564 {
565- static std::string s;
566+ static const std::string s{"com.ubuntu.trust.store"};
567 return s;
568 }
569
570- static const std::string& name()
571- {
572- return mutable_name();
573- }
574-
575 struct Error
576 {
577 struct AddingRequest
578
579=== modified file 'src/core/trust/expose.cpp'
580--- src/core/trust/expose.cpp 2014-05-06 11:32:13 +0000
581+++ src/core/trust/expose.cpp 2014-07-17 18:44:56 +0000
582@@ -69,13 +69,15 @@
583 std::map<Key, Value> map;
584 };
585
586-struct Token : public core::trust::Token, public dbus::Skeleton<core::trust::dbus::Store>
587+struct Token : public core::trust::Token
588 {
589- Token(const std::shared_ptr<dbus::Bus>& bus,
590+ Token(const std::string& service_name,
591+ const std::shared_ptr<dbus::Bus>& bus,
592 const std::shared_ptr<core::trust::Store>& store)
593- : dbus::Skeleton<core::trust::dbus::Store>(bus),
594- store(store),
595- object(access_service()->add_object_for_path(dbus::types::ObjectPath::root()))
596+ : store(store),
597+ bus(bus),
598+ service(dbus::Service::add_service(bus, service_name)),
599+ object(service->add_object_for_path(dbus::types::ObjectPath::root()))
600 {
601 object->install_method_handler<core::trust::dbus::Store::Add>([this](const core::dbus::Message::Ptr& msg)
602 {
603@@ -97,15 +99,18 @@
604 handle_remove_query(msg);
605 });
606
607- worker = std::move(std::thread([this](){access_bus()->run();}));
608+ worker = std::move(std::thread([this](){Token::bus->run();}));
609 }
610
611 ~Token()
612 {
613 object->uninstall_method_handler<core::trust::dbus::Store::Add>();
614 object->uninstall_method_handler<core::trust::dbus::Store::Reset>();
615-
616- access_bus()->stop();
617+ object->uninstall_method_handler<core::trust::dbus::Store::AddQuery>();
618+ object->uninstall_method_handler<core::trust::dbus::Store::RemoveQuery>();
619+
620+ bus->stop();
621+
622 if (worker.joinable())
623 worker.join();
624 }
625@@ -125,12 +130,12 @@
626 core::trust::dbus::Store::Error::AddingRequest::name(),
627 e.what());
628
629- access_bus()->send(error);
630+ bus->send(error);
631 return;
632 }
633
634 auto reply = dbus::Message::make_method_return(msg);
635- access_bus()->send(reply);
636+ bus->send(reply);
637 }
638
639 void handle_reset(const core::dbus::Message::Ptr& msg)
640@@ -145,11 +150,11 @@
641 core::trust::dbus::Store::Error::ResettingStore::name(),
642 e.what());
643
644- access_bus()->send(error);
645+ bus->send(error);
646 }
647
648 auto reply = dbus::Message::make_method_return(msg);
649- access_bus()->send(reply);
650+ bus->send(reply);
651 }
652
653 void handle_add_query(const core::dbus::Message::Ptr& msg)
654@@ -160,16 +165,16 @@
655 {
656 core::dbus::types::ObjectPath path{"/queries/" + std::to_string(query_counter++)};
657 auto query = store->query();
658- auto object = access_service()->add_object_for_path(path);
659- auto bus = access_bus();
660- object->install_method_handler<core::trust::dbus::Store::Query::All>([bus, query](const core::dbus::Message::Ptr& msg)
661+ auto object = service->add_object_for_path(path);
662+
663+ object->install_method_handler<core::trust::dbus::Store::Query::All>([this, query](const core::dbus::Message::Ptr& msg)
664 {
665 query->all();
666
667 auto reply = core::dbus::Message::make_method_return(msg);
668 bus->send(reply);
669 });
670- object->install_method_handler<core::trust::dbus::Store::Query::Current>([bus, query](const core::dbus::Message::Ptr& msg)
671+ object->install_method_handler<core::trust::dbus::Store::Query::Current>([this, query](const core::dbus::Message::Ptr& msg)
672 {
673 try
674 {
675@@ -177,7 +182,7 @@
676 auto reply = core::dbus::Message::make_method_return(msg);
677 reply->writer() << request;
678 bus->send(reply);
679- } catch(const core::trust::Store::Query::Error::NoCurrentResult& e)
680+ } catch(const core::trust::Store::Query::Errors::NoCurrentResult& e)
681 {
682 auto error = core::dbus::Message::make_error(
683 msg,
684@@ -187,21 +192,21 @@
685 bus->send(error);
686 }
687 });
688- object->install_method_handler<core::trust::dbus::Store::Query::Erase>([bus, query](const core::dbus::Message::Ptr& msg)
689+ object->install_method_handler<core::trust::dbus::Store::Query::Erase>([this, query](const core::dbus::Message::Ptr& msg)
690 {
691 query->erase();
692
693 auto reply = core::dbus::Message::make_method_return(msg);
694 bus->send(reply);
695 });
696- object->install_method_handler<core::trust::dbus::Store::Query::Execute>([bus, query](const core::dbus::Message::Ptr& msg)
697+ object->install_method_handler<core::trust::dbus::Store::Query::Execute>([this, query](const core::dbus::Message::Ptr& msg)
698 {
699 query->execute();
700
701 auto reply = core::dbus::Message::make_method_return(msg);
702 bus->send(reply);
703 });
704- object->install_method_handler<core::trust::dbus::Store::Query::ForAnswer>([bus, query](const core::dbus::Message::Ptr& msg)
705+ object->install_method_handler<core::trust::dbus::Store::Query::ForAnswer>([this, query](const core::dbus::Message::Ptr& msg)
706 {
707 core::trust::Request::Answer a; msg->reader() >> a;
708 query->for_answer(a);
709@@ -209,7 +214,7 @@
710 auto reply = core::dbus::Message::make_method_return(msg);
711 bus->send(reply);
712 });
713- object->install_method_handler<core::trust::dbus::Store::Query::ForApplicationId>([bus, query](const core::dbus::Message::Ptr& msg)
714+ object->install_method_handler<core::trust::dbus::Store::Query::ForApplicationId>([this, query](const core::dbus::Message::Ptr& msg)
715 {
716 std::string app_id; msg->reader() >> app_id;
717 query->for_application_id(app_id);
718@@ -217,7 +222,7 @@
719 auto reply = core::dbus::Message::make_method_return(msg);
720 bus->send(reply);
721 });
722- object->install_method_handler<core::trust::dbus::Store::Query::ForFeature>([bus, query](const core::dbus::Message::Ptr& msg)
723+ object->install_method_handler<core::trust::dbus::Store::Query::ForFeature>([this, query](const core::dbus::Message::Ptr& msg)
724 {
725 std::uint64_t feature; msg->reader() >> feature;
726 query->for_feature(feature);
727@@ -225,7 +230,7 @@
728 auto reply = core::dbus::Message::make_method_return(msg);
729 bus->send(reply);
730 });
731- object->install_method_handler<core::trust::dbus::Store::Query::ForInterval>([bus, query](const core::dbus::Message::Ptr& msg)
732+ object->install_method_handler<core::trust::dbus::Store::Query::ForInterval>([this, query](const core::dbus::Message::Ptr& msg)
733 {
734 std::tuple<std::int64_t, std::int64_t> interval; msg->reader() >> interval;
735
736@@ -237,14 +242,14 @@
737 auto reply = core::dbus::Message::make_method_return(msg);
738 bus->send(reply);
739 });
740- object->install_method_handler<core::trust::dbus::Store::Query::Next>([bus, query](const core::dbus::Message::Ptr& msg)
741+ object->install_method_handler<core::trust::dbus::Store::Query::Next>([this, query](const core::dbus::Message::Ptr& msg)
742 {
743 query->next();
744
745 auto reply = core::dbus::Message::make_method_return(msg);
746 bus->send(reply);
747 });
748- object->install_method_handler<core::trust::dbus::Store::Query::Status>([bus, query](const core::dbus::Message::Ptr& msg)
749+ object->install_method_handler<core::trust::dbus::Store::Query::Status>([this, query](const core::dbus::Message::Ptr& msg)
750 {
751 auto reply = core::dbus::Message::make_method_return(msg);
752 reply->writer() << query->status();
753@@ -255,7 +260,7 @@
754
755 auto reply = dbus::Message::make_method_return(msg);
756 reply->writer() << path;
757- access_bus()->send(reply);
758+ bus->send(reply);
759 } catch(const std::runtime_error& e)
760 {
761 auto error = core::dbus::Message::make_error(
762@@ -263,7 +268,7 @@
763 core::trust::dbus::Store::Error::CreatingQuery::name(),
764 e.what());
765
766- access_bus()->send(error);
767+ bus->send(error);
768 }
769 }
770
771@@ -275,17 +280,19 @@
772 query_store.erase(path);
773
774 auto reply = dbus::Message::make_method_return(msg);
775- access_bus()->send(reply);
776+ bus->send(reply);
777 } catch(...)
778 {
779 }
780
781 auto reply = dbus::Message::make_method_return(msg);
782 reply->writer();
783- access_bus()->send(reply);
784+ bus->send(reply);
785 }
786
787 std::shared_ptr<core::trust::Store> store;
788+ std::shared_ptr<dbus::Bus> bus;
789+ std::shared_ptr<dbus::Service> service;
790 std::shared_ptr<dbus::Object> object;
791 std::thread worker;
792
793@@ -301,10 +308,17 @@
794 const std::string& name)
795 {
796 if (name.empty())
797- throw Error::ServiceNameMustNotBeEmpty{};
798+ throw Errors::ServiceNameMustNotBeEmpty{};
799
800- core::trust::dbus::Store::mutable_name() = "com.ubuntu.trust.store." + name;
801- return std::move(std::unique_ptr<core::trust::Token>(new detail::Token{bus, store}));
802+ return std::move(std::unique_ptr<core::trust::Token>
803+ {
804+ new detail::Token
805+ {
806+ "com.ubuntu.trust.store." + name,
807+ bus,
808+ store
809+ }
810+ });
811 }
812
813 std::unique_ptr<core::trust::Token>
814
815=== modified file 'src/core/trust/impl/sqlite3/store.cpp'
816--- src/core/trust/impl/sqlite3/store.cpp 2014-07-16 06:59:46 +0000
817+++ src/core/trust/impl/sqlite3/store.cpp 2014-07-17 18:44:56 +0000
818@@ -18,6 +18,8 @@
819
820 #include <core/trust/store.h>
821
822+#include <core/posix/this_process.h>
823+
824 #include <sqlite3.h>
825
826 #include <cstring>
827@@ -32,18 +34,14 @@
828 {
829 std::string home()
830 {
831- return std::string{::getenv("HOME")};
832+ return core::posix::this_process::env::get_or_throw("HOME");
833 }
834
835 std::string runtime_persistent_data_dir()
836 {
837- char* value = ::getenv("XDG_DATA_HOME");
838- if (!value || value[0] == '0')
839- {
840- return std::string{home() + "/.local/share"};
841- }
842-
843- return std::string{value};
844+ return core::posix::this_process::env::get(
845+ "XDG_DATA_HOME",
846+ home() + "/.local/share");
847 }
848
849 struct Directory
850@@ -594,9 +592,9 @@
851 {
852 switch(d.status)
853 {
854- case Status::error: throw Error::QueryIsInErrorState{};
855- case Status::eor: throw Error::NoCurrentResult{};
856- case Status::armed: throw Error::NoCurrentResult{};
857+ case Status::error: throw Errors::QueryIsInErrorState{};
858+ case Status::eor: throw Errors::NoCurrentResult{};
859+ case Status::armed: throw Errors::NoCurrentResult{};
860 default:
861 {
862 trust::Request request
863@@ -721,7 +719,7 @@
864 std::shared_ptr<core::trust::Store> core::trust::create_default_store(const std::string& service_name)
865 {
866 if (service_name.empty())
867- throw core::trust::Error::ServiceNameMustNotBeEmpty();
868+ throw core::trust::Errors::ServiceNameMustNotBeEmpty();
869
870 return std::shared_ptr<trust::Store>{new sqlite::Store(service_name)};
871 }
872
873=== added directory 'src/core/trust/mir'
874=== added file 'src/core/trust/mir/agent.cpp'
875--- src/core/trust/mir/agent.cpp 1970-01-01 00:00:00 +0000
876+++ src/core/trust/mir/agent.cpp 2014-07-17 18:44:56 +0000
877@@ -0,0 +1,267 @@
878+/*
879+ * Copyright © 2014 Canonical Ltd.
880+ *
881+ * This program is free software: you can redistribute it and/or modify it
882+ * under the terms of the GNU Lesser General Public License version 3,
883+ * as published by the Free Software Foundation.
884+ *
885+ * This program is distributed in the hope that it will be useful,
886+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
887+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
888+ * GNU Lesser General Public License for more details.
889+ *
890+ * You should have received a copy of the GNU Lesser General Public License
891+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
892+ *
893+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
894+ */
895+
896+#include "agent.h"
897+
898+#include "prompt_main.h"
899+
900+namespace mir = core::trust::mir;
901+
902+// Invoked whenever a request for creation of pre-authenticated fds succeeds.
903+void mir::PromptSessionVirtualTable::mir_client_fd_callback(MirPromptSession */*prompt_session*/, size_t count, int const* fds, void* context)
904+{
905+ if (count == 0)
906+ return;
907+
908+ auto ctxt = static_cast<mir::PromptSessionVirtualTable::Context*>(context);
909+
910+ if (not ctxt)
911+ return;
912+
913+ ctxt->fd = fds[0];
914+}
915+
916+mir::PromptSessionVirtualTable::PromptSessionVirtualTable(MirPromptSession* prompt_session)
917+ : prompt_session(prompt_session)
918+{
919+}
920+
921+int mir::PromptSessionVirtualTable::new_fd_for_prompt_provider()
922+{
923+ static const unsigned int fd_count = 1;
924+
925+ mir::PromptSessionVirtualTable::Context context;
926+
927+ mir_wait_for(mir_prompt_session_new_fds_for_prompt_providers(
928+ prompt_session,
929+ fd_count,
930+ PromptSessionVirtualTable::mir_client_fd_callback,
931+ &context));
932+
933+ if (context.fd == Context::invalid_fd) throw std::runtime_error
934+ {
935+ "Could not acquire pre-authenticated file descriptors for Mir prompt session."
936+ };
937+
938+ return context.fd;
939+}
940+
941+void mir::PromptSessionVirtualTable::release_sync()
942+{
943+ mir_prompt_session_release_sync(prompt_session);
944+}
945+
946+mir::ConnectionVirtualTable::ConnectionVirtualTable(MirConnection* connection)
947+ : connection{connection}
948+{
949+}
950+
951+mir::PromptSessionVirtualTable::Ptr mir::ConnectionVirtualTable::create_prompt_session_sync(
952+ // The process id of the requesting app/service
953+ pid_t app_pid,
954+ // Callback handling prompt session state changes.
955+ mir_prompt_session_state_change_callback cb,
956+ // Callback context
957+ void* context)
958+{
959+ return PromptSessionVirtualTable::Ptr
960+ {
961+ new PromptSessionVirtualTable
962+ {
963+ mir_connection_create_prompt_session_sync(connection, app_pid, cb, context)
964+ }
965+ };
966+}
967+
968+mir::PromptProviderHelper::PromptProviderHelper(
969+ const mir::PromptProviderHelper::CreationArguments& args) : creation_arguments(args)
970+{
971+}
972+
973+core::posix::ChildProcess mir::PromptProviderHelper::exec_prompt_provider_with_arguments(
974+ const mir::PromptProviderHelper::InvocationArguments& args)
975+{
976+ static auto child_setup = []() {};
977+
978+ std::vector<std::string> argv
979+ {
980+ "--" + std::string{core::trust::mir::cli::option_server_socket}, "fd://" + std::to_string(args.fd),
981+ "--" + std::string{core::trust::mir::cli::option_title}, args.application_id,
982+ "--" + std::string{core::trust::mir::cli::option_description}, args.description
983+ };
984+
985+ // We just copy the environment
986+ std::map<std::string, std::string> env;
987+ core::posix::this_process::env::for_each([&env](const std::string& key, const std::string& value)
988+ {
989+ env.insert(std::make_pair(key, value));
990+ });
991+
992+
993+ auto result = core::posix::exec(creation_arguments.path_to_helper_executable,
994+ argv,
995+ env,
996+ core::posix::StandardStream::empty,
997+ child_setup);
998+
999+ return result;
1000+}
1001+
1002+void mir::Agent::on_trust_session_changed_state(
1003+ // The prompt session instance that just changed state.
1004+ MirPromptSession* /*prompt_provider*/,
1005+ // The new state of the prompt session instance.
1006+ MirPromptSessionState state,
1007+ // The context of type context.
1008+ void* context)
1009+{
1010+ if (mir_prompt_session_state_stopped != state)
1011+ return;
1012+
1013+ auto ctxt = static_cast<mir::Agent::OnTrustSessionStateChangedCallbackContext*>(context);
1014+
1015+ if (not ctxt)
1016+ return;
1017+
1018+ std::error_code ec;
1019+ // If the trust session ended (for whatever reason), we send a SIG_KILL to the
1020+ // prompt provider process. We hereby ensure that we never return Answer::granted
1021+ // unless the prompt provider cleanly exited prior to the trust session stopping.
1022+ ctxt->prompt_provider_process.send_signal(core::posix::Signal::sig_kill, ec);
1023+ // The required wait for the child process happens in prompt_user_for_request(...).
1024+ // TODO(tvoss): We should log ec in case of errors.
1025+}
1026+
1027+std::function<core::trust::Request::Answer(const core::posix::wait::Result&)> mir::Agent::translator_only_accepting_exit_status_success()
1028+{
1029+ return [](const core::posix::wait::Result& result) -> core::trust::Request::Answer
1030+ {
1031+ // We now analyze the result of the process execution.
1032+ if (core::posix::wait::Result::Status::exited != result.status) throw std::logic_error
1033+ {
1034+ "The prompt provider process was signaled or stopped, "
1035+ "unable to determine a conclusive answer from the user"
1036+ };
1037+
1038+ // If the child process did not exit cleanly, we deny access to the resource.
1039+ if (core::posix::exit::Status::failure == result.detail.if_exited.status)
1040+ return core::trust::Request::Answer::denied;
1041+
1042+ return core::trust::Request::Answer::granted;
1043+ };
1044+}
1045+
1046+mir::Agent::Agent(
1047+ // VTable object providing access to Mir's trusted prompting functionality.
1048+ const mir::ConnectionVirtualTable::Ptr& connection_vtable,
1049+ // Exec helper for starting up prompt provider child processes with the correct setup
1050+ // of command line arguments and environment variables.
1051+ const mir::PromptProviderHelper::Ptr& exec_helper,
1052+ // A translator function for mapping child process exit states to trust::Request answers.
1053+ const std::function<core::trust::Request::Answer(const core::posix::wait::Result&)>& translator)
1054+ : connection_vtable(connection_vtable),
1055+ exec_helper(exec_helper),
1056+ translator(translator)
1057+{
1058+}
1059+
1060+// From core::trust::Agent:
1061+core::trust::Request::Answer mir::Agent::prompt_user_for_request(pid_t app_pid, const std::string& app_id, const std::string& description)
1062+{
1063+ // We initialize our callback context with an invalid child-process for setup
1064+ // purposes. Later on, once we have acquired a pre-authenticated fd for the
1065+ // prompt provider, we exec the actual provider in a child process and replace the
1066+ // instance here.
1067+ mir::Agent::OnTrustSessionStateChangedCallbackContext cb_context
1068+ {
1069+ core::posix::ChildProcess::invalid()
1070+ };
1071+
1072+ // We ensure that the prompt session is always released cleanly, either on return or on throw.
1073+ struct Scope
1074+ {
1075+ ~Scope() { prompt_session->release_sync(); }
1076+ mir::PromptSessionVirtualTable::Ptr prompt_session;
1077+ } scope
1078+ {
1079+ // We setup the prompt session and wire up to our own internal callback helper.
1080+ connection_vtable->create_prompt_session_sync(
1081+ app_pid,
1082+ Agent::on_trust_session_changed_state,
1083+ &cb_context)
1084+ };
1085+
1086+ // Acquire a new fd for the prompt provider.
1087+ auto fd = scope.prompt_session->new_fd_for_prompt_provider();
1088+
1089+ // And prepare the actual execution in a child process.
1090+ mir::PromptProviderHelper::InvocationArguments args
1091+ {
1092+ fd,
1093+ app_id,
1094+ description
1095+ };
1096+
1097+ // Ask the helper to fire up the prompt provider.
1098+ cb_context.prompt_provider_process = exec_helper->exec_prompt_provider_with_arguments(args);
1099+ // And subsequently wait for it to finish.
1100+ auto result = cb_context.prompt_provider_process.wait_for(core::posix::wait::Flags::untraced);
1101+
1102+ return translator(result);
1103+}
1104+
1105+bool mir::operator==(const mir::PromptProviderHelper::InvocationArguments& lhs, const mir::PromptProviderHelper::InvocationArguments& rhs)
1106+{
1107+ return std::tie(lhs.application_id, lhs.description, lhs.fd) == std::tie(rhs.application_id, rhs.description, rhs.fd);
1108+}
1109+
1110+#include <core/trust/mir_agent.h>
1111+
1112+#include "config.h"
1113+
1114+std::shared_ptr<core::trust::Agent> mir::create_agent_for_mir_connection(MirConnection* connection)
1115+{
1116+ mir::ConnectionVirtualTable::Ptr cvt
1117+ {
1118+ new mir::ConnectionVirtualTable
1119+ {
1120+ connection
1121+ }
1122+ };
1123+
1124+ mir::PromptProviderHelper::Ptr pph
1125+ {
1126+ new mir::PromptProviderHelper
1127+ {
1128+ mir::PromptProviderHelper::CreationArguments
1129+ {
1130+ core::trust::mir::trust_prompt_executable_in_lib_dir
1131+ }
1132+ }
1133+ };
1134+
1135+ return mir::Agent::Ptr
1136+ {
1137+ new mir::Agent
1138+ {
1139+ cvt,
1140+ pph,
1141+ mir::Agent::translator_only_accepting_exit_status_success()
1142+ }
1143+ };
1144+}
1145
1146=== added file 'src/core/trust/mir/agent.h'
1147--- src/core/trust/mir/agent.h 1970-01-01 00:00:00 +0000
1148+++ src/core/trust/mir/agent.h 2014-07-17 18:44:56 +0000
1149@@ -0,0 +1,196 @@
1150+/*
1151+ * Copyright © 2014 Canonical Ltd.
1152+ *
1153+ * This program is free software: you can redistribute it and/or modify it
1154+ * under the terms of the GNU Lesser General Public License version 3,
1155+ * as published by the Free Software Foundation.
1156+ *
1157+ * This program is distributed in the hope that it will be useful,
1158+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1159+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1160+ * GNU Lesser General Public License for more details.
1161+ *
1162+ * You should have received a copy of the GNU Lesser General Public License
1163+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1164+ *
1165+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
1166+ */
1167+
1168+#ifndef CORE_TRUST_MIR_MIR_AGENT_H_
1169+#define CORE_TRUST_MIR_MIR_AGENT_H_
1170+
1171+#include <core/trust/agent.h>
1172+
1173+#include <core/posix/child_process.h>
1174+#include <core/posix/exec.h>
1175+
1176+#include <mirclient/mir_toolkit/mir_client_library.h>
1177+#include <mirclient/mir_toolkit/mir_prompt_session.h>
1178+
1179+#include <condition_variable>
1180+#include <functional>
1181+#include <mutex>
1182+
1183+namespace core
1184+{
1185+namespace trust
1186+{
1187+namespace mir
1188+{
1189+// We wrap the Mir prompt session API into a struct to
1190+// ease with testing and mocking.
1191+struct CORE_TRUST_DLL_PUBLIC PromptSessionVirtualTable
1192+{
1193+ // Just a convenience typedef
1194+ typedef std::shared_ptr<PromptSessionVirtualTable> Ptr;
1195+
1196+ // Just a helper struct to be passed to client_fd_callbacks.
1197+ struct Context
1198+ {
1199+ // Marks the value of an invalid fd.
1200+ static constexpr const int invalid_fd{-1};
1201+ // The fd contained within this context instance.
1202+ int fd{invalid_fd};
1203+ };
1204+
1205+ // Invoked whenever a request for creation of pre-authenticated fds succeeds.
1206+ static void mir_client_fd_callback(MirPromptSession */*prompt_session*/, size_t count, int const* fds, void* context);
1207+
1208+ // Create a MirPromptSessionVirtualTable for a given prompt session instance.
1209+ // Please note that no change of ownwership is happening here. Instead, we expect
1210+ // the calling code to handle object lifetimes.
1211+ PromptSessionVirtualTable(MirPromptSession* prompt_session);
1212+ virtual ~PromptSessionVirtualTable() = default;
1213+
1214+ // Requests a new, pre-authenticated fd for associating prompt providers.
1215+ // Returns the fd or throws std::runtime_error.
1216+ virtual int new_fd_for_prompt_provider();
1217+
1218+ // Finalizes and releases the given prompt session instance.
1219+ virtual void release_sync();
1220+
1221+ // The underlying prompt session instance.
1222+ MirPromptSession* prompt_session;
1223+};
1224+
1225+struct CORE_TRUST_DLL_PUBLIC ConnectionVirtualTable
1226+{
1227+ // Just a convenience typedef
1228+ typedef std::shared_ptr<ConnectionVirtualTable> Ptr;
1229+
1230+ // Create a new instance of MirConnectionVirtualTable
1231+ // using a pre-existing connection to Mir. Please note
1232+ // that we do not take ownership of the MirConnection but
1233+ // expect the calling code to coordinate object lifetimes.
1234+ ConnectionVirtualTable(MirConnection* connection);
1235+ virtual ~ConnectionVirtualTable() = default;
1236+
1237+ // Creates a new trusted prompt session instance synchronously.
1238+ virtual PromptSessionVirtualTable::Ptr create_prompt_session_sync(
1239+ // The process id of the requesting app/service
1240+ pid_t app_pid,
1241+ // Callback handling prompt session state changes.
1242+ mir_prompt_session_state_change_callback cb,
1243+ // Callback context
1244+ void* context);
1245+
1246+ // We do not take over ownership of the connection object.
1247+ MirConnection* connection;
1248+};
1249+
1250+// Abstracts common functionality required for running external helpers.
1251+struct CORE_TRUST_DLL_PUBLIC PromptProviderHelper
1252+{
1253+ // Just a convenience typedef.
1254+ typedef std::shared_ptr<PromptProviderHelper> Ptr;
1255+
1256+ // Creation-time arguments.
1257+ struct CreationArguments
1258+ {
1259+ // Path to the helper executable that provides the prompting UI.
1260+ std::string path_to_helper_executable;
1261+ };
1262+
1263+ // Invocation arguments for exec_prompt_provider_with_arguments
1264+ struct InvocationArguments
1265+ {
1266+ // The pre-authenticated fd that the helper
1267+ // should use for connecting to Mir.
1268+ int fd;
1269+ // The application id of the requesting app.
1270+ std::string application_id;
1271+ // The extended description that should be presented to the user.
1272+ std::string description;
1273+ };
1274+
1275+ PromptProviderHelper(const CreationArguments& args);
1276+ virtual ~PromptProviderHelper() = default;
1277+
1278+ // Execs the executable provided at construction time for the arguments and
1279+ // returns the corresponding child process.
1280+ virtual core::posix::ChildProcess exec_prompt_provider_with_arguments(const InvocationArguments& args);
1281+
1282+ // We store all arguments passed at construction.
1283+ CreationArguments creation_arguments;
1284+};
1285+
1286+// Implements the trust::Agent interface and dispatches calls to a helper
1287+// prompt provider, tying it together with the requesting service and app
1288+// by leveraging Mir's trusted session/prompting support.
1289+struct CORE_TRUST_DLL_PUBLIC Agent : public core::trust::Agent
1290+{
1291+ // Convenience typedef
1292+ typedef std::shared_ptr<Agent> Ptr;
1293+
1294+ // Helper struct for injecting state into on_trust_changed_state_state callbacks.
1295+ // Used in prompt_user_for_request to wait for the trust session to be stopped.
1296+ struct OnTrustSessionStateChangedCallbackContext
1297+ {
1298+ // The process that provides the prompting UI.
1299+ core::posix::ChildProcess prompt_provider_process;
1300+ };
1301+
1302+ // Handles state changes of trust sessions and sigkills the child process
1303+ // provided in context (of type OnTrustSessionStateChangedCallbackContext).
1304+ static void on_trust_session_changed_state(
1305+ // The prompt session instance that just changed state.
1306+ MirPromptSession* prompt_provider,
1307+ // The new state of the prompt session instance.
1308+ MirPromptSessionState state,
1309+ // The context of type context.
1310+ void* context);
1311+
1312+ // Returns a wait result -> trust::Request::Answer translator that only returns Answer::granted if
1313+ // the prompt provider child process exits cleanly with status success.
1314+ // Throws std::logic_error if the process did not exit but was signaled.
1315+ static std::function<core::trust::Request::Answer(const core::posix::wait::Result&)> translator_only_accepting_exit_status_success();
1316+
1317+ // Creates a new MirAgent instance with the given MirConnectionVirtualTable instance.
1318+ Agent(
1319+ // VTable object providing access to Mir's trusted prompting functionality.
1320+ const ConnectionVirtualTable::Ptr& connection_vtable,
1321+ // Exec helper for starting up prompt provider child processes with the correct setup
1322+ // of command line arguments and environment variables.
1323+ const PromptProviderHelper::Ptr& exec_helper,
1324+ // A translator function for mapping child process exit states to trust::Request answers.
1325+ const std::function<core::trust::Request::Answer(const core::posix::wait::Result&)>& translator);
1326+
1327+ // From core::trust::Agent:
1328+ // Throws a std::logic_error if anything unforeseen happens during execution, thus
1329+ // indicating that no conclusive answer could be obtained from the user.
1330+ core::trust::Request::Answer prompt_user_for_request(pid_t app_pid, const std::string& app_id, const std::string& description) override;
1331+
1332+ // The connection VTable used for creating trusted prompting sessions.
1333+ ConnectionVirtualTable::Ptr connection_vtable;
1334+ // Execution helper for firing up prompt provider executables.
1335+ PromptProviderHelper::Ptr exec_helper;
1336+ // Translator instance.
1337+ std::function<core::trust::Request::Answer(const core::posix::wait::Result&)> translator;
1338+};
1339+
1340+CORE_TRUST_DLL_PUBLIC bool operator==(const PromptProviderHelper::InvocationArguments&, const PromptProviderHelper::InvocationArguments&);
1341+}
1342+}
1343+}
1344+
1345+#endif // CORE_TRUST_MIR_MIR_AGENT_H_
1346
1347=== added file 'src/core/trust/mir/config.h.in'
1348--- src/core/trust/mir/config.h.in 1970-01-01 00:00:00 +0000
1349+++ src/core/trust/mir/config.h.in 2014-07-17 18:44:56 +0000
1350@@ -0,0 +1,41 @@
1351+/*
1352+ * Copyright © 2014 Canonical Ltd.
1353+ *
1354+ * This program is free software: you can redistribute it and/or modify it
1355+ * under the terms of the GNU Lesser General Public License version 3,
1356+ * as published by the Free Software Foundation.
1357+ *
1358+ * This program is distributed in the hope that it will be useful,
1359+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1360+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1361+ * GNU Lesser General Public License for more details.
1362+ *
1363+ * You should have received a copy of the GNU Lesser General Public License
1364+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1365+ *
1366+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
1367+ */
1368+
1369+#ifndef CORE_TRUST_MIR_CONFIG_H_
1370+#define CORE_TRUST_MIR_CONFIG_H_
1371+
1372+namespace core
1373+{
1374+namespace trust
1375+{
1376+namespace mir
1377+{
1378+static constexpr const char* trust_prompt_executable_in_build_dir
1379+{
1380+ "@CMAKE_BINARY_DIR@/src/trust-prompt"
1381+};
1382+
1383+static constexpr const char* trust_prompt_executable_in_lib_dir
1384+{
1385+ "@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/trust-prompt"
1386+};
1387+}
1388+}
1389+}
1390+
1391+#endif // CORE_TRUST_MIR_CONFIG_H_
1392
1393=== added file 'src/core/trust/mir/prompt_config.h.in'
1394--- src/core/trust/mir/prompt_config.h.in 1970-01-01 00:00:00 +0000
1395+++ src/core/trust/mir/prompt_config.h.in 2014-07-17 18:44:56 +0000
1396@@ -0,0 +1,39 @@
1397+/*
1398+ * Copyright © 2014 Canonical Ltd.
1399+ *
1400+ * This program is free software: you can redistribute it and/or modify it
1401+ * under the terms of the GNU Lesser General Public License version 3,
1402+ * as published by the Free Software Foundation.
1403+ *
1404+ * This program is distributed in the hope that it will be useful,
1405+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1406+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1407+ * GNU Lesser General Public License for more details.
1408+ *
1409+ * You should have received a copy of the GNU Lesser General Public License
1410+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1411+ *
1412+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
1413+ */
1414+
1415+#include <QtCore/QCoreApplication>
1416+#include <QtCore/QDir>
1417+
1418+// Returns true if the application is called from its installed location.
1419+inline bool isRunningInstalled()
1420+{
1421+ static bool installed
1422+ {
1423+ QCoreApplication::applicationDirPath() ==
1424+ QDir(("@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@")).canonicalPath()
1425+ };
1426+
1427+ return installed;
1428+}
1429+
1430+inline QString appDirectory()
1431+{
1432+ return isRunningInstalled() ?
1433+ QString("@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_DATADIR@/core/trust/mir/") :
1434+ QString("@CMAKE_SOURCE_DIR@/src/core/trust/mir/");
1435+}
1436
1437=== added file 'src/core/trust/mir/prompt_main.cpp'
1438--- src/core/trust/mir/prompt_main.cpp 1970-01-01 00:00:00 +0000
1439+++ src/core/trust/mir/prompt_main.cpp 2014-07-17 18:44:56 +0000
1440@@ -0,0 +1,191 @@
1441+/*
1442+ * Copyright © 2014 Canonical Ltd.
1443+ *
1444+ * This program is free software: you can redistribute it and/or modify it
1445+ * under the terms of the GNU Lesser General Public License version 3,
1446+ * as published by the Free Software Foundation.
1447+ *
1448+ * This program is distributed in the hope that it will be useful,
1449+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1450+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1451+ * GNU Lesser General Public License for more details.
1452+ *
1453+ * You should have received a copy of the GNU Lesser General Public License
1454+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1455+ *
1456+ * Authors:
1457+ * Olivier Tilloy <olivier.tilloy@canonical.com>
1458+ * Nick Dedekind <nick.dedekind@canonical.com>
1459+ * Thomas Voß <thomas.voss@canonical.com>
1460+ */
1461+
1462+// Qt
1463+#include <QCommandLineParser>
1464+#include <QGuiApplication>
1465+
1466+#include <QQuickView>
1467+#include <QQuickItem>
1468+#include <QQmlContext>
1469+#include <QQmlEngine>
1470+
1471+#include <boost/program_options.hpp>
1472+#include <boost/program_options/environment_iterator.hpp>
1473+
1474+#include <core/posix/this_process.h>
1475+
1476+#include "prompt_config.h"
1477+#include "prompt_main.h"
1478+
1479+namespace core
1480+{
1481+namespace trust
1482+{
1483+namespace mir
1484+{
1485+class SignalTrap : public QObject
1486+{
1487+ Q_OBJECT
1488+
1489+public Q_SLOTS:
1490+ void quit(int code)
1491+ {
1492+ QCoreApplication::exit(code);
1493+ }
1494+};
1495+}
1496+}
1497+}
1498+
1499+namespace
1500+{
1501+namespace testing
1502+{
1503+void validate_command_line_arguments(const boost::program_options::variables_map& vm)
1504+{
1505+ // We are throwing exceptions here, which immediately calls abort and still gives a
1506+ // helpful error message on the terminal.
1507+ if (vm.count(core::trust::mir::cli::option_title) == 0) throw std::logic_error
1508+ {
1509+ "Missing option title."
1510+ };
1511+
1512+ if (vm.count(core::trust::mir::cli::option_description) == 0) throw std::logic_error
1513+ {
1514+ "Missing option description"
1515+ };
1516+
1517+ if (vm.count(core::trust::mir::cli::option_server_socket) == 0) throw std::logic_error
1518+ {
1519+ "Missing option mir_server_socket"
1520+ };
1521+
1522+ std::string mir_server_socket = vm[core::trust::mir::cli::option_server_socket].as<std::string>();
1523+
1524+ if (mir_server_socket.find("fd://") != 0) throw std::logic_error
1525+ {
1526+ "mir_server_socket does not being with fd://"
1527+ };
1528+}
1529+}
1530+}
1531+
1532+
1533+int main(int argc, char** argv)
1534+{
1535+ boost::program_options::options_description options;
1536+ options.add_options()
1537+ (core::trust::mir::cli::option_server_socket, boost::program_options::value<std::string>(), "Mir server socket to connect to.")
1538+ (core::trust::mir::cli::option_description, boost::program_options::value<std::string>(), "Extended description of the prompt.")
1539+ (core::trust::mir::cli::option_title, boost::program_options::value<std::string>(), "Title of the prompt.")
1540+ (core::trust::mir::cli::option_testing, "Only checks command-line parameters and does not execute any actions.");
1541+
1542+ auto parsed_options = boost::program_options::command_line_parser{argc, argv}
1543+ .options(options)
1544+ .allow_unregistered()
1545+ .run();
1546+
1547+ // Consider the command line.
1548+ boost::program_options::variables_map vm;
1549+ boost::program_options::store(parsed_options, vm);
1550+ boost::program_options::notify(vm);
1551+
1552+ // And the environment for option passing.
1553+ parsed_options = boost::program_options::parse_environment(options, "CORE_TRUST_MIR_PROMPT_");
1554+ boost::program_options::store(parsed_options, vm);
1555+ boost::program_options::notify(vm);
1556+
1557+ // We just verify command line arguments in testing and immediately return.
1558+ if (vm.count(core::trust::mir::cli::option_testing) > 0)
1559+ {
1560+ testing::validate_command_line_arguments(vm); return 0;
1561+ }
1562+
1563+ // Let's validate the arguments.
1564+ if (vm.count(core::trust::mir::cli::option_title) == 0)
1565+ abort(); // We have to call abort here to make sure that we get signalled.
1566+
1567+ std::string title = vm[core::trust::mir::cli::option_title].as<std::string>();
1568+
1569+ std::string description;
1570+ if (vm.count(core::trust::mir::cli::option_description) > 0)
1571+ description = vm[core::trust::mir::cli::option_description].as<std::string>();
1572+
1573+ if (vm.count(core::trust::mir::cli::option_server_socket) > 0)
1574+ core::posix::this_process::env::set_or_throw(
1575+ core::trust::mir::env::option_mir_socket,
1576+ vm[core::trust::mir::cli::option_server_socket].as<std::string>());
1577+
1578+ // We install a custom message handler to silence Qt's chattiness
1579+ qInstallMessageHandler([](QtMsgType type, const QMessageLogContext&, const QString& msg)
1580+ {
1581+ switch (type)
1582+ {
1583+ // We only handle critical and fatal messages.
1584+ case QtCriticalMsg:
1585+ case QtFatalMsg:
1586+ std::cerr << qPrintable(msg) << std::endl;
1587+ break;
1588+ // And just drop the rest.
1589+ default:
1590+ break;
1591+ }
1592+ });
1593+
1594+ // We already parsed the command line arguments and do not parse them
1595+ // to the application.
1596+ QGuiApplication app{argc, argv};
1597+
1598+ core::trust::mir::SignalTrap signal_trap;
1599+
1600+ QQuickView* view = new QQuickView();
1601+ view->setResizeMode(QQuickView::SizeRootObjectToView);
1602+ view->setTitle(QGuiApplication::applicationName());
1603+
1604+ // Make some properties known to the root context.
1605+ // The title of the dialog.
1606+ view->rootContext()->setContextProperty(
1607+ core::trust::mir::cli::option_title,
1608+ title.c_str());
1609+ // The description of the dialog.
1610+ view->rootContext()->setContextProperty(
1611+ core::trust::mir::cli::option_description,
1612+ description.c_str());
1613+
1614+ // Point the engine to the right directory. Please note that
1615+ // the respective value changes with the installation state.
1616+ view->engine()->setBaseUrl(QUrl::fromLocalFile(appDirectory()));
1617+
1618+ view->setSource(QUrl::fromLocalFile("prompt_main.qml"));
1619+ view->show();
1620+
1621+ QObject::connect(
1622+ view->rootObject(),
1623+ SIGNAL(quit(int)),
1624+ &signal_trap,
1625+ SLOT(quit(int)));
1626+
1627+ return app.exec();
1628+}
1629+
1630+#include "prompt_main.moc"
1631+
1632
1633=== added file 'src/core/trust/mir/prompt_main.h'
1634--- src/core/trust/mir/prompt_main.h 1970-01-01 00:00:00 +0000
1635+++ src/core/trust/mir/prompt_main.h 2014-07-17 18:44:56 +0000
1636@@ -0,0 +1,65 @@
1637+/*
1638+ * Copyright © 2014 Canonical Ltd.
1639+ *
1640+ * This program is free software: you can redistribute it and/or modify it
1641+ * under the terms of the GNU Lesser General Public License version 3,
1642+ * as published by the Free Software Foundation.
1643+ *
1644+ * This program is distributed in the hope that it will be useful,
1645+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1646+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1647+ * GNU Lesser General Public License for more details.
1648+ *
1649+ * You should have received a copy of the GNU Lesser General Public License
1650+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1651+ *
1652+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
1653+ */
1654+
1655+#ifndef CORE_TRUST_MIR_PROMPT_MAIN_H_
1656+#define CORE_TRUST_MIR_PROMPT_MAIN_H_
1657+
1658+namespace core
1659+{
1660+namespace trust
1661+{
1662+namespace mir
1663+{
1664+namespace env
1665+{
1666+static constexpr const char* option_mir_socket
1667+{
1668+ "MIR_SOCKET"
1669+};
1670+}
1671+namespace cli
1672+{
1673+/** @brief Mir server socket to connect to. */
1674+static constexpr const char* option_server_socket
1675+{
1676+ "mir_server_socket"
1677+};
1678+
1679+/** @brief Title of the prompt. */
1680+static constexpr const char* option_title
1681+{
1682+ "title"
1683+};
1684+
1685+/** @brief Extended description of the prompt. */
1686+static constexpr const char* option_description
1687+{
1688+ "description"
1689+};
1690+
1691+/** @brief Only checks command-line parameters and does not execute any actions. */
1692+static constexpr const char* option_testing
1693+{
1694+ "testing"
1695+};
1696+}
1697+}
1698+}
1699+}
1700+
1701+#endif // CORE_TRUST_MIR_PROMPT_MAIN_H_
1702
1703=== added file 'src/core/trust/mir/prompt_main.qml'
1704--- src/core/trust/mir/prompt_main.qml 1970-01-01 00:00:00 +0000
1705+++ src/core/trust/mir/prompt_main.qml 2014-07-17 18:44:56 +0000
1706@@ -0,0 +1,53 @@
1707+import QtQuick 2.0
1708+import Ubuntu.Components 0.1
1709+import QtQuick.Layouts 1.1
1710+
1711+Rectangle {
1712+ anchors.fill: parent
1713+
1714+ signal quit(int code)
1715+
1716+ ColumnLayout {
1717+ anchors.fill: parent
1718+ anchors.margins: units.gu(2)
1719+ spacing: units.gu(2)
1720+
1721+ Label {
1722+ anchors.horizontalCenter: parent.horizontalCenter
1723+
1724+ text: title
1725+ font.family: "Ubuntu"
1726+ fontSize: "large"
1727+
1728+ maximumLineCount: 1
1729+ elide: Text.ElideRight
1730+ }
1731+
1732+ Label {
1733+
1734+ text: description
1735+ font.family: "Ubuntu"
1736+ fontSize: "medium"
1737+ Layout.maximumWidth: parent.width
1738+ Layout.fillHeight: true
1739+
1740+ wrapMode: Text.WordWrap
1741+ }
1742+
1743+ RowLayout {
1744+ spacing: units.gu(1)
1745+ height: units.gu(3)
1746+ anchors.right: parent.right
1747+
1748+ Button {
1749+ text: "Deny"
1750+ onClicked: quit(1)
1751+ }
1752+ Button {
1753+ text: "Grant"
1754+ onClicked: quit(0)
1755+ }
1756+ }
1757+ }
1758+}
1759+
1760
1761=== modified file 'src/core/trust/request.cpp'
1762--- src/core/trust/request.cpp 2014-05-06 11:32:13 +0000
1763+++ src/core/trust/request.cpp 2014-07-17 18:44:56 +0000
1764@@ -18,6 +18,55 @@
1765
1766 #include <core/trust/request.h>
1767
1768+#include <core/trust/agent.h>
1769+#include <core/trust/store.h>
1770+
1771+core::trust::Request::Answer core::trust::process_trust_request(const core::trust::RequestParameters& params)
1772+{
1773+ // We verify parameters first:
1774+ if (not params.agent) throw std::logic_error
1775+ {
1776+ "Cannot operate without an agent implementation."
1777+ };
1778+
1779+ if (not params.store) throw std::logic_error
1780+ {
1781+ "Cannot operate without a store implementation."
1782+ };
1783+
1784+ // Let's see if the store has an answer for app-id and feature.
1785+ auto query = params.store->query();
1786+
1787+ // Narrow it down to the specific app and the specific feature
1788+ query->for_application_id(params.application_id);
1789+ query->for_feature(params.feature);
1790+
1791+ query->execute();
1792+
1793+ // We have got results and we take the most recent one as the most appropriate.
1794+ if (query->status() == core::trust::Store::Query::Status::has_more_results)
1795+ {
1796+ // And we are returning early.
1797+ return query->current().answer;
1798+ }
1799+
1800+ // We do not have results available in the store, prompting the user
1801+ auto answer = params.agent->prompt_user_for_request(
1802+ params.application_pid,
1803+ params.application_id,
1804+ params.description);
1805+
1806+ params.store->add(core::trust::Request
1807+ {
1808+ params.application_id,
1809+ params.feature,
1810+ std::chrono::system_clock::now(),
1811+ answer
1812+ });
1813+
1814+ return answer;
1815+}
1816+
1817 bool core::trust::operator==(const core::trust::Request& lhs, const core::trust::Request& rhs)
1818 {
1819 return lhs.from == rhs.from &&
1820
1821=== modified file 'src/core/trust/resolve.cpp'
1822--- src/core/trust/resolve.cpp 2014-05-06 11:32:13 +0000
1823+++ src/core/trust/resolve.cpp 2014-07-17 18:44:56 +0000
1824@@ -41,15 +41,14 @@
1825 };
1826 }
1827
1828-struct Store :
1829- public core::trust::Store,
1830- public dbus::Stub<core::trust::dbus::Store>
1831+struct Store : public core::trust::Store
1832 {
1833- Store(const std::shared_ptr<core::dbus::Bus>& bus)
1834- : dbus::Stub<core::trust::dbus::Store>(bus),
1835- bus(bus),
1836- worker{[this]() { this->bus->run(); }},
1837- proxy(access_service()->object_for_path(dbus::types::ObjectPath::root()))
1838+ Store(const std::shared_ptr<dbus::Service>& service,
1839+ const std::shared_ptr<core::dbus::Bus>& bus)
1840+ : bus(bus),
1841+ worker{[this]() { Store::bus->run(); }},
1842+ service(service),
1843+ proxy(service->object_for_path(dbus::types::ObjectPath::root()))
1844 {
1845 }
1846
1847@@ -99,7 +98,7 @@
1848
1849 if (result.is_error())
1850 {
1851- throw core::trust::Store::Query::Error::NoCurrentResult{};
1852+ throw core::trust::Store::Query::Errors::NoCurrentResult{};
1853 }
1854
1855 return result.value();
1856@@ -224,7 +223,7 @@
1857 {
1858 path,
1859 proxy,
1860- access_service()->object_for_path(path)
1861+ service->object_for_path(path)
1862 });
1863
1864 return query;
1865@@ -232,6 +231,7 @@
1866
1867 std::shared_ptr<core::dbus::Bus> bus;
1868 std::thread worker;
1869+ std::shared_ptr<dbus::Service> service;
1870 std::shared_ptr<dbus::Object> proxy;
1871 };
1872 }
1873@@ -242,10 +242,16 @@
1874 const std::string& name)
1875 {
1876 if (name.empty())
1877- throw Error::ServiceNameMustNotBeEmpty{};
1878+ throw Errors::ServiceNameMustNotBeEmpty{};
1879
1880- core::trust::dbus::Store::mutable_name() = "com.ubuntu.trust.store." + name;
1881- return std::shared_ptr<core::trust::Store>{new detail::Store(bus)};
1882+ return std::shared_ptr<core::trust::Store>
1883+ {
1884+ new detail::Store
1885+ {
1886+ core::dbus::Service::use_service(bus, "com.ubuntu.trust.store." + name),
1887+ bus
1888+ }
1889+ };
1890 }
1891
1892 std::shared_ptr<core::trust::Store> core::trust::resolve_store_in_session_with_name(
1893
1894=== modified file 'tests/CMakeLists.txt'
1895--- tests/CMakeLists.txt 2014-07-16 06:59:46 +0000
1896+++ tests/CMakeLists.txt 2014-07-17 18:44:56 +0000
1897@@ -1,20 +1,24 @@
1898-set (OLD_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
1899-# Don't treat warnings as errors in 3rd_party/{gmock,cucumber-cpp}
1900-string (REPLACE " -Werror " " " CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
1901-find_package(Gtest REQUIRED)
1902-include_directories(
1903- ${GMOCK_INCLUDE_DIR}
1904- ${GTEST_INCLUDE_DIR}
1905-)
1906-set (CMAKE_CXX_FLAGS ${OLD_CMAKE_CXX_FLAGS})
1907+include_directories(${CMAKE_BINARY_DIR}/src)
1908+
1909+# Build with system gmock and embedded gtest
1910+set (GMOCK_INCLUDE_DIR "/usr/include/gmock/include" CACHE PATH "gmock source include directory")
1911+set (GMOCK_SOURCE_DIR "/usr/src/gmock" CACHE PATH "gmock source directory")
1912+set (GTEST_INCLUDE_DIR "${GMOCK_SOURCE_DIR}/gtest/include" CACHE PATH "gtest source include directory")
1913+
1914+add_subdirectory(${GMOCK_SOURCE_DIR} "${CMAKE_CURRENT_BINARY_DIR}/gmock")
1915
1916 pkg_check_modules(DBUS dbus-1)
1917
1918 add_definitions(-DCORE_DBUS_ENABLE_GOOGLE_TEST_FIXTURE)
1919
1920+configure_file(test_data.h.in test_data.h @ONLY)
1921+
1922 include_directories(
1923+ ${CMAKE_SOURCE_DIR}/src
1924+
1925 ${CMAKE_CURRENT_BINARY_DIR}
1926- ${GTEST_INCLUDE_DIRS}
1927+ ${GMOCK_INCLUDE_DIR}
1928+ ${GTEST_INCLUDE_DIR}
1929 ${PROCESS_CPP_INCLUDE_DIRS}
1930 ${DBUS_INCLUDE_DIRS}
1931 )
1932@@ -29,12 +33,25 @@
1933 remote_trust_store_test.cpp
1934 )
1935
1936+add_executable(
1937+ request_processor_test
1938+ request_processor_test.cpp
1939+)
1940+
1941+add_executable(
1942+ mir_agent_test
1943+ mir_agent_test.cpp
1944+)
1945+
1946 target_link_libraries(
1947 trust_store_test
1948
1949 trust-store
1950
1951- ${GTEST_BOTH_LIBRARIES}
1952+ gmock
1953+
1954+ gtest
1955+ gtest_main
1956 )
1957
1958 target_link_libraries(
1959@@ -42,9 +59,46 @@
1960
1961 trust-store
1962
1963- ${GTEST_BOTH_LIBRARIES}
1964+ gmock
1965+
1966+ gtest
1967+ gtest_main
1968+
1969+ ${PROCESS_CPP_LIBRARIES}
1970+)
1971+
1972+target_link_libraries(
1973+ request_processor_test
1974+
1975+ trust-store
1976+
1977+ gmock
1978+
1979+ gtest
1980+ gtest_main
1981+
1982+ ${PROCESS_CPP_LIBRARIES}
1983+)
1984+
1985+target_link_libraries(
1986+ mir_agent_test
1987+
1988+ trust-store
1989+
1990+ gmock
1991+
1992+ gtest
1993+ gtest_main
1994+
1995 ${PROCESS_CPP_LIBRARIES}
1996 )
1997
1998 add_test(trust_store_test ${CMAKE_CURRENT_BINARY_DIR}/trust_store_test)
1999 add_test(remote_trust_store_test ${CMAKE_CURRENT_BINARY_DIR}/remote_trust_store_test)
2000+add_test(request_processor_test ${CMAKE_CURRENT_BINARY_DIR}/request_processor_test)
2001+add_test(mir_agent_test ${CMAKE_CURRENT_BINARY_DIR}/mir_agent_test --gtest_filter=*-*requires_mir)
2002+
2003+install(
2004+ TARGETS trust_store_test remote_trust_store_test request_processor_test mir_agent_test
2005+ RUNTIME DESTINATION bin/trust-store-tests
2006+)
2007
2008=== added file 'tests/mir_agent_test.cpp'
2009--- tests/mir_agent_test.cpp 1970-01-01 00:00:00 +0000
2010+++ tests/mir_agent_test.cpp 2014-07-17 18:44:56 +0000
2011@@ -0,0 +1,420 @@
2012+/*
2013+ * Copyright © 2013 Canonical Ltd.
2014+ *
2015+ * This program is free software: you can redistribute it and/or modify it
2016+ * under the terms of the GNU Lesser General Public License version 3,
2017+ * as published by the Free Software Foundation.
2018+ *
2019+ * This program is distributed in the hope that it will be useful,
2020+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2021+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2022+ * GNU Lesser General Public License for more details.
2023+ *
2024+ * You should have received a copy of the GNU Lesser General Public License
2025+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2026+ *
2027+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
2028+ */
2029+
2030+// Implementation-specific header
2031+#include <core/trust/mir/agent.h>
2032+#include <core/trust/mir/config.h>
2033+
2034+#include <core/trust/agent.h>
2035+#include <core/trust/request.h>
2036+#include <core/trust/store.h>
2037+
2038+#include "test_data.h"
2039+
2040+#include <core/posix/fork.h>
2041+
2042+#include <gmock/gmock.h>
2043+#include <gtest/gtest.h>
2044+
2045+#include <random>
2046+#include <thread>
2047+
2048+namespace
2049+{
2050+struct MockPromptSessionVirtualTable : public core::trust::mir::PromptSessionVirtualTable
2051+{
2052+ MockPromptSessionVirtualTable() : core::trust::mir::PromptSessionVirtualTable{nullptr}
2053+ {
2054+ }
2055+
2056+ // Requests a new, pre-authenticated fd for associating prompt providers.
2057+ // Returns the fd or throws std::runtime_error.
2058+ MOCK_METHOD0(new_fd_for_prompt_provider, int());
2059+
2060+ // Adds a prompt provider process to the prompting session, identified by its PID.
2061+ // Returns true if addition of the prompt provider succeeded.
2062+ MOCK_METHOD1(add_prompt_provider_sync, bool(pid_t));
2063+
2064+ // Finalizes and releases the given prompt session instance.
2065+ MOCK_METHOD0(release_sync, void());
2066+};
2067+
2068+struct MockConnectionVirtualTable : public core::trust::mir::ConnectionVirtualTable
2069+{
2070+ MockConnectionVirtualTable() : core::trust::mir::ConnectionVirtualTable{nullptr}
2071+ {
2072+ }
2073+
2074+ // Creates a new trusted prompt session instance synchronously.
2075+ MOCK_METHOD3(create_prompt_session_sync,
2076+ core::trust::mir::PromptSessionVirtualTable::Ptr(
2077+ // The process id of the requesting app/service
2078+ pid_t app_pid,
2079+ // Callback handling prompt session state changes.
2080+ mir_prompt_session_state_change_callback,
2081+ // Callback context
2082+ void*));
2083+};
2084+
2085+struct MockPromptProviderHelper : public core::trust::mir::PromptProviderHelper
2086+{
2087+ MockPromptProviderHelper(const core::trust::mir::PromptProviderHelper::CreationArguments& args)
2088+ : core::trust::mir::PromptProviderHelper{args}
2089+ {
2090+ using namespace ::testing;
2091+ ON_CALL(*this, exec_prompt_provider_with_arguments(_))
2092+ .WillByDefault(
2093+ Invoke(this, &MockPromptProviderHelper::super_exec_prompt_provider_with_arguments));
2094+ }
2095+
2096+ // Execs the executable provided at construction time for the arguments and
2097+ // returns the corresponding child process.
2098+ MOCK_METHOD1(exec_prompt_provider_with_arguments,
2099+ core::posix::ChildProcess(
2100+ const core::trust::mir::PromptProviderHelper::InvocationArguments&));
2101+
2102+ core::posix::ChildProcess super_exec_prompt_provider_with_arguments(const core::trust::mir::PromptProviderHelper::InvocationArguments& args)
2103+ {
2104+ return core::trust::mir::PromptProviderHelper::exec_prompt_provider_with_arguments(args);
2105+ }
2106+};
2107+
2108+struct MockTranslator
2109+{
2110+ MOCK_METHOD1(translate, core::trust::Request::Answer(const core::posix::wait::Result&));
2111+};
2112+
2113+std::shared_ptr<MockPromptSessionVirtualTable> a_mocked_prompt_session_vtable()
2114+{
2115+ return std::make_shared<testing::NiceMock<MockPromptSessionVirtualTable>>();
2116+}
2117+
2118+std::shared_ptr<MockPromptProviderHelper> a_mocked_prompt_provider_calling_bin_false()
2119+{
2120+ return std::make_shared<MockPromptProviderHelper>(
2121+ core::trust::mir::PromptProviderHelper::CreationArguments
2122+ {
2123+ "/bin/false"
2124+ });
2125+}
2126+}
2127+
2128+TEST(DefaultProcessStateTranslator, throws_for_signalled_process)
2129+{
2130+ core::posix::wait::Result result;
2131+ result.status = core::posix::wait::Result::Status::signaled;
2132+ result.detail.if_signaled.signal = core::posix::Signal::sig_kill;
2133+ result.detail.if_signaled.core_dumped = true;
2134+
2135+ auto translator = core::trust::mir::Agent::translator_only_accepting_exit_status_success();
2136+ EXPECT_THROW(translator(result), std::logic_error);
2137+}
2138+
2139+TEST(DefaultProcessStateTranslator, throws_for_stopped_process)
2140+{
2141+ core::posix::wait::Result result;
2142+ result.status = core::posix::wait::Result::Status::stopped;
2143+ result.detail.if_stopped.signal = core::posix::Signal::sig_stop;
2144+
2145+ auto translator = core::trust::mir::Agent::translator_only_accepting_exit_status_success();
2146+ EXPECT_THROW(translator(result), std::logic_error);
2147+}
2148+
2149+TEST(DefaultProcessStateTranslator, returns_denied_for_process_exiting_with_failure)
2150+{
2151+ core::posix::wait::Result result;
2152+ result.status = core::posix::wait::Result::Status::exited;
2153+ result.detail.if_exited.status = core::posix::exit::Status::failure;
2154+
2155+ auto translator = core::trust::mir::Agent::translator_only_accepting_exit_status_success();
2156+ EXPECT_EQ(core::trust::Request::Answer::denied, translator(result));
2157+}
2158+
2159+TEST(DefaultProcessStateTranslator, returns_granted_for_process_exiting_successfully)
2160+{
2161+ core::posix::wait::Result result;
2162+ result.status = core::posix::wait::Result::Status::exited;
2163+ result.detail.if_exited.status = core::posix::exit::Status::success;
2164+
2165+ auto translator = core::trust::mir::Agent::translator_only_accepting_exit_status_success();
2166+ EXPECT_EQ(core::trust::Request::Answer::granted, translator(result));
2167+}
2168+
2169+TEST(DefaultPromptProviderHelper, correctly_passes_arguments_to_prompt_executable)
2170+{
2171+ core::posix::this_process::env::set_or_throw("CORE_TRUST_MIR_PROMPT_TESTING", "1");
2172+
2173+ core::trust::mir::PromptProviderHelper::CreationArguments cargs
2174+ {
2175+ core::trust::mir::trust_prompt_executable_in_build_dir
2176+ };
2177+
2178+ core::trust::mir::PromptProviderHelper::InvocationArguments iargs
2179+ {
2180+ 42,
2181+ "does.not.exist.application",
2182+ "Just an extended description"
2183+ };
2184+
2185+ core::trust::mir::PromptProviderHelper helper{cargs};
2186+ auto child = helper.exec_prompt_provider_with_arguments(iargs);
2187+
2188+ auto result = child.wait_for(core::posix::wait::Flags::untraced);
2189+
2190+ EXPECT_EQ(core::posix::wait::Result::Status::exited, result.status);
2191+ EXPECT_EQ(core::posix::exit::Status::success, result.detail.if_exited.status);
2192+
2193+ // And clean up.
2194+ core::posix::this_process::env::unset_or_throw("CORE_TRUST_MIR_PROMPT_TESTING");
2195+}
2196+
2197+TEST(MirAgent, creates_prompt_session_and_execs_helper_with_preauthenticated_fd)
2198+{
2199+ using namespace ::testing;
2200+
2201+ const pid_t app_pid {21};
2202+ const std::string app_id {"does.not.exist.application"};
2203+ const std::string app_description {"This is just an extended description"};
2204+ const int pre_authenticated_fd {42};
2205+
2206+ const core::trust::mir::PromptProviderHelper::InvocationArguments reference_invocation_args
2207+ {
2208+ pre_authenticated_fd,
2209+ app_id,
2210+ app_description
2211+ };
2212+
2213+ auto connection_vtable = std::make_shared<MockConnectionVirtualTable>();
2214+ auto prompt_session_vtable = a_mocked_prompt_session_vtable();
2215+
2216+ auto prompt_provider_exec_helper = a_mocked_prompt_provider_calling_bin_false();
2217+
2218+ ON_CALL(*connection_vtable, create_prompt_session_sync(_, _, _))
2219+ .WillByDefault(Return(prompt_session_vtable));
2220+
2221+ ON_CALL(*prompt_session_vtable, new_fd_for_prompt_provider())
2222+ .WillByDefault(Return(pre_authenticated_fd));
2223+
2224+ ON_CALL(*prompt_session_vtable, add_prompt_provider_sync(_))
2225+ .WillByDefault(Return(true));
2226+
2227+ EXPECT_CALL(*connection_vtable, create_prompt_session_sync(app_pid, _, _)).Times(1);
2228+ EXPECT_CALL(*prompt_session_vtable, new_fd_for_prompt_provider()).Times(1);
2229+ EXPECT_CALL(*prompt_provider_exec_helper,
2230+ exec_prompt_provider_with_arguments(
2231+ reference_invocation_args)).Times(1);
2232+
2233+ core::trust::mir::Agent agent
2234+ {
2235+ connection_vtable,
2236+ prompt_provider_exec_helper,
2237+ core::trust::mir::Agent::translator_only_accepting_exit_status_success()
2238+ };
2239+
2240+ EXPECT_EQ(core::trust::Request::Answer::denied, // /bin/false exits with failure.
2241+ agent.prompt_user_for_request(app_pid, app_id, app_description));
2242+}
2243+
2244+TEST(MirAgent, sig_kills_prompt_provider_process_on_status_change)
2245+{
2246+ using namespace ::testing;
2247+
2248+ const pid_t app_pid {21};
2249+ const std::string app_id {"does.not.exist.application"};
2250+ const std::string app_description {"This is just an extended description"};
2251+ const int pre_authenticated_fd {42};
2252+
2253+ auto a_spinning_process = []()
2254+ {
2255+ while (true)
2256+ {
2257+ std::this_thread::sleep_for(std::chrono::milliseconds{20});
2258+ }
2259+ return core::posix::exit::Status::success;
2260+ };
2261+
2262+ auto connection_vtable = std::make_shared<MockConnectionVirtualTable>();
2263+ auto prompt_session_vtable = a_mocked_prompt_session_vtable();
2264+
2265+ auto prompt_provider_helper = std::make_shared<MockPromptProviderHelper>(
2266+ core::trust::mir::PromptProviderHelper::CreationArguments{"/bin/false"});
2267+
2268+ void* prompt_session_state_callback_context{nullptr};
2269+
2270+ ON_CALL(*prompt_provider_helper, exec_prompt_provider_with_arguments(_))
2271+ .WillByDefault(
2272+ Return(
2273+ core::posix::fork(
2274+ a_spinning_process,
2275+ core::posix::StandardStream::empty)));
2276+
2277+ ON_CALL(*prompt_session_vtable, new_fd_for_prompt_provider())
2278+ .WillByDefault(
2279+ Return(
2280+ pre_authenticated_fd));
2281+
2282+ ON_CALL(*prompt_session_vtable, add_prompt_provider_sync(_))
2283+ .WillByDefault(
2284+ Return(
2285+ true));
2286+
2287+ // An invocation results in a session being created. In addition,
2288+ // we store pointers to callback and context provided by the implementation
2289+ // for being able to later trigger the callback.
2290+ ON_CALL(*connection_vtable, create_prompt_session_sync(app_pid, _, _))
2291+ .WillByDefault(
2292+ DoAll(
2293+ SaveArg<2>(&prompt_session_state_callback_context),
2294+ Return(prompt_session_vtable)));
2295+
2296+ core::trust::mir::Agent agent
2297+ {
2298+ connection_vtable,
2299+ prompt_provider_helper,
2300+ core::trust::mir::Agent::translator_only_accepting_exit_status_success()
2301+ };
2302+
2303+ std::thread asynchronously_stop_the_prompting_session
2304+ {
2305+ [&prompt_session_state_callback_context]()
2306+ {
2307+ std::this_thread::sleep_for(std::chrono::seconds{1});
2308+
2309+ core::trust::mir::Agent::on_trust_session_changed_state(
2310+ nullptr,
2311+ mir_prompt_session_state_stopped,
2312+ prompt_session_state_callback_context);
2313+ }
2314+ };
2315+
2316+ // The spinning prompt provider should get signalled if the prompting session is stopped.
2317+ // If that does not happen, the prompt provider returns success and we would have a result
2318+ // granted.
2319+ EXPECT_THROW(agent.prompt_user_for_request(app_pid, app_id, app_description),
2320+ std::logic_error);
2321+
2322+ // And some clean up.
2323+ if (asynchronously_stop_the_prompting_session.joinable())
2324+ asynchronously_stop_the_prompting_session.join();
2325+}
2326+
2327+TEST(TrustPrompt, aborts_for_missing_title)
2328+{
2329+ // And we pass in an empty argument vector
2330+ std::vector<std::string> argv;
2331+
2332+ // We pass in the empty env
2333+ std::map<std::string, std::string> env;
2334+
2335+ auto child = core::posix::exec(
2336+ core::trust::testing::trust_prompt_executable_in_build_dir,
2337+ argv,
2338+ env,
2339+ core::posix::StandardStream::empty);
2340+
2341+ auto result = child.wait_for(core::posix::wait::Flags::untraced);
2342+
2343+ EXPECT_EQ(core::posix::wait::Result::Status::signaled, result.status);
2344+ EXPECT_EQ(core::posix::Signal::sig_abrt, result.detail.if_signaled.signal);
2345+}
2346+
2347+/***********************************************************************
2348+* All tests requiring a running Mir instance go here. *
2349+* They are tagged with _requires_mir and taken out of the *
2350+* automatic build and test cycle. *
2351+***********************************************************************/
2352+
2353+#include <core/trust/mir_agent.h>
2354+
2355+#include <core/trust/mir/config.h>
2356+#include <core/trust/mir/prompt_main.h>
2357+
2358+namespace
2359+{
2360+std::map<std::string, std::string> a_copy_of_the_env()
2361+{
2362+ std::map<std::string, std::string> result;
2363+ core::posix::this_process::env::for_each([&result](const std::string& key, const std::string& value)
2364+ {
2365+ result.insert(std::make_pair(key, value)) ;
2366+ });
2367+ return result;
2368+}
2369+
2370+std::string mir_socket()
2371+{
2372+ // We either take the XDG_RUNTIME_DIR or fall back to /tmp if XDG_RUNTIME_DIR is not set.
2373+ std::string dir = core::posix::this_process::env::get("XDG_RUNTIME_DIR", "/tmp");
2374+ return dir + "/mir_socket";
2375+}
2376+
2377+std::string trusted_mir_socket()
2378+{
2379+ // We either take the XDG_RUNTIME_DIR or fall back to /tmp if XDG_RUNTIME_DIR is not set.
2380+ std::string dir = core::posix::this_process::env::get("XDG_RUNTIME_DIR", "/tmp");
2381+ return dir + "/mir_socket_trusted";
2382+}
2383+}
2384+
2385+TEST(MirAgent, default_agent_works_correctly_against_running_mir_instance_requires_mir)
2386+{
2387+ std::string pretty_function{__PRETTY_FUNCTION__};
2388+
2389+ // We start up an application in a child process and simulate that it is requesting access
2390+ // to a trusted system service/resource.
2391+ std::vector<std::string> argv
2392+ {
2393+ "--" + std::string{core::trust::mir::cli::option_server_socket} + "=" + mir_socket(),
2394+ "--" + std::string{core::trust::mir::cli::option_title} + "=" + pretty_function,
2395+ "--" + std::string{core::trust::mir::cli::option_description} + "=" + pretty_function,
2396+ // We have to circumvent unity8's authentication mechanism and just provide
2397+ // the desktop_file_hint as part of the command line.
2398+ "--desktop_file_hint=/usr/share/applications/webbrowser-app.desktop"
2399+ };
2400+
2401+ core::posix::ChildProcess app = core::posix::exec(
2402+ core::trust::mir::trust_prompt_executable_in_lib_dir,
2403+ argv,
2404+ a_copy_of_the_env(),
2405+ core::posix::StandardStream::empty);
2406+
2407+ // We give the app some time to come up.
2408+ std::this_thread::sleep_for(std::chrono::seconds{5});
2409+
2410+ // We pretend to be a trusted helper and connect to mir via its trusted socket.
2411+ auto mir_connection = mir_connect_sync(trusted_mir_socket().c_str(), pretty_function.c_str());
2412+
2413+ // Based on the mir connection, we create a prompting agent.
2414+ auto mir_agent = core::trust::mir::create_agent_for_mir_connection(mir_connection);
2415+
2416+ // And issue a prompt request. As a result, the user is presented with a prompting dialog.
2417+ auto answer = mir_agent->prompt_user_for_request(app.pid(), "embedded prompt", "embedded prompt");
2418+
2419+ // And we cross-check with the user:
2420+ std::cout << "You answered the trust prompt with: " << answer << "."
2421+ << "Is that correct? [y/n]:";
2422+
2423+ char y_or_n{'n'}; std::cin >> y_or_n;
2424+ EXPECT_EQ('y', y_or_n);
2425+
2426+ // We are bit rude here, but we have no more use for the app.
2427+ app.send_signal_or_throw(core::posix::Signal::sig_kill);
2428+
2429+ // And wait for the app to complete to avoid zombies.
2430+ app.wait_for(core::posix::wait::Flags::untraced);
2431+}
2432
2433=== added file 'tests/request_processor_test.cpp'
2434--- tests/request_processor_test.cpp 1970-01-01 00:00:00 +0000
2435+++ tests/request_processor_test.cpp 2014-07-17 18:44:56 +0000
2436@@ -0,0 +1,287 @@
2437+/*
2438+ * Copyright © 2013 Canonical Ltd.
2439+ *
2440+ * This program is free software: you can redistribute it and/or modify it
2441+ * under the terms of the GNU Lesser General Public License version 3,
2442+ * as published by the Free Software Foundation.
2443+ *
2444+ * This program is distributed in the hope that it will be useful,
2445+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2446+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2447+ * GNU Lesser General Public License for more details.
2448+ *
2449+ * You should have received a copy of the GNU Lesser General Public License
2450+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2451+ *
2452+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
2453+ */
2454+
2455+#include <core/trust/agent.h>
2456+#include <core/trust/request.h>
2457+#include <core/trust/store.h>
2458+
2459+#include <gmock/gmock.h>
2460+#include <gtest/gtest.h>
2461+
2462+#include <random>
2463+
2464+namespace
2465+{
2466+struct MockAgent : public core::trust::Agent
2467+{
2468+ /**
2469+ * @brief Presents the given request to the user, returning the user-provided answer.
2470+ * @param request The trust request that a user has to answer.
2471+ * @param description Extended description of the trust request.
2472+ */
2473+ MOCK_METHOD3(prompt_user_for_request, core::trust::Request::Answer(pid_t, const std::string&, const std::string&));
2474+};
2475+
2476+struct MockStore : public core::trust::Store
2477+{
2478+ struct MockQuery : public core::trust::Store::Query
2479+ {
2480+ /** @brief Access the status of the query. */
2481+ MOCK_CONST_METHOD0(status, core::trust::Store::Query::Status());
2482+
2483+ /** @brief Limit the query to a specific application Id. */
2484+ MOCK_METHOD1(for_application_id, void(const std::string&));
2485+
2486+ /** @brief Limit the query to a service-specific feature. */
2487+ MOCK_METHOD1(for_feature, void(std::uint64_t));
2488+
2489+ /** @brief Limit the query to the specified time interval. */
2490+ MOCK_METHOD2(for_interval, void(const core::trust::Request::Timestamp&, const core::trust::Request::Timestamp&));
2491+
2492+ /** @brief Limit the query for a specific answer. */
2493+ MOCK_METHOD1(for_answer, void(core::trust::Request::Answer));
2494+
2495+ /** @brief Query all stored requests. */
2496+ MOCK_METHOD0(all, void());
2497+
2498+ /** @brief Execute the query against the store. */
2499+ MOCK_METHOD0(execute, void());
2500+
2501+ /** @brief After successful execution, advance to the next request. */
2502+ MOCK_METHOD0(next, void());
2503+
2504+ /** @brief After successful execution, erase the current element and advance to the next request. */
2505+ MOCK_METHOD0(erase, void());
2506+
2507+ /** @brief Access the request the query currently points to. */
2508+ MOCK_METHOD0(current, core::trust::Request());
2509+ };
2510+
2511+ /** @brief Resets the state of the store, implementations should discard
2512+ * all persistent and non-persistent state.
2513+ */
2514+ MOCK_METHOD0(reset, void());
2515+
2516+ /** @brief Add the provided request to the store. When this function returns true,
2517+ * the request has been persisted by the implementation.
2518+ */
2519+ MOCK_METHOD1(add, void(const core::trust::Request&));
2520+
2521+ /**
2522+ * @brief Create a query for this store.
2523+ */
2524+ MOCK_METHOD0(query, std::shared_ptr<core::trust::Store::Query>());
2525+};
2526+
2527+pid_t the_default_pid_for_testing()
2528+{
2529+ return 42;
2530+}
2531+
2532+std::uint64_t the_default_feature_for_testing()
2533+{
2534+ return 0;
2535+}
2536+
2537+std::shared_ptr<core::trust::Agent> a_null_agent()
2538+{
2539+ return std::shared_ptr<core::trust::Agent>{};
2540+}
2541+
2542+std::shared_ptr<testing::NiceMock<MockAgent>> a_mocked_agent()
2543+{
2544+ return std::make_shared<testing::NiceMock<MockAgent>>();
2545+}
2546+
2547+std::shared_ptr<core::trust::Store> a_null_store()
2548+{
2549+ return std::shared_ptr<core::trust::Store>{};
2550+}
2551+
2552+std::shared_ptr<testing::NiceMock<MockStore>> a_mocked_store()
2553+{
2554+ return std::make_shared<testing::NiceMock<MockStore>>();
2555+}
2556+
2557+std::shared_ptr<core::trust::Store::Query> a_null_query()
2558+{
2559+ return std::shared_ptr<core::trust::Store::Query>{};
2560+}
2561+
2562+std::shared_ptr<testing::NiceMock<MockStore::MockQuery>> a_mocked_query()
2563+{
2564+ return std::make_shared<testing::NiceMock<MockStore::MockQuery>>();
2565+}
2566+
2567+core::trust::RequestParameters default_request_parameters_for_testing()
2568+{
2569+ return core::trust::RequestParameters
2570+ {
2571+ a_null_agent(),
2572+ a_null_store(),
2573+ the_default_pid_for_testing(),
2574+ "this.is.just.for.testing.purposes",
2575+ the_default_feature_for_testing(),
2576+ "Someone wants to access all your credentials and steal your identity."
2577+ };
2578+}
2579+
2580+core::trust::Request::Answer throw_a_dice()
2581+{
2582+ // We seed the rng with the current time to ensure randomness across test runs.
2583+ static std::default_random_engine generator
2584+ {
2585+ static_cast<long unsigned int>(std::chrono::system_clock::now().time_since_epoch().count())
2586+ };
2587+ // Our dice :)
2588+ static std::uniform_int_distribution<int> distribution
2589+ {
2590+ 1,
2591+ 6
2592+ };
2593+
2594+ return distribution(generator) <= 3 ?
2595+ core::trust::Request::Answer::denied :
2596+ core::trust::Request::Answer::granted;
2597+}
2598+
2599+}
2600+
2601+TEST(RequestProcessing, throws_for_missing_agent_implementation)
2602+{
2603+ auto params = default_request_parameters_for_testing();
2604+
2605+ params.store = a_mocked_store();
2606+
2607+ EXPECT_THROW(core::trust::process_trust_request(params), std::logic_error);
2608+}
2609+
2610+TEST(RequestProcessing, throws_for_missing_store_implementation)
2611+{
2612+ auto params = default_request_parameters_for_testing();
2613+
2614+ params.agent = a_mocked_agent();
2615+
2616+ EXPECT_THROW(core::trust::process_trust_request(params), std::logic_error);
2617+}
2618+
2619+TEST(RequestProcessing, queries_store_for_cached_results_and_returns_cached_value)
2620+{
2621+ using namespace ::testing;
2622+
2623+ auto answer = throw_a_dice();
2624+
2625+ auto params = default_request_parameters_for_testing();
2626+
2627+ core::trust::Request request
2628+ {
2629+ params.application_id,
2630+ params.feature,
2631+ std::chrono::system_clock::now(),
2632+ answer
2633+ };
2634+
2635+ auto mocked_agent = a_mocked_agent();
2636+ auto mocked_query = a_mocked_query();
2637+ auto mocked_store = a_mocked_store();
2638+
2639+ ON_CALL(*mocked_query, status())
2640+ .WillByDefault(
2641+ Return(
2642+ core::trust::Store::Query::Status::has_more_results));
2643+
2644+ ON_CALL(*mocked_query, current())
2645+ .WillByDefault(
2646+ Return(
2647+ request));
2648+
2649+ ON_CALL(*mocked_store, query())
2650+ .WillByDefault(
2651+ Return(
2652+ mocked_query));
2653+
2654+ EXPECT_CALL(*mocked_store, query()).Times(1);
2655+ // We expect the processor to limit the query to the respective application id
2656+ // and to the respective feature.
2657+ EXPECT_CALL(*mocked_query, for_application_id(params.application_id)).Times(1);
2658+ EXPECT_CALL(*mocked_query, for_feature(params.feature)).Times(1);
2659+ // The setup ensures that a previously stored answer is available in the store.
2660+ // For that, the agent should not be queried.
2661+ EXPECT_CALL(*mocked_agent, prompt_user_for_request(_, _, _)).Times(0);
2662+
2663+ params.agent = mocked_agent;
2664+ params.store = mocked_store;
2665+
2666+ EXPECT_EQ(answer, core::trust::process_trust_request(params));
2667+}
2668+
2669+TEST(RequestProcessing, queries_agent_if_no_cached_results_and_returns_users_answer)
2670+{
2671+ using namespace ::testing;
2672+
2673+ auto answer = throw_a_dice();
2674+
2675+ auto params = default_request_parameters_for_testing();
2676+
2677+ core::trust::Request request
2678+ {
2679+ params.application_id,
2680+ params.feature,
2681+ std::chrono::system_clock::now(),
2682+ answer
2683+ };
2684+
2685+ auto mocked_agent = a_mocked_agent();
2686+ auto mocked_query = a_mocked_query();
2687+ auto mocked_store = a_mocked_store();
2688+
2689+ ON_CALL(*mocked_agent, prompt_user_for_request(params.application_pid, params.application_id, params.description))
2690+ .WillByDefault(
2691+ Return(
2692+ answer));
2693+
2694+ // We return EndOfRecord for queries, and expect the request processor
2695+ // to subsequently ask the user for his answer.
2696+ ON_CALL(*mocked_query, status())
2697+ .WillByDefault(
2698+ Return(
2699+ core::trust::Store::Query::Status::eor));
2700+
2701+ ON_CALL(*mocked_store, query())
2702+ .WillByDefault(
2703+ Return(
2704+ mocked_query));
2705+
2706+ EXPECT_CALL(*mocked_query, current()).Times(0);
2707+ EXPECT_CALL(*mocked_store, query()).Times(1);
2708+ // We expect the processor to limit the query to the respective application id
2709+ // and to the respective feature.
2710+ EXPECT_CALL(*mocked_query, for_application_id(params.application_id)).Times(1);
2711+ EXPECT_CALL(*mocked_query, for_feature(params.feature)).Times(1);
2712+ // The setup ensures that a previously stored answer is available in the store.
2713+ // For that, the agent should not be queried.
2714+ EXPECT_CALL(*mocked_agent, prompt_user_for_request(params.application_pid, params.application_id, params.description)).Times(1);
2715+
2716+ params.agent = mocked_agent;
2717+ params.store = mocked_store;
2718+
2719+ EXPECT_EQ(answer, core::trust::process_trust_request(params));
2720+}
2721+
2722+
2723+
2724
2725=== modified file 'tests/test_data.h.in'
2726--- tests/test_data.h.in 2014-05-06 11:32:13 +0000
2727+++ tests/test_data.h.in 2014-07-17 18:44:56 +0000
2728@@ -21,88 +21,14 @@
2729
2730 namespace core
2731 {
2732+namespace trust
2733+{
2734 namespace testing
2735 {
2736-
2737-const char* session_bus_configuration_file()
2738-{
2739- return "@CMAKE_SOURCE_DIR@/data/session.conf";
2740-}
2741-
2742-const char* system_bus_configuration_file()
2743-{
2744- return "@CMAKE_SOURCE_DIR@/data/system.conf";
2745-}
2746-
2747-namespace com
2748-{
2749-namespace canonical
2750-{
2751-const char* user_metrics_introspection_file()
2752-{
2753- return "@CMAKE_CURRENT_SOURCE_DIR@/data/com.canonical.UserMetrics.xml";
2754-}
2755-
2756-const char* url_dispatcher_introspection_file()
2757-{
2758- return "@CMAKE_CURRENT_SOURCE_DIR@/data/com.canonical.URLDispatcher.xml";
2759-}
2760-}
2761-}
2762-
2763-namespace org
2764-{
2765-namespace freedesktop
2766-{
2767-namespace modem_manager
2768-{
2769-namespace modem
2770-{
2771-constexpr const char* cdma_introspection_file()
2772-{
2773- return "@CMAKE_CURRENT_SOURCE_DIR@/data/org.freedesktop.ModemManager.Modem.Cdma.xml";
2774-}
2775-
2776-constexpr const char* firmware_introspection_file()
2777-{
2778- return "@CMAKE_CURRENT_SOURCE_DIR@/data/org.freedesktop.ModemManager.Modem.Firmware.xml";
2779-}
2780-
2781-namespace gsm
2782-{
2783-constexpr const char* card_introspection_file()
2784-{
2785- return "@CMAKE_CURRENT_SOURCE_DIR@/data/org.freedesktop.ModemManager.Modem.Gsm.Card.xml";
2786-}
2787-
2788-constexpr const char* contact_introspection_file()
2789-{
2790- return "@CMAKE_CURRENT_SOURCE_DIR@/data/org.freedesktop.ModemManager.Modem.Gsm.Contacts.xml";
2791-}
2792-
2793-constexpr const char* hso_introspection_file()
2794-{
2795- return "@CMAKE_CURRENT_SOURCE_DIR@/data/org.freedesktop.ModemManager.Modem.Gsm.Hso.xml";
2796-}
2797-
2798-constexpr const char* network_introspection_file()
2799-{
2800- return "@CMAKE_CURRENT_SOURCE_DIR@/data/org.freedesktop.ModemManager.Modem.Gsm.Network.xml";
2801-}
2802-
2803-constexpr const char* sms_introspection_file()
2804-{
2805- return "@CMAKE_CURRENT_SOURCE_DIR@/data/org.freedesktop.ModemManager.Modem.Gsm.SMS.xml";
2806-}
2807-
2808-constexpr const char* ussd_introspection_file()
2809-{
2810- return "@CMAKE_CURRENT_SOURCE_DIR@/data/org.freedesktop.ModemManager.Modem.Gsm.Ussd.xml";
2811-}
2812-}
2813-}
2814-}
2815-}
2816+static constexpr const char* trust_prompt_executable_in_build_dir
2817+{
2818+ "@CMAKE_BINARY_DIR@/src/trust-prompt"
2819+};
2820 }
2821 }
2822 }

Subscribers

People subscribed via source and target branches