Merge lp:~chipaca/ubuntu-push/appname-from-dbus-path into lp:ubuntu-push

Proposed by John Lenton
Status: Superseded
Proposed branch: lp:~chipaca/ubuntu-push/appname-from-dbus-path
Merge into: lp:ubuntu-push
Diff against target: 1038 lines (+498/-98)
19 files modified
bus/endpoint.go (+4/-4)
bus/testing/testing_endpoint_test.go (+1/-1)
client/service/service.go (+19/-25)
client/service/service_test.go (+16/-39)
debian/changelog (+14/-0)
debian/rules (+0/-1)
nih/nih.go (+1/-1)
scripts/register (+43/-0)
scripts/unicast (+2/-0)
server/acceptance/acceptanceclient.go (+1/-1)
server/acceptance/cmd/acceptanceclient.go (+3/-1)
server/acceptance/suites/suite.go (+2/-2)
server/acceptance/suites/unicast.go (+11/-3)
server/api/handlers.go (+104/-8)
server/api/handlers_test.go (+193/-2)
server/store/inmemory.go (+26/-0)
server/store/inmemory_test.go (+44/-0)
server/store/store.go (+8/-0)
signing-helper/signing-helper.cpp (+6/-10)
To merge this branch: bzr merge lp:~chipaca/ubuntu-push/appname-from-dbus-path
Reviewer Review Type Date Requested Status
Ubuntu Push Hackers Pending
Review via email: mp+222319@code.launchpad.net

This proposal has been superseded by a proposal from 2014-06-06.

Commit message

switch dbus api to retrieve appname from dbus path

Description of the change

switch dbus api to retrieve appname from dbus path

To post a comment you must log in.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bus/endpoint.go'
2--- bus/endpoint.go 2014-05-15 11:28:06 +0000
3+++ bus/endpoint.go 2014-06-06 12:14:20 +0000
4@@ -29,7 +29,7 @@
5 * Endpoint (and its implementation)
6 */
7
8-type BusMethod func([]interface{}, []interface{}) ([]interface{}, error)
9+type BusMethod func(string, []interface{}, []interface{}) ([]interface{}, error)
10 type DispatchMap map[string]BusMethod
11
12 // bus.Endpoint represents the DBus connection itself.
13@@ -249,12 +249,12 @@
14 endp.log.Errorf("WatchMethod: unknown method %s", msg.Member)
15 } else {
16 args := msg.AllArgs()
17- rvals, err := meth(args, extra)
18+ rvals, err := meth(string(msg.Path), args, extra)
19 if err != nil {
20 reply = dbus.NewErrorMessage(msg, err_iface, err.Error())
21- endp.log.Errorf("WatchMethod: %s(%#v, %#v) failure: %#v", msg.Member, args, extra, err)
22+ endp.log.Errorf("WatchMethod: %s(%v, %#v, %#v) failure: %#v", msg.Member, msg.Path, args, extra, err)
23 } else {
24- endp.log.Debugf("WatchMethod: %s(%#v, %#v) success: %#v", msg.Member, args, extra, rvals)
25+ endp.log.Debugf("WatchMethod: %s(%v, %#v, %#v) success: %#v", msg.Member, msg.Path, args, extra, rvals)
26 reply = dbus.NewMethodReturnMessage(msg)
27 err = reply.AppendArgs(rvals...)
28 if err != nil {
29
30=== modified file 'bus/testing/testing_endpoint_test.go'
31--- bus/testing/testing_endpoint_test.go 2014-06-02 10:04:34 +0000
32+++ bus/testing/testing_endpoint_test.go 2014-06-06 12:14:20 +0000
33@@ -229,7 +229,7 @@
34 // Test that WatchMethod updates callArgs
35 func (s *TestingEndpointSuite) TestWatchMethodUpdatesCallArgs(c *C) {
36 endp := NewTestingEndpoint(nil, condition.Work(true))
37- foo := func([]interface{}, []interface{}) ([]interface{}, error) { return nil, nil }
38+ foo := func(string, []interface{}, []interface{}) ([]interface{}, error) { return nil, nil }
39 foomp := bus.DispatchMap{"foo": foo}
40 endp.WatchMethod(foomp)
41 c.Check(GetCallArgs(endp), DeepEquals, []callArgs{
42
43=== modified file 'client/service/service.go'
44--- client/service/service.go 2014-05-21 10:03:18 +0000
45+++ client/service/service.go 2014-06-06 12:14:20 +0000
46@@ -21,10 +21,12 @@
47 import (
48 "errors"
49 "os"
50+ "strings"
51 "sync"
52
53 "launchpad.net/ubuntu-push/bus"
54 "launchpad.net/ubuntu-push/logger"
55+ "launchpad.net/ubuntu-push/nih"
56 )
57
58 // Service is the dbus api
59@@ -51,7 +53,7 @@
60 AlreadyStarted = errors.New("already started")
61 BusAddress = bus.Address{
62 Interface: "com.ubuntu.PushNotifications",
63- Path: "/com/ubuntu/PushNotifications",
64+ Path: "/com/ubuntu/PushNotifications/*",
65 Name: "com.ubuntu.PushNotifications",
66 }
67 )
68@@ -133,31 +135,26 @@
69 BadArgType = errors.New("Bad argument type")
70 )
71
72-func (svc *Service) register(args []interface{}, _ []interface{}) ([]interface{}, error) {
73- if len(args) != 1 {
74+func (svc *Service) register(path string, args, _ []interface{}) ([]interface{}, error) {
75+ if len(args) != 0 {
76 return nil, BadArgCount
77 }
78- appname, ok := args[0].(string)
79- if !ok {
80- return nil, BadArgType
81- }
82+ raw_appname := path[strings.LastIndex(path, "/")+1:]
83+ appname := string(nih.Unquote([]byte(raw_appname)))
84
85- rv := os.Getenv("PUSH_REG_" + appname)
86+ rv := os.Getenv("PUSH_REG_" + raw_appname)
87 if rv == "" {
88- rv = "this-is-an-opaque-block-of-random-bits-i-promise"
89+ rv = appname + "::this-is-an-opaque-block-of-random-bits-i-promise"
90 }
91
92 return []interface{}{rv}, nil
93 }
94
95-func (svc *Service) notifications(args []interface{}, _ []interface{}) ([]interface{}, error) {
96- if len(args) != 1 {
97+func (svc *Service) notifications(path string, args, _ []interface{}) ([]interface{}, error) {
98+ if len(args) != 0 {
99 return nil, BadArgCount
100 }
101- appname, ok := args[0].(string)
102- if !ok {
103- return nil, BadArgType
104- }
105+ appname := string(nih.Unquote([]byte(path[strings.LastIndex(path, "/")+1:])))
106
107 svc.lock.Lock()
108 defer svc.lock.Unlock()
109@@ -171,18 +168,15 @@
110 return []interface{}{msgs}, nil
111 }
112
113-func (svc *Service) inject(args []interface{}, _ []interface{}) ([]interface{}, error) {
114- if len(args) != 2 {
115+func (svc *Service) inject(path string, args, _ []interface{}) ([]interface{}, error) {
116+ if len(args) != 1 {
117 return nil, BadArgCount
118 }
119- appname, ok := args[0].(string)
120- if !ok {
121- return nil, BadArgType
122- }
123- notif, ok := args[1].(string)
124- if !ok {
125- return nil, BadArgType
126- }
127+ notif, ok := args[0].(string)
128+ if !ok {
129+ return nil, BadArgType
130+ }
131+ appname := string(nih.Unquote([]byte(path[strings.LastIndex(path, "/")+1:])))
132
133 return nil, svc.Inject(appname, notif)
134 }
135
136=== modified file 'client/service/service_test.go'
137--- client/service/service_test.go 2014-05-21 10:05:24 +0000
138+++ client/service/service_test.go 2014-06-06 12:14:20 +0000
139@@ -97,23 +97,13 @@
140 // registration tests
141
142 func (ss *serviceSuite) TestRegistrationFailsIfBadArgs(c *C) {
143- for i, s := range []struct {
144- args []interface{}
145- errt error
146- }{
147- {nil, BadArgCount}, // no args
148- {[]interface{}{}, BadArgCount}, // still no args
149- {[]interface{}{42}, BadArgType}, // bad arg type
150- {[]interface{}{1, 2}, BadArgCount}, // too many args
151- } {
152- reg, err := new(Service).register(s.args, nil)
153- c.Check(reg, IsNil, Commentf("iteration #%d", i))
154- c.Check(err, Equals, s.errt, Commentf("iteration #%d", i))
155- }
156+ reg, err := new(Service).register("", []interface{}{1}, nil)
157+ c.Check(reg, IsNil)
158+ c.Check(err, Equals, BadArgCount)
159 }
160
161 func (ss *serviceSuite) TestRegistrationWorks(c *C) {
162- reg, err := new(Service).register([]interface{}{"this"}, nil)
163+ reg, err := new(Service).register("/this", nil, nil)
164 c.Assert(reg, HasLen, 1)
165 regs, ok := reg[0].(string)
166 c.Check(ok, Equals, true)
167@@ -125,7 +115,7 @@
168 os.Setenv("PUSH_REG_stuff", "42")
169 defer os.Setenv("PUSH_REG_stuff", "")
170
171- reg, err := new(Service).register([]interface{}{"stuff"}, nil)
172+ reg, err := new(Service).register("/stuff", nil, nil)
173 c.Assert(reg, HasLen, 1)
174 regs, ok := reg[0].(string)
175 c.Check(ok, Equals, true)
176@@ -138,10 +128,10 @@
177
178 func (ss *serviceSuite) TestInjectWorks(c *C) {
179 svc := NewService(ss.bus, ss.log)
180- rvs, err := svc.inject([]interface{}{"hello", "world"}, nil)
181+ rvs, err := svc.inject("/hello", []interface{}{"world"}, nil)
182 c.Assert(err, IsNil)
183 c.Check(rvs, IsNil)
184- rvs, err = svc.inject([]interface{}{"hello", "there"}, nil)
185+ rvs, err = svc.inject("/hello", []interface{}{"there"}, nil)
186 c.Assert(err, IsNil)
187 c.Check(rvs, IsNil)
188 c.Assert(svc.mbox, HasLen, 1)
189@@ -162,7 +152,7 @@
190 condition.Work(false))
191 svc := NewService(bus, ss.log)
192 svc.SetMessageHandler(func([]byte) error { return errors.New("fail") })
193- _, err := svc.inject([]interface{}{"hello", "xyzzy"}, nil)
194+ _, err := svc.inject("/hello", []interface{}{"xyzzy"}, nil)
195 c.Check(err, NotNil)
196 }
197
198@@ -173,13 +163,10 @@
199 }{
200 {nil, BadArgCount},
201 {[]interface{}{}, BadArgCount},
202- {[]interface{}{1}, BadArgCount},
203- {[]interface{}{1, 2}, BadArgType},
204- {[]interface{}{"1", 2}, BadArgType},
205- {[]interface{}{1, "2"}, BadArgType},
206- {[]interface{}{1, 2, 3}, BadArgCount},
207+ {[]interface{}{1}, BadArgType},
208+ {[]interface{}{1, 2}, BadArgCount},
209 } {
210- reg, err := new(Service).inject(s.args, nil)
211+ reg, err := new(Service).inject("", s.args, nil)
212 c.Check(reg, IsNil, Commentf("iteration #%d", i))
213 c.Check(err, Equals, s.errt, Commentf("iteration #%d", i))
214 }
215@@ -189,7 +176,7 @@
216 // Notifications tests
217 func (ss *serviceSuite) TestNotificationsWorks(c *C) {
218 svc := NewService(ss.bus, ss.log)
219- nots, err := svc.notifications([]interface{}{"hello"}, nil)
220+ nots, err := svc.notifications("/hello", nil, nil)
221 c.Assert(err, IsNil)
222 c.Assert(nots, NotNil)
223 c.Assert(nots, HasLen, 1)
224@@ -198,7 +185,7 @@
225 svc.mbox = make(map[string][]string)
226 }
227 svc.mbox["hello"] = append(svc.mbox["hello"], "this", "thing")
228- nots, err = svc.notifications([]interface{}{"hello"}, nil)
229+ nots, err = svc.notifications("/hello", nil, nil)
230 c.Assert(err, IsNil)
231 c.Assert(nots, NotNil)
232 c.Assert(nots, HasLen, 1)
233@@ -206,19 +193,9 @@
234 }
235
236 func (ss *serviceSuite) TestNotificationsFailsIfBadArgs(c *C) {
237- for i, s := range []struct {
238- args []interface{}
239- errt error
240- }{
241- {nil, BadArgCount}, // no args
242- {[]interface{}{}, BadArgCount}, // still no args
243- {[]interface{}{42}, BadArgType}, // bad arg type
244- {[]interface{}{1, 2}, BadArgCount}, // too many args
245- } {
246- reg, err := new(Service).notifications(s.args, nil)
247- c.Check(reg, IsNil, Commentf("iteration #%d", i))
248- c.Check(err, Equals, s.errt, Commentf("iteration #%d", i))
249- }
250+ reg, err := new(Service).notifications("/foo", []interface{}{1}, nil)
251+ c.Check(reg, IsNil)
252+ c.Check(err, Equals, BadArgCount)
253 }
254
255 func (ss *serviceSuite) TestMessageHandler(c *C) {
256
257=== modified file 'debian/changelog'
258--- debian/changelog 2014-06-05 09:42:22 +0000
259+++ debian/changelog 2014-06-06 12:14:20 +0000
260@@ -1,3 +1,17 @@
261+ubuntu-push (0.31ubuntu1) UNRELEASED; urgency=medium
262+
263+ [ Samuele Pedroni ]
264+ * Support registering tokens and sending notifications with a token
265+ * Register script and scripts unicast support
266+
267+ [ Roberto Alsina ]
268+ * Make signing-helper generate a HTTP header instead of a querystring.
269+
270+ [ John R. Lenton ]
271+ * Switch dbus api to retrieve app name from dbus path.
272+
273+ -- John R. Lenton <john.lenton@canonical.com> Fri, 06 Jun 2014 12:02:56 +0100
274+
275 ubuntu-push (0.3+14.10.20140605-0ubuntu1) utopic; urgency=medium
276
277 [ John Lenton ]
278
279=== modified file 'debian/rules'
280--- debian/rules 2014-05-02 12:42:27 +0000
281+++ debian/rules 2014-06-06 12:14:20 +0000
282@@ -5,7 +5,6 @@
283 export UBUNTU_PUSH_TEST_RESOURCES_ROOT := $(CURDIR)
284
285 override_dh_auto_build:
286- cd $$( find ./ -type d -regex '\./[^/]*/src/launchpad.net' -printf "%h\n" | head -n1)
287 dh_auto_build --buildsystem=golang
288 (cd signing-helper && cmake . && make)
289
290
291=== modified file 'nih/nih.go'
292--- nih/nih.go 2014-05-23 12:56:27 +0000
293+++ nih/nih.go 2014-06-06 12:14:20 +0000
294@@ -45,7 +45,7 @@
295 return out
296 }
297
298-// Quote() takes a byte slice and undoes the damage done to it by the quoting.
299+// Unquote() takes a byte slice and undoes the damage done to it by Quote().
300 func Unquote(s []byte) []byte {
301 out := make([]byte, 0, len(s))
302
303
304=== added file 'scripts/register'
305--- scripts/register 1970-01-01 00:00:00 +0000
306+++ scripts/register 2014-06-06 12:14:20 +0000
307@@ -0,0 +1,43 @@
308+#!/usr/bin/python3
309+"""
310+request a unicast registration
311+"""
312+import argparse
313+import json
314+import requests
315+import subprocess
316+import datetime
317+import sys
318+
319+
320+def main():
321+ parser = argparse.ArgumentParser(description=__doc__)
322+ parser.add_argument('deviceid', nargs=1)
323+ parser.add_argument('appid', nargs=1)
324+ parser.add_argument('-H', '--host',
325+ help="host:port (default: %(default)s)",
326+ default="localhost:8080")
327+ parser.add_argument('--no-https', action='store_true', default=False)
328+ parser.add_argument('--insecure', action='store_true', default=False,
329+ help="don't check host/certs with https")
330+ parser.add_argument('--auth_helper', default="")
331+ args = parser.parse_args()
332+ scheme = 'https'
333+ if args.no_https:
334+ scheme = 'http'
335+ url = "%s://%s/register" % (scheme, args.host)
336+ body = {
337+ 'deviceid': args.deviceid[0],
338+ 'appid': args.appid[0],
339+ }
340+ headers = {'Content-Type': 'application/json'}
341+ if args.auth_helper:
342+ auth = subprocess.check_output([args.auth_helper, url]).strip()
343+ headers['Authorization'] = auth
344+ r = requests.post(url, data=json.dumps(body), headers=headers,
345+ verify=not args.insecure)
346+ print(r.status_code)
347+ print(r.text)
348+
349+if __name__ == '__main__':
350+ main()
351
352=== modified file 'scripts/unicast'
353--- scripts/unicast 2014-05-29 14:55:19 +0000
354+++ scripts/unicast 2014-06-06 12:14:20 +0000
355@@ -52,6 +52,8 @@
356 userid, devid = reg.split(':', 1)
357 body['userid'] = userid
358 body['deviceid'] = devid
359+ else:
360+ body['token'] = reg
361 xauth = {}
362 if args.user and args.password:
363 xauth = {'auth': requests.auth.HTTPBasicAuth(args.user, args.password)}
364
365=== modified file 'server/acceptance/acceptanceclient.go'
366--- server/acceptance/acceptanceclient.go 2014-05-23 12:30:32 +0000
367+++ server/acceptance/acceptanceclient.go 2014-06-06 12:14:20 +0000
368@@ -155,7 +155,7 @@
369 return err
370 }
371 events <- fmt.Sprintf("%sbroadcast chan:%v app:%v topLevel:%d payloads:%s", sess.Prefix, recv.ChanId, recv.AppId, recv.TopLevel, pack)
372- case "connwarn":
373+ case "warn", "connwarn":
374 events <- fmt.Sprintf("%sconnwarn %s", sess.Prefix, recv.Reason)
375 }
376 }
377
378=== modified file 'server/acceptance/cmd/acceptanceclient.go'
379--- server/acceptance/cmd/acceptanceclient.go 2014-05-21 07:58:26 +0000
380+++ server/acceptance/cmd/acceptanceclient.go 2014-06-06 12:14:20 +0000
381@@ -90,7 +90,9 @@
382 }
383 }
384 if len(cfg.AuthHelper) != 0 {
385- auth, err := exec.Command(cfg.AuthHelper[0], cfg.AuthHelper[1:]...).Output()
386+ helperArgs := cfg.AuthHelper[1:]
387+ helperArgs = append(helperArgs, "https://push.ubuntu.com/")
388+ auth, err := exec.Command(cfg.AuthHelper[0], helperArgs...).Output()
389 if err != nil {
390 log.Fatalf("auth helper: %v", err)
391 }
392
393=== modified file 'server/acceptance/suites/suite.go'
394--- server/acceptance/suites/suite.go 2014-05-02 09:56:49 +0000
395+++ server/acceptance/suites/suite.go 2014-06-06 12:14:20 +0000
396@@ -89,7 +89,7 @@
397 // to kill the server process
398 KillGroup map[string]func(os.Signal)
399 // hook to adjust requests
400- MassageRequest func(req *http.Request) *http.Request
401+ MassageRequest func(req *http.Request, message interface{}) *http.Request
402 // other state
403 httpClient *http.Client
404 }
405@@ -124,7 +124,7 @@
406 request.Header.Set("Content-Type", "application/json")
407
408 if s.MassageRequest != nil {
409- request = s.MassageRequest(request)
410+ request = s.MassageRequest(request, message)
411 }
412
413 resp, err := s.httpClient.Do(request)
414
415=== modified file 'server/acceptance/suites/unicast.go'
416--- server/acceptance/suites/unicast.go 2014-05-15 16:41:54 +0000
417+++ server/acceptance/suites/unicast.go 2014-06-06 12:14:20 +0000
418@@ -40,11 +40,19 @@
419 }
420
421 func (s *UnicastAcceptanceSuite) TestUnicastToConnected(c *C) {
422- userId, auth := s.associatedAuth("DEV1")
423+ _, auth := s.associatedAuth("DEV1")
424+ res, err := s.PostRequest("/register", &api.Registration{
425+ DeviceId: "DEV1",
426+ AppId: "app1",
427+ })
428+ c.Assert(err, IsNil)
429+ c.Assert(res, Matches, ".*ok.*")
430+ var reg map[string]interface{}
431+ err = json.Unmarshal([]byte(res), &reg)
432+ c.Assert(err, IsNil)
433 events, errCh, stop := s.StartClientAuth(c, "DEV1", nil, auth)
434 got, err := s.PostRequest("/notify", &api.Unicast{
435- UserId: userId,
436- DeviceId: "DEV1",
437+ Token: reg["token"].(string),
438 AppId: "app1",
439 ExpireOn: future,
440 Data: json.RawMessage(`{"a": 42}`),
441
442=== modified file 'server/api/handlers.go'
443--- server/api/handlers.go 2014-05-29 16:22:00 +0000
444+++ server/api/handlers.go 2014-06-06 12:14:20 +0000
445@@ -51,6 +51,8 @@
446 ioError = "io-error"
447 invalidRequest = "invalid-request"
448 unknownChannel = "unknown-channel"
449+ unknownToken = "unknown-token"
450+ unauthorized = "unauthorized"
451 unavailable = "unavailable"
452 internalError = "internal"
453 )
454@@ -121,6 +123,11 @@
455 unknownChannel,
456 "Unknown channel",
457 }
458+ ErrUnknownToken = &APIError{
459+ http.StatusBadRequest,
460+ unknownToken,
461+ "Unknown token",
462+ }
463 ErrUnknown = &APIError{
464 http.StatusInternalServerError,
465 internalError,
466@@ -136,16 +143,33 @@
467 unavailable,
468 "Could not store notification",
469 }
470+ ErrCouldNotMakeToken = &APIError{
471+ http.StatusServiceUnavailable,
472+ unavailable,
473+ "Could not make token",
474+ }
475+ ErrCouldNotResolveToken = &APIError{
476+ http.StatusServiceUnavailable,
477+ unavailable,
478+ "Could not resolve token",
479+ }
480+ ErrUnauthorized = &APIError{
481+ http.StatusUnauthorized,
482+ unauthorized,
483+ "Unauthorized",
484+ }
485 )
486
487-type castCommon struct {
488+type Registration struct {
489+ DeviceId string `json:"deviceid"`
490+ AppId string `json:"appid"`
491 }
492
493 type Unicast struct {
494+ Token string `json:"token"`
495 UserId string `json:"userid"`
496 DeviceId string `json:"deviceid"`
497 AppId string `json:"appid"`
498- //Registration string `json:"registration"`
499 //CoalesceTag string `json:"coalesce_tag"`
500 ExpireOn string `json:"expire_on"`
501 Data json.RawMessage `json:"data"`
502@@ -183,15 +207,15 @@
503 }
504
505 func checkRequestAsPost(request *http.Request, maxBodySize int64) *APIError {
506+ if request.Method != "POST" {
507+ return ErrWrongRequestMethod
508+ }
509 if err := checkContentLength(request, maxBodySize); err != nil {
510 return err
511 }
512 if request.Header.Get("Content-Type") != JSONMediaType {
513 return ErrWrongContentType
514 }
515- if request.Method != "POST" {
516- return ErrWrongRequestMethod
517- }
518 return nil
519 }
520
521@@ -325,7 +349,10 @@
522 }
523
524 func checkUnicast(ucast *Unicast) (time.Time, *APIError) {
525- if ucast.UserId == "" || ucast.DeviceId == "" || ucast.AppId == "" {
526+ if ucast.AppId == "" {
527+ return zeroTime, ErrMissingIdField
528+ }
529+ if ucast.Token == "" && (ucast.UserId == "" || ucast.DeviceId == "") {
530 return zeroTime, ErrMissingIdField
531 }
532 return checkCastCommon(ucast.Data, ucast.ExpireOn)
533@@ -341,9 +368,21 @@
534 if apiErr != nil {
535 return apiErr
536 }
537- chanId := store.UnicastInternalChannelId(ucast.UserId, ucast.DeviceId)
538+ chanId, err := sto.GetInternalChannelIdFromToken(ucast.Token, ucast.AppId, ucast.UserId, ucast.DeviceId)
539+ if err != nil {
540+ switch err {
541+ case store.ErrUnknownToken:
542+ return ErrUnknownToken
543+ case store.ErrUnauthorized:
544+ return ErrUnauthorized
545+ default:
546+ h.logger.Errorf("could not resolve token: %v", err)
547+ return ErrCouldNotResolveToken
548+ }
549+ }
550+
551 msgId := generateMsgId()
552- err := sto.AppendToUnicastChannel(chanId, ucast.AppId, ucast.Data, msgId, expire)
553+ err = sto.AppendToUnicastChannel(chanId, ucast.AppId, ucast.Data, msgId, expire)
554 if err != nil {
555 h.logger.Errorf("could not store notification: %v", err)
556 return ErrCouldNotStoreNotification
557@@ -378,6 +417,62 @@
558 fmt.Fprintf(writer, `{"ok":true}`)
559 }
560
561+type RegisterHandler struct {
562+ *context
563+}
564+
565+func checkRegister(reg *Registration) *APIError {
566+ if reg.DeviceId == "" || reg.AppId == "" {
567+ return ErrMissingIdField
568+ }
569+ return nil
570+}
571+
572+func (h *RegisterHandler) doRegister(sto store.PendingStore, reg *Registration) (string, *APIError) {
573+ apiErr := checkRegister(reg)
574+ if apiErr != nil {
575+ return "", apiErr
576+ }
577+ token, err := sto.Register(reg.DeviceId, reg.AppId)
578+ if err != nil {
579+ h.logger.Errorf("could not make a token: %v", err)
580+ return "", ErrCouldNotMakeToken
581+ }
582+ return token, nil
583+}
584+
585+func (h *RegisterHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
586+ var apiErr *APIError
587+ defer func() {
588+ if apiErr != nil {
589+ RespondError(writer, apiErr)
590+ }
591+ }()
592+
593+ reg := &Registration{}
594+
595+ sto, apiErr := h.prepare(writer, request, reg)
596+ if apiErr != nil {
597+ return
598+ }
599+ defer sto.Close()
600+
601+ token, apiErr := h.doRegister(sto, reg)
602+ if apiErr != nil {
603+ return
604+ }
605+
606+ writer.Header().Set("Content-Type", "application/json")
607+ res, err := json.Marshal(map[string]interface{}{
608+ "ok": true,
609+ "token": token,
610+ })
611+ if err != nil {
612+ panic(fmt.Errorf("couldn't marshal our own response: %v", err))
613+ }
614+ writer.Write(res)
615+}
616+
617 // MakeHandlersMux makes a handler that dispatches for the various API endpoints.
618 func MakeHandlersMux(storeForRequest StoreForRequest, broker broker.BrokerSending, logger logger.Logger) *http.ServeMux {
619 ctx := &context{
620@@ -388,5 +483,6 @@
621 mux := http.NewServeMux()
622 mux.Handle("/broadcast", &BroadcastHandler{context: ctx})
623 mux.Handle("/notify", &UnicastHandler{context: ctx})
624+ mux.Handle("/register", &RegisterHandler{context: ctx})
625 return mux
626 }
627
628=== modified file 'server/api/handlers_test.go'
629--- server/api/handlers_test.go 2014-05-01 18:58:16 +0000
630+++ server/api/handlers_test.go 2014-06-06 12:14:20 +0000
631@@ -192,6 +192,16 @@
632 intercept func(meth string, err error) error
633 }
634
635+func (isto *interceptInMemoryPendingStore) Register(appId, deviceId string) (string, error) {
636+ token, err := isto.InMemoryPendingStore.Register(appId, deviceId)
637+ return token, isto.intercept("Register", err)
638+}
639+
640+func (isto *interceptInMemoryPendingStore) GetInternalChannelIdFromToken(token, appId, userId, deviceId string) (store.InternalChannelId, error) {
641+ chanId, err := isto.InMemoryPendingStore.GetInternalChannelIdFromToken(token, appId, userId, deviceId)
642+ return chanId, isto.intercept("GetInternalChannelIdFromToken", err)
643+}
644+
645 func (isto *interceptInMemoryPendingStore) GetInternalChannelId(channel string) (store.InternalChannelId, error) {
646 chanId, err := isto.InMemoryPendingStore.GetInternalChannelId(channel)
647 return chanId, isto.intercept("GetInternalChannelId", err)
648@@ -262,6 +272,14 @@
649
650 u = unicast()
651 u.UserId = ""
652+ u.DeviceId = ""
653+ u.Token = "TOKEN"
654+ expire, apiErr = checkUnicast(u)
655+ c.Assert(apiErr, IsNil)
656+ c.Check(expire.Format(time.RFC3339), Equals, future)
657+
658+ u = unicast()
659+ u.UserId = ""
660 expire, apiErr = checkUnicast(u)
661 c.Check(apiErr, Equals, ErrMissingIdField)
662
663@@ -353,6 +371,40 @@
664 c.Check(s.testlog.Captured(), Equals, "ERROR could not store notification: fail\n")
665 }
666
667+func (s *handlersSuite) TestDoUnicastFromTokenFailures(c *C) {
668+ fail := errors.New("fail")
669+ sto := &interceptInMemoryPendingStore{
670+ store.NewInMemoryPendingStore(),
671+ func(meth string, err error) error {
672+ if meth == "GetInternalChannelIdFromToken" {
673+ return fail
674+ }
675+ return err
676+ },
677+ }
678+ ctx := &context{logger: s.testlog}
679+ bh := &UnicastHandler{ctx}
680+ u := &Unicast{
681+ Token: "tok",
682+ AppId: "app1",
683+ ExpireOn: future,
684+ Data: json.RawMessage(`{"a": 1}`),
685+ }
686+ apiErr := bh.doUnicast(sto, u)
687+ c.Check(apiErr, Equals, ErrCouldNotResolveToken)
688+ c.Check(s.testlog.Captured(), Equals, "ERROR could not resolve token: fail\n")
689+ s.testlog.ResetCapture()
690+
691+ fail = store.ErrUnknownToken
692+ apiErr = bh.doUnicast(sto, u)
693+ c.Check(apiErr, Equals, ErrUnknownToken)
694+ c.Check(s.testlog.Captured(), Equals, "")
695+ fail = store.ErrUnauthorized
696+ apiErr = bh.doUnicast(sto, u)
697+ c.Check(apiErr, Equals, ErrUnauthorized)
698+ c.Check(s.testlog.Captured(), Equals, "")
699+}
700+
701 func newPostRequest(path string, message interface{}, server *httptest.Server) *http.Request {
702 packedMessage, err := json.Marshal(message)
703 if err != nil {
704@@ -427,7 +479,7 @@
705 dest := make(map[string]bool)
706 err = json.Unmarshal(body, &dest)
707 c.Assert(err, IsNil)
708- c.Check(dest, DeepEquals, map[string]bool{"ok": true})
709+ c.Assert(dest, DeepEquals, map[string]bool{"ok": true})
710
711 top, _, err := sto.GetChannelSnapshot(store.SystemInternalChannelId)
712 c.Assert(err, IsNil)
713@@ -639,7 +691,7 @@
714 c.Check(response.Header.Get("Content-Type"), Equals, "application/json")
715 body, err := getResponseBody(response)
716 c.Assert(err, IsNil)
717- c.Check(string(body), Matches, ".*ok.*")
718+ c.Assert(string(body), Matches, ".*ok.*")
719
720 chanId := store.UnicastInternalChannelId("user2", "dev3")
721 c.Check(<-bsend.chanId, Equals, chanId)
722@@ -682,3 +734,142 @@
723 c.Assert(err, IsNil)
724 checkError(c, response, ErrMissingIdField)
725 }
726+
727+func (s *handlersSuite) TestCheckRegister(c *C) {
728+ registration := func() *Registration {
729+ return &Registration{
730+ DeviceId: "DEV1",
731+ AppId: "app1",
732+ }
733+ }
734+ reg := registration()
735+ apiErr := checkRegister(reg)
736+ c.Assert(apiErr, IsNil)
737+
738+ reg = registration()
739+ reg.AppId = ""
740+ apiErr = checkRegister(reg)
741+ c.Check(apiErr, Equals, ErrMissingIdField)
742+
743+ reg = registration()
744+ reg.DeviceId = ""
745+ apiErr = checkRegister(reg)
746+ c.Check(apiErr, Equals, ErrMissingIdField)
747+}
748+
749+func (s *handlersSuite) TestDoRegisterMissingIdField(c *C) {
750+ sto := store.NewInMemoryPendingStore()
751+ rh := &RegisterHandler{}
752+ token, apiErr := rh.doRegister(sto, &Registration{})
753+ c.Check(apiErr, Equals, ErrMissingIdField)
754+ c.Check(token, Equals, "")
755+}
756+
757+func (s *handlersSuite) TestDoRegisterCouldNotMakeToken(c *C) {
758+ sto := &interceptInMemoryPendingStore{
759+ store.NewInMemoryPendingStore(),
760+ func(meth string, err error) error {
761+ if meth == "Register" {
762+ return errors.New("fail")
763+ }
764+ return err
765+ },
766+ }
767+ ctx := &context{logger: s.testlog}
768+ rh := &RegisterHandler{ctx}
769+ _, apiErr := rh.doRegister(sto, &Registration{
770+ DeviceId: "DEV1",
771+ AppId: "app1",
772+ })
773+ c.Check(apiErr, Equals, ErrCouldNotMakeToken)
774+ c.Check(s.testlog.Captured(), Equals, "ERROR could not make a token: fail\n")
775+}
776+
777+func (s *handlersSuite) TestRespondsToRegisterAndUnicast(c *C) {
778+ sto := store.NewInMemoryPendingStore()
779+ stoForReq := func(http.ResponseWriter, *http.Request) (store.PendingStore, error) {
780+ return sto, nil
781+ }
782+ bsend := testBrokerSending{make(chan store.InternalChannelId, 1)}
783+ testServer := httptest.NewServer(MakeHandlersMux(stoForReq, bsend, nil))
784+ defer testServer.Close()
785+
786+ request := newPostRequest("/register", &Registration{
787+ DeviceId: "dev3",
788+ AppId: "app2",
789+ }, testServer)
790+
791+ response, err := s.client.Do(request)
792+ c.Assert(err, IsNil)
793+
794+ c.Check(response.StatusCode, Equals, http.StatusOK)
795+ c.Check(response.Header.Get("Content-Type"), Equals, "application/json")
796+ body, err := getResponseBody(response)
797+ c.Assert(err, IsNil)
798+ c.Assert(string(body), Matches, ".*ok.*")
799+ var reg map[string]interface{}
800+ err = json.Unmarshal(body, &reg)
801+ c.Assert(err, IsNil)
802+
803+ token, ok := reg["token"].(string)
804+ c.Assert(ok, Equals, true)
805+ c.Check(token, Not(Equals), nil)
806+
807+ payload := json.RawMessage(`{"foo":"bar"}`)
808+
809+ request = newPostRequest("/notify", &Unicast{
810+ Token: token,
811+ AppId: "app2",
812+ ExpireOn: future,
813+ Data: payload,
814+ }, testServer)
815+
816+ response, err = s.client.Do(request)
817+ c.Assert(err, IsNil)
818+
819+ c.Check(response.StatusCode, Equals, http.StatusOK)
820+ c.Check(response.Header.Get("Content-Type"), Equals, "application/json")
821+ body, err = getResponseBody(response)
822+ c.Assert(err, IsNil)
823+ c.Assert(string(body), Matches, ".*ok.*")
824+
825+ chanId := store.UnicastInternalChannelId("dev3", "dev3")
826+ c.Check(<-bsend.chanId, Equals, chanId)
827+ top, notifications, err := sto.GetChannelSnapshot(chanId)
828+ c.Assert(err, IsNil)
829+ c.Check(top, Equals, int64(0))
830+ c.Check(notifications, HasLen, 1)
831+}
832+
833+func (s *handlersSuite) TestCannotRegisterWithMissingFields(c *C) {
834+ stoForReq := func(http.ResponseWriter, *http.Request) (store.PendingStore, error) {
835+ return store.NewInMemoryPendingStore(), nil
836+ }
837+ ctx := &context{stoForReq, nil, nil}
838+ testServer := httptest.NewServer(&RegisterHandler{ctx})
839+ defer testServer.Close()
840+
841+ request := newPostRequest("/", &Registration{
842+ DeviceId: "DEV1",
843+ }, testServer)
844+
845+ response, err := s.client.Do(request)
846+ c.Assert(err, IsNil)
847+ checkError(c, response, ErrMissingIdField)
848+}
849+
850+func (s *handlersSuite) TestCannotRegisterWithNonPOST(c *C) {
851+ stoForReq := func(http.ResponseWriter, *http.Request) (store.PendingStore, error) {
852+ return store.NewInMemoryPendingStore(), nil
853+ }
854+ ctx := &context{stoForReq, nil, nil}
855+ testServer := httptest.NewServer(&RegisterHandler{ctx})
856+ defer testServer.Close()
857+
858+ request, err := http.NewRequest("GET", testServer.URL, nil)
859+ c.Assert(err, IsNil)
860+
861+ response, err := s.client.Do(request)
862+ c.Assert(err, IsNil)
863+ checkError(c, response, ErrWrongRequestMethod)
864+}
865
866=== modified file 'server/store/inmemory.go'
867--- server/store/inmemory.go 2014-05-02 15:10:18 +0000
868+++ server/store/inmemory.go 2014-06-06 12:14:20 +0000
869@@ -17,7 +17,10 @@
870 package store
871
872 import (
873+ "encoding/base64"
874 "encoding/json"
875+ "fmt"
876+ "strings"
877 "sync"
878 "time"
879
880@@ -44,6 +47,29 @@
881 }
882 }
883
884+func (sto *InMemoryPendingStore) Register(deviceId, appId string) (string, error) {
885+ return base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s::%s", appId, deviceId))), nil
886+}
887+
888+func (sto *InMemoryPendingStore) GetInternalChannelIdFromToken(token, appId, userId, deviceId string) (InternalChannelId, error) {
889+ if token != "" && appId != "" {
890+ decoded, err := base64.StdEncoding.DecodeString(token)
891+ if err != nil {
892+ return "", ErrUnknownToken
893+ }
894+ token = string(decoded)
895+ if !strings.HasPrefix(token, appId+"::") {
896+ return "", ErrUnauthorized
897+ }
898+ deviceId := token[len(appId)+2:]
899+ return UnicastInternalChannelId(deviceId, deviceId), nil
900+ }
901+ if userId != "" && deviceId != "" {
902+ return UnicastInternalChannelId(userId, deviceId), nil
903+ }
904+ return "", ErrUnknownToken
905+}
906+
907 func (sto *InMemoryPendingStore) GetInternalChannelId(name string) (InternalChannelId, error) {
908 if name == "system" {
909 return SystemInternalChannelId, nil
910
911=== modified file 'server/store/inmemory_test.go'
912--- server/store/inmemory_test.go 2014-04-30 17:44:56 +0000
913+++ server/store/inmemory_test.go 2014-06-06 12:14:20 +0000
914@@ -30,6 +30,50 @@
915
916 var _ = Suite(&inMemorySuite{})
917
918+func (s *inMemorySuite) TestRegister(c *C) {
919+ sto := NewInMemoryPendingStore()
920+
921+ tok1, err := sto.Register("DEV1", "app1")
922+ c.Assert(err, IsNil)
923+ tok2, err := sto.Register("DEV1", "app1")
924+ c.Assert(err, IsNil)
925+ c.Check(len(tok1), Not(Equals), 0)
926+ c.Check(tok1, Equals, tok2)
927+}
928+
929+func (s *inMemorySuite) TestGetInternalChannelIdFromToken(c *C) {
930+ sto := NewInMemoryPendingStore()
931+
932+ tok1, err := sto.Register("DEV1", "app1")
933+ c.Assert(err, IsNil)
934+ chanId, err := sto.GetInternalChannelIdFromToken(tok1, "app1", "", "")
935+ c.Assert(err, IsNil)
936+ c.Check(chanId, Equals, UnicastInternalChannelId("DEV1", "DEV1"))
937+}
938+
939+func (s *inMemorySuite) TestGetInternalChannelIdFromTokenFallback(c *C) {
940+ sto := NewInMemoryPendingStore()
941+
942+ chanId, err := sto.GetInternalChannelIdFromToken("", "app1", "u1", "d1")
943+ c.Assert(err, IsNil)
944+ c.Check(chanId, Equals, UnicastInternalChannelId("u1", "d1"))
945+}
946+
947+func (s *inMemorySuite) TestGetInternalChannelIdFromTokenErrors(c *C) {
948+ sto := NewInMemoryPendingStore()
949+ tok1, err := sto.Register("DEV1", "app1")
950+ c.Assert(err, IsNil)
951+
952+ _, err = sto.GetInternalChannelIdFromToken(tok1, "app2", "", "")
953+ c.Assert(err, Equals, ErrUnauthorized)
954+
955+ _, err = sto.GetInternalChannelIdFromToken("", "app2", "", "")
956+ c.Assert(err, Equals, ErrUnknownToken)
957+
958+ _, err = sto.GetInternalChannelIdFromToken("****", "app2", "", "")
959+ c.Assert(err, Equals, ErrUnknownToken)
960+}
961+
962 func (s *inMemorySuite) TestGetInternalChannelId(c *C) {
963 sto := NewInMemoryPendingStore()
964
965
966=== modified file 'server/store/store.go'
967--- server/store/store.go 2014-05-02 15:10:18 +0000
968+++ server/store/store.go 2014-06-06 12:14:20 +0000
969@@ -52,6 +52,8 @@
970 }
971
972 var ErrUnknownChannel = errors.New("unknown channel name")
973+var ErrUnknownToken = errors.New("unknown token")
974+var ErrUnauthorized = errors.New("unauthorized")
975 var ErrFull = errors.New("channel is full")
976 var ErrExpected128BitsHexRepr = errors.New("expected 128 bits hex repr")
977
978@@ -98,11 +100,17 @@
979
980 // PendingStore let store notifications into channels.
981 type PendingStore interface {
982+ // Register returns a token for a device id, application id pair.
983+ Register(deviceId, appId string) (token string, err error)
984 // GetInternalChannelId returns the internal store id for a channel
985 // given the name.
986 GetInternalChannelId(name string) (InternalChannelId, error)
987 // AppendToChannel appends a notification to the channel.
988 AppendToChannel(chanId InternalChannelId, notification json.RawMessage, expiration time.Time) error
989+ // GetInternalChannelIdFromToken returns the matching internal store
990+ // id for a channel given a registered token and application id or
991+ // directly a device id, user id pair.
992+ GetInternalChannelIdFromToken(token, appId, userId, deviceId string) (InternalChannelId, error)
993 // AppendToUnicastChannel appends a notification to the unicast channel.
994 // GetChannelSnapshot gets all the current notifications and
995 AppendToUnicastChannel(chanId InternalChannelId, appId string, notification json.RawMessage, msgId string, expiration time.Time) error
996
997=== modified file 'signing-helper/signing-helper.cpp'
998--- signing-helper/signing-helper.cpp 2014-05-01 10:24:23 +0000
999+++ signing-helper/signing-helper.cpp 2014-06-06 12:14:20 +0000
1000@@ -33,6 +33,7 @@
1001 #include <QObject>
1002 #include <QString>
1003 #include <QTimer>
1004+#include <QUrlQuery>
1005
1006 #include "ssoservice.h"
1007 #include "token.h"
1008@@ -63,12 +64,8 @@
1009 void SigningExample::handleCredentialsFound(Token token)
1010 {
1011 qDebug() << "Credentials found, signing url.";
1012-
1013- QString authHeader = token.signUrl(this->url, QStringLiteral("GET"), true);
1014-
1015- std::cout << authHeader.toStdString() << "\n";
1016+ std::cout << token.signUrl(this->url, QStringLiteral("POST")).toStdString();
1017 QCoreApplication::instance()->exit(0);
1018-
1019 }
1020
1021 void SigningExample::handleCredentialsNotFound()
1022@@ -84,13 +81,12 @@
1023 int main(int argc, char *argv[])
1024 {
1025 QCoreApplication a(argc, argv);
1026-
1027- UbuntuOne::SigningExample *example = new UbuntuOne::SigningExample(&a);
1028-
1029+ if (argc<2) {
1030+ return 2;
1031+ }
1032+ UbuntuOne::SigningExample *example = new UbuntuOne::SigningExample(&a, argv[1]);
1033 QObject::connect(example, SIGNAL(finished()), &a, SLOT(quit()));
1034-
1035 QTimer::singleShot(0, example, SLOT(doExample()));
1036-
1037 return a.exec();
1038 }
1039

Subscribers

People subscribed via source and target branches