go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/server/auth/signing/info.go (about) 1 // Copyright 2015 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package signing 16 17 import ( 18 "context" 19 "encoding/json" 20 "fmt" 21 "strings" 22 "time" 23 24 "go.chromium.org/luci/auth/identity" 25 26 "go.chromium.org/luci/server/auth/internal" 27 "go.chromium.org/luci/server/caching/layered" 28 ) 29 30 // See auth/cache.go for where __luciauth__ appears for the first time. We 31 // derive from it here for consistency. 32 const infoCacheNamespace = "__luciauth__.signing.info" 33 34 // URL string => *ServiceInfo. 35 var infoCache = layered.RegisterCache(layered.Parameters[*ServiceInfo]{ 36 ProcessCacheCapacity: 256, 37 GlobalNamespace: infoCacheNamespace, 38 Marshal: func(info *ServiceInfo) ([]byte, error) { 39 return json.Marshal(info) 40 }, 41 Unmarshal: func(blob []byte) (*ServiceInfo, error) { 42 out := &ServiceInfo{} 43 err := json.Unmarshal(blob, out) 44 return out, err 45 }, 46 }) 47 48 // ServiceInfo describes identity of some service. 49 // 50 // It matches JSON format of /auth/api/v1/server/info endpoint. 51 type ServiceInfo struct { 52 AppID string `json:"app_id,omitempty"` 53 AppRuntime string `json:"app_runtime,omitempty"` 54 AppRuntimeVersion string `json:"app_runtime_version,omitempty"` 55 AppVersion string `json:"app_version,omitempty"` 56 ServiceAccountName string `json:"service_account_name,omitempty"` 57 } 58 59 // FetchServiceInfo fetches information about the service from the given URL. 60 // 61 // The server is expected to reply with JSON described by ServiceInfo struct 62 // (like LUCI services do). Uses process cache to cache the response for 1h. 63 // 64 // LUCI services serve the service info at /auth/api/v1/server/info. 65 func FetchServiceInfo(ctx context.Context, url string) (*ServiceInfo, error) { 66 return infoCache.GetOrCreate(ctx, url, func() (*ServiceInfo, time.Duration, error) { 67 info := &ServiceInfo{} 68 req := internal.Request{ 69 Method: "GET", 70 URL: url, 71 Out: info, 72 } 73 if err := req.Do(ctx); err != nil { 74 return nil, 0, err 75 } 76 return info, time.Hour, nil 77 }, layered.WithRandomizedExpiration(10*time.Minute)) 78 } 79 80 // FetchServiceInfoFromLUCIService is shortcut for FetchServiceInfo that uses 81 // LUCI-specific endpoint. 82 // 83 // 'serviceURL' is root URL of the service (e.g. 'https://example.com'). 84 func FetchServiceInfoFromLUCIService(ctx context.Context, serviceURL string) (*ServiceInfo, error) { 85 serviceURL = strings.ToLower(serviceURL) 86 if !strings.HasPrefix(serviceURL, "https://") { 87 return nil, fmt.Errorf("not an https:// URL - %q", serviceURL) 88 } 89 domain := strings.TrimPrefix(serviceURL, "https://") 90 if domain == "" || strings.ContainsRune(domain, '/') { 91 return nil, fmt.Errorf("not a root URL - %q", serviceURL) 92 } 93 return FetchServiceInfo(ctx, serviceURL+"/auth/api/v1/server/info") 94 } 95 96 // FetchLUCIServiceIdentity returns "service:<app-id>" of a LUCI service. 97 // 98 // It is the same thing as inf.AppID returned by FetchServiceInfoFromLUCIService 99 // except it is cached more aggressively because service ID is static (unlike 100 // some other ServiceInfo fields). 101 // 102 // 'serviceURL' is root URL of the service (e.g. 'https://example.com'). 103 func FetchLUCIServiceIdentity(ctx context.Context, serviceURL string) (identity.Identity, error) { 104 info, err := FetchServiceInfoFromLUCIService(ctx, serviceURL) 105 if err != nil { 106 return "", err 107 } 108 return identity.MakeIdentity("service:" + info.AppID) 109 }