render context: add asset_download_token
This commit is contained in:
@ -8,6 +8,7 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"path"
|
"path"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/cespare/xxhash"
|
"github.com/cespare/xxhash"
|
||||||
@ -290,6 +291,14 @@ func (ctx *renderContext) templateFuncs(ctxMap map[string]any) map[string]any {
|
|||||||
"host_download_token": func() (s string) {
|
"host_download_token": func() (s string) {
|
||||||
return "{{ host_download_token }}"
|
return "{{ host_download_token }}"
|
||||||
},
|
},
|
||||||
|
"asset_download_token": func(args ...string) (s string) {
|
||||||
|
argsStr := new(strings.Builder)
|
||||||
|
for _, arg := range args {
|
||||||
|
argsStr.WriteByte(' ')
|
||||||
|
argsStr.WriteString(strconv.Quote(arg))
|
||||||
|
}
|
||||||
|
return "{{ asset_download_token" + argsStr.String() + " }}"
|
||||||
|
},
|
||||||
|
|
||||||
"hosts_of_group": func() (hosts []any) {
|
"hosts_of_group": func() (hosts []any) {
|
||||||
hosts = make([]any, 0)
|
hosts = make([]any, 0)
|
||||||
|
15
cmd/dkl-local-server/html.go
Normal file
15
cmd/dkl-local-server/html.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
func htmlHeader(title string) string {
|
||||||
|
return `<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>` + title + `</title>
|
||||||
|
<style>@import url('/ui/style.css');@import url('/ui/app.css');</style>
|
||||||
|
</head>
|
||||||
|
<body><h1>` + title + `</h1>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
var htmlFooter = `</body>
|
||||||
|
</html>`
|
@ -14,6 +14,7 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
"time"
|
||||||
|
|
||||||
cfsslconfig "github.com/cloudflare/cfssl/config"
|
cfsslconfig "github.com/cloudflare/cfssl/config"
|
||||||
restful "github.com/emicklei/go-restful"
|
restful "github.com/emicklei/go-restful"
|
||||||
@ -237,6 +238,32 @@ func (ctx *renderContext) TemplateFuncs() map[string]any {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
},
|
||||||
|
"asset_download_token": func(asset string, params ...string) (token string, err error) {
|
||||||
|
now := time.Now()
|
||||||
|
exp := now.Add(24 * time.Hour) // expire in 24h by default
|
||||||
|
if len(params) != 0 {
|
||||||
|
exp, err = parseCertDuration(params[0], now)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set := DownloadSet{
|
||||||
|
Expiry: exp,
|
||||||
|
Items: []DownloadSetItem{
|
||||||
|
{
|
||||||
|
Kind: "host",
|
||||||
|
Name: ctx.Host.Name,
|
||||||
|
Assets: []string{asset},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
privKey, _ := dlsSigningKeys()
|
||||||
|
token = set.Signed(privKey)
|
||||||
|
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
|
@ -75,6 +75,28 @@ func (s *DownloadSet) Decode(encoded string) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s DownloadSet) Signed(privKey ed25519.PrivateKey) string {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
{
|
||||||
|
setBytes := []byte(s.Encode())
|
||||||
|
|
||||||
|
w := lz4.NewWriter(buf)
|
||||||
|
w.Write(setBytes)
|
||||||
|
w.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
setBytes := buf.Bytes()
|
||||||
|
sig := ed25519.Sign(privKey, setBytes)
|
||||||
|
|
||||||
|
buf = bytes.NewBuffer(make([]byte, 0, 1+len(sig)+len(setBytes)))
|
||||||
|
buf.WriteByte(byte(len(sig)))
|
||||||
|
buf.Write(sig)
|
||||||
|
buf.Write(setBytes)
|
||||||
|
|
||||||
|
enc := base32.StdEncoding.WithPadding(base32.NoPadding)
|
||||||
|
return enc.EncodeToString(buf.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
type DownloadSetItem struct {
|
type DownloadSetItem struct {
|
||||||
Kind string
|
Kind string
|
||||||
Name string
|
Name string
|
||||||
@ -143,32 +165,8 @@ func wsSignDownloadSet(req *restful.Request, resp *restful.Response) {
|
|||||||
Items: setReq.Items,
|
Items: setReq.Items,
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
privKey, _ := dlsSigningKeys()
|
||||||
{
|
resp.WriteEntity(set.Signed(privKey))
|
||||||
setBytes := []byte(set.Encode())
|
|
||||||
|
|
||||||
w := lz4.NewWriter(buf)
|
|
||||||
w.Write(setBytes)
|
|
||||||
w.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
setBytes := buf.Bytes()
|
|
||||||
|
|
||||||
privkey, pubkey := dlsSigningKeys()
|
|
||||||
sig := ed25519.Sign(privkey, setBytes)
|
|
||||||
|
|
||||||
if !ed25519.Verify(pubkey, setBytes, sig) {
|
|
||||||
wsError(resp, fmt.Errorf("signature self-check failed"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
buf = bytes.NewBuffer(make([]byte, 0, 1+len(sig)+len(setBytes)))
|
|
||||||
buf.WriteByte(byte(len(sig)))
|
|
||||||
buf.Write(sig)
|
|
||||||
buf.Write(setBytes)
|
|
||||||
|
|
||||||
enc := base32.StdEncoding.WithPadding(base32.NoPadding)
|
|
||||||
resp.WriteEntity(enc.EncodeToString(buf.Bytes()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDlSet(req *restful.Request) (*DownloadSet, *httperr.Error) {
|
func getDlSet(req *restful.Request) (*DownloadSet, *httperr.Error) {
|
||||||
@ -248,28 +246,13 @@ func wsDownloadSet(req *restful.Request, resp *restful.Response) {
|
|||||||
set, err := getDlSet(req)
|
set, err := getDlSet(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.WriteHeader(err.Status)
|
resp.WriteHeader(err.Status)
|
||||||
resp.Write([]byte(`<!doctype html>
|
resp.Write([]byte(htmlHeader(err.Error())))
|
||||||
<html>
|
resp.Write([]byte(htmlFooter))
|
||||||
<head>
|
|
||||||
<title>` + err.Error() + `</title>
|
|
||||||
<style src="/ui/style.css"/>
|
|
||||||
<style src="/ui/app.css"/>
|
|
||||||
</head>
|
|
||||||
<body><h1>` + err.Error() + `</h1></body>
|
|
||||||
</html>`))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
buf.WriteString(`<!doctype html>
|
buf.WriteString(htmlHeader("Download set"))
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Download set</title>
|
|
||||||
<style src="/ui/style.css"/>
|
|
||||||
<style src="/ui/app.css"/>
|
|
||||||
</head>
|
|
||||||
<body><h1>Download set</h1>
|
|
||||||
`)
|
|
||||||
|
|
||||||
cfg, err2 := readConfig()
|
cfg, err2 := readConfig()
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
@ -304,6 +287,6 @@ func wsDownloadSet(req *restful.Request, resp *restful.Response) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.WriteString("</body></html>")
|
buf.WriteString(htmlFooter)
|
||||||
buf.WriteTo(resp)
|
buf.WriteTo(resp)
|
||||||
}
|
}
|
||||||
|
@ -180,34 +180,20 @@ func wsDownloadPage(req *restful.Request, resp *restful.Response) {
|
|||||||
spec, ok := wState.Get().Downloads[token]
|
spec, ok := wState.Get().Downloads[token]
|
||||||
if !ok {
|
if !ok {
|
||||||
resp.WriteHeader(http.StatusNotFound)
|
resp.WriteHeader(http.StatusNotFound)
|
||||||
resp.Write([]byte(`<!doctype html>
|
resp.Write([]byte(htmlHeader("Token not found")))
|
||||||
<html>
|
resp.Write([]byte(htmlFooter))
|
||||||
<head>
|
|
||||||
<title>Token not found</title>
|
|
||||||
<style src="/ui/style.css"/>
|
|
||||||
<style src="/ui/app.css"/>
|
|
||||||
</head>
|
|
||||||
<body><h1>Token not found</h1></body>
|
|
||||||
</html>`))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
fmt.Fprintf(buf, `<!doctype html>
|
buf.WriteString(htmlHeader(fmt.Sprintf("Token assets: %s %s", spec.Kind, spec.Name)))
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Token assets: %s %s</title>
|
|
||||||
<style src="/ui/style.css"/>
|
|
||||||
<style src="/ui/app.css"/>
|
|
||||||
</head>
|
|
||||||
<body><h1>Token assets: %s %s</h1>
|
|
||||||
<ul>
|
|
||||||
`, spec.Kind, spec.Name, spec.Kind, spec.Name)
|
|
||||||
|
|
||||||
|
buf.WriteString("<ul>")
|
||||||
for _, asset := range spec.Assets {
|
for _, asset := range spec.Assets {
|
||||||
fmt.Fprintf(buf, "<li><a href=\"%s\" download>%s</a></li>\n", asset, asset)
|
fmt.Fprintf(buf, "<li><a href=\"%s\" download>%s</a></li>\n", asset, asset)
|
||||||
}
|
}
|
||||||
|
buf.WriteString("</ul>")
|
||||||
|
|
||||||
buf.WriteString("</ul></body></html>")
|
buf.WriteString(htmlFooter)
|
||||||
buf.WriteTo(resp)
|
buf.WriteTo(resp)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user