2023-06-26 11:56:21 +01:00
|
|
|
package http_acme
|
2023-06-23 23:00:09 +01:00
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/rand"
|
|
|
|
"crypto/rsa"
|
|
|
|
"fmt"
|
|
|
|
"github.com/MrMelon54/mjwt"
|
|
|
|
"github.com/MrMelon54/mjwt/auth"
|
|
|
|
"github.com/MrMelon54/mjwt/claims"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
|
|
|
"net/url"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type fakeTransport struct {
|
|
|
|
verify mjwt.Verifier
|
|
|
|
req *http.Request
|
|
|
|
clean bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *fakeTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
|
|
bearer := req.Header.Get("Authorization")
|
|
|
|
if !strings.HasPrefix(bearer, "Bearer ") {
|
|
|
|
return nil, fmt.Errorf("invalid bearer token")
|
|
|
|
}
|
|
|
|
_, b, err := mjwt.ExtractClaims[auth.AccessTokenClaims](f.verify, bearer[7:])
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// check perms
|
|
|
|
if !f.clean && !b.Claims.Perms.Has("test:acme:present") {
|
|
|
|
return nil, fmt.Errorf("missing perm 'test:acme:present'")
|
|
|
|
}
|
|
|
|
if f.clean && !b.Claims.Perms.Has("test:acme:clean") {
|
|
|
|
return nil, fmt.Errorf("missing perm 'test:acme:clean'")
|
|
|
|
}
|
|
|
|
rec := httptest.NewRecorder()
|
|
|
|
rec.WriteHeader(http.StatusOK)
|
|
|
|
f.req = req
|
|
|
|
return rec.Result(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestHttpAcmeProvider_Present(t *testing.T) {
|
|
|
|
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
// perms
|
|
|
|
ps := claims.NewPermStorage()
|
|
|
|
ps.Set("test:acme:present")
|
|
|
|
|
|
|
|
// signer
|
|
|
|
signer := mjwt.NewMJwtSigner("Test", privateKey)
|
|
|
|
accessToken, err := signer.GenerateJwt("", "", nil, 5*time.Minute, auth.AccessTokenClaims{Perms: ps})
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
ft := &fakeTransport{verify: signer}
|
|
|
|
prov := &HttpAcmeProvider{
|
|
|
|
accessToken,
|
|
|
|
"",
|
|
|
|
"https://api.example.com/acme/present/%domain%/%token%/%content%",
|
|
|
|
"https://api.example.com/acme/clean/%domain%/%token%",
|
|
|
|
ft,
|
|
|
|
}
|
|
|
|
assert.NoError(t, prov.Present("example.com", "1234", "1234abcd"))
|
|
|
|
assert.Equal(t, *ft.req.URL, url.URL{
|
|
|
|
Scheme: "https",
|
|
|
|
Host: "api.example.com",
|
|
|
|
Path: "/acme/present/example.com/1234/1234abcd",
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestHttpAcmeProvider_CleanUp(t *testing.T) {
|
|
|
|
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
// perms
|
|
|
|
ps := claims.NewPermStorage()
|
|
|
|
ps.Set("test:acme:clean")
|
|
|
|
|
|
|
|
// signer
|
|
|
|
signer := mjwt.NewMJwtSigner("Test", privateKey)
|
|
|
|
accessToken, err := signer.GenerateJwt("", "", nil, 5*time.Minute, auth.AccessTokenClaims{Perms: ps})
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
ft := &fakeTransport{verify: signer, clean: true}
|
|
|
|
prov := &HttpAcmeProvider{
|
|
|
|
accessToken,
|
|
|
|
"",
|
|
|
|
"https://api.example.com/acme/present/%domain%/%token%/%content%",
|
|
|
|
"https://api.example.com/acme/clean/%domain%/%token%",
|
|
|
|
ft,
|
|
|
|
}
|
|
|
|
assert.NoError(t, prov.CleanUp("example.com", "1234", "1234abcd"))
|
|
|
|
assert.Equal(t, *ft.req.URL, url.URL{
|
|
|
|
Scheme: "https",
|
|
|
|
Host: "api.example.com",
|
|
|
|
Path: "/acme/clean/example.com/1234",
|
|
|
|
})
|
|
|
|
}
|