mirror of
https://github.com/1f349/lavender.git
synced 2024-09-08 03:06:37 +01:00
Improve page templates and custom template loading
This commit is contained in:
parent
e06c23eec4
commit
ba56a628d0
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,3 +2,5 @@
|
||||
*.local
|
||||
.idea/
|
||||
.data/
|
||||
node_modules/
|
||||
pages/style-minify.css
|
||||
|
2
go.mod
2
go.mod
@ -27,7 +27,7 @@ require (
|
||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.15.11 // indirect
|
||||
github.com/klauspost/compress v1.17.8 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
|
4
go.sum
4
go.sum
@ -75,8 +75,8 @@ github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4d
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
|
||||
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c=
|
||||
github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
|
||||
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
|
||||
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
|
3
pages/header.go.html
Normal file
3
pages/header.go.html
Normal file
@ -0,0 +1,3 @@
|
||||
<header>
|
||||
<h1>{{.ServiceName}}</h1>
|
||||
</header>
|
@ -2,12 +2,11 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{.ServiceName}}</title>
|
||||
<link rel="stylesheet" href="/theme/style.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="/assets/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>{{.ServiceName}}</h1>
|
||||
</header>
|
||||
{{template "header.go.html" .}}
|
||||
<main>
|
||||
<div>Not logged in</div>
|
||||
<div>
|
||||
|
@ -2,14 +2,13 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{.ServiceName}}</title>
|
||||
<link rel="stylesheet" href="/theme/style.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="/assets/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>{{.ServiceName}}</h1>
|
||||
</header>
|
||||
{{template "header.go.html" .}}
|
||||
<main>
|
||||
<div>Logged in as: {{.DisplayName}} ({{.Subject}})</div>
|
||||
<div>Logged in as: {{.Auth.UserInfo.name}} ({{.Auth.Subject}})</div>
|
||||
<div>
|
||||
<form method="GET" action="/manage/apps">
|
||||
<button type="submit">Manage Applications</button>
|
||||
|
@ -2,12 +2,11 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{.ServiceName}}</title>
|
||||
<link rel="stylesheet" href="/theme/style.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="/assets/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>{{.ServiceName}}</h1>
|
||||
</header>
|
||||
{{template "header.go.html" .}}
|
||||
<main>
|
||||
<div>Log in as: <span>{{.LoginName}}</span></div>
|
||||
<div>
|
||||
|
@ -2,12 +2,11 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{.ServiceName}}</title>
|
||||
<link rel="stylesheet" href="/theme/style.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="/assets/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>{{.ServiceName}}</h1>
|
||||
</header>
|
||||
{{template "header.go.html" .}}
|
||||
<main>
|
||||
<form method="POST" action="/login">
|
||||
<input type="hidden" name="redirect" value="{{.Redirect}}"/>
|
||||
|
@ -2,7 +2,8 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{.ServiceName}}</title>
|
||||
<link rel="stylesheet" href="/theme/style.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="/assets/style.css">
|
||||
<script>
|
||||
window.addEventListener("load", function () {
|
||||
selectText("app-secret");
|
||||
@ -29,9 +30,7 @@
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>{{.ServiceName}}</h1>
|
||||
</header>
|
||||
{{template "header.go.html" .}}
|
||||
<main>
|
||||
<form method="GET" action="/">
|
||||
<button type="submit">Home</button>
|
||||
|
@ -2,12 +2,11 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{.ServiceName}}</title>
|
||||
<link rel="stylesheet" href="/theme/style.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="/assets/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>{{.ServiceName}}</h1>
|
||||
</header>
|
||||
{{template "header.go.html" .}}
|
||||
<main>
|
||||
<form method="GET" action="/">
|
||||
<button type="submit">Home</button>
|
||||
|
@ -2,12 +2,11 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{.ServiceName}}</title>
|
||||
<link rel="stylesheet" href="/theme/style.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="/assets/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>{{.ServiceName}}</h1>
|
||||
</header>
|
||||
{{template "header.go.html" .}}
|
||||
<main>
|
||||
<form method="POST" action="/authorize">
|
||||
<div>The application {{.AppName}} wants to access your account ({{.DisplayName}}). It requests the following permissions:</div>
|
||||
|
@ -1,34 +1,36 @@
|
||||
package pages
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"embed"
|
||||
_ "embed"
|
||||
"errors"
|
||||
"github.com/1f349/lavender/logger"
|
||||
"github.com/1f349/lavender/utils"
|
||||
"github.com/1f349/overlapfs"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed *.go.html
|
||||
//go:embed *.go.html assets/*.css
|
||||
wwwPages embed.FS
|
||||
wwwTemplates *template.Template
|
||||
loadOnce sync.Once
|
||||
loadOnce utils.Once[error]
|
||||
cssAssetMap = make(map[string][]byte)
|
||||
)
|
||||
|
||||
func LoadPages(wd string) (err error) {
|
||||
loadOnce.Do(func() {
|
||||
func LoadPages(wd string) error {
|
||||
return loadOnce.Do(func() (err error) {
|
||||
var o fs.FS = wwwPages
|
||||
if wd != "" {
|
||||
wwwDir := filepath.Join(wd, "www")
|
||||
err = os.Mkdir(wwwDir, os.ModePerm)
|
||||
if err != nil && !errors.Is(err, os.ErrExist) {
|
||||
return
|
||||
return err
|
||||
}
|
||||
wdFs := os.DirFS(wwwDir)
|
||||
o = overlapfs.OverlapFS{A: wwwPages, B: wdFs}
|
||||
@ -36,8 +38,19 @@ func LoadPages(wd string) (err error) {
|
||||
wwwTemplates, err = template.New("pages").Funcs(template.FuncMap{
|
||||
"emailHide": EmailHide,
|
||||
}).ParseFS(o, "*.go.html")
|
||||
|
||||
glob, err := fs.Glob(o, "assets/*")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, i := range glob {
|
||||
cssAssetMap[i], err = fs.ReadFile(o, i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func RenderPageTemplate(wr io.Writer, name string, data any) {
|
||||
@ -47,6 +60,14 @@ func RenderPageTemplate(wr io.Writer, name string, data any) {
|
||||
}
|
||||
}
|
||||
|
||||
func RenderCss(name string) io.ReadSeeker {
|
||||
b, ok := cssAssetMap[name]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return bytes.NewReader(b)
|
||||
}
|
||||
|
||||
func EmailHide(a string) string {
|
||||
b := []byte(a)
|
||||
for i := range b {
|
||||
|
@ -2,8 +2,6 @@ package server
|
||||
|
||||
import (
|
||||
"github.com/1f349/lavender/database"
|
||||
"github.com/1f349/mjwt"
|
||||
"github.com/1f349/mjwt/auth"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@ -13,18 +11,18 @@ import (
|
||||
type UserHandler func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, auth UserAuth)
|
||||
|
||||
type UserAuth struct {
|
||||
ID string
|
||||
Subject string
|
||||
DisplayName string
|
||||
UserInfo UserInfoFields
|
||||
}
|
||||
|
||||
func (u UserAuth) IsGuest() bool { return u.ID == "" }
|
||||
func (u UserAuth) IsGuest() bool { return u.Subject == "" }
|
||||
|
||||
func (h *HttpServer) RequireAdminAuthentication(next UserHandler) httprouter.Handle {
|
||||
return h.RequireAuthentication(func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, auth UserAuth) {
|
||||
var roles string
|
||||
if h.DbTx(rw, func(tx *database.Tx) (err error) {
|
||||
roles, err = tx.GetUserRoles(auth.ID)
|
||||
roles, err = tx.GetUserRoles(auth.Subject)
|
||||
return
|
||||
}) {
|
||||
return
|
||||
@ -50,29 +48,23 @@ func (h *HttpServer) RequireAuthentication(next UserHandler) httprouter.Handle {
|
||||
|
||||
func (h *HttpServer) OptionalAuthentication(next UserHandler) httprouter.Handle {
|
||||
return func(rw http.ResponseWriter, req *http.Request, params httprouter.Params) {
|
||||
auth, err := h.internalAuthenticationHandler(req)
|
||||
authUser, err := h.internalAuthenticationHandler(req)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if auth.IsGuest() {
|
||||
// if this fails internally it just sees the user as logged out
|
||||
h.readLoginDataCookie(req, &auth)
|
||||
}
|
||||
next(rw, req, params, auth)
|
||||
next(rw, req, params, authUser)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HttpServer) internalAuthenticationHandler(req *http.Request) (UserAuth, error) {
|
||||
if loginCookie, err := req.Cookie("lavender-login-data"); err == nil {
|
||||
_, b, err := mjwt.ExtractClaims[auth.AccessTokenClaims](h.signingKey, loginCookie.Value)
|
||||
if err != nil {
|
||||
return UserAuth{}, err
|
||||
}
|
||||
return UserAuth{ID: b.Subject}, nil
|
||||
var u UserAuth
|
||||
err := h.readLoginDataCookie(req, &u)
|
||||
if err != nil {
|
||||
// not logged in
|
||||
return UserAuth{}, nil
|
||||
}
|
||||
// not logged in
|
||||
return UserAuth{}, nil
|
||||
return u, nil
|
||||
}
|
||||
|
||||
func PrepareRedirectUrl(targetPath string, origin *url.URL) *url.URL {
|
||||
|
@ -30,7 +30,7 @@ func (h *HttpServer) Home(rw http.ResponseWriter, _ *http.Request, _ httprouter.
|
||||
|
||||
var isAdmin bool
|
||||
h.DbTx(rw, func(tx *database.Tx) (err error) {
|
||||
roles, err := tx.GetUserRoles(auth.ID)
|
||||
roles, err := tx.GetUserRoles(auth.Subject)
|
||||
isAdmin = HasRole(roles, "lavender:admin")
|
||||
return err
|
||||
})
|
||||
@ -38,8 +38,6 @@ func (h *HttpServer) Home(rw http.ResponseWriter, _ *http.Request, _ httprouter.
|
||||
pages.RenderPageTemplate(rw, "index", map[string]any{
|
||||
"ServiceName": h.conf.ServiceName,
|
||||
"Auth": auth,
|
||||
"Subject": auth.ID,
|
||||
"DisplayName": auth.DisplayName,
|
||||
"Nonce": lNonce,
|
||||
"IsAdmin": isAdmin,
|
||||
})
|
||||
|
@ -2,17 +2,14 @@ package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/1f349/lavender/database"
|
||||
"github.com/1f349/lavender/issuer"
|
||||
"github.com/1f349/lavender/pages"
|
||||
"github.com/1f349/mjwt"
|
||||
"github.com/1f349/mjwt/auth"
|
||||
"github.com/1f349/mjwt/claims"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
@ -112,7 +109,7 @@ func (h *HttpServer) loginCallback(rw http.ResponseWriter, req *http.Request, _
|
||||
}
|
||||
|
||||
sessionData, err := h.fetchUserInfo(flowState.sso, token)
|
||||
if sessionData.ID == "" {
|
||||
if err != nil || sessionData.Subject == "" {
|
||||
http.Error(rw, "Failed to fetch user info", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@ -122,15 +119,15 @@ func (h *HttpServer) loginCallback(rw http.ResponseWriter, req *http.Request, _
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tx.GetUser(sessionData.ID)
|
||||
_, err = tx.GetUser(sessionData.Subject)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
uEmail := sessionData.UserInfo.GetStringOrDefault("email", "unknown@localhost")
|
||||
uEmailVerified, _ := sessionData.UserInfo.GetBoolean("email_verified")
|
||||
return tx.InsertUser(sessionData.ID, uEmail, uEmailVerified, "", string(jBytes), true)
|
||||
return tx.InsertUser(sessionData.Subject, uEmail, uEmailVerified, "", string(jBytes), true)
|
||||
}
|
||||
uEmail := sessionData.UserInfo.GetStringOrDefault("email", "unknown@localhost")
|
||||
uEmailVerified, _ := sessionData.UserInfo.GetBoolean("email_verified")
|
||||
return tx.UpdateUserInfo(sessionData.ID, uEmail, uEmailVerified, string(jBytes))
|
||||
return tx.UpdateUserInfo(sessionData.Subject, uEmail, uEmailVerified, string(jBytes))
|
||||
}) {
|
||||
return
|
||||
}
|
||||
@ -139,7 +136,7 @@ func (h *HttpServer) loginCallback(rw http.ResponseWriter, req *http.Request, _
|
||||
auth = sessionData
|
||||
|
||||
if h.DbTx(rw, func(tx *database.Tx) error {
|
||||
return tx.UpdateUserToken(auth.ID, token.AccessToken, token.RefreshToken, token.Expiry)
|
||||
return tx.UpdateUserToken(auth.Subject, token.AccessToken, token.RefreshToken, token.Expiry)
|
||||
}) {
|
||||
return
|
||||
}
|
||||
@ -156,9 +153,21 @@ func (h *HttpServer) loginCallback(rw http.ResponseWriter, req *http.Request, _
|
||||
|
||||
const oneYear = 365 * 24 * time.Hour
|
||||
|
||||
type lavenderLoginData struct {
|
||||
UserInfo UserInfoFields `json:"user_info"`
|
||||
auth.AccessTokenClaims
|
||||
}
|
||||
|
||||
func (l lavenderLoginData) Valid() error { return nil }
|
||||
|
||||
func (l lavenderLoginData) Type() string { return "lavender-login-data" }
|
||||
|
||||
func (h *HttpServer) setLoginDataCookie(rw http.ResponseWriter, authData UserAuth) bool {
|
||||
ps := claims.NewPermStorage()
|
||||
gen, err := h.signingKey.GenerateJwt(authData.ID, uuid.NewString(), jwt.ClaimStrings{h.conf.BaseUrl}, oneYear, auth.AccessTokenClaims{Perms: ps})
|
||||
gen, err := h.signingKey.GenerateJwt(authData.Subject, uuid.NewString(), jwt.ClaimStrings{h.conf.BaseUrl}, oneYear, lavenderLoginData{
|
||||
UserInfo: authData.UserInfo,
|
||||
AccessTokenClaims: auth.AccessTokenClaims{Perms: ps},
|
||||
})
|
||||
if err != nil {
|
||||
http.Error(rw, "Failed to generate cookie token", http.StatusInternalServerError)
|
||||
return true
|
||||
@ -174,34 +183,20 @@ func (h *HttpServer) setLoginDataCookie(rw http.ResponseWriter, authData UserAut
|
||||
return false
|
||||
}
|
||||
|
||||
func (h *HttpServer) readLoginDataCookie(req *http.Request, u *UserAuth) {
|
||||
func (h *HttpServer) readLoginDataCookie(req *http.Request, u *UserAuth) error {
|
||||
loginCookie, err := req.Cookie("lavender-login-data")
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
hexData, err := hex.DecodeString(loginCookie.Value)
|
||||
_, b, err := mjwt.ExtractClaims[lavenderLoginData](h.signingKey, loginCookie.Value)
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
decData, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, h.signingKey.PrivateKey(), hexData, []byte("lavender-login-data"))
|
||||
if err != nil {
|
||||
return
|
||||
*u = UserAuth{
|
||||
Subject: b.Subject,
|
||||
UserInfo: b.Claims.UserInfo,
|
||||
}
|
||||
|
||||
userId := string(decData)
|
||||
var token oauth2.Token
|
||||
if h.DbTxRaw(func(tx *database.Tx) error {
|
||||
return tx.GetUserToken(userId, &token.AccessToken, &token.RefreshToken, &token.Expiry)
|
||||
}) {
|
||||
return
|
||||
}
|
||||
|
||||
sso := h.manager.FindServiceFromLogin(userId)
|
||||
if sso == nil {
|
||||
return
|
||||
}
|
||||
|
||||
*u, _ = h.fetchUserInfo(sso, &token)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *HttpServer) fetchUserInfo(sso *issuer.WellKnownOIDC, token *oauth2.Token) (UserAuth, error) {
|
||||
@ -221,10 +216,8 @@ func (h *HttpServer) fetchUserInfo(sso *issuer.WellKnownOIDC, token *oauth2.Toke
|
||||
}
|
||||
subject += "@" + sso.Config.Namespace
|
||||
|
||||
displayName := userInfoJson.GetStringOrDefault("name", "Unknown Name")
|
||||
return UserAuth{
|
||||
ID: subject,
|
||||
DisplayName: displayName,
|
||||
UserInfo: userInfoJson,
|
||||
Subject: subject,
|
||||
UserInfo: userInfoJson,
|
||||
}, nil
|
||||
}
|
||||
|
@ -26,11 +26,11 @@ func (h *HttpServer) ManageAppsGet(rw http.ResponseWriter, req *http.Request, _
|
||||
var roles string
|
||||
var appList []database.ClientInfoDbOutput
|
||||
if h.DbTx(rw, func(tx *database.Tx) (err error) {
|
||||
roles, err = tx.GetUserRoles(auth.ID)
|
||||
roles, err = tx.GetUserRoles(auth.Subject)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
appList, err = tx.GetAppList(auth.ID, HasRole(roles, "lavender:admin"), offset)
|
||||
appList, err = tx.GetAppList(auth.Subject, HasRole(roles, "lavender:admin"), offset)
|
||||
return
|
||||
}) {
|
||||
return
|
||||
@ -80,7 +80,7 @@ func (h *HttpServer) ManageAppsPost(rw http.ResponseWriter, req *http.Request, _
|
||||
if sso || hasPerms {
|
||||
var roles string
|
||||
if h.DbTx(rw, func(tx *database.Tx) (err error) {
|
||||
roles, err = tx.GetUserRoles(auth.ID)
|
||||
roles, err = tx.GetUserRoles(auth.Subject)
|
||||
return
|
||||
}) {
|
||||
return
|
||||
@ -98,7 +98,7 @@ func (h *HttpServer) ManageAppsPost(rw http.ResponseWriter, req *http.Request, _
|
||||
switch action {
|
||||
case "create":
|
||||
if h.DbTx(rw, func(tx *database.Tx) error {
|
||||
return tx.InsertClientApp(name, domain, auth.ID, perms, public, sso, active)
|
||||
return tx.InsertClientApp(name, domain, auth.Subject, perms, public, sso, active)
|
||||
}) {
|
||||
return
|
||||
}
|
||||
@ -108,7 +108,7 @@ func (h *HttpServer) ManageAppsPost(rw http.ResponseWriter, req *http.Request, _
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tx.UpdateClientApp(sub, auth.ID, name, domain, perms, hasPerms, public, sso, active)
|
||||
return tx.UpdateClientApp(sub, auth.Subject, name, domain, perms, hasPerms, public, sso, active)
|
||||
}) {
|
||||
return
|
||||
}
|
||||
@ -124,7 +124,7 @@ func (h *HttpServer) ManageAppsPost(rw http.ResponseWriter, req *http.Request, _
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
secret, err = tx.ResetClientAppSecret(sub, auth.ID)
|
||||
secret, err = tx.ResetClientAppSecret(sub, auth.Subject)
|
||||
return err
|
||||
}) {
|
||||
return
|
||||
|
@ -24,7 +24,7 @@ func (h *HttpServer) ManageUsersGet(rw http.ResponseWriter, req *http.Request, _
|
||||
var roles string
|
||||
var userList []database.User
|
||||
if h.DbTx(rw, func(tx *database.Tx) (err error) {
|
||||
roles, err = tx.GetUserRoles(auth.ID)
|
||||
roles, err = tx.GetUserRoles(auth.Subject)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -43,7 +43,7 @@ func (h *HttpServer) ManageUsersGet(rw http.ResponseWriter, req *http.Request, _
|
||||
"Users": userList,
|
||||
"Offset": offset,
|
||||
"EmailShow": req.URL.Query().Has("show-email"),
|
||||
"CurrentAdmin": auth.ID,
|
||||
"CurrentAdmin": auth.Subject,
|
||||
}
|
||||
if q.Has("edit") {
|
||||
for _, i := range userList {
|
||||
@ -71,7 +71,7 @@ func (h *HttpServer) ManageUsersPost(rw http.ResponseWriter, req *http.Request,
|
||||
|
||||
var roles string
|
||||
if h.DbTx(rw, func(tx *database.Tx) (err error) {
|
||||
roles, err = tx.GetUserRoles(auth.ID)
|
||||
roles, err = tx.GetUserRoles(auth.Subject)
|
||||
return
|
||||
}) {
|
||||
return
|
||||
|
@ -147,5 +147,5 @@ func (h *HttpServer) oauthUserAuthorization(rw http.ResponseWriter, req *http.Re
|
||||
http.Redirect(rw, req, redirectUrl.String(), http.StatusFound)
|
||||
return "", nil
|
||||
}
|
||||
return auth.ID, nil
|
||||
return auth.Subject, nil
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/subtle"
|
||||
"encoding/json"
|
||||
"github.com/1f349/cache"
|
||||
@ -10,8 +9,8 @@ import (
|
||||
"github.com/1f349/lavender/issuer"
|
||||
"github.com/1f349/lavender/logger"
|
||||
"github.com/1f349/lavender/openid"
|
||||
"github.com/1f349/lavender/pages"
|
||||
scope2 "github.com/1f349/lavender/scope"
|
||||
"github.com/1f349/lavender/theme"
|
||||
"github.com/1f349/mjwt"
|
||||
"github.com/go-oauth2/oauth2/v4/errors"
|
||||
"github.com/go-oauth2/oauth2/v4/manage"
|
||||
@ -20,6 +19,7 @@ import (
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
@ -44,6 +44,7 @@ type flowStateData struct {
|
||||
|
||||
func NewHttpServer(conf Conf, db *database.DB, signingKey mjwt.Signer) *http.Server {
|
||||
r := httprouter.New()
|
||||
contentCache := time.Now()
|
||||
|
||||
// remove last slash from baseUrl
|
||||
{
|
||||
@ -139,8 +140,14 @@ func NewHttpServer(conf Conf, db *database.DB, signingKey mjwt.Signer) *http.Ser
|
||||
}))
|
||||
|
||||
// theme styles
|
||||
r.GET("/theme/style.css", func(rw http.ResponseWriter, req *http.Request, params httprouter.Params) {
|
||||
http.ServeContent(rw, req, "style.css", time.Now(), bytes.NewReader(theme.DefaultThemeCss))
|
||||
r.GET("/assets/*filepath", func(rw http.ResponseWriter, req *http.Request, params httprouter.Params) {
|
||||
name := params.ByName("filepath")
|
||||
if strings.Contains(name, "..") {
|
||||
http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
out := pages.RenderCss(path.Join("assets", name))
|
||||
http.ServeContent(rw, req, path.Base(name), contentCache, out)
|
||||
})
|
||||
|
||||
// management pages
|
||||
|
@ -1,6 +0,0 @@
|
||||
package theme
|
||||
|
||||
import _ "embed"
|
||||
|
||||
//go:embed style.css
|
||||
var DefaultThemeCss []byte
|
15
utils/once.go
Normal file
15
utils/once.go
Normal file
@ -0,0 +1,15 @@
|
||||
package utils
|
||||
|
||||
import "sync"
|
||||
|
||||
type Once[T any] struct {
|
||||
once sync.Once
|
||||
value T
|
||||
}
|
||||
|
||||
func (o *Once[T]) Do(f func() T) T {
|
||||
o.once.Do(func() {
|
||||
o.value = f()
|
||||
})
|
||||
return o.value
|
||||
}
|
Loading…
Reference in New Issue
Block a user