Merge lp:~dobey/pay-service/trust-store-iap into lp:pay-service
- trust-store-iap
- Merge into trunk.15.10
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | dobey | ||||
Approved revision: | 106 | ||||
Merged at revision: | 91 | ||||
Proposed branch: | lp:~dobey/pay-service/trust-store-iap | ||||
Merge into: | lp:pay-service | ||||
Prerequisite: | lp:~dobey/pay-service/vendor-gettext | ||||
Diff against target: |
912 lines (+477/-44) 17 files modified
.bzrignore (+2/-10) CMakeLists.txt (+3/-1) data/CMakeLists.txt (+7/-0) data/pay-service-trust-stored.conf (+23/-0) debian/control (+6/-0) debian/pay-service.install (+1/-0) po/CMakeLists.txt (+39/-0) po/POTFILES.in (+1/-0) po/genpotfiles.sh (+6/-0) po/pay-service.pot (+22/-0) service-ng/src/pay-service-2/main.go (+2/-2) service-ng/src/pay-service-2/service/fake_webclient.go (+5/-0) service-ng/src/pay-service-2/service/pay_service.go (+80/-7) service-ng/src/pay-service-2/service/pay_service_test.go (+269/-18) service-ng/src/pay-service-2/service/service.go (+3/-3) service-ng/src/pay-service-2/service/service_test.go (+3/-3) service-ng/src/pay-service-2/service/webclient.go (+5/-0) |
||||
To merge this branch: | bzr merge lp:~dobey/pay-service/trust-store-iap | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Thomas Voß (community) | Needs Information | ||
Charles Kerr (community) | Approve | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Review via email: mp+280348@code.launchpad.net |
Commit message
Add trust-store integration for requests to purchase items.
Introduce translations support, as trust-store had a displayed string.
Description of the change
PS Jenkins bot (ps-jenkins) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:95
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:96
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:97
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:98
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:99
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:100
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:101
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:102
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Charles Kerr (charlesk) : | # |
Thomas Voß (thomas-voss) : | # |
Kyle Fazzari (kyrofa) : | # |
Preview Diff
1 | === modified file '.bzrignore' |
2 | --- .bzrignore 2015-08-27 17:49:53 +0000 |
3 | +++ .bzrignore 2016-01-14 03:09:12 +0000 |
4 | @@ -4,17 +4,9 @@ |
5 | Makefile |
6 | pay-service |
7 | Testing |
8 | -tests/dbus-interface-tests |
9 | tests/gmock |
10 | -tests/test_data.h |
11 | -item-memory-tests |
12 | -pay-service.conf |
13 | -verification-curl-tests |
14 | -tests/verification-curl-endpoints/good/simple |
15 | -purchase-ual-tests |
16 | -libpay.so.1.0.0 |
17 | -libpay-tests |
18 | -verification-curl-endpoints/package-name |
19 | +*.so.* |
20 | +*.mo |
21 | *.service |
22 | |
23 | |
24 | |
25 | === modified file 'CMakeLists.txt' |
26 | --- CMakeLists.txt 2015-10-01 17:13:08 +0000 |
27 | +++ CMakeLists.txt 2016-01-14 03:09:12 +0000 |
28 | @@ -36,10 +36,12 @@ |
29 | pkg_check_modules (SERVICE_DEPS REQUIRED |
30 | Qt5Core |
31 | click-0.4 |
32 | + dbus-cpp |
33 | dbustest-1 |
34 | gio-2.0 |
35 | gio-unix-2.0 |
36 | properties-cpp |
37 | + trust-store |
38 | ubuntu-app-launch-2>=0.5 |
39 | ) |
40 | include_directories (SYSTEM ${SERVICE_DEPS_INCLUDE_DIRS}) |
41 | @@ -91,7 +93,7 @@ |
42 | add_subdirectory(tests) |
43 | endif () |
44 | add_subdirectory(pay-test-app) |
45 | - |
46 | +add_subdirectory(po) |
47 | |
48 | ## |
49 | ## Coverage Reports |
50 | |
51 | === modified file 'data/CMakeLists.txt' |
52 | --- data/CMakeLists.txt 2015-09-25 16:29:16 +0000 |
53 | +++ data/CMakeLists.txt 2016-01-14 03:09:12 +0000 |
54 | @@ -1,4 +1,11 @@ |
55 | |
56 | +############################## |
57 | +# Upstart Jobs |
58 | +############################## |
59 | +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/pay-service-trust-stored.conf |
60 | + DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/upstart/sessions/) |
61 | + |
62 | + |
63 | # New dbus service file |
64 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/com.canonical.payments.service.in |
65 | ${CMAKE_CURRENT_BINARY_DIR}/com.canonical.payments.service |
66 | |
67 | === added file 'data/pay-service-trust-stored.conf' |
68 | --- data/pay-service-trust-stored.conf 1970-01-01 00:00:00 +0000 |
69 | +++ data/pay-service-trust-stored.conf 2016-01-14 03:09:12 +0000 |
70 | @@ -0,0 +1,23 @@ |
71 | +description "Pay Service Trust Store Daemon" |
72 | + |
73 | +# Try to start this once pay-service finishes starting |
74 | +start on (started dbus and xsession SESSION=ubuntu-touch) or \ |
75 | + dbus BUS=session SIGNAL=NameOwnerChanged INTERFACE=org.freedesktop.DBus OBJPATH=/org/freedesktop/DBus ARG0=com.canonical.payments ARG2!="" |
76 | + |
77 | +stop on desktop-end |
78 | + |
79 | +respawn |
80 | + |
81 | +script |
82 | + # XXX LP #1369692 |
83 | + sleep 2 |
84 | + |
85 | + exec /usr/bin/trust-stored-skeleton \ |
86 | + --remote-agent SessionServiceDBusRemoteAgent \ |
87 | + --bus=session \ |
88 | + --local-agent MirAgent \ |
89 | + --trusted-mir-socket=/var/run/user/$(id -u)/mir_socket_trusted \ |
90 | + --for-service InAppPurchases \ |
91 | + --with-text-domain pay-service \ |
92 | + --store-bus session |
93 | +end script |
94 | |
95 | === modified file 'debian/control' |
96 | --- debian/control 2015-11-24 23:03:24 +0000 |
97 | +++ debian/control 2016-01-14 03:09:12 +0000 |
98 | @@ -13,14 +13,20 @@ |
99 | golang-go [!ppc64el], |
100 | golang-go.tools [amd64 armhf i386], |
101 | google-mock, |
102 | + intltool, |
103 | lcov, |
104 | libclick-0.4-dev, |
105 | + libdbus-1-dev, |
106 | + libdbus-cpp-dev, |
107 | libdbustest1-dev, |
108 | libglib2.0-dev, |
109 | libgtest-dev, |
110 | libmirclient-dev (>= 0.5), |
111 | + libprocess-cpp-dev (>= 2.0.0), |
112 | libproperties-cpp-dev, |
113 | + libtrust-store-dev, |
114 | libubuntu-app-launch2-dev (>= 0.5), |
115 | + pkg-config, |
116 | python3-dbusmock, |
117 | qt5-default, |
118 | qtbase5-dev, |
119 | |
120 | === modified file 'debian/pay-service.install' |
121 | --- debian/pay-service.install 2015-09-25 16:29:16 +0000 |
122 | +++ debian/pay-service.install 2016-01-14 03:09:12 +0000 |
123 | @@ -2,3 +2,4 @@ |
124 | usr/lib/*/pay-service/setup-staging.sh |
125 | usr/lib/*/ubuntu-app-launch/pay-ui/* |
126 | usr/share/dbus-1/services/*.service |
127 | +usr/share/upstart/sessions/*.conf |
128 | \ No newline at end of file |
129 | |
130 | === added directory 'po' |
131 | === added file 'po/CMakeLists.txt' |
132 | --- po/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
133 | +++ po/CMakeLists.txt 2016-01-14 03:09:12 +0000 |
134 | @@ -0,0 +1,39 @@ |
135 | +include(FindGettext) |
136 | +find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext) |
137 | +find_program(INTLTOOL_UPDATE intltool-update) |
138 | + |
139 | +set(GETTEXT_PACKAGE ${PROJECT_NAME}) |
140 | +set(POT_FILE ${GETTEXT_PACKAGE}.pot) |
141 | +file(GLOB POFILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.po) |
142 | + |
143 | +# Creates POTFILES |
144 | +add_custom_target(POTFILES ALL |
145 | + COMMENT "Generating POTFILES" |
146 | + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/genpotfiles.sh ${CMAKE_SOURCE_DIR} |
147 | +) |
148 | + |
149 | +# Creates the .pot file containing the translations template |
150 | +set(INTLTOOL_ENV |
151 | + XGETTEXT="${GETTEXT_XGETTEXT_EXECUTABLE}" |
152 | + XGETTEXT_ARGS="--keyword=Gettext;--keyword=NGettext:1,2" |
153 | + srcdir="${CMAKE_CURRENT_SOURCE_DIR}" |
154 | +) |
155 | +add_custom_target(${POT_FILE} |
156 | + COMMENT "Generating translation template" |
157 | + COMMAND ${INTLTOOL_ENV} ${INTLTOOL_UPDATE} --gettext-package ${GETTEXT_PACKAGE} --pot |
158 | + COMMAND cp -f ${POT_FILE} ${CMAKE_CURRENT_SOURCE_DIR}/ |
159 | + DEPENDS POTFILES |
160 | +) |
161 | + |
162 | +# Builds the binary translations catalog for each language |
163 | +# it finds source translations (*.po) for |
164 | +foreach(POFILE ${POFILES}) |
165 | + string(REPLACE ".po" "" LANG ${POFILE}) |
166 | + list(APPEND PO_FILES "${POFILE}") |
167 | + gettext_process_po_files(${LANG} ALL PO_FILES "${POFILE}") |
168 | + set(INSTALL_DIR ${CMAKE_INSTALL_LOCALEDIR}/${LANG}/LC_MESSAGES) |
169 | + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${LANG}.gmo |
170 | + DESTINATION ${INSTALL_DIR} |
171 | + RENAME ${GETTEXT_PACKAGE}.mo |
172 | + ) |
173 | +endforeach(POFILE) |
174 | |
175 | === added file 'po/POTFILES.in' |
176 | --- po/POTFILES.in 1970-01-01 00:00:00 +0000 |
177 | +++ po/POTFILES.in 2016-01-14 03:09:12 +0000 |
178 | @@ -0,0 +1,1 @@ |
179 | +service-ng/src/pay-service-2/service/pay_service.go |
180 | |
181 | === added file 'po/genpotfiles.sh' |
182 | --- po/genpotfiles.sh 1970-01-01 00:00:00 +0000 |
183 | +++ po/genpotfiles.sh 2016-01-14 03:09:12 +0000 |
184 | @@ -0,0 +1,6 @@ |
185 | +#!/bin/sh |
186 | + |
187 | +sed '/^#/d |
188 | + s/^[[].*] *// |
189 | + /^[ ]*$/d' \ |
190 | + "`dirname ${0}`/POTFILES.in" | sed '$!s/$/ \\/' > POTFILES |
191 | |
192 | === added file 'po/pay-service.pot' |
193 | --- po/pay-service.pot 1970-01-01 00:00:00 +0000 |
194 | +++ po/pay-service.pot 2016-01-14 03:09:12 +0000 |
195 | @@ -0,0 +1,22 @@ |
196 | +# SOME DESCRIPTIVE TITLE. |
197 | +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER |
198 | +# This file is distributed under the same license as the PACKAGE package. |
199 | +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. |
200 | +# |
201 | +#, fuzzy |
202 | +msgid "" |
203 | +msgstr "" |
204 | +"Project-Id-Version: PACKAGE VERSION\n" |
205 | +"Report-Msgid-Bugs-To: \n" |
206 | +"POT-Creation-Date: 2016-01-12 14:49-0500\n" |
207 | +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
208 | +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
209 | +"Language-Team: LANGUAGE <LL@li.org>\n" |
210 | +"Language: \n" |
211 | +"MIME-Version: 1.0\n" |
212 | +"Content-Type: text/plain; charset=CHARSET\n" |
213 | +"Content-Transfer-Encoding: 8bit\n" |
214 | + |
215 | +#: ../service-ng/src/pay-service-2/service/pay_service.go:351 |
216 | +msgid "Allow requesting in-app purchases?" |
217 | +msgstr "" |
218 | |
219 | === modified file 'service-ng/src/pay-service-2/main.go' |
220 | --- service-ng/src/pay-service-2/main.go 2015-09-24 18:38:39 +0000 |
221 | +++ service-ng/src/pay-service-2/main.go 2016-01-14 03:09:12 +0000 |
222 | @@ -1,4 +1,4 @@ |
223 | -/* -*- mode: go; tab-width: 4; indent-tabs-mode: nil -*- */ |
224 | +/* -*- mode: go; tab-width: 4; indent-tabs-mode: nil -*- */ |
225 | /* |
226 | * Copyright © 2015 Canonical Ltd. |
227 | * |
228 | @@ -41,7 +41,7 @@ |
229 | timer := time.AfterFunc(service.ShutdownTimeout, shutdown) |
230 | auth := new(service.UbuntuOneAuth) |
231 | client := service.NewWebClient(auth) |
232 | - daemon, err := service.New(client, timer) |
233 | + daemon, err := service.New(client, timer, true) |
234 | if err != nil { |
235 | log.Fatalf("Unable to create daemon: %s", err) |
236 | } |
237 | |
238 | === modified file 'service-ng/src/pay-service-2/service/fake_webclient.go' |
239 | --- service-ng/src/pay-service-2/service/fake_webclient.go 2015-11-03 21:12:33 +0000 |
240 | +++ service-ng/src/pay-service-2/service/fake_webclient.go 2016-01-14 03:09:12 +0000 |
241 | @@ -262,6 +262,11 @@ |
242 | }`, nil |
243 | } |
244 | |
245 | + // An HTTP error condition |
246 | + if parsed.Path == "/inventory/api/v1/packages/foo.example/items/by-sku/error" { |
247 | + return "", fmt.Errorf("HTTP Error: 404: File Not Found") |
248 | + } |
249 | + |
250 | return "", nil |
251 | } |
252 | |
253 | |
254 | === modified file 'service-ng/src/pay-service-2/service/pay_service.go' |
255 | --- service-ng/src/pay-service-2/service/pay_service.go 2015-11-03 21:12:33 +0000 |
256 | +++ service-ng/src/pay-service-2/service/pay_service.go 2016-01-14 03:09:12 +0000 |
257 | @@ -21,49 +21,80 @@ |
258 | import ( |
259 | "encoding/json" |
260 | "fmt" |
261 | - "github.com/godbus/dbus" |
262 | "net/http" |
263 | "os" |
264 | "path" |
265 | "reflect" |
266 | "strconv" |
267 | "time" |
268 | + |
269 | + "github.com/godbus/dbus" |
270 | + "github.com/gosexy/gettext" |
271 | + "launchpad.net/go-ual/ual" |
272 | + "launchpad.net/go-trust-store/trust" |
273 | + trustdbus "launchpad.net/go-trust-store/trust/dbus" |
274 | ) |
275 | |
276 | |
277 | const ( |
278 | + FeaturePurchaseItem trust.Feature = iota |
279 | + |
280 | // payBaseUrl is the default base URL for the REST API. |
281 | payBaseUrl = "https://myapps.developer.ubuntu.com" |
282 | ) |
283 | |
284 | type ItemDetails map[string]dbus.Variant |
285 | -type LaunchPayUiFunction func(appId string, purchaseUrl string) PayUiFeedback |
286 | +type LaunchPayUiFunction func(appId, purchaseUrl string) PayUiFeedback |
287 | +type TripletToAppIdFunction func(packageName, appName, version string) string |
288 | +type GetPrimaryPidFunction func(appId string) uint32 |
289 | |
290 | type PayService struct { |
291 | - dbusConnection DbusWrapper |
292 | - baseObjectPath dbus.ObjectPath |
293 | - shutdownTimer Timer |
294 | - client WebClientIface |
295 | + dbusConnection DbusWrapper |
296 | + baseObjectPath dbus.ObjectPath |
297 | + shutdownTimer Timer |
298 | + client WebClientIface |
299 | + |
300 | + // This is optional since there's a dbus-cpp bug preventing the trust store |
301 | + // agent from being used multiple times in the same process (e.g. during |
302 | + // testing). |
303 | + useTrustStore bool |
304 | + trustStoreAgent trust.Agent |
305 | |
306 | launchPayUiFunction LaunchPayUiFunction |
307 | + tripletToAppIdFunction TripletToAppIdFunction |
308 | + getPrimaryPidFunction GetPrimaryPidFunction |
309 | } |
310 | |
311 | func NewPayService(dbusConnection DbusWrapper, |
312 | interfaceName string, baseObjectPath dbus.ObjectPath, |
313 | - shutdownTimer Timer, client WebClientIface) (*PayService, error) { |
314 | + shutdownTimer Timer, client WebClientIface, |
315 | + useTrustStore bool) (*PayService, error) { |
316 | payiface := &PayService{ |
317 | dbusConnection: dbusConnection, |
318 | shutdownTimer: shutdownTimer, |
319 | client: client, |
320 | + useTrustStore: useTrustStore, |
321 | } |
322 | |
323 | if !baseObjectPath.IsValid() { |
324 | return nil, fmt.Errorf(`Invalid base object path: "%s"`, baseObjectPath) |
325 | } |
326 | |
327 | + if useTrustStore { |
328 | + var err error |
329 | + payiface.trustStoreAgent, err = |
330 | + trustdbus.CreatePerUserAgentForBusConnection( |
331 | + trustdbus.WellKnownBusSession, "InAppPurchases") |
332 | + if err != nil { |
333 | + return nil, fmt.Errorf("Unable to create trust store agent: %s", err) |
334 | + } |
335 | + } |
336 | + |
337 | payiface.baseObjectPath = baseObjectPath |
338 | |
339 | payiface.launchPayUiFunction = LaunchPayUi |
340 | + payiface.tripletToAppIdFunction = ual.TripletToAppId |
341 | + payiface.getPrimaryPidFunction = ual.GetPrimaryPid |
342 | |
343 | return payiface, nil |
344 | } |
345 | @@ -225,6 +256,15 @@ |
346 | defer iface.resetTimer() |
347 | packageName := packageNameFromPath(message) |
348 | |
349 | + if iface.useTrustStore && packageName != "click-scope"{ |
350 | + err := iface.authorizePurchaseItem(packageName) |
351 | + if err != nil { |
352 | + return nil, dbus.NewError( |
353 | + "org.freedesktop.DBus.Error.AccessDenied", |
354 | + []interface{}{err.Error()}) |
355 | + } |
356 | + } |
357 | + |
358 | // Purchase the item and return the item info and status. |
359 | purchaseUrl := "purchase://" |
360 | if packageName != "click-scope" { |
361 | @@ -289,6 +329,39 @@ |
362 | return iface.shutdownTimer.Reset(ShutdownTimeout) |
363 | } |
364 | |
365 | +func (iface *PayService) authorizePurchaseItem(packageName string) error { |
366 | + appId := iface.tripletToAppIdFunction(packageName, "", "") |
367 | + if appId == "" { |
368 | + return fmt.Errorf(`Unable to obtain application ID for "%s"`, |
369 | + packageName) |
370 | + } |
371 | + |
372 | + appPid := iface.getPrimaryPidFunction(appId) |
373 | + if appPid == 0 { |
374 | + return fmt.Errorf(`Unable to obtain application PID for "%s"`, appId) |
375 | + } |
376 | + |
377 | + params := &trust.RequestParameters{ |
378 | + Application: trust.Application{ |
379 | + Uid: os.Getuid(), |
380 | + Pid: int(appPid), |
381 | + Id: appId, |
382 | + }, |
383 | + Feature: FeaturePurchaseItem, |
384 | + Description: gettext.Gettext("Allow requesting in-app purchases?"), |
385 | + } |
386 | + |
387 | + // Ask the trust store if this app is allowed to request purchases. If |
388 | + // the trust store doesn't know, it'll prompt the user and save the |
389 | + // response. |
390 | + answer := iface.trustStoreAgent.AuthenticateRequestWithParameters(params) |
391 | + if answer == trust.AnswerGranted { |
392 | + return nil // Purchase is authorized |
393 | + } |
394 | + |
395 | + return fmt.Errorf("Purchase request was denied by the user") |
396 | +} |
397 | + |
398 | /* Make the call to the URL and return either the data or an error |
399 | */ |
400 | func (iface *PayService) getDataForUrl(iri string, method string, headers http.Header, body string) (interface{}, error) { |
401 | |
402 | === modified file 'service-ng/src/pay-service-2/service/pay_service_test.go' |
403 | --- service-ng/src/pay-service-2/service/pay_service_test.go 2015-10-15 16:34:05 +0000 |
404 | +++ service-ng/src/pay-service-2/service/pay_service_test.go 2016-01-14 03:09:12 +0000 |
405 | @@ -20,8 +20,11 @@ |
406 | |
407 | import ( |
408 | "fmt" |
409 | + "os" |
410 | + "testing" |
411 | + |
412 | "github.com/godbus/dbus" |
413 | - "testing" |
414 | + "launchpad.net/go-trust-store/trust/fakes" |
415 | ) |
416 | |
417 | |
418 | @@ -31,7 +34,7 @@ |
419 | timer := NewFakeTimer(ShutdownTimeout) |
420 | client := new(FakeWebClient) |
421 | |
422 | - payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client) |
423 | + payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client, false) |
424 | if err != nil { |
425 | t.Fatalf("Unexpected error while creating pay service: %s", err) |
426 | } |
427 | @@ -67,7 +70,7 @@ |
428 | timer := NewFakeTimer(ShutdownTimeout) |
429 | client := new(FakeWebClient) |
430 | |
431 | - payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client) |
432 | + payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client, false) |
433 | if err != nil { |
434 | t.Fatalf("Unexpected error while creating pay service: %s", err) |
435 | } |
436 | @@ -102,7 +105,7 @@ |
437 | timer := NewFakeTimer(ShutdownTimeout) |
438 | client := new(FakeWebClient) |
439 | |
440 | - payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client) |
441 | + payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client, false) |
442 | if err != nil { |
443 | t.Fatalf("Unexpected error while creating pay service: %s", err) |
444 | } |
445 | @@ -128,13 +131,45 @@ |
446 | } |
447 | } |
448 | |
449 | +func TestGetItemHTTPError(t *testing.T) { |
450 | + dbusServer := new(FakeDbusServer) |
451 | + dbusServer.InitializeSignals() |
452 | + timer := NewFakeTimer(ShutdownTimeout) |
453 | + client := new(FakeWebClient) |
454 | + |
455 | + payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client, false) |
456 | + if err != nil { |
457 | + t.Fatalf("Unexpected error while creating pay service: %s", err) |
458 | + } |
459 | + |
460 | + if payiface == nil { |
461 | + t.Fatalf("Pay service not created.") |
462 | + } |
463 | + |
464 | + var m dbus.Message |
465 | + m.Headers = make(map[dbus.HeaderField]dbus.Variant) |
466 | + m.Headers[dbus.FieldPath] = dbus.MakeVariant("/com/canonical/pay/store/foo_2Eexample") |
467 | + _, dbusErr := payiface.GetItem(m, "error") |
468 | + if dbusErr == nil { |
469 | + t.Errorf("Expected error and received none.") |
470 | + } |
471 | + |
472 | + if !timer.stopCalled { |
473 | + t.Errorf("Timer was not stopped.") |
474 | + } |
475 | + |
476 | + if !timer.resetCalled { |
477 | + t.Errorf("Timer was not reset.") |
478 | + } |
479 | +} |
480 | + |
481 | func TestGetItemClickScope(t *testing.T) { |
482 | dbusServer := new(FakeDbusServer) |
483 | dbusServer.InitializeSignals() |
484 | timer := NewFakeTimer(ShutdownTimeout) |
485 | client := new(FakeWebClient) |
486 | |
487 | - payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client) |
488 | + payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client, false) |
489 | if err != nil { |
490 | t.Fatalf("Unexpected error while creating pay service: %s", err) |
491 | } |
492 | @@ -166,7 +201,7 @@ |
493 | timer := new(FakeTimer) |
494 | client := new(FakeWebClient) |
495 | |
496 | - payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client) |
497 | + payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client, false) |
498 | if err != nil { |
499 | t.Fatalf("Unexpected error while creating pay service: %s", err) |
500 | } |
501 | @@ -213,7 +248,7 @@ |
502 | timer := new(FakeTimer) |
503 | client := new(FakeWebClient) |
504 | |
505 | - payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client) |
506 | + payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client, false) |
507 | if err != nil { |
508 | t.Fatalf("Unexpected error while creating pay service: %s", err) |
509 | } |
510 | @@ -255,7 +290,7 @@ |
511 | timer := new(FakeTimer) |
512 | client := new(FakeWebClient) |
513 | |
514 | - payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client) |
515 | + payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client, false) |
516 | if err != nil { |
517 | t.Fatalf("Unexpected error while creating pay service: %s", err) |
518 | } |
519 | @@ -297,7 +332,7 @@ |
520 | timer := NewFakeTimer(ShutdownTimeout) |
521 | client := new(FakeWebClient) |
522 | |
523 | - payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client) |
524 | + payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client, false) |
525 | if err != nil { |
526 | t.Fatalf("Unexpected error while creating pay service: %s", err) |
527 | } |
528 | @@ -333,7 +368,7 @@ |
529 | timer := NewFakeTimer(ShutdownTimeout) |
530 | client := new(FakeWebClient) |
531 | |
532 | - payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client) |
533 | + payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client, false) |
534 | if err != nil { |
535 | t.Fatalf("Unexpected error while creating pay service: %s", err) |
536 | } |
537 | @@ -369,7 +404,7 @@ |
538 | timer := NewFakeTimer(ShutdownTimeout) |
539 | client := new(FakeWebClient) |
540 | |
541 | - payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client) |
542 | + payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client, false) |
543 | if err != nil { |
544 | t.Fatalf("Unexpected error while creating pay service: %s", err) |
545 | } |
546 | @@ -405,7 +440,7 @@ |
547 | timer := NewFakeTimer(ShutdownTimeout) |
548 | client := new(FakeWebClient) |
549 | |
550 | - payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client) |
551 | + payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client, false) |
552 | if err != nil { |
553 | t.Fatalf("Unexpected error while creating pay service: %s", err) |
554 | } |
555 | @@ -441,7 +476,7 @@ |
556 | timer := NewFakeTimer(ShutdownTimeout) |
557 | client := new(FakeWebClient) |
558 | |
559 | - payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client) |
560 | + payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client, false) |
561 | if err != nil { |
562 | t.Fatalf("Unexpected error while creating pay service: %s", err) |
563 | } |
564 | @@ -450,8 +485,23 @@ |
565 | t.Fatalf("Pay service not created.") |
566 | } |
567 | |
568 | + // Setup fake trust store agent |
569 | + fakeAgent := &fakes.Agent{GrantAuthentication: true} |
570 | + payiface.trustStoreAgent = fakeAgent |
571 | + payiface.useTrustStore = true |
572 | + |
573 | + // Setup fake trust-store-related functions |
574 | + payiface.tripletToAppIdFunction = func(string, string, string) string { |
575 | + return "foo_bar_1.2.3" |
576 | + } |
577 | + |
578 | + payiface.getPrimaryPidFunction = func(string) uint32 { |
579 | + return 42 |
580 | + } |
581 | + |
582 | + // Setup fake pay-ui launcher |
583 | launchCalled := false |
584 | - payiface.launchPayUiFunction = func(appId string, purchaseUrl string) PayUiFeedback { |
585 | + payiface.launchPayUiFunction = func(string, string) PayUiFeedback { |
586 | launchCalled = true |
587 | feedback := PayUiFeedback{ |
588 | Finished: make(chan struct{}), |
589 | @@ -484,6 +534,31 @@ |
590 | if !launchCalled { |
591 | t.Error("Expected LaunchPayUi() to be called.") |
592 | } |
593 | + |
594 | + if !fakeAgent.AuthenticateRequestWithParametersCalled { |
595 | + t.Error("Expected agent AuthenticateRequestWithParameters() to be called.") |
596 | + } |
597 | + |
598 | + parameters := fakeAgent.AuthenticationRequestParameters |
599 | + if parameters.Application.Uid != os.Getuid() { |
600 | + t.Errorf("Authentication request uid was %d, expected %d", |
601 | + parameters.Application.Uid, os.Getuid()) |
602 | + } |
603 | + |
604 | + if parameters.Application.Pid != 42 { |
605 | + t.Errorf("Authentication request pid was %d, expected 42", |
606 | + parameters.Application.Pid) |
607 | + } |
608 | + |
609 | + if parameters.Application.Id != "foo_bar_1.2.3" { |
610 | + t.Errorf(`Authentication request id was "%s", expected "foo_bar_1.2.3"`, |
611 | + parameters.Application.Id) |
612 | + } |
613 | + |
614 | + if parameters.Feature != FeaturePurchaseItem { |
615 | + t.Errorf("Authentication request feature was %d, expected %d", |
616 | + parameters.Feature, FeaturePurchaseItem) |
617 | + } |
618 | } |
619 | |
620 | func TestPurchaseItem_payUiError(t *testing.T) { |
621 | @@ -492,7 +567,7 @@ |
622 | timer := NewFakeTimer(ShutdownTimeout) |
623 | client := new(FakeWebClient) |
624 | |
625 | - payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client) |
626 | + payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client, false) |
627 | if err != nil { |
628 | t.Fatalf("Unexpected error while creating pay service: %s", err) |
629 | } |
630 | @@ -502,7 +577,7 @@ |
631 | } |
632 | |
633 | launchCalled := false |
634 | - payiface.launchPayUiFunction = func(appId string, purchaseUrl string) PayUiFeedback { |
635 | + payiface.launchPayUiFunction = func(string, string) PayUiFeedback { |
636 | launchCalled = true |
637 | feedback := PayUiFeedback{ |
638 | Finished: make(chan struct{}), |
639 | @@ -540,13 +615,189 @@ |
640 | } |
641 | } |
642 | |
643 | +func TestPurchaseItem_trustClickScope(t *testing.T) { |
644 | + dbusServer := new(FakeDbusServer) |
645 | + dbusServer.InitializeSignals() |
646 | + timer := NewFakeTimer(ShutdownTimeout) |
647 | + client := new(FakeWebClient) |
648 | + |
649 | + payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client, false) |
650 | + if err != nil { |
651 | + t.Fatalf("Unexpected error while creating pay service: %s", err) |
652 | + } |
653 | + |
654 | + if payiface == nil { |
655 | + t.Fatalf("Pay service not created.") |
656 | + } |
657 | + |
658 | + // Setup fake trust store agent |
659 | + fakeAgent := &fakes.Agent{GrantAuthentication: false} |
660 | + payiface.trustStoreAgent = fakeAgent |
661 | + payiface.useTrustStore = true |
662 | + |
663 | + // Setup fake trust-store-related functions |
664 | + payiface.tripletToAppIdFunction = func(string, string, string) string { |
665 | + return "foo_bar_1.2.3" |
666 | + } |
667 | + |
668 | + payiface.getPrimaryPidFunction = func(string) uint32 { |
669 | + return 42 |
670 | + } |
671 | + |
672 | + // Setup fake pay-ui launcher |
673 | + launchCalled := false |
674 | + payiface.launchPayUiFunction = func(string, string) PayUiFeedback { |
675 | + launchCalled = true |
676 | + feedback := PayUiFeedback{ |
677 | + Finished: make(chan struct{}), |
678 | + Error: make(chan error, 1), |
679 | + } |
680 | + |
681 | + // Finished |
682 | + close(feedback.Error) |
683 | + close(feedback.Finished) |
684 | + |
685 | + return feedback |
686 | + } |
687 | + |
688 | + var m dbus.Message |
689 | + m.Headers = make(map[dbus.HeaderField]dbus.Variant) |
690 | + m.Headers[dbus.FieldPath] = dbus.MakeVariant("/com/canonical/pay/store/click_2Dscope") |
691 | + _, dbusErr := payiface.PurchaseItem(m, "app.foo") |
692 | + if dbusErr != nil { |
693 | + t.Errorf("Unexpected error for click-scope: %s", dbusErr) |
694 | + } |
695 | + |
696 | + if fakeAgent.AuthenticateRequestWithParametersCalled { |
697 | + t.Error("Expected agent AuthenticateRequestWithParameters() to not be called.") |
698 | + } |
699 | +} |
700 | + |
701 | +func TestPurchaseItem_accessDenied(t *testing.T) { |
702 | + dbusServer := new(FakeDbusServer) |
703 | + dbusServer.InitializeSignals() |
704 | + timer := NewFakeTimer(ShutdownTimeout) |
705 | + client := new(FakeWebClient) |
706 | + |
707 | + payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client, false) |
708 | + if err != nil { |
709 | + t.Fatalf("Unexpected error while creating pay service: %s", err) |
710 | + } |
711 | + |
712 | + if payiface == nil { |
713 | + t.Fatalf("Pay service not created.") |
714 | + } |
715 | + |
716 | + // Setup fake trust store agent |
717 | + fakeAgent := &fakes.Agent{GrantAuthentication: false} |
718 | + payiface.trustStoreAgent = fakeAgent |
719 | + payiface.useTrustStore = true |
720 | + |
721 | + // Setup fake trust-store-related functions |
722 | + payiface.tripletToAppIdFunction = func(string, string, string) string { |
723 | + return "foo_bar_1.2.3" |
724 | + } |
725 | + |
726 | + payiface.getPrimaryPidFunction = func(string) uint32 { |
727 | + return 42 |
728 | + } |
729 | + |
730 | + var m dbus.Message |
731 | + m.Headers = make(map[dbus.HeaderField]dbus.Variant) |
732 | + m.Headers[dbus.FieldPath] = dbus.MakeVariant("/com/canonical/pay/store/foo_2Eexample") |
733 | + _, dbusErr := payiface.PurchaseItem(m, "consumable") |
734 | + if dbusErr == nil { |
735 | + t.Errorf("Expected an error due to purchase access being denied") |
736 | + } |
737 | + |
738 | + if !fakeAgent.AuthenticateRequestWithParametersCalled { |
739 | + t.Error("Expected agent AuthenticateRequestWithParameters() to be called.") |
740 | + } |
741 | +} |
742 | + |
743 | +func TestPurchaseItem_errorFindingAppId(t *testing.T) { |
744 | + dbusServer := new(FakeDbusServer) |
745 | + dbusServer.InitializeSignals() |
746 | + timer := NewFakeTimer(ShutdownTimeout) |
747 | + client := new(FakeWebClient) |
748 | + |
749 | + payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client, false) |
750 | + if err != nil { |
751 | + t.Fatalf("Unexpected error while creating pay service: %s", err) |
752 | + } |
753 | + |
754 | + if payiface == nil { |
755 | + t.Fatalf("Pay service not created.") |
756 | + } |
757 | + |
758 | + // Setup fake trust store agent |
759 | + fakeAgent := &fakes.Agent{GrantAuthentication: true} |
760 | + payiface.trustStoreAgent = fakeAgent |
761 | + payiface.useTrustStore = true |
762 | + |
763 | + // Setup fake trust-store-related functions |
764 | + payiface.tripletToAppIdFunction = func(string, string, string) string { |
765 | + return "" // This is an error |
766 | + } |
767 | + |
768 | + payiface.getPrimaryPidFunction = func(string) uint32 { |
769 | + return 42 |
770 | + } |
771 | + |
772 | + var m dbus.Message |
773 | + m.Headers = make(map[dbus.HeaderField]dbus.Variant) |
774 | + m.Headers[dbus.FieldPath] = dbus.MakeVariant("/com/canonical/pay/store/foo_2Eexample") |
775 | + _, dbusErr := payiface.PurchaseItem(m, "consumable") |
776 | + if dbusErr == nil { |
777 | + t.Errorf("Expected an error due to inability to get application ID") |
778 | + } |
779 | +} |
780 | + |
781 | +func TestPurchaseItem_errorFindingAppPid(t *testing.T) { |
782 | + dbusServer := new(FakeDbusServer) |
783 | + dbusServer.InitializeSignals() |
784 | + timer := NewFakeTimer(ShutdownTimeout) |
785 | + client := new(FakeWebClient) |
786 | + |
787 | + payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client, false) |
788 | + if err != nil { |
789 | + t.Fatalf("Unexpected error while creating pay service: %s", err) |
790 | + } |
791 | + |
792 | + if payiface == nil { |
793 | + t.Fatalf("Pay service not created.") |
794 | + } |
795 | + |
796 | + // Setup fake trust store agent |
797 | + fakeAgent := &fakes.Agent{GrantAuthentication: true} |
798 | + payiface.trustStoreAgent = fakeAgent |
799 | + payiface.useTrustStore = true |
800 | + |
801 | + // Setup fake trust-store-related functions |
802 | + payiface.tripletToAppIdFunction = func(string, string, string) string { |
803 | + return "foo_bar_1.2.3" |
804 | + } |
805 | + |
806 | + payiface.getPrimaryPidFunction = func(string) uint32 { |
807 | + return 0 // This is an error |
808 | + } |
809 | + |
810 | + var m dbus.Message |
811 | + m.Headers = make(map[dbus.HeaderField]dbus.Variant) |
812 | + m.Headers[dbus.FieldPath] = dbus.MakeVariant("/com/canonical/pay/store/foo_2Eexample") |
813 | + _, dbusErr := payiface.PurchaseItem(m, "consumable") |
814 | + if dbusErr == nil { |
815 | + t.Errorf("Expected an error due to inability to get application PID") |
816 | + } |
817 | +} |
818 | + |
819 | func TestRefundItem(t *testing.T) { |
820 | dbusServer := new(FakeDbusServer) |
821 | dbusServer.InitializeSignals() |
822 | timer := NewFakeTimer(ShutdownTimeout) |
823 | client := new(FakeWebClient) |
824 | |
825 | - payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client) |
826 | + payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client, false) |
827 | if err != nil { |
828 | t.Fatalf("Unexpected error while creating pay service: %s", err) |
829 | } |
830 | @@ -586,7 +837,7 @@ |
831 | timer := new(FakeTimer) |
832 | client := new(FakeWebClient) |
833 | |
834 | - payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client) |
835 | + payiface, err := NewPayService(dbusServer, "foo", "/foo", timer, client, false) |
836 | if err != nil { |
837 | t.Fatalf("Unexpected error while creating pay service: %s", err) |
838 | } |
839 | |
840 | === modified file 'service-ng/src/pay-service-2/service/service.go' |
841 | --- service-ng/src/pay-service-2/service/service.go 2015-09-24 18:38:39 +0000 |
842 | +++ service-ng/src/pay-service-2/service/service.go 2016-01-14 03:09:12 +0000 |
843 | @@ -29,7 +29,7 @@ |
844 | // busName is the name to be requested from the DBus session bus. |
845 | busName = "com.canonical.payments" |
846 | |
847 | - // baseObjectPath is the base object path |
848 | + // baseObjectPath is the base object path |
849 | baseObjectPath = "/com/canonical/pay/store" |
850 | |
851 | // interfaceName is the name of the interface being implemented here. |
852 | @@ -60,14 +60,14 @@ |
853 | * - New daemon |
854 | * - Error (nil if none) |
855 | */ |
856 | -func New(webClient WebClientIface, timer Timer) (*Service, error) { |
857 | +func New(webClient WebClientIface, timer Timer, useTrustStore bool) (*Service, error) { |
858 | service := new(Service) |
859 | |
860 | service.server = new(DbusServer) |
861 | |
862 | var err error |
863 | service.payiface, err = NewPayService(service.server, |
864 | - interfaceName, baseObjectPath, timer, webClient) |
865 | + interfaceName, baseObjectPath, timer, webClient, useTrustStore) |
866 | if err != nil { |
867 | return nil, fmt.Errorf("Unable to create pay service interface: %s", err) |
868 | } |
869 | |
870 | === modified file 'service-ng/src/pay-service-2/service/service_test.go' |
871 | --- service-ng/src/pay-service-2/service/service_test.go 2015-09-24 18:38:39 +0000 |
872 | +++ service-ng/src/pay-service-2/service/service_test.go 2016-01-14 03:09:12 +0000 |
873 | @@ -1,4 +1,4 @@ |
874 | -/* -*- mode: go; tab-width: 4; indent-tabs-mode: nil -*- */ |
875 | +/* -*- mode: go; tab-width: 4; indent-tabs-mode: nil -*- */ |
876 | /* |
877 | * Copyright © 2015 Canonical Ltd. |
878 | * |
879 | @@ -25,7 +25,7 @@ |
880 | func TestNew(t *testing.T) { |
881 | client := new(FakeWebClient) |
882 | timer := NewFakeTimer(ShutdownTimeout) |
883 | - service, err := New(client, timer) |
884 | + service, err := New(client, timer, false) |
885 | if err != nil { |
886 | t.Fatalf("Unexpected error instantiating service: %s", err) |
887 | } |
888 | @@ -38,7 +38,7 @@ |
889 | func TestServiceRunStop(t *testing.T) { |
890 | client := new(FakeWebClient) |
891 | timer := NewFakeTimer(ShutdownTimeout) |
892 | - service, err := New(client, timer) |
893 | + service, err := New(client, timer, false) |
894 | if err != nil { |
895 | t.Fatalf("Unexpected error instantiating service: %s", err) |
896 | } |
897 | |
898 | === modified file 'service-ng/src/pay-service-2/service/webclient.go' |
899 | --- service-ng/src/pay-service-2/service/webclient.go 2015-10-21 13:58:43 +0000 |
900 | +++ service-ng/src/pay-service-2/service/webclient.go 2016-01-14 03:09:12 +0000 |
901 | @@ -73,6 +73,11 @@ |
902 | } |
903 | defer response.Body.Close() |
904 | |
905 | + if response.StatusCode != 200 { |
906 | + return "", fmt.Errorf("HTTP Error: %d: %s", |
907 | + response.StatusCode, response.Status) |
908 | + } |
909 | + |
910 | body, err := ioutil.ReadAll(response.Body) |
911 | if err != nil { |
912 | return "", fmt.Errorf("Error reading response body: %s", err) |
FAILED: Continuous integration, rev:94 jenkins. qa.ubuntu. com/job/ pay-service- ci/131/ jenkins. qa.ubuntu. com/job/ pay-service- wily-amd64- ci/61/console jenkins. qa.ubuntu. com/job/ pay-service- wily-armhf- ci/61/console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/pay- service- ci/131/ rebuild
http://