github.com/Schaudge/grailbase@v0.0.0-20240223061707-44c758a471c0/security/keycrypt/lookup.go (about)

     1  // Copyright 2018 GRAIL, Inc. All rights reserved.
     2  // Use of this source code is governed by the Apache-2.0
     3  // license that can be found in the LICENSE file.
     4  
     5  package keycrypt
     6  
     7  import (
     8  	"fmt"
     9  	"net/url"
    10  	"strings"
    11  	"sync"
    12  )
    13  
    14  var (
    15  	mu           sync.Mutex
    16  	resolvers    = map[string]Resolver{}
    17  	localSchemes = []string{"keychain", "localfile"}
    18  )
    19  
    20  // Register associates a Resolver with a scheme.
    21  func Register(scheme string, resolver Resolver) {
    22  	mu.Lock()
    23  	resolvers[scheme] = resolver
    24  	mu.Unlock()
    25  }
    26  
    27  // For testing.
    28  func unregister(scheme string) {
    29  	mu.Lock()
    30  	resolvers[scheme] = nil
    31  	mu.Unlock()
    32  }
    33  
    34  // RegisterFunc associates a Resolver (given by a func)
    35  // with a scheme.
    36  func RegisterFunc(scheme string, f func(string) Keycrypt) {
    37  	Register(scheme, ResolverFunc(f))
    38  }
    39  
    40  // Lookup retrieves a secret based on a URL, in the standard form:
    41  // scheme://host/path. The URL is interpreted according to the
    42  // Resolver registered with the given scheme. The scheme "local"
    43  // is a special scheme that attempts known local storage schemes:
    44  // first "keychain", and then "file".
    45  func Lookup(rawurl string) (Secret, error) {
    46  	u, err := url.Parse(rawurl)
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  	mu.Lock()
    51  	defer mu.Unlock()
    52  	var r Resolver
    53  	if u.Scheme == "local" {
    54  		for _, s := range localSchemes {
    55  			r = resolvers[s]
    56  			if r != nil {
    57  				break
    58  			}
    59  		}
    60  		if r == nil {
    61  			return nil, fmt.Errorf("no local resolvers found, tried: %s", strings.Join(localSchemes, ", "))
    62  		}
    63  	} else {
    64  		r = resolvers[u.Scheme]
    65  	}
    66  	if r == nil {
    67  		return nil, fmt.Errorf("unknown scheme \"%s\"", u.Scheme)
    68  	}
    69  	name := u.Path
    70  	if name != "" && name[0] == '/' {
    71  		name = name[1:]
    72  	}
    73  	return r.Resolve(u.Host).Lookup(name), nil
    74  }
    75  
    76  // Get data from a keycrypt URL.
    77  func Get(rawurl string) ([]byte, error) {
    78  	s, err := Lookup(rawurl)
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  	return s.Get()
    83  }
    84  
    85  // Put writes data to a keycrypt URL.
    86  func Put(rawurl string, data []byte) error {
    87  	s, err := Lookup(rawurl)
    88  	if err != nil {
    89  		return err
    90  	}
    91  	return s.Put(data)
    92  }