Files
CLIProxyAPI/internal/pluginstore/request_identity.go

66 lines
2.5 KiB
Go

package pluginstore
import (
"crypto/sha256"
"fmt"
"net/http"
"net/url"
"strings"
"time"
)
type preparedPluginStoreAuth struct {
requestURL string
kind string
headers http.Header
authenticated bool
}
// LatestReleaseCacheKey identifies a repository under the effective credentials
// and network scope, without retaining credential material in the key.
func (c Client) LatestReleaseCacheKey(plugin Plugin) (string, error) {
_, key, errPrepare := c.PrepareLatestRelease(plugin)
return key, errPrepare
}
// PrepareLatestRelease binds the cache identity and initial release request to
// the same credential snapshot, even when environment values change in a queue.
func (c Client) PrepareLatestRelease(plugin Plugin) (Client, string, error) {
owner, repo, errRepository := GitHubRepositoryParts(plugin.Repository)
if errRepository != nil {
return Client{}, "", errRepository
}
requestURL := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/latest", url.PathEscape(owner), url.PathEscape(repo))
headers, authenticated, errHeaders := c.authHeaders(requestURL, RequestKindMetadata)
if errHeaders != nil {
return Client{}, "", errHeaders
}
c.preparedAuth = &preparedPluginStoreAuth{requestURL: requestURL, kind: RequestKindMetadata, headers: headers, authenticated: authenticated}
return c, strings.ToLower(requestURL) + "/" + requestIdentity(c.NetworkScope, headers, authenticated), nil
}
func (c Client) authHeaders(requestURL, kind string) (http.Header, bool, error) {
if errURL := validatePluginStoreRequestURL(c.Auth, requestURL, kind); errURL != nil {
return nil, false, errURL
}
if errExpiry := validateResolvedAuthExpiry(c.ResolvedAuth, c.ResolvedAuthExpiresAt, time.Now().UTC(), requestURL, kind); errExpiry != nil {
return nil, false, errExpiry
}
if prepared := c.preparedAuth; prepared != nil && prepared.requestURL == requestURL && prepared.kind == kind {
return prepared.headers.Clone(), prepared.authenticated, nil
}
headers := make(http.Header)
authenticated, errAuth := applyPluginStoreAuthForClient(headers, c.ResolvedAuth, c.Auth, requestURL, kind)
return headers, authenticated, errAuth
}
func requestIdentity(networkScope string, headers http.Header, authenticated bool) string {
hash := sha256.New()
_, _ = fmt.Fprintf(hash, "%s\x00%t\x00", networkScope, authenticated)
if authenticated {
// Header.Write sorts header names, making equivalent auth rules share a key.
_ = headers.Write(hash)
}
return fmt.Sprintf("%x", hash.Sum(nil))
}