OLD | NEW |
---|---|
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package main | 5 package main |
6 | 6 |
7 import ( | 7 import ( |
8 "crypto/ecdsa" | |
9 "encoding/base64" | 8 "encoding/base64" |
10 "encoding/json" | 9 "encoding/json" |
11 "fmt" | 10 "fmt" |
12 "log" | 11 "log" |
13 "net/url" | 12 "net/url" |
14 "sync" | 13 "sync" |
15 | 14 |
16 "mojo/public/go/application" | 15 "mojo/public/go/application" |
17 "mojo/public/go/bindings" | 16 "mojo/public/go/bindings" |
18 "mojo/public/go/system" | 17 "mojo/public/go/system" |
19 "mojo/public/interfaces/network/url_request" | 18 "mojo/public/interfaces/network/url_request" |
20 auth "mojo/services/authentication/interfaces/authentication" | 19 auth "mojo/services/authentication/interfaces/authentication" |
21 network "mojo/services/network/interfaces/network_service" | 20 network "mojo/services/network/interfaces/network_service" |
22 "mojo/services/network/interfaces/url_loader" | 21 "mojo/services/network/interfaces/url_loader" |
23 vpkg "mojo/services/vanadium/security/interfaces/principal" | 22 vpkg "mojo/services/vanadium/security/interfaces/principal" |
24 ) | 23 ) |
25 | 24 |
26 //#include "mojo/public/c/system/types.h" | 25 //#include "mojo/public/c/system/types.h" |
27 import "C" | 26 import "C" |
28 | 27 |
29 const blesserURL = "https://dev.v.io/auth/google/bless" | 28 const ( |
29 » blesserURL = "https://dev.v.io/auth/google/bless" | |
30 » tokenInfoURL = "https://www.googleapis.com/oauth2/v1/tokeninfo" | |
ukode
2015/10/22 20:19:01
Is this URL right. I thought they moved to v2 or v
ataly
2015/10/23 21:14:53
Oh yes, thanks for pointing this out. At the momen
ukode
2015/10/23 22:45:43
Thanks for the clean up.
| |
31 ) | |
30 | 32 |
31 type principalServiceImpl struct { | 33 type principalServiceImpl struct { |
32 app vpkg.AppInstanceName | 34 app vpkg.AppInstanceName |
33 psd *principalServiceDelegate | 35 psd *principalServiceDelegate |
34 } | 36 } |
35 | 37 |
36 func (pImpl *principalServiceImpl) Login() (*vpkg.Blessing, error) { | 38 func (pImpl *principalServiceImpl) Login() (*vpkg.UserId, error) { |
37 » if b := pImpl.psd.getBlessing(pImpl.app); b != nil { | 39 » p, err := pImpl.psd.initPrincipal(pImpl.app) |
38 » » // The app has already logged in. | 40 » if err != nil { |
39 » » return b, nil | 41 » » return nil, err |
40 } | 42 } |
41 | 43 |
42 token, err := pImpl.psd.getOAuth2Token() | 44 token, err := pImpl.psd.getOAuth2Token() |
43 if err != nil { | 45 if err != nil { |
44 return nil, err | 46 return nil, err |
45 } | 47 } |
46 | 48 |
47 » pub, priv, err := newPrincipalKey() | 49 » blessings, err := pImpl.psd.getBlessings(token, p.publicKey()) |
48 if err != nil { | 50 if err != nil { |
49 return nil, err | 51 return nil, err |
50 } | 52 } |
51 | 53 |
52 » wb, err := pImpl.psd.fetchWireBlessings(token, pub) | 54 » id, err := userIdFromBlessings(blessings) |
53 if err != nil { | 55 if err != nil { |
54 return nil, err | 56 return nil, err |
55 } | 57 } |
56 | 58 |
57 » pImpl.psd.addPrincipal(pImpl.app, &principal{wb, priv}) | 59 » p.addUser(id, blessings) |
60 » return &id, nil | |
61 } | |
62 | |
63 func (pImpl *principalServiceImpl) GetUsers() ([]vpkg.UserId, *vpkg.UserId, erro r) { | |
64 » if p := pImpl.psd.principal(pImpl.app); p != nil { | |
65 » » users, curr := p.users() | |
66 » » return users, curr, nil | |
67 » } | |
68 » return nil, nil, fmt.Errorf("no principal available for app %v", pImpl.a pp) | |
69 } | |
70 | |
71 func (pImpl *principalServiceImpl) SetCurrentUser(id vpkg.UserId) (*string, erro r) { | |
72 » if p := pImpl.psd.principal(pImpl.app); p != nil { | |
73 » » return p.setCurrentUser(id), nil | |
74 » } | |
75 » str := fmt.Sprintf("no principal available for app %v; please invoke Log in()", pImpl.app) | |
76 » return &str, nil | |
77 } | |
78 | |
79 func (pImpl *principalServiceImpl) Logout() (err error) { | |
80 » if p := pImpl.psd.principal(pImpl.app); p != nil { | |
81 » » p.unsetCurrentUser() | |
82 » } | |
83 » return nil | |
84 } | |
85 | |
86 func (pImpl *principalServiceImpl) GetUserBlessing(app *vpkg.AppInstanceName) (* vpkg.Blessing, error) { | |
87 » if app == nil { | |
88 » » app = &pImpl.app | |
89 » } | |
90 » p := pImpl.psd.principal(*app) | |
91 » if p == nil { | |
92 » » return nil, fmt.Errorf("no principal available for app %v", pImp l.app) | |
93 » } | |
94 » wb := p.currentBlessing() | |
95 » if wb == nil { | |
96 » » return nil, fmt.Errorf("no blessing available for app %v", pImpl .app) | |
97 » } | |
58 return newBlessing(wb), nil | 98 return newBlessing(wb), nil |
59 } | 99 } |
60 | 100 |
61 func (pImpl *principalServiceImpl) Logout() (err error) { | |
62 pImpl.psd.deletePrincipal(pImpl.app) | |
63 return | |
64 } | |
65 | |
66 func (pImpl *principalServiceImpl) GetUserBlessing(app vpkg.AppInstanceName) (*v pkg.Blessing, error) { | |
67 return pImpl.psd.getBlessing(app), nil | |
68 } | |
69 | |
70 func (pImpl *principalServiceImpl) Create(req vpkg.PrincipalService_Request) { | 101 func (pImpl *principalServiceImpl) Create(req vpkg.PrincipalService_Request) { |
71 stub := vpkg.NewPrincipalServiceStub(req, pImpl, bindings.GetAsyncWaiter ()) | 102 stub := vpkg.NewPrincipalServiceStub(req, pImpl, bindings.GetAsyncWaiter ()) |
72 pImpl.psd.addStubForCleanup(stub) | 103 pImpl.psd.addStubForCleanup(stub) |
73 go func() { | 104 go func() { |
74 for { | 105 for { |
75 if err := stub.ServeRequest(); err != nil { | 106 if err := stub.ServeRequest(); err != nil { |
76 connectionError, ok := err.(*bindings.Connection Error) | 107 connectionError, ok := err.(*bindings.Connection Error) |
77 if !ok || !connectionError.Closed() { | 108 if !ok || !connectionError.Closed() { |
78 log.Println(err) | 109 log.Println(err) |
79 } | 110 } |
80 break | 111 break |
81 } | 112 } |
82 } | 113 } |
83 }() | 114 }() |
84 } | 115 } |
85 | 116 |
86 type principal struct { | |
87 blessing *wireBlessings | |
88 private *ecdsa.PrivateKey | |
89 } | |
90 | |
91 type principalServiceDelegate struct { | 117 type principalServiceDelegate struct { |
92 » table map[vpkg.AppInstanceName]*principal | 118 » Ctx application.Context |
93 » Ctx application.Context | 119 » mu sync.Mutex |
94 » mu sync.Mutex | 120 » stubs []*bindings.Stub // GUARDED_BY(mu) |
95 » stubs []*bindings.Stub | 121 » principals map[vpkg.AppInstanceName]*principal // GUARDED_BY(mu) |
96 } | 122 } |
97 | 123 |
98 func (psd *principalServiceDelegate) Initialize(context application.Context) { | 124 func (psd *principalServiceDelegate) Initialize(context application.Context) { |
99 » psd.table = make(map[vpkg.AppInstanceName]*principal) | 125 » psd.principals = make(map[vpkg.AppInstanceName]*principal) |
100 psd.Ctx = context | 126 psd.Ctx = context |
101 } | 127 } |
102 | 128 |
103 func (psd *principalServiceDelegate) AcceptConnection(connection *application.Co nnection) { | 129 func (psd *principalServiceDelegate) AcceptConnection(connection *application.Co nnection) { |
104 app := vpkg.AppInstanceName{ | 130 app := vpkg.AppInstanceName{ |
105 Url: connection.RequestorURL(), | 131 Url: connection.RequestorURL(), |
106 Qualifier: nil, | 132 Qualifier: nil, |
107 } | 133 } |
108 connection.ProvideServices(&vpkg.PrincipalService_ServiceFactory{&princi palServiceImpl{app, psd}}) | 134 connection.ProvideServices(&vpkg.PrincipalService_ServiceFactory{&princi palServiceImpl{app, psd}}) |
109 } | 135 } |
110 | 136 |
111 func (psd *principalServiceDelegate) addStubForCleanup(stub *bindings.Stub) { | 137 func (psd *principalServiceDelegate) addStubForCleanup(stub *bindings.Stub) { |
112 psd.mu.Lock() | 138 psd.mu.Lock() |
113 defer psd.mu.Unlock() | 139 defer psd.mu.Unlock() |
114 psd.stubs = append(psd.stubs, stub) | 140 psd.stubs = append(psd.stubs, stub) |
115 } | 141 } |
116 | 142 |
143 func (psd *principalServiceDelegate) principal(app vpkg.AppInstanceName) *princi pal { | |
144 psd.mu.Lock() | |
145 defer psd.mu.Unlock() | |
146 return psd.principals[app] | |
147 } | |
148 | |
149 func (psd *principalServiceDelegate) initPrincipal(app vpkg.AppInstanceName) (*p rincipal, error) { | |
150 psd.mu.Lock() | |
151 defer psd.mu.Unlock() | |
152 if p, ok := psd.principals[app]; ok { | |
153 return p, nil | |
154 } | |
155 p, err := newPrincipal() | |
156 if err != nil { | |
157 return nil, err | |
158 } | |
159 psd.principals[app] = p | |
160 return p, nil | |
161 } | |
162 | |
117 func (psd *principalServiceDelegate) getOAuth2Token() (string, error) { | 163 func (psd *principalServiceDelegate) getOAuth2Token() (string, error) { |
118 authReq, authPtr := auth.CreateMessagePipeForAuthenticationService() | 164 authReq, authPtr := auth.CreateMessagePipeForAuthenticationService() |
119 psd.Ctx.ConnectToApplication("mojo:authentication").ConnectToService(&au thReq) | 165 psd.Ctx.ConnectToApplication("mojo:authentication").ConnectToService(&au thReq) |
120 authProxy := auth.NewAuthenticationServiceProxy(authPtr, bindings.GetAsy ncWaiter()) | 166 authProxy := auth.NewAuthenticationServiceProxy(authPtr, bindings.GetAsy ncWaiter()) |
121 | 167 |
122 name, errString, _ := authProxy.SelectAccount(false /*return_last_select ed*/) | 168 name, errString, _ := authProxy.SelectAccount(false /*return_last_select ed*/) |
123 if name == nil { | 169 if name == nil { |
124 » » return "", fmt.Errorf("Failed to select an account for user:%s", errString) | 170 » » return "", fmt.Errorf("failed to select an account for user:%s", errString) |
125 } | 171 } |
126 | |
127 token, errString, _ := authProxy.GetOAuth2Token(*name, []string{"email"} ) | 172 token, errString, _ := authProxy.GetOAuth2Token(*name, []string{"email"} ) |
128 if token == nil { | 173 if token == nil { |
129 » » return "", fmt.Errorf("Failed to obtain OAuth2 token for selecte d account:%s", errString) | 174 » » return "", fmt.Errorf("failed to obtain OAuth2 token for selecte d account:%s", errString) |
130 } | 175 } |
131 return *token, nil | 176 return *token, nil |
132 } | 177 } |
133 | 178 |
134 func (psd *principalServiceDelegate) fetchWireBlessings(token string, pub public Key) (*wireBlessings, error) { | 179 func (psd *principalServiceDelegate) getBlessings(token string, pub publicKey) ( *wireBlessings, error) { |
135 networkReq, networkPtr := network.CreateMessagePipeForNetworkService() | 180 networkReq, networkPtr := network.CreateMessagePipeForNetworkService() |
136 psd.Ctx.ConnectToApplication("mojo:network_service").ConnectToService(&n etworkReq) | 181 psd.Ctx.ConnectToApplication("mojo:network_service").ConnectToService(&n etworkReq) |
137 networkProxy := network.NewNetworkServiceProxy(networkPtr, bindings.GetA syncWaiter()) | 182 networkProxy := network.NewNetworkServiceProxy(networkPtr, bindings.GetA syncWaiter()) |
138 | 183 |
139 urlLoaderReq, urlLoaderPtr := url_loader.CreateMessagePipeForUrlLoader() | 184 urlLoaderReq, urlLoaderPtr := url_loader.CreateMessagePipeForUrlLoader() |
140 if err := networkProxy.CreateUrlLoader(urlLoaderReq); err != nil { | 185 if err := networkProxy.CreateUrlLoader(urlLoaderReq); err != nil { |
141 » » return nil, fmt.Errorf("Failed to create url loader: %v", err) | 186 » » return nil, fmt.Errorf("failed to create url loader: %v", err) |
142 } | 187 } |
143 » urlLoaderProxy := url_loader.NewUrlLoaderProxy(urlLoaderPtr, bindings.Ge tAsyncWaiter()) | 188 » urlLoader := url_loader.NewUrlLoaderProxy(urlLoaderPtr, bindings.GetAsyn cWaiter()) |
144 | 189 |
145 req, err := blessingRequestURL(token, pub) | 190 req, err := blessingRequestURL(token, pub) |
146 if err != nil { | 191 if err != nil { |
147 return nil, err | 192 return nil, err |
148 } | 193 } |
149 | 194 |
150 » resp, err := urlLoaderProxy.Start(*req) | 195 » resp, err := urlLoader.Start(*req) |
151 if err != nil || resp.Error != nil { | 196 if err != nil || resp.Error != nil { |
152 » » return nil, fmt.Errorf("Blessings request to Vanadium Identity P rovider failed: %v(%v)", err, resp.Error) | 197 » » return nil, fmt.Errorf("blessings request to Vanadium Identity P rovider failed: %v(%v)", err, resp.Error) |
153 } | 198 } |
154 | 199 |
155 res, b := (*resp.Body).ReadData(system.MOJO_READ_DATA_FLAG_ALL_OR_NONE) | 200 res, b := (*resp.Body).ReadData(system.MOJO_READ_DATA_FLAG_ALL_OR_NONE) |
156 if res != system.MOJO_RESULT_OK { | 201 if res != system.MOJO_RESULT_OK { |
157 » » return nil, fmt.Errorf("Failed to read response (blessings) from Vanadium Identity Provider. Result: %v", res) | 202 » » return nil, fmt.Errorf("failed to read response (blessings) from Vanadium Identity Provider. Result: %v", res) |
158 } | 203 } |
159 | 204 |
160 var wb wireBlessings | 205 var wb wireBlessings |
161 if err := json.Unmarshal(b, &wb); err != nil { | 206 if err := json.Unmarshal(b, &wb); err != nil { |
162 » » return nil, fmt.Errorf("Failed to unmarshal response (blessings) from Vanadium Identity Provider: %v", err) | 207 » » return nil, fmt.Errorf("failed to unmarshal response (blessings) from Vanadium Identity Provider: %v", err) |
163 } | 208 } |
164 » // TODO(ataly, gauthamt): We should verify all signatures on the certifi cate chains in the | 209 |
165 » // wire blessings to ensure that it was not tampered with. | 210 » // TODO(ataly, gauthamt): We should verify all signatures on the certifi cate |
211 » // chains in the wire blessings to ensure that it was not tampered with. | |
166 return &wb, nil | 212 return &wb, nil |
167 } | 213 } |
168 | 214 |
169 func (psd *principalServiceDelegate) addPrincipal(app vpkg.AppInstanceName, p *p rincipal) { | |
170 psd.mu.Lock() | |
171 defer psd.mu.Unlock() | |
172 psd.table[app] = p | |
173 } | |
174 | |
175 func (psd *principalServiceDelegate) getBlessing(app vpkg.AppInstanceName) *vpkg .Blessing { | |
176 psd.mu.Lock() | |
177 defer psd.mu.Unlock() | |
178 if p, ok := psd.table[app]; ok { | |
179 return newBlessing(p.blessing) | |
180 } | |
181 return nil | |
182 } | |
183 | |
184 func (psd *principalServiceDelegate) deletePrincipal(app vpkg.AppInstanceName) { | |
185 psd.mu.Lock() | |
186 defer psd.mu.Unlock() | |
187 delete(psd.table, app) | |
188 } | |
189 | |
190 func (psd *principalServiceDelegate) Quit() { | 215 func (psd *principalServiceDelegate) Quit() { |
191 psd.mu.Lock() | 216 psd.mu.Lock() |
192 defer psd.mu.Unlock() | 217 defer psd.mu.Unlock() |
193 for _, stub := range psd.stubs { | 218 for _, stub := range psd.stubs { |
194 stub.Close() | 219 stub.Close() |
195 } | 220 } |
196 } | 221 } |
197 | 222 |
198 func blessingRequestURL(token string, pub publicKey) (*url_request.UrlRequest, e rror) { | 223 func blessingRequestURL(token string, pub publicKey) (*url_request.UrlRequest, e rror) { |
199 baseURL, err := url.Parse(blesserURL) | 224 baseURL, err := url.Parse(blesserURL) |
(...skipping 16 matching lines...) Expand all Loading... | |
216 } | 241 } |
217 | 242 |
218 //export MojoMain | 243 //export MojoMain |
219 func MojoMain(handle C.MojoHandle) C.MojoResult { | 244 func MojoMain(handle C.MojoHandle) C.MojoResult { |
220 application.Run(&principalServiceDelegate{}, system.MojoHandle(handle)) | 245 application.Run(&principalServiceDelegate{}, system.MojoHandle(handle)) |
221 return C.MOJO_RESULT_OK | 246 return C.MOJO_RESULT_OK |
222 } | 247 } |
223 | 248 |
224 func main() { | 249 func main() { |
225 } | 250 } |
OLD | NEW |