Massive cleanups on http frontend aspects
This commit is contained in:
36
app/ipasso/envEndpoint.go
Normal file
36
app/ipasso/envEndpoint.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"git.thequux.com/thequux/ipasso/util/startup"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"go.uber.org/zap"
|
||||
"net/http"
|
||||
"net/http/fcgi"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var templateLogger *zap.Logger
|
||||
|
||||
func init() {
|
||||
startup.Routes.Add(func(router *httprouter.Router) {
|
||||
router.HandlerFunc("GET", "/env/*rest", dumpEnv)
|
||||
})
|
||||
startup.Logger.Add(func() {
|
||||
templateLogger = zap.L().Named("template")
|
||||
})
|
||||
}
|
||||
|
||||
func dumpEnv(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
|
||||
var headers = map[string]string{}
|
||||
for k, v := range r.Header {
|
||||
headers[k] = strings.Join(v, "\n")
|
||||
}
|
||||
var args = envdocArgs{
|
||||
ProcessEnv: fcgi.ProcessEnv(r),
|
||||
Headers: headers,
|
||||
ReqURL: r.URL.String(),
|
||||
}
|
||||
RenderPage(w, r, "env", args)
|
||||
}
|
||||
@@ -92,14 +92,15 @@ func init() {
|
||||
ldapRootLogger.Debug("Configured local kerberos principal", zap.String("principal", *krb5Principal))
|
||||
}
|
||||
|
||||
gssapiClient, err = gssapi.NewClientWithKeytab(*krb5Principal, *krb5realm, *keytab, *krb5conf)
|
||||
gssapiClient, err := gssapi.NewClientWithKeytab(*krb5Principal, *krb5realm, *keytab, *krb5conf)
|
||||
if err != nil {
|
||||
ldapRootLogger.Fatal("Failed to initialize kerberos", zap.Error(err))
|
||||
}
|
||||
|
||||
// Create the LDAP pool
|
||||
ldapPool = genpool.NewPool[ldap.Conn](&ldapPoolManager{gssapiClient: gssapiClient}, 5)
|
||||
|
||||
})
|
||||
startup.Startup.Add(func() {
|
||||
// Test the pool...
|
||||
conn, err := ldapPool.Get()
|
||||
if err != nil {
|
||||
68
app/ipasso/loginKrb.go
Normal file
68
app/ipasso/loginKrb.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"git.thequux.com/thequux/ipasso/sso-proxy/backend"
|
||||
"git.thequux.com/thequux/ipasso/util/startup"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/fcgi"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
startup.Routes.Add(func(router *httprouter.Router) {
|
||||
router.HandlerFunc("POST", "/login/krb5", loginKrb)
|
||||
})
|
||||
}
|
||||
|
||||
func loginKrb(w http.ResponseWriter, r *http.Request) {
|
||||
// Uses information from the process environment; assumes that Apache has done a krb5 login
|
||||
// The fact that we got here implies that the login succeeded
|
||||
env := fcgi.ProcessEnv(r)
|
||||
user := env["GSS_NAME"]
|
||||
uid := strings.Split(user, "@")[0]
|
||||
expirationTxt, hasExpiration := env["GSS_SESSION_EXPIRATION"]
|
||||
var expiration time.Time
|
||||
if hasExpiration {
|
||||
expInt, err := strconv.ParseInt(expirationTxt, 10, 64)
|
||||
if err != nil {
|
||||
// Invalid expiration date; bail
|
||||
log.Printf("Invalid expiration date: %#v", expirationTxt)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
} else {
|
||||
expiration = time.Unix(expInt, 0)
|
||||
}
|
||||
} else {
|
||||
log.Print(ErrNoExpiration)
|
||||
ReportError(w, ErrNoExpiration)
|
||||
return
|
||||
}
|
||||
|
||||
SessionID, err := datastore.NewSessionID()
|
||||
if err != nil {
|
||||
ReportError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
entry, err := getUserByPrincipal(user)
|
||||
if err != nil {
|
||||
ReportError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
session := backend.Session{
|
||||
SessionID: SessionID,
|
||||
Expiration: expiration,
|
||||
UserID: uid,
|
||||
LdapDN: entry.DN,
|
||||
}
|
||||
sessionCache, err := buildSessionCache(&session, entry)
|
||||
if err != nil {
|
||||
ReportError(w, err)
|
||||
return
|
||||
}
|
||||
_ = datastore.PutSession(session, sessionCache)
|
||||
}
|
||||
@@ -1,22 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"git.thequux.com/thequux/ipasso/resources"
|
||||
"git.thequux.com/thequux/ipasso/sso-proxy/backend"
|
||||
"git.thequux.com/thequux/ipasso/util/startup"
|
||||
"github.com/CloudyKit/jet/v6"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"gitlab.com/jamietanna/content-negotiation-go"
|
||||
"go.uber.org/zap"
|
||||
htemplate "html/template"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/fcgi"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -43,52 +45,20 @@ func main() {
|
||||
startup.Logger.Run()
|
||||
|
||||
startup.PostFlags.Run()
|
||||
startup.Startup.Run()
|
||||
|
||||
router := httprouter.New()
|
||||
startup.Routes.Run(router)
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/env/", http.HandlerFunc(dumpEnv))
|
||||
mux.Handle("/login/krb5", http.HandlerFunc(loginKrb))
|
||||
l.Info("Starting")
|
||||
listener, err := net.Listen("tcp", *listen)
|
||||
if err != nil {
|
||||
log.Fatalln("Failed to listen: ", err)
|
||||
}
|
||||
l.Info("Listening", zap.Stringer("addr", listener.Addr()))
|
||||
log.Fatal(fcgi.Serve(listener, mux))
|
||||
log.Fatal(fcgi.Serve(listener, router))
|
||||
}
|
||||
|
||||
var envTemplate = htemplate.Must(htemplate.New("envdoc").Parse(`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
pre { margin: 0 1ex; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Request to:</h1>
|
||||
<pre>{{ .ReqURL }}</pre>
|
||||
<h1>Headers</h1>
|
||||
<table>
|
||||
{{- range $k,$v := .Headers }}
|
||||
<tr>
|
||||
<td><pre>[{{ $k }}]</pre></td>
|
||||
<td><pre>{{ $v }}</pre></td>
|
||||
</tr>
|
||||
{{- end }}
|
||||
</table>
|
||||
<h1>Process Environment</h1>
|
||||
<table>
|
||||
{{- range $k,$v := .ProcessEnv }}
|
||||
<tr>
|
||||
<td><pre>{{ $k }}</pre></td>
|
||||
<td><pre>{{ $v }}</pre></td>
|
||||
</tr>
|
||||
{{- end }}
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
`))
|
||||
|
||||
type envdocArgs struct {
|
||||
Headers map[string]string
|
||||
ProcessEnv map[string]string
|
||||
@@ -110,72 +80,6 @@ func continueNegotate(w http.ResponseWriter, r *http.Request) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func dumpEnv(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
|
||||
var headers = map[string]string{}
|
||||
for k, v := range r.Header {
|
||||
headers[k] = strings.Join(v, "\n")
|
||||
}
|
||||
var args = envdocArgs{
|
||||
ProcessEnv: fcgi.ProcessEnv(r),
|
||||
Headers: headers,
|
||||
ReqURL: r.URL.String(),
|
||||
}
|
||||
if err := envTemplate.Execute(w, args); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func loginKrb(w http.ResponseWriter, r *http.Request) {
|
||||
// Uses information from the process environment; assumes that Apache has done a krb5 login
|
||||
// The fact that we got here implies that the login succeeded
|
||||
env := fcgi.ProcessEnv(r)
|
||||
user := env["GSS_NAME"]
|
||||
uid := strings.Split(user, "@")[0]
|
||||
expirationTxt, hasExpiration := env["GSS_SESSION_EXPIRATION"]
|
||||
var expiration time.Time
|
||||
if hasExpiration {
|
||||
expInt, err := strconv.ParseInt(expirationTxt, 10, 64)
|
||||
if err != nil {
|
||||
// Invalid expiration date; bail
|
||||
log.Printf("Invalid expiration date: %#v", expirationTxt)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
} else {
|
||||
expiration = time.Unix(expInt, 0)
|
||||
}
|
||||
} else {
|
||||
log.Print(ErrNoExpiration)
|
||||
ReportError(w, ErrNoExpiration)
|
||||
return
|
||||
}
|
||||
|
||||
SessionID, err := datastore.NewSessionID()
|
||||
if err != nil {
|
||||
ReportError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
entry, err := getUserByPrincipal(user)
|
||||
if err != nil {
|
||||
ReportError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
session := backend.Session{
|
||||
SessionID: SessionID,
|
||||
Expiration: expiration,
|
||||
UserID: uid,
|
||||
LdapDN: entry.DN,
|
||||
}
|
||||
sessionCache, err := buildSessionCache(&session, entry)
|
||||
if err != nil {
|
||||
ReportError(w, err)
|
||||
return
|
||||
}
|
||||
_ = datastore.PutSession(session, sessionCache)
|
||||
}
|
||||
|
||||
type RespErr struct {
|
||||
Err string
|
||||
Status string
|
||||
@@ -190,3 +94,53 @@ func ReportError(w http.ResponseWriter, err error) {
|
||||
}
|
||||
_, _ = w.Write(js)
|
||||
}
|
||||
|
||||
var negotiator = contentnegotiation.NewNegotiator("text/html", "text/plain", "application/json")
|
||||
|
||||
func RenderPage(w http.ResponseWriter, req *http.Request, template string, data interface{}) {
|
||||
ctype, _, err := negotiator.Negotiate(req.Header.Get("Accept"))
|
||||
if err != nil {
|
||||
templateLogger.Warn("Negotiation failed",
|
||||
zap.String("accept", req.Header.Get("Accept")),
|
||||
zap.Error(err),
|
||||
)
|
||||
ReportError(w, err)
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", ctype.String())
|
||||
|
||||
var result []byte
|
||||
var jetTemplate *jet.Template
|
||||
|
||||
if ctype.String() == "text/html" {
|
||||
template = template + ".html"
|
||||
jetTemplate, err = resources.Templates.HtmlTemplate(template)
|
||||
} else if ctype.String() == "text/plain" {
|
||||
template = template + ".txt"
|
||||
jetTemplate, err = resources.Templates.TextTemplate(template)
|
||||
} else if ctype.String() == "application/json" {
|
||||
jetTemplate = nil
|
||||
result, err = json.MarshalIndent(data, "", " ")
|
||||
} else {
|
||||
templateLogger.Warn("Negotiation returned something unexpected", zap.Stringer("negotiated", &ctype))
|
||||
err = errors.New("unexpected negotiation result")
|
||||
}
|
||||
|
||||
if err == nil && jetTemplate != nil && len(result) == 0 {
|
||||
var builder bytes.Buffer
|
||||
err := jetTemplate.Execute(&builder, nil, data)
|
||||
if err != nil {
|
||||
templateLogger.Warn("Failed to render template", zap.String("tmpl", template), zap.Error(err))
|
||||
} else {
|
||||
result = builder.Bytes()
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
w.Header().Set("Content-Type", ctype.String())
|
||||
w.Header().Set("Content-Length", strconv.FormatInt(int64(len(result)), 10))
|
||||
w.WriteHeader(200)
|
||||
_, _ = w.Write(result)
|
||||
} else {
|
||||
ReportError(w, err)
|
||||
}
|
||||
}
|
||||
|
||||
6
go.mod
6
go.mod
@@ -5,13 +5,18 @@ go 1.20
|
||||
replace github.com/go-ldap/ldap => ./_patch/go-ldap
|
||||
|
||||
require (
|
||||
github.com/CloudyKit/jet/v6 v6.2.0
|
||||
github.com/go-ldap/ldap v3.0.3+incompatible
|
||||
github.com/go-ldap/ldap/v3 v3.4.6
|
||||
github.com/jcmturner/gokrb5/v8 v8.4.4
|
||||
github.com/julienschmidt/httprouter v1.3.0
|
||||
gitlab.com/jamietanna/content-negotiation-go v0.2.0
|
||||
go.uber.org/zap v1.26.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect
|
||||
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
|
||||
github.com/google/uuid v1.3.1 // indirect
|
||||
@@ -20,7 +25,6 @@ require (
|
||||
github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect
|
||||
github.com/jcmturner/gofork v1.7.6 // indirect
|
||||
github.com/jcmturner/goidentity/v6 v6.0.1 // indirect
|
||||
github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect
|
||||
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
|
||||
go.uber.org/multierr v1.10.0 // indirect
|
||||
golang.org/x/crypto v0.13.0 // indirect
|
||||
|
||||
8
go.sum
8
go.sum
@@ -1,5 +1,9 @@
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=
|
||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
|
||||
github.com/CloudyKit/jet/v6 v6.2.0 h1:EpcZ6SR9n28BUGtNJSvlBqf90IpjeFr36Tizxhn/oME=
|
||||
github.com/CloudyKit/jet/v6 v6.2.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4=
|
||||
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA=
|
||||
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -31,6 +35,8 @@ github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh6
|
||||
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
|
||||
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
|
||||
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
|
||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -42,6 +48,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
gitlab.com/jamietanna/content-negotiation-go v0.2.0 h1:vT0OLEPQ6DYRG3/1F7joXSNjVQHGivJ6+JzODlJfjWw=
|
||||
gitlab.com/jamietanna/content-negotiation-go v0.2.0/go.mod h1:n4ZZ8/X5TstnjYRnjEtR/fC7MCTe+aRKM7PQlLBH3PQ=
|
||||
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
|
||||
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
||||
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
|
||||
146
resources/resources.go
Normal file
146
resources/resources.go
Normal file
@@ -0,0 +1,146 @@
|
||||
package resources
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"flag"
|
||||
"git.thequux.com/thequux/ipasso/util/startup"
|
||||
"github.com/CloudyKit/jet/v6"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"go.uber.org/zap"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
dumpResources = flag.Bool("extract-resources", false, "Extract resources to directory indicated by --external-resources")
|
||||
externalResources = flag.String("external-resources", "", "Directory in which to find external resources")
|
||||
)
|
||||
|
||||
//go:embed static templates
|
||||
var embedded embed.FS
|
||||
|
||||
var Resources fs.FS = &embedded
|
||||
|
||||
var l *zap.Logger
|
||||
var Templates TemplateSource
|
||||
var StaticFiles fs.FS
|
||||
|
||||
func init() {
|
||||
|
||||
startup.Logger.Add(func() {
|
||||
l = zap.L().Named("resources")
|
||||
})
|
||||
startup.PostFlags.Add(func() {
|
||||
var err error
|
||||
if *dumpResources {
|
||||
if *externalResources == "" {
|
||||
l.Fatal("Cannot extract resources without --external-resources")
|
||||
}
|
||||
extractResources(*externalResources)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if *externalResources != "" {
|
||||
Resources = os.DirFS(*externalResources)
|
||||
}
|
||||
|
||||
fs.WalkDir(Resources, ".", func(path string, d fs.DirEntry, err error) error {
|
||||
l.Debug("found static file", zap.String("path", path))
|
||||
return nil
|
||||
})
|
||||
|
||||
templateFS, err := fs.Sub(Resources, "templates")
|
||||
if err != nil {
|
||||
l.Fatal("Unable to find templates")
|
||||
}
|
||||
|
||||
Templates = loadTemplates(templateFS, *externalResources == "")
|
||||
StaticFiles, err = fs.Sub(Resources, "static")
|
||||
if err != nil {
|
||||
l.Fatal("Cannot find static files", zap.Error(err))
|
||||
}
|
||||
})
|
||||
|
||||
startup.Routes.Add(func(router *httprouter.Router) {
|
||||
router.NotFound = http.FileServer(http.FS(StaticFiles))
|
||||
})
|
||||
}
|
||||
|
||||
func extractResources(dst string) {
|
||||
if err := os.MkdirAll(dst, 0755); err != nil {
|
||||
l.Fatal("Failed to create external resource directory", zap.Error(err))
|
||||
}
|
||||
_ = fs.WalkDir(embedded, "/", func(filePath string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
l.Warn("Failed to walk file", zap.String("path", filePath), zap.Error(err))
|
||||
return nil
|
||||
}
|
||||
fullPath := path.Join(dst, filePath)
|
||||
if d.IsDir() {
|
||||
if err = os.Mkdir(fullPath, 0755); err != nil {
|
||||
l.Warn("Failed to extract directory", zap.String("dst", fullPath), zap.Error(err))
|
||||
return fs.SkipDir
|
||||
}
|
||||
} else {
|
||||
data, err := fs.ReadFile(embedded, filePath)
|
||||
if err == nil {
|
||||
err = os.WriteFile(fullPath, data, 0644)
|
||||
}
|
||||
if err != nil {
|
||||
l.Warn("Failed to copy file", zap.String("dst", fullPath), zap.Error(err))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
type TemplateSource struct {
|
||||
htmlCache *jet.Set
|
||||
textCache *jet.Set
|
||||
}
|
||||
|
||||
func loadTemplates(src fs.FS, devMode bool) TemplateSource {
|
||||
var devModeOpt jet.Option
|
||||
if devMode {
|
||||
devModeOpt = jet.InDevelopmentMode()
|
||||
} else {
|
||||
devModeOpt = func(set *jet.Set) {}
|
||||
}
|
||||
|
||||
loader := genFsLoader{fs: src}
|
||||
return TemplateSource{
|
||||
htmlCache: jet.NewSet(loader, devModeOpt),
|
||||
textCache: jet.NewSet(loader, devModeOpt, jet.WithSafeWriter(nil)),
|
||||
}
|
||||
}
|
||||
|
||||
func (ts TemplateSource) HtmlTemplate(name string) (*jet.Template, error) {
|
||||
return ts.htmlCache.GetTemplate(name)
|
||||
}
|
||||
|
||||
func (ts TemplateSource) TextTemplate(name string) (*jet.Template, error) {
|
||||
return ts.textCache.GetTemplate(name)
|
||||
}
|
||||
|
||||
type genFsLoader struct {
|
||||
fs fs.FS
|
||||
}
|
||||
|
||||
func (g genFsLoader) Exists(templatePath string) bool {
|
||||
templatePath = strings.TrimPrefix(templatePath, "/")
|
||||
l.Debug("Probe", zap.String("path", templatePath))
|
||||
v, err := g.fs.Open(templatePath)
|
||||
if err == nil {
|
||||
_ = v.Close()
|
||||
}
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (g genFsLoader) Open(templatePath string) (io.ReadCloser, error) {
|
||||
templatePath = strings.TrimPrefix(templatePath, "/")
|
||||
return g.fs.Open(templatePath)
|
||||
}
|
||||
13
resources/static/index.html
Normal file
13
resources/static/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>IpaSSO</title>
|
||||
</head>
|
||||
<body>
|
||||
<li>
|
||||
<a href="/login/krb5">KRB login</a>
|
||||
<a href="/env">Dump environment</a>
|
||||
<a href="/env/krb5">Dump GSSAPI environment</a>
|
||||
</li>
|
||||
</body>
|
||||
</html>
|
||||
30
resources/templates/env.html
Normal file
30
resources/templates/env.html
Normal file
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
pre { margin: 0 1ex; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Request to:</h1>
|
||||
<pre>{{ .ReqURL }}</pre>
|
||||
<h1>Headers</h1>
|
||||
<table>
|
||||
{{- range k,v := .Headers }}
|
||||
<tr>
|
||||
<td><pre>{{ k }}</pre></td>
|
||||
<td><pre>{{ v }}</pre></td>
|
||||
</tr>
|
||||
{{- end }}
|
||||
</table>
|
||||
<h1>Process Environment</h1>
|
||||
<table>
|
||||
{{- range k,v := .ProcessEnv }}
|
||||
<tr>
|
||||
<td><pre>{{ k }}</pre></td>
|
||||
<td><pre>{{ v }}</pre></td>
|
||||
</tr>
|
||||
{{- end }}
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
9
resources/templates/env.txt
Normal file
9
resources/templates/env.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
Request: {{ .ReqURL }}
|
||||
ProccessEnv:
|
||||
{{- range k,v := .ProcessEnv }}
|
||||
{{ k }}: {{ v }}
|
||||
{{- end }}
|
||||
headers:
|
||||
{{- range k,v := .Headers }}
|
||||
{{ k }}: {{ v }}
|
||||
{{- end }}
|
||||
@@ -1,17 +1,22 @@
|
||||
package startup
|
||||
|
||||
import "github.com/julienschmidt/httprouter"
|
||||
|
||||
// Pre-defined queues...
|
||||
var (
|
||||
Logger StartupQueue
|
||||
PostFlags StartupQueue
|
||||
Logger Phase
|
||||
// Phase
|
||||
PostFlags Phase
|
||||
Startup Phase
|
||||
Routes ParameterizedPhase[*httprouter.Router]
|
||||
)
|
||||
|
||||
type StartupQueue struct {
|
||||
type Phase struct {
|
||||
items []func()
|
||||
hasRun bool
|
||||
}
|
||||
|
||||
func (q *StartupQueue) Add(initFn ...func()) {
|
||||
func (q *Phase) Add(initFn ...func()) {
|
||||
if q.hasRun {
|
||||
panic("Added init function after startup")
|
||||
}
|
||||
@@ -20,7 +25,7 @@ func (q *StartupQueue) Add(initFn ...func()) {
|
||||
}
|
||||
}
|
||||
|
||||
func (q *StartupQueue) Run() {
|
||||
func (q *Phase) Run() {
|
||||
if q.hasRun {
|
||||
panic("Attempted to run init function twice")
|
||||
}
|
||||
@@ -29,3 +34,27 @@ func (q *StartupQueue) Run() {
|
||||
fn()
|
||||
}
|
||||
}
|
||||
|
||||
type ParameterizedPhase[Param interface{}] struct {
|
||||
items []func(Param)
|
||||
hasRun bool
|
||||
}
|
||||
|
||||
func (q *ParameterizedPhase[Param]) Add(initFn ...func(Param)) {
|
||||
if q.hasRun {
|
||||
panic("Added init function after startup")
|
||||
}
|
||||
for _, fn := range initFn {
|
||||
q.items = append(q.items, fn)
|
||||
}
|
||||
}
|
||||
|
||||
func (q *ParameterizedPhase[Param]) Run(param Param) {
|
||||
if q.hasRun {
|
||||
panic("Attempted to run init function twice")
|
||||
}
|
||||
q.hasRun = true
|
||||
for _, fn := range q.items {
|
||||
fn(param)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user