mirror of
https://github.com/willnorris/imageproxy.git
synced 2024-12-30 22:34:18 -05:00
add imageproxy-sign tool for calculating signatures
it's a little bit rough, but seems to work pretty well. Ref #145
This commit is contained in:
parent
38d3bcc7fe
commit
e1558d5626
3 changed files with 120 additions and 0 deletions
84
cmd/imageproxy-sign/main.go
Normal file
84
cmd/imageproxy-sign/main.go
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
// The imageproxy-sign tool creates signature values for a provided URL and
|
||||||
|
// signing key.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"willnorris.com/go/imageproxy"
|
||||||
|
)
|
||||||
|
|
||||||
|
var key = flag.String("key", "@/etc/imageproxy.key", "signing key, or file containing key prefixed with '@'")
|
||||||
|
var urlOnly = flag.Bool("url", false, "only sign the URL value, do not include options")
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if flag.NArg() < 1 {
|
||||||
|
fmt.Println("imageproxy-sign url [key]")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
u := parseURL(flag.Arg(0))
|
||||||
|
if u == nil {
|
||||||
|
fmt.Printf("unable to parse URL: %v\n", flag.Arg(0))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if *urlOnly {
|
||||||
|
u.Fragment = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
k, err := parseKey(*key)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("error parsing key: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
mac := hmac.New(sha256.New, []byte(k))
|
||||||
|
mac.Write([]byte(u.String()))
|
||||||
|
sig := mac.Sum(nil)
|
||||||
|
|
||||||
|
fmt.Printf("url: %v\n", u)
|
||||||
|
fmt.Printf("signature: %v\n", base64.URLEncoding.EncodeToString(sig))
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseKey(s string) ([]byte, error) {
|
||||||
|
if strings.HasPrefix(s, "@") {
|
||||||
|
return ioutil.ReadFile(s[1:])
|
||||||
|
}
|
||||||
|
return []byte(s), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseURL parses s as either an imageproxy request URL or a remote URL with
|
||||||
|
// options in the URL fragment. Any existing signature values are stripped,
|
||||||
|
// and the final remote URL returned with remaining options in the fragment.
|
||||||
|
func parseURL(s string) *url.URL {
|
||||||
|
u, err := url.Parse(s)
|
||||||
|
if s == "" || err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// first try to parse this as an imageproxy URL, containing
|
||||||
|
// transformation options and the remote URL embedded
|
||||||
|
if r, err := imageproxy.NewRequest(&http.Request{URL: u}, nil); err == nil {
|
||||||
|
r.Options.Signature = ""
|
||||||
|
r.URL.Fragment = r.Options.String()
|
||||||
|
return r.URL
|
||||||
|
}
|
||||||
|
|
||||||
|
// second, we assume that this is the remote URL itself. If a fragment
|
||||||
|
// is present, treat it as an option string.
|
||||||
|
opt := imageproxy.ParseOptions(u.Fragment)
|
||||||
|
opt.Signature = ""
|
||||||
|
u.Fragment = opt.String()
|
||||||
|
return u
|
||||||
|
}
|
34
cmd/imageproxy-sign/main_test.go
Normal file
34
cmd/imageproxy-sign/main_test.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseURL(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
input, output string
|
||||||
|
}{
|
||||||
|
{"/", "/#0x0"},
|
||||||
|
|
||||||
|
// imageproxy URLs
|
||||||
|
{"http://localhost:8080//http://example.com/", "http://example.com/#0x0"},
|
||||||
|
{"http://localhost:8080/10,r90,jpeg/http://example.com/", "http://example.com/#10x10,jpeg,r90"},
|
||||||
|
|
||||||
|
// remote URLs, with and without options
|
||||||
|
{"http://example.com/", "http://example.com/#0x0"},
|
||||||
|
{"http://example.com/#r90,jpeg", "http://example.com/#0x0,jpeg,r90"},
|
||||||
|
|
||||||
|
// ensure signature values are stripped
|
||||||
|
{"http://localhost:8080/sc0ffee/http://example.com/", "http://example.com/#0x0"},
|
||||||
|
{"http://example.com/#sc0ffee", "http://example.com/#0x0"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
want, _ := url.Parse(tt.output)
|
||||||
|
got := parseURL(tt.input)
|
||||||
|
if got.String() != want.String() {
|
||||||
|
t.Errorf("parseURL(%q) returned %q, want %q", tt.input, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -65,6 +65,8 @@ options are sorted, moving `q75` before `r90`.
|
||||||
Here are examples of calculating signatures in a variety of languages. These
|
Here are examples of calculating signatures in a variety of languages. These
|
||||||
demonstrate the HMAC-SHA256 bits, but not the option canonicalization.
|
demonstrate the HMAC-SHA256 bits, but not the option canonicalization.
|
||||||
|
|
||||||
|
See also the [imageproxy-sign tool](/cmd/imageproxy-sign).
|
||||||
|
|
||||||
### Go
|
### Go
|
||||||
|
|
||||||
main.go:
|
main.go:
|
||||||
|
|
Loading…
Reference in a new issue