p1: /v2/scrape, /v1/fetch, auth, CLI, contract tests
Single Fetcher.Fetch on the Guard that holds DenyPrefixes. skipTlsVerification is ignored. No 502. keys add writes the secret only to --out. Release binary has no -test-allow-prefix.
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
)
|
||||
|
||||
type Caller struct {
|
||||
Name string `toml:"name"`
|
||||
Workspace string `toml:"workspace"`
|
||||
KeySHA256 string `toml:"key_sha256"`
|
||||
}
|
||||
|
||||
type file struct {
|
||||
Caller []Caller `toml:"caller"`
|
||||
}
|
||||
|
||||
// Store holds hashed keys. Reload on SIGHUP via Load.
|
||||
type Store struct {
|
||||
mu sync.RWMutex
|
||||
path string
|
||||
callers []Caller
|
||||
}
|
||||
|
||||
func Load(path string) (*Store, error) {
|
||||
s := &Store{path: path}
|
||||
if err := s.reload(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s *Store) Reload() error { return s.reload() }
|
||||
|
||||
func (s *Store) reload() error {
|
||||
b, err := os.ReadFile(s.path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var f file
|
||||
if err := toml.Unmarshal(b, &f); err != nil {
|
||||
return err
|
||||
}
|
||||
s.mu.Lock()
|
||||
s.callers = f.Caller
|
||||
s.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// LookupBearer returns the caller for a raw bearer token, or nil.
|
||||
func (s *Store) LookupBearer(token string) *Caller {
|
||||
if token == "" || s == nil {
|
||||
return nil
|
||||
}
|
||||
sum := sha256.Sum256([]byte(token))
|
||||
want := hex.EncodeToString(sum[:])
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
var found *Caller
|
||||
for i := range s.callers {
|
||||
got := strings.ToLower(strings.TrimSpace(s.callers[i].KeySHA256))
|
||||
if len(got) != len(want) {
|
||||
continue
|
||||
}
|
||||
if subtle.ConstantTimeCompare([]byte(got), []byte(want)) == 1 {
|
||||
c := s.callers[i]
|
||||
found = &c
|
||||
}
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
func HashKey(key string) string {
|
||||
sum := sha256.Sum256([]byte(key))
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
func GenerateKey() (string, error) {
|
||||
var b [32]byte
|
||||
if _, err := rand.Read(b[:]); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.RawURLEncoding.EncodeToString(b[:]), nil
|
||||
}
|
||||
|
||||
func Add(path, name, workspace, keyOut string) error {
|
||||
if _, err := os.Stat(keyOut); err == nil {
|
||||
return fmt.Errorf("refusing to overwrite %s", keyOut)
|
||||
}
|
||||
key, err := GenerateKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.WriteFile(keyOut, []byte(key+"\n"), 0o600); err != nil {
|
||||
return err
|
||||
}
|
||||
var f file
|
||||
if b, err := os.ReadFile(path); err == nil {
|
||||
_ = toml.Unmarshal(b, &f)
|
||||
}
|
||||
f.Caller = append(f.Caller, Caller{Name: name, Workspace: workspace, KeySHA256: HashKey(key)})
|
||||
out, err := toml.Marshal(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmp := path + ".tmp"
|
||||
if err := os.WriteFile(tmp, out, 0o640); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Rename(tmp, path)
|
||||
}
|
||||
|
||||
func List(path string) ([]Caller, error) {
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var f file
|
||||
if err := toml.Unmarshal(b, &f); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f.Caller, nil
|
||||
}
|
||||
|
||||
func Revoke(path, name string) error {
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var f file
|
||||
if err := toml.Unmarshal(b, &f); err != nil {
|
||||
return err
|
||||
}
|
||||
out := f.Caller[:0]
|
||||
for _, c := range f.Caller {
|
||||
if c.Name != name {
|
||||
out = append(out, c)
|
||||
}
|
||||
}
|
||||
f.Caller = out
|
||||
raw, err := toml.Marshal(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(path, raw, 0o640)
|
||||
}
|
||||
Reference in New Issue
Block a user