Merge lp:~dobey/pay-service/trust-store-iap into lp:pay-service

Proposed by dobey
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
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.

To post a comment you must log in.
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)
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)
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)
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: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Charles Kerr (charlesk) :
review: Approve
Revision history for this message
Thomas Voß (thomas-voss) :
review: Needs Information
Revision history for this message
Kyle Fazzari (kyrofa) :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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)

Subscribers

People subscribed via source and target branches

to all changes: