OLD | NEW |
1 // Copyright 2016 The LUCI Authors. All rights reserved. | 1 // Copyright 2016 The LUCI Authors. All rights reserved. |
2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
4 | 4 |
5 package authdb | 5 package authdb |
6 | 6 |
7 import ( | 7 import ( |
8 "fmt" | 8 "fmt" |
9 "net" | 9 "net" |
10 "strings" | 10 "strings" |
| 11 "sync" |
| 12 "time" |
11 | 13 |
12 "golang.org/x/net/context" | 14 "golang.org/x/net/context" |
13 | 15 |
| 16 "github.com/luci/luci-go/common/clock" |
| 17 "github.com/luci/luci-go/common/data/caching/lazyslot" |
14 "github.com/luci/luci-go/common/logging" | 18 "github.com/luci/luci-go/common/logging" |
15 "github.com/luci/luci-go/server/auth/identity" | 19 "github.com/luci/luci-go/server/auth/identity" |
16 "github.com/luci/luci-go/server/auth/service/protocol" | 20 "github.com/luci/luci-go/server/auth/service/protocol" |
| 21 "github.com/luci/luci-go/server/auth/signing" |
17 "github.com/luci/luci-go/server/secrets" | 22 "github.com/luci/luci-go/server/secrets" |
18 ) | 23 ) |
19 | 24 |
20 // OAuth client_id of https://apis-explorer.appspot.com/. | 25 // OAuth client_id of https://apis-explorer.appspot.com/. |
21 const googleAPIExplorerClientID = "292824132082.apps.googleusercontent.com" | 26 const googleAPIExplorerClientID = "292824132082.apps.googleusercontent.com" |
22 | 27 |
23 // SnapshotDB implements DB using AuthDB proto message. | 28 // SnapshotDB implements DB using AuthDB proto message. |
24 // | 29 // |
25 // Use NewSnapshotDB to create new instances. Don't touch public fields | 30 // Use NewSnapshotDB to create new instances. Don't touch public fields |
26 // of existing instances. | 31 // of existing instances. |
27 type SnapshotDB struct { | 32 type SnapshotDB struct { |
28 AuthServiceURL string // where it was fetched from | 33 AuthServiceURL string // where it was fetched from |
29 Rev int64 // its revision number | 34 Rev int64 // its revision number |
30 | 35 |
31 tokenServiceURL string // URL of the token server as provided by Auth se
rvice | 36 tokenServiceURL string // URL of the token server as provided by Auth se
rvice |
32 | 37 |
33 clientIDs map[string]struct{} // set of allowed client IDs | 38 clientIDs map[string]struct{} // set of allowed client IDs |
34 groups map[string]*group // map of all known groups | 39 groups map[string]*group // map of all known groups |
35 secrets secrets.StaticStore // secrets shared by all service with this
DB | 40 secrets secrets.StaticStore // secrets shared by all service with this
DB |
36 | 41 |
37 assignments map[identity.Identity]string // IP whitelist assignements | 42 assignments map[identity.Identity]string // IP whitelist assignements |
38 whitelists map[string][]net.IPNet // IP whitelists | 43 whitelists map[string][]net.IPNet // IP whitelists |
| 44 |
| 45 // Certs are loaded lazily in GetCertificates since they are used only w
hen |
| 46 // checking delegation tokens, which is relatively rare. |
| 47 certs lazyslot.Slot |
39 } | 48 } |
40 | 49 |
| 50 var _ DB = &SnapshotDB{} |
| 51 |
41 // group is a node in a group graph. Nested groups are referenced directly via | 52 // group is a node in a group graph. Nested groups are referenced directly via |
42 // pointer. | 53 // pointer. |
43 type group struct { | 54 type group struct { |
44 members map[identity.Identity]struct{} // set of all members | 55 members map[identity.Identity]struct{} // set of all members |
45 globs []identity.Glob // list of all identity globs | 56 globs []identity.Glob // list of all identity globs |
46 nested []*group // pointers to nested groups | 57 nested []*group // pointers to nested groups |
47 } | 58 } |
48 | 59 |
49 var _ DB = &SnapshotDB{} | 60 // certMap is used in GetCertificate and fetchTrustedCerts. |
| 61 type certMap map[identity.Identity]*signing.PublicCertificates |
50 | 62 |
51 // NewSnapshotDB creates new instance of SnapshotDB. | 63 // NewSnapshotDB creates new instance of SnapshotDB. |
52 // | 64 // |
53 // It does some preprocessing to speed up subsequent checks. Return errors if | 65 // It does some preprocessing to speed up subsequent checks. Return errors if |
54 // it encounters inconsistencies. | 66 // it encounters inconsistencies. |
55 func NewSnapshotDB(authDB *protocol.AuthDB, authServiceURL string, rev int64) (*
SnapshotDB, error) { | 67 func NewSnapshotDB(authDB *protocol.AuthDB, authServiceURL string, rev int64) (*
SnapshotDB, error) { |
56 db := &SnapshotDB{ | 68 db := &SnapshotDB{ |
57 AuthServiceURL: authServiceURL, | 69 AuthServiceURL: authServiceURL, |
58 Rev: rev, | 70 Rev: rev, |
59 | 71 |
60 tokenServiceURL: authDB.GetTokenServerUrl(), | 72 tokenServiceURL: authDB.GetTokenServerUrl(), |
61 } | 73 } |
62 | 74 |
| 75 // Load all trusted certificates lazily and refresh each hour. |
| 76 db.certs.Fetcher = func(c context.Context, prev lazyslot.Value) (lazyslo
t.Value, error) { |
| 77 mapping, err := db.fetchTrustedCerts(c) |
| 78 if err != nil { |
| 79 return lazyslot.Value{}, err |
| 80 } |
| 81 return lazyslot.Value{ |
| 82 Value: mapping, |
| 83 Expiration: clock.Now(c).Add(time.Hour), |
| 84 }, nil |
| 85 } |
| 86 |
63 // Set of all allowed clientIDs. | 87 // Set of all allowed clientIDs. |
64 db.clientIDs = make(map[string]struct{}, 2+len(authDB.GetOauthAdditional
ClientIds())) | 88 db.clientIDs = make(map[string]struct{}, 2+len(authDB.GetOauthAdditional
ClientIds())) |
65 db.clientIDs[googleAPIExplorerClientID] = struct{}{} | 89 db.clientIDs[googleAPIExplorerClientID] = struct{}{} |
66 if authDB.GetOauthClientId() != "" { | 90 if authDB.GetOauthClientId() != "" { |
67 db.clientIDs[authDB.GetOauthClientId()] = struct{}{} | 91 db.clientIDs[authDB.GetOauthClientId()] = struct{}{} |
68 } | 92 } |
69 for _, cid := range authDB.GetOauthAdditionalClientIds() { | 93 for _, cid := range authDB.GetOauthAdditionalClientIds() { |
70 if cid != "" { | 94 if cid != "" { |
71 db.clientIDs[cid] = struct{}{} | 95 db.clientIDs[cid] = struct{}{} |
72 } | 96 } |
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
251 } | 275 } |
252 | 276 |
253 // SharedSecrets is secrets.Store with secrets in Auth DB. | 277 // SharedSecrets is secrets.Store with secrets in Auth DB. |
254 // | 278 // |
255 // Such secrets are usually generated on central Auth Service and are known | 279 // Such secrets are usually generated on central Auth Service and are known |
256 // to all trusted services (so that they can use them to exchange data). | 280 // to all trusted services (so that they can use them to exchange data). |
257 func (db *SnapshotDB) SharedSecrets(c context.Context) (secrets.Store, error) { | 281 func (db *SnapshotDB) SharedSecrets(c context.Context) (secrets.Store, error) { |
258 return db.secrets, nil | 282 return db.secrets, nil |
259 } | 283 } |
260 | 284 |
| 285 // GetCertificates returns a bundle with certificates of a trusted signer. |
| 286 func (db *SnapshotDB) GetCertificates(c context.Context, signerID identity.Ident
ity) (*signing.PublicCertificates, error) { |
| 287 val, err := db.certs.Get(c) |
| 288 if err != nil { |
| 289 return nil, err |
| 290 } |
| 291 trustedCertsMap := val.Value.(certMap) |
| 292 return trustedCertsMap[signerID], nil |
| 293 } |
| 294 |
261 // GetWhitelistForIdentity returns name of the IP whitelist to use to check | 295 // GetWhitelistForIdentity returns name of the IP whitelist to use to check |
262 // IP of requests from given `ident`. | 296 // IP of requests from given `ident`. |
263 // | 297 // |
264 // It's used to restrict access for certain account to certain IP subnets. | 298 // It's used to restrict access for certain account to certain IP subnets. |
265 // | 299 // |
266 // Returns ("", nil) if `ident` is not IP restricted. | 300 // Returns ("", nil) if `ident` is not IP restricted. |
267 func (db *SnapshotDB) GetWhitelistForIdentity(c context.Context, ident identity.
Identity) (string, error) { | 301 func (db *SnapshotDB) GetWhitelistForIdentity(c context.Context, ident identity.
Identity) (string, error) { |
268 return db.assignments[ident], nil | 302 return db.assignments[ident], nil |
269 } | 303 } |
270 | 304 |
(...skipping 17 matching lines...) Expand all Loading... |
288 func (db *SnapshotDB) GetAuthServiceURL(c context.Context) (string, error) { | 322 func (db *SnapshotDB) GetAuthServiceURL(c context.Context) (string, error) { |
289 return db.AuthServiceURL, nil | 323 return db.AuthServiceURL, nil |
290 } | 324 } |
291 | 325 |
292 // GetTokenServiceURL returns root URL ("https://<host>") of the token server. | 326 // GetTokenServiceURL returns root URL ("https://<host>") of the token server. |
293 // | 327 // |
294 // This is needed to implement authdb.DB interface. | 328 // This is needed to implement authdb.DB interface. |
295 func (db *SnapshotDB) GetTokenServiceURL(c context.Context) (string, error) { | 329 func (db *SnapshotDB) GetTokenServiceURL(c context.Context) (string, error) { |
296 return db.tokenServiceURL, nil | 330 return db.tokenServiceURL, nil |
297 } | 331 } |
| 332 |
| 333 //// Implementation details. |
| 334 |
| 335 // fetchTrustedCerts is called by db.certs to fetch certificates. |
| 336 func (db *SnapshotDB) fetchTrustedCerts(c context.Context) (certMap, error) { |
| 337 // TODO(vadimsh): Stop loading certs from AuthServiceURL when all tokens |
| 338 // are generated by the token server. |
| 339 var urls []string |
| 340 if db.tokenServiceURL != "" { |
| 341 urls = []string{db.tokenServiceURL, db.AuthServiceURL} |
| 342 } else { |
| 343 urls = []string{db.AuthServiceURL} |
| 344 } |
| 345 |
| 346 certs := make([]*signing.PublicCertificates, len(urls)) |
| 347 errs := make([]error, len(urls)) |
| 348 |
| 349 // Fetch in parallel. |
| 350 wg := sync.WaitGroup{} |
| 351 for i := range urls { |
| 352 wg.Add(1) |
| 353 go func(i int) { |
| 354 defer wg.Done() |
| 355 certs[i], errs[i] = signing.FetchCertificatesFromLUCISer
vice(c, urls[i]) |
| 356 }(i) |
| 357 } |
| 358 wg.Wait() |
| 359 |
| 360 // Each certificate bundle is available under two keys: "service:<app-id
>" and |
| 361 // "user:<service-account-name>". This is so because there's one to one |
| 362 // association between GAE apps and corresponding @appspot.gserviceaccou
nt.com |
| 363 // service accounts. |
| 364 mapping := make(certMap, 2*len(certs)) |
| 365 for i, cert := range certs { |
| 366 if errs[i] != nil { |
| 367 return nil, errs[i] |
| 368 } |
| 369 if cert.AppID != "" { |
| 370 id, err := identity.MakeIdentity("service:" + cert.AppID
) |
| 371 if err != nil { |
| 372 return nil, fmt.Errorf("invalid app_id %q in fet
ched certificates bundle - %s", cert.AppID, err) |
| 373 } |
| 374 mapping[id] = cert |
| 375 } |
| 376 if cert.ServiceAccountName != "" { |
| 377 id, err := identity.MakeIdentity("user:" + cert.ServiceA
ccountName) |
| 378 if err != nil { |
| 379 return nil, fmt.Errorf("invalid service_account_
name %q in fetched certificates bundle - %s", cert.ServiceAccountName, err) |
| 380 } |
| 381 mapping[id] = cert |
| 382 } |
| 383 } |
| 384 |
| 385 return mapping, nil |
| 386 } |
OLD | NEW |