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  }