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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user