presign

package
v0.7.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 4, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Overview

Package presign gives a store with no URL scheme of its own (the local and memory drivers) the URLs of the storage contract, served by the application: a Signer makes the URLs, and Handler serves them.

A public object's URL is Base + "/" + the key. A private object's signed URL adds "expires" (Unix seconds) and "signature" (HMAC-SHA256 over the key and the expiry, with Secret); it stops working at the expiry, and a URL with any part changed does not work at all. Handler serves an object only through a URL the Signer would have made, so a private object is never readable without one.

framework.New mounts this for the local and memory drivers, at GOMBIT_STORAGE_LOCAL_URL ("/_storage" by default).

Index

Constants

View Source
const MinSecretBytes = 32

MinSecretBytes is the shortest Secret a Signer accepts.

Variables

View Source
var (
	// ErrExpired: a signed URL whose expiry has passed.
	ErrExpired = errors.New("presign: the URL has expired")
	// ErrSignature: a URL without a valid signature for its key and
	// expiry (missing, malformed, or for other values).
	ErrSignature = errors.New("presign: the URL's signature is invalid")
)

Functions

func Handler

func Handler(store storage.Storage, s *Signer) http.Handler

Handler serves the objects of store at the URLs s makes: GET and HEAD on s.Path() + "/" + key. A private object needs a valid signed URL (403 otherwise), a missing one is 404, and errors are D10 envelopes.

Objects are served as the store has them, inline (with byte ranges and conditional requests when the store's reader can seek, as the local and memory drivers' can), with "X-Content-Type-Options: nosniff" and a sandboxing Content-Security-Policy, so an uploaded HTML or SVG file served from the application's origin cannot run script there. A signed URL's response may be cached privately until the URL expires; a public one's, not at all (the object can be replaced under the same URL).

PUT stores a direct upload (Signer.UploadURL): only with an unexpired upload signature, a Content-Type equal to the signed one, and a body of exactly the signed length (403 otherwise, as S3 refuses a request that differs from its presigned one). It answers 200 with the new ETag.

func StoreURL

func StoreURL(ctx context.Context, s *Signer, key string, opts storage.URLOptions) (string, error)

StoreURL is Storage.URL for a driver whose URLs come from s (nil: none): it validates key and opts, honors ctx, and wraps the result as the contract asks. The local and memory drivers' URL is this.

func StoreUploadURL

func StoreUploadURL(ctx context.Context, s *Signer, key string, opts storage.UploadURLOptions) (storage.UploadRequest, error)

StoreUploadURL is storage.DirectUploader's UploadURL for a driver whose URLs come from s (nil: none), as StoreURL is for URL.

Types

type Config

type Config struct {
	// Base is where Handler serves: an absolute URL
	// ("https://app.example.com/_storage") or a root-relative path
	// ("/_storage"), without a query, a fragment, or a trailing '/'.
	Base string
	// Secret is the HMAC-SHA256 key, at least MinSecretBytes long. Anyone
	// holding it can sign URLs for every object.
	Secret []byte
	// PublicPrefix makes the keys under it public (storage.IsPublic): their
	// URLs need no signature. Empty makes no object public.
	PublicPrefix string
	// Scope is signed into every URL, so a URL works only on Signers with
	// the same Scope and Secret: set it to what tells apps sharing a secret
	// apart (framework.New uses the app's name and environment, so a
	// staging URL does not open production's object of the same key).
	Scope string
	// Now is the clock (time.Now when nil), for tests.
	Now func() time.Time
}

Config configures a Signer.

type Signer

type Signer struct {
	// contains filtered or unexported fields
}

Signer makes and checks the URLs of one store.

func New

func New(cfg Config) (*Signer, error)

New returns a Signer for cfg.

func (*Signer) Path

func (s *Signer) Path() string

Path is the URL path Handler serves under (the path of Base).

func (*Signer) URL

func (s *Signer) URL(key string, opts storage.URLOptions) (string, error)

URL returns key's URL, as Storage.URL specifies: a public one for a public key (ErrNotPublic for a private one), or a signed one that expires opts.Expires (whole seconds) after the start of the second it is signed in, as S3's SigV4 counts it. key and opts must be valid (a driver checks them first).

func (*Signer) UploadURL

func (s *Signer) UploadURL(key string, opts storage.UploadURLOptions) (storage.UploadRequest, error)

UploadURL returns a signed direct upload (storage.DirectUploader): a PUT to key's URL whose query signs the length, the content type, and the metadata, and the Content-Type header the client must send. Handler stores the body only when all of them match.

func (*Signer) Verify

func (s *Signer) Verify(key string, q url.Values) error

Verify reports whether a request for key with query q may read it: a request carrying a signature (or an expiry) only if it is an unexpired signature the Signer made for key (ErrExpired, ErrSignature), whether key is public or not; an unsigned request only for a public key.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL