Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(346)

Side by Side Diff: services/vanadium/security/principal_service.go

Issue 1418013004: Principal Service: Add support for multiple user accounts (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 blesserURL = "https://dev.v.io/auth/google/bless"
30 29
31 type principalServiceImpl struct { 30 type principalServiceImpl struct {
32 app vpkg.AppInstanceName 31 app vpkg.AppInstanceName
33 psd *principalServiceDelegate 32 psd *principalServiceDelegate
34 } 33 }
35 34
36 func (pImpl *principalServiceImpl) Login() (*vpkg.Blessing, error) { 35 func (pImpl *principalServiceImpl) Login() (*vpkg.User, error) {
37 » if b := pImpl.psd.getBlessing(pImpl.app); b != nil { 36 » p, err := pImpl.psd.initPrincipal(pImpl.app)
38 » » // The app has already logged in. 37 » if err != nil {
39 » » return b, nil 38 » » return nil, err
40 } 39 }
41 40
42 token, err := pImpl.psd.getOAuth2Token() 41 token, err := pImpl.psd.getOAuth2Token()
43 if err != nil { 42 if err != nil {
44 return nil, err 43 return nil, err
45 } 44 }
46 45
47 » pub, priv, err := newPrincipalKey() 46 » wb, err := pImpl.psd.getBlessings(token, p.publicKey())
48 if err != nil { 47 if err != nil {
49 return nil, err 48 return nil, err
50 } 49 }
51 50
52 » wb, err := pImpl.psd.fetchWireBlessings(token, pub) 51 » email, err := emailFromBlessings(wb)
53 if err != nil { 52 if err != nil {
54 return nil, err 53 return nil, err
55 } 54 }
56 55
57 » pImpl.psd.addPrincipal(pImpl.app, &principal{wb, priv}) 56 » user := vpkg.User{Email: email, UserBlessing: newBlessing(wb)}
58 » return newBlessing(wb), nil 57 » p.addUser(user)
58 » return &user, nil
59 }
60
61 func (pImpl *principalServiceImpl) GetUsers() ([]vpkg.User, *vpkg.User, error) {
62 » if p := pImpl.psd.principal(pImpl.app); p != nil {
63 » » users, curr := p.getUsers()
64 » » return users, curr, nil
65 » }
66 » return nil, nil, fmt.Errorf("no principal available for app %v", pImpl.a pp)
67 }
68
69 func (pImpl *principalServiceImpl) SetCurrentUser(user vpkg.User) (*string, erro r) {
70 » if p := pImpl.psd.principal(pImpl.app); p != nil {
71 » » return p.setCurrentUser(user), nil
72 » }
73 » str := fmt.Sprintf("no principal available for app %v; please invoke Log in()", pImpl.app)
74 » return &str, nil
ashankar 2015/10/30 02:24:13 Shouldn't we be returning an error instead of sett
59 } 75 }
60 76
61 func (pImpl *principalServiceImpl) Logout() (err error) { 77 func (pImpl *principalServiceImpl) Logout() (err error) {
62 » pImpl.psd.deletePrincipal(pImpl.app) 78 » if p := pImpl.psd.principal(pImpl.app); p != nil {
63 » return 79 » » p.unsetCurrentUser()
80 » }
81 » return nil
64 } 82 }
65 83
66 func (pImpl *principalServiceImpl) GetUserBlessing(app vpkg.AppInstanceName) (*v pkg.Blessing, error) { 84 func (pImpl *principalServiceImpl) GetUser(app *vpkg.AppInstanceName) (*vpkg.Use r, error) {
67 » return pImpl.psd.getBlessing(app), nil 85 » if app == nil {
ashankar 2015/10/30 02:24:13 Why is this needed? Who would call this with nil a
86 » » app = &pImpl.app
87 » }
88 » p := pImpl.psd.principal(*app)
89 » if p == nil {
90 » » return nil, fmt.Errorf("no principal available for app %v", pImp l.app)
91 » }
92 » return p.curr, nil
68 } 93 }
69 94
70 func (pImpl *principalServiceImpl) Create(req vpkg.PrincipalService_Request) { 95 func (pImpl *principalServiceImpl) Create(req vpkg.PrincipalService_Request) {
71 stub := vpkg.NewPrincipalServiceStub(req, pImpl, bindings.GetAsyncWaiter ()) 96 stub := vpkg.NewPrincipalServiceStub(req, pImpl, bindings.GetAsyncWaiter ())
72 pImpl.psd.addStubForCleanup(stub) 97 pImpl.psd.addStubForCleanup(stub)
73 go func() { 98 go func() {
74 for { 99 for {
75 if err := stub.ServeRequest(); err != nil { 100 if err := stub.ServeRequest(); err != nil {
76 connectionError, ok := err.(*bindings.Connection Error) 101 connectionError, ok := err.(*bindings.Connection Error)
77 if !ok || !connectionError.Closed() { 102 if !ok || !connectionError.Closed() {
78 log.Println(err) 103 log.Println(err)
79 } 104 }
80 break 105 break
81 } 106 }
82 } 107 }
83 }() 108 }()
84 } 109 }
85 110
86 type principal struct {
87 blessing *wireBlessings
88 private *ecdsa.PrivateKey
89 }
90
91 type principalServiceDelegate struct { 111 type principalServiceDelegate struct {
92 » table map[vpkg.AppInstanceName]*principal 112 » Ctx application.Context
93 » Ctx application.Context 113 » mu sync.Mutex
94 » mu sync.Mutex 114 » stubs []*bindings.Stub // GUARDED_BY(mu)
95 » stubs []*bindings.Stub 115 » appPrincipals map[vpkg.AppInstanceName]*principal // GUARDED_BY(mu)
96 } 116 }
97 117
98 func (psd *principalServiceDelegate) Initialize(context application.Context) { 118 func (psd *principalServiceDelegate) Initialize(context application.Context) {
99 » psd.table = make(map[vpkg.AppInstanceName]*principal) 119 » psd.appPrincipals = make(map[vpkg.AppInstanceName]*principal)
100 psd.Ctx = context 120 psd.Ctx = context
101 } 121 }
102 122
103 func (psd *principalServiceDelegate) AcceptConnection(connection *application.Co nnection) { 123 func (psd *principalServiceDelegate) AcceptConnection(connection *application.Co nnection) {
104 app := vpkg.AppInstanceName{ 124 app := vpkg.AppInstanceName{
105 Url: connection.RequestorURL(), 125 Url: connection.RequestorURL(),
106 Qualifier: nil, 126 Qualifier: nil,
107 } 127 }
108 connection.ProvideServices(&vpkg.PrincipalService_ServiceFactory{&princi palServiceImpl{app, psd}}) 128 connection.ProvideServices(&vpkg.PrincipalService_ServiceFactory{&princi palServiceImpl{app, psd}})
109 } 129 }
110 130
111 func (psd *principalServiceDelegate) addStubForCleanup(stub *bindings.Stub) { 131 func (psd *principalServiceDelegate) addStubForCleanup(stub *bindings.Stub) {
112 psd.mu.Lock() 132 psd.mu.Lock()
113 defer psd.mu.Unlock() 133 defer psd.mu.Unlock()
114 psd.stubs = append(psd.stubs, stub) 134 psd.stubs = append(psd.stubs, stub)
115 } 135 }
116 136
137 func (psd *principalServiceDelegate) principal(app vpkg.AppInstanceName) *princi pal {
138 psd.mu.Lock()
139 defer psd.mu.Unlock()
140 return psd.appPrincipals[app]
141 }
142
143 func (psd *principalServiceDelegate) initPrincipal(app vpkg.AppInstanceName) (*p rincipal, error) {
144 psd.mu.Lock()
145 defer psd.mu.Unlock()
146 if p, ok := psd.appPrincipals[app]; ok {
147 return p, nil
148 }
149 p, err := newPrincipal()
150 if err != nil {
151 return nil, err
152 }
153 psd.appPrincipals[app] = p
154 return p, nil
155 }
156
117 func (psd *principalServiceDelegate) getOAuth2Token() (string, error) { 157 func (psd *principalServiceDelegate) getOAuth2Token() (string, error) {
118 authReq, authPtr := auth.CreateMessagePipeForAuthenticationService() 158 authReq, authPtr := auth.CreateMessagePipeForAuthenticationService()
119 psd.Ctx.ConnectToApplication("mojo:authentication").ConnectToService(&au thReq) 159 psd.Ctx.ConnectToApplication("mojo:authentication").ConnectToService(&au thReq)
120 authProxy := auth.NewAuthenticationServiceProxy(authPtr, bindings.GetAsy ncWaiter()) 160 authProxy := auth.NewAuthenticationServiceProxy(authPtr, bindings.GetAsy ncWaiter())
121 161
122 name, errString, _ := authProxy.SelectAccount(false /*return_last_select ed*/) 162 name, errString, _ := authProxy.SelectAccount(false /*return_last_select ed*/)
123 if name == nil { 163 if name == nil {
124 » » return "", fmt.Errorf("Failed to select an account for user:%s", errString) 164 » » return "", fmt.Errorf("failed to select an account for user:%s", errString)
125 } 165 }
126
127 token, errString, _ := authProxy.GetOAuth2Token(*name, []string{"email"} ) 166 token, errString, _ := authProxy.GetOAuth2Token(*name, []string{"email"} )
128 if token == nil { 167 if token == nil {
129 » » return "", fmt.Errorf("Failed to obtain OAuth2 token for selecte d account:%s", errString) 168 » » return "", fmt.Errorf("failed to obtain OAuth2 token for selecte d account:%s", errString)
130 } 169 }
131 return *token, nil 170 return *token, nil
132 } 171 }
133 172
134 func (psd *principalServiceDelegate) fetchWireBlessings(token string, pub public Key) (*wireBlessings, error) { 173 func (psd *principalServiceDelegate) getBlessings(token string, pub publicKey) ( *wireBlessings, error) {
135 networkReq, networkPtr := network.CreateMessagePipeForNetworkService() 174 networkReq, networkPtr := network.CreateMessagePipeForNetworkService()
136 psd.Ctx.ConnectToApplication("mojo:network_service").ConnectToService(&n etworkReq) 175 psd.Ctx.ConnectToApplication("mojo:network_service").ConnectToService(&n etworkReq)
137 networkProxy := network.NewNetworkServiceProxy(networkPtr, bindings.GetA syncWaiter()) 176 networkProxy := network.NewNetworkServiceProxy(networkPtr, bindings.GetA syncWaiter())
138 177
139 urlLoaderReq, urlLoaderPtr := url_loader.CreateMessagePipeForUrlLoader() 178 urlLoaderReq, urlLoaderPtr := url_loader.CreateMessagePipeForUrlLoader()
140 if err := networkProxy.CreateUrlLoader(urlLoaderReq); err != nil { 179 if err := networkProxy.CreateUrlLoader(urlLoaderReq); err != nil {
141 » » return nil, fmt.Errorf("Failed to create url loader: %v", err) 180 » » return nil, fmt.Errorf("failed to create url loader: %v", err)
142 } 181 }
143 » urlLoaderProxy := url_loader.NewUrlLoaderProxy(urlLoaderPtr, bindings.Ge tAsyncWaiter()) 182 » urlLoader := url_loader.NewUrlLoaderProxy(urlLoaderPtr, bindings.GetAsyn cWaiter())
144 183
145 req, err := blessingRequestURL(token, pub) 184 req, err := blessingRequestURL(token, pub)
146 if err != nil { 185 if err != nil {
147 return nil, err 186 return nil, err
148 } 187 }
149 188
150 » resp, err := urlLoaderProxy.Start(*req) 189 » resp, err := urlLoader.Start(*req)
151 if err != nil || resp.Error != nil { 190 if err != nil || resp.Error != nil {
152 » » return nil, fmt.Errorf("Blessings request to Vanadium Identity P rovider failed: %v(%v)", err, resp.Error) 191 » » return nil, fmt.Errorf("blessings request to Vanadium Identity P rovider failed: %v(%v)", err, resp.Error)
153 } 192 }
154 193
155 res, b := (*resp.Body).ReadData(system.MOJO_READ_DATA_FLAG_ALL_OR_NONE) 194 res, b := (*resp.Body).ReadData(system.MOJO_READ_DATA_FLAG_ALL_OR_NONE)
156 if res != system.MOJO_RESULT_OK { 195 if res != system.MOJO_RESULT_OK {
157 » » return nil, fmt.Errorf("Failed to read response (blessings) from Vanadium Identity Provider. Result: %v", res) 196 » » return nil, fmt.Errorf("failed to read response (blessings) from Vanadium Identity Provider. Result: %v", res)
158 } 197 }
159 198
160 var wb wireBlessings 199 var wb wireBlessings
161 if err := json.Unmarshal(b, &wb); err != nil { 200 if err := json.Unmarshal(b, &wb); err != nil {
162 » » return nil, fmt.Errorf("Failed to unmarshal response (blessings) from Vanadium Identity Provider: %v", err) 201 » » return nil, fmt.Errorf("failed to unmarshal response (blessings) from Vanadium Identity Provider: %v", err)
163 } 202 }
164 » // TODO(ataly, gauthamt): We should verify all signatures on the certifi cate chains in the 203
165 » // wire blessings to ensure that it was not tampered with. 204 » // TODO(ataly, gauthamt): We should verify all signatures on the certifi cate
205 » // chains in the wire blessings to ensure that it was not tampered with.
166 return &wb, nil 206 return &wb, nil
167 } 207 }
168 208
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() { 209 func (psd *principalServiceDelegate) Quit() {
191 psd.mu.Lock() 210 psd.mu.Lock()
192 defer psd.mu.Unlock() 211 defer psd.mu.Unlock()
193 for _, stub := range psd.stubs { 212 for _, stub := range psd.stubs {
194 stub.Close() 213 stub.Close()
195 } 214 }
196 } 215 }
197 216
198 func blessingRequestURL(token string, pub publicKey) (*url_request.UrlRequest, e rror) { 217 func blessingRequestURL(token string, pub publicKey) (*url_request.UrlRequest, e rror) {
199 baseURL, err := url.Parse(blesserURL) 218 baseURL, err := url.Parse(blesserURL)
(...skipping 16 matching lines...) Expand all
216 } 235 }
217 236
218 //export MojoMain 237 //export MojoMain
219 func MojoMain(handle C.MojoHandle) C.MojoResult { 238 func MojoMain(handle C.MojoHandle) C.MojoResult {
220 application.Run(&principalServiceDelegate{}, system.MojoHandle(handle)) 239 application.Run(&principalServiceDelegate{}, system.MojoHandle(handle))
221 return C.MOJO_RESULT_OK 240 return C.MOJO_RESULT_OK
222 } 241 }
223 242
224 func main() { 243 func main() {
225 } 244 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698