github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/externals/services.go (about) 1 // Copyright 2015 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package externals 5 6 import ( 7 "context" 8 "encoding/json" 9 "sort" 10 "strings" 11 "sync" 12 13 libkb "github.com/keybase/client/go/libkb" 14 keybase1 "github.com/keybase/client/go/protocol/keybase1" 15 ) 16 17 // SupportedVersion is which version of ParamProofs is supported by this client. 18 const SupportedVersion int = 1 19 20 // Contains both the statically known services and loads the configurations for 21 // known services from the server 22 type proofServices struct { 23 sync.Mutex 24 libkb.Contextified 25 loadedHash *keybase1.MerkleStoreKitHash 26 externalServices map[string]libkb.ServiceType // map keys are ServiceType.Key() 27 displayConfigs map[string]keybase1.ServiceDisplayConfig 28 suggestionFold int 29 } 30 31 func NewProofServices(g *libkb.GlobalContext) libkb.ExternalServicesCollector { 32 return newProofServices(g) 33 } 34 35 func newProofServices(g *libkb.GlobalContext) *proofServices { 36 p := &proofServices{ 37 Contextified: libkb.NewContextified(g), 38 externalServices: make(map[string]libkb.ServiceType), 39 displayConfigs: make(map[string]keybase1.ServiceDisplayConfig), 40 } 41 p.registerServiceTypes(getStaticProofServices()) 42 return p 43 } 44 45 func (p *proofServices) Shutdown() { 46 p.Lock() 47 defer p.Unlock() 48 p.clearServiceTypes() 49 p.loadedHash = nil 50 } 51 52 func (p *proofServices) clearServiceTypes() { 53 p.externalServices = make(map[string]libkb.ServiceType) 54 p.displayConfigs = make(map[string]keybase1.ServiceDisplayConfig) 55 } 56 57 func (p *proofServices) registerServiceTypes(services []libkb.ServiceType) { 58 for _, st := range services { 59 if !useDevelProofCheckers && st.IsDevelOnly() { 60 continue 61 } 62 p.externalServices[st.Key()] = st 63 } 64 } 65 66 func (p *proofServices) GetServiceType(ctx context.Context, s string) libkb.ServiceType { 67 p.Lock() 68 defer p.Unlock() 69 p.loadServiceConfigs(p.MetaContext(ctx)) 70 return p.externalServices[strings.ToLower(s)] 71 } 72 73 func (p *proofServices) ListProofCheckers(mctx libkb.MetaContext) []string { 74 p.Lock() 75 defer p.Unlock() 76 p.loadServiceConfigs(mctx) 77 var ret []string 78 for k := range p.externalServices { 79 ret = append(ret, k) 80 } 81 return ret 82 } 83 84 type serviceAndPriority struct { 85 name string 86 priority int 87 } 88 89 func (p *proofServices) ListServicesThatAcceptNewProofs(mctx libkb.MetaContext) []string { 90 p.Lock() 91 defer p.Unlock() 92 p.loadServiceConfigs(mctx) 93 var services []serviceAndPriority 94 experimentalGenericProofs := mctx.G().FeatureFlags.Enabled(mctx, libkb.ExperimentalGenericProofs) 95 for k, v := range p.externalServices { 96 if experimentalGenericProofs || v.CanMakeNewProofsSkipFeatureFlag(mctx) { 97 s := serviceAndPriority{name: k, priority: v.DisplayPriority()} 98 services = append(services, s) 99 } 100 } 101 sort.Slice(services, func(i, j int) bool { 102 return services[i].priority < services[j].priority 103 }) 104 var serviceNames []string 105 for _, service := range services { 106 serviceNames = append(serviceNames, service.name) 107 } 108 return serviceNames 109 } 110 111 func (p *proofServices) ListDisplayConfigs(mctx libkb.MetaContext) (res []keybase1.ServiceDisplayConfig) { 112 p.Lock() 113 defer p.Unlock() 114 p.loadServiceConfigs(mctx) 115 for _, config := range p.displayConfigs { 116 res = append(res, config) 117 } 118 return res 119 } 120 121 func (p *proofServices) SuggestionFoldPriority(mctx libkb.MetaContext) int { 122 p.Lock() 123 defer p.Unlock() 124 p.loadServiceConfigs(mctx) 125 return p.suggestionFold 126 } 127 128 func (p *proofServices) loadServiceConfigs(mctx libkb.MetaContext) { 129 tracer := p.G().CTimeTracer(mctx.Ctx(), "proofServices.loadServiceConfigs", false) 130 defer tracer.Finish() 131 132 entry, err := p.G().GetParamProofStore().GetLatestEntryWithKnown(mctx, p.loadedHash) 133 if err != nil { 134 mctx.Debug("unable to load paramproofs: %v", err) 135 return 136 } 137 if entry == nil { 138 // Latest config already loaded. 139 return 140 } 141 defer mctx.Trace("proofServices.loadServiceConfigsBulk", &err)() 142 tracer.Stage("parse") 143 config, err := p.parseServerConfig(mctx, *entry) 144 if err != nil { 145 mctx.Debug("unable to parse paramproofs: %v", err) 146 return 147 } 148 tracer.Stage("fill") 149 p.suggestionFold = config.SuggestionFold 150 services := []libkb.ServiceType{} 151 for _, config := range config.ProofConfigs { 152 services = append(services, NewGenericSocialProofServiceType(config)) 153 } 154 tracer.Stage("register") 155 p.clearServiceTypes() 156 p.registerServiceTypes(getStaticProofServices()) 157 p.registerServiceTypes(services) 158 tracer.Stage("disp") 159 for _, config := range config.DisplayConfigs { 160 p.displayConfigs[config.Key] = *config 161 if service, ok := p.externalServices[config.Key]; ok { 162 service.SetDisplayConfig(config) 163 } 164 } 165 p.loadedHash = &entry.Hash 166 } 167 168 type parsedServerConfig struct { 169 SuggestionFold int 170 ProofConfigs []*GenericSocialProofConfig 171 DisplayConfigs []*keybase1.ServiceDisplayConfig 172 } 173 174 type proofServicesT struct { 175 SuggestionFold int `json:"suggestion_fold"` 176 Services []keybase1.ExternalServiceConfig `json:"services"` 177 } 178 179 func (p *proofServices) parseServerConfig(mctx libkb.MetaContext, entry keybase1.MerkleStoreEntry) (res parsedServerConfig, err error) { 180 b := []byte(entry.Entry) 181 services := proofServicesT{} 182 183 if err := json.Unmarshal(b, &services); err != nil { 184 return res, err 185 } 186 187 res.SuggestionFold = services.SuggestionFold 188 for _, service := range services.Services { 189 if service.Config != nil { 190 // Do some basic validation of what we parsed 191 validConf, err := NewGenericSocialProofConfig(p.G(), *service.Config) 192 if err != nil { 193 mctx.Debug("Unable to validate config for %s: %v", service.Config.DisplayName, err) 194 continue 195 } 196 res.ProofConfigs = append(res.ProofConfigs, validConf) 197 } 198 if service.Display != nil { 199 if service.Config != nil && service.Config.Domain != service.Display.Key { 200 mctx.Debug("Invalid display config, key mismatch %s != %s", service.Config.Domain, service.Display.Key) 201 continue 202 } 203 res.DisplayConfigs = append(res.DisplayConfigs, service.Display) 204 } 205 } 206 return res, nil 207 }