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