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
- Variables
- func Handler(store storage.Storage, s *Signer) http.Handler
- func StoreURL(ctx context.Context, s *Signer, key string, opts storage.URLOptions) (string, error)
- func StoreUploadURL(ctx context.Context, s *Signer, key string, opts storage.UploadURLOptions) (storage.UploadRequest, error)
- type Config
- type Signer
Constants ¶
const MinSecretBytes = 32
MinSecretBytes is the shortest Secret a Signer accepts.
Variables ¶
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 ¶
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 ¶
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 (*Signer) URL ¶
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.