Remove session library

This commit is contained in:
Melon 2024-02-15 15:09:14 +00:00
parent 894149305b
commit 374a708899
Signed by: melon
GPG Key ID: 6C9D970C50D26A25
9 changed files with 58 additions and 92 deletions

5
go.mod
View File

@ -9,14 +9,13 @@ require (
github.com/1f349/violet v0.0.13
github.com/MrMelon54/exit-reload v0.0.1
github.com/go-oauth2/oauth2/v4 v4.5.2
github.com/go-session/session v3.1.2+incompatible
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/google/subcommands v1.2.0
github.com/google/uuid v1.6.0
github.com/julienschmidt/httprouter v1.3.0
github.com/mattn/go-sqlite3 v1.14.22
github.com/stretchr/testify v1.8.4
golang.org/x/oauth2 v0.16.0
golang.org/x/oauth2 v0.17.0
)
require (
@ -30,7 +29,7 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/tidwall/btree v1.7.0 // indirect
github.com/tidwall/buntdb v1.3.0 // indirect
github.com/tidwall/gjson v1.17.0 // indirect
github.com/tidwall/gjson v1.17.1 // indirect
github.com/tidwall/grect v0.1.4 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect

9
go.sum
View File

@ -30,7 +30,6 @@ github.com/gavv/httpexpect v2.0.0+incompatible h1:1X9kcRshkSKEjNJJxX9Y9mQ5BRfbxU
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
github.com/go-oauth2/oauth2/v4 v4.5.2 h1:CuZhD3lhGuI6aNLyUbRHXsgG2RwGRBOuCBfd4WQKqBQ=
github.com/go-oauth2/oauth2/v4 v4.5.2/go.mod h1:wk/2uLImWIa9VVQDgxz99H2GDbhmfi/9/Xr+GvkSUSQ=
github.com/go-session/session v3.1.2+incompatible h1:yStchEObKg4nk2F7JGE7KoFIrA/1Y078peagMWcrncg=
github.com/go-session/session v3.1.2+incompatible/go.mod h1:8B3iivBQjrz/JtC68Np2T1yBBLxTan3mn/3OM0CyRt0=
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
@ -125,8 +124,8 @@ github.com/tidwall/buntdb v1.3.0 h1:gdhWO+/YwoB2qZMeAU9JcWWsHSYU3OvcieYgFRS0zwA=
github.com/tidwall/buntdb v1.3.0/go.mod h1:lZZrZUWzlyDJKlLQ6DKAy53LnG7m5kHyrEHvvcDmBpU=
github.com/tidwall/gjson v1.3.4/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls=
github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U=
github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/grect v0.0.0-20161006141115-ba9a043346eb/go.mod h1:lKYYLFIr9OIgdgrtgkZ9zgRxRdvPYsExnYBsEAd8W5M=
github.com/tidwall/grect v0.1.4 h1:dA3oIgNgWdSspFzn1kS4S/RDpZFLrIxAZOdJKjYapOg=
github.com/tidwall/grect v0.1.4/go.mod h1:9FBsaYRaR0Tcy4UwefBX/UDcDcDy9V5jUcxHzv2jd5Q=
@ -182,8 +181,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=

View File

@ -1,9 +1,9 @@
package server
import (
"fmt"
"github.com/1f349/lavender/database"
"github.com/go-session/session"
"github.com/1f349/mjwt"
"github.com/1f349/mjwt/auth"
"github.com/julienschmidt/httprouter"
"net/http"
"net/url"
@ -13,30 +13,18 @@ import (
type UserHandler func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, auth UserAuth)
type UserAuth struct {
Session session.Store
Data SessionData
}
type SessionData struct {
ID string
DisplayName string
UserInfo UserInfoFields
}
func (u UserAuth) IsGuest() bool {
return u.Data.ID == ""
}
func (u UserAuth) SaveSessionData() error {
u.Session.Set("session-data", u.Data)
return u.Session.Save()
}
func (u UserAuth) IsGuest() bool { return u.ID == "" }
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.Data.ID)
roles, err = tx.GetUserRoles(auth.ID)
return
}) {
return
@ -62,7 +50,7 @@ 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 := internalAuthenticationHandler(rw, req)
auth, err := h.internalAuthenticationHandler(req)
if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
@ -75,27 +63,16 @@ func (h *HttpServer) OptionalAuthentication(next UserHandler) httprouter.Handle
}
}
func internalAuthenticationHandler(rw http.ResponseWriter, req *http.Request) (UserAuth, error) {
ss, err := session.Start(req.Context(), rw, req)
func (h *HttpServer) internalAuthenticationHandler(req *http.Request) (UserAuth, error) {
if loginCookie, err := req.Cookie("tulip-login-data"); err == nil {
_, b, err := mjwt.ExtractClaims[auth.AccessTokenClaims](h.signingKey, loginCookie.Value)
if err != nil {
return UserAuth{}, fmt.Errorf("failed to start session")
return UserAuth{}, err
}
// get auth object
userIdRaw, ok := ss.Get("session-data")
if !ok {
return UserAuth{Session: ss}, nil
return UserAuth{ID: b.Subject}, nil
}
userData, ok := userIdRaw.(SessionData)
if !ok {
ss.Delete("session-data")
err := ss.Save()
if err != nil {
return UserAuth{Session: ss}, fmt.Errorf("failed to reset invalid session data")
}
}
return UserAuth{Session: ss, Data: userData}, nil
// not logged in
return UserAuth{}, nil
}
func PrepareRedirectUrl(targetPath string, origin *url.URL) *url.URL {

View File

@ -6,10 +6,21 @@ import (
"github.com/google/uuid"
"github.com/julienschmidt/httprouter"
"net/http"
"time"
)
func (h *HttpServer) Home(rw http.ResponseWriter, _ *http.Request, _ httprouter.Params, auth UserAuth) {
rw.Header().Set("Content-Type", "text/html")
lNonce := uuid.NewString()
http.SetCookie(rw, &http.Cookie{
Name: "tulip-nonce",
Value: lNonce,
Path: "/",
Expires: time.Now().Add(10 * time.Minute),
Secure: true,
SameSite: http.SameSiteLaxMode,
})
if auth.IsGuest() {
pages.RenderPageTemplate(rw, "index-guest", map[string]any{
"ServiceName": h.conf.ServiceName,
@ -19,23 +30,16 @@ 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.Data.ID)
roles, err := tx.GetUserRoles(auth.ID)
isAdmin = HasRole(roles, "lavender:admin")
return err
})
lNonce := uuid.NewString()
auth.Session.Set("action-nonce", lNonce)
if auth.Session.Save() != nil {
http.Error(rw, "Failed to save session", http.StatusInternalServerError)
return
}
pages.RenderPageTemplate(rw, "index", map[string]any{
"ServiceName": h.conf.ServiceName,
"Auth": auth,
"Subject": auth.Data.ID,
"DisplayName": auth.Data.DisplayName,
"Subject": auth.ID,
"DisplayName": auth.DisplayName,
"Nonce": lNonce,
"IsAdmin": isAdmin,
})

View File

@ -127,19 +127,15 @@ func (h *HttpServer) loginCallback(rw http.ResponseWriter, req *http.Request, _
}
// only continues if the above tx succeeds
auth.Data = sessionData
if err := auth.SaveSessionData(); err != nil {
http.Error(rw, "Failed to save session", http.StatusInternalServerError)
return
}
auth = sessionData
if h.DbTx(rw, func(tx *database.Tx) error {
return tx.UpdateUserToken(auth.Data.ID, token.AccessToken, token.RefreshToken, token.Expiry)
return tx.UpdateUserToken(auth.ID, token.AccessToken, token.RefreshToken, token.Expiry)
}) {
return
}
if h.setLoginDataCookie(rw, auth.Data.ID) {
if h.setLoginDataCookie(rw, auth.ID) {
http.Error(rw, "Failed to save login cookie", http.StatusInternalServerError)
return
}
@ -193,28 +189,28 @@ func (h *HttpServer) readLoginDataCookie(req *http.Request, u *UserAuth) {
return
}
u.Data, _ = h.fetchUserInfo(sso, &token)
*u, _ = h.fetchUserInfo(sso, &token)
}
func (h *HttpServer) fetchUserInfo(sso *issuer.WellKnownOIDC, token *oauth2.Token) (SessionData, error) {
func (h *HttpServer) fetchUserInfo(sso *issuer.WellKnownOIDC, token *oauth2.Token) (UserAuth, error) {
res, err := sso.OAuth2Config.Client(context.Background(), token).Get(sso.UserInfoEndpoint)
if err != nil || res.StatusCode != http.StatusOK {
return SessionData{}, fmt.Errorf("request failed")
return UserAuth{}, fmt.Errorf("request failed")
}
defer res.Body.Close()
var userInfoJson UserInfoFields
if err := json.NewDecoder(res.Body).Decode(&userInfoJson); err != nil {
return SessionData{}, err
return UserAuth{}, err
}
subject, ok := userInfoJson.GetString("sub")
if !ok {
return SessionData{}, fmt.Errorf("invalid subject")
return UserAuth{}, fmt.Errorf("invalid subject")
}
subject += "@" + sso.Config.Namespace
displayName := userInfoJson.GetStringOrDefault("name", "Unknown Name")
return SessionData{
return UserAuth{
ID: subject,
DisplayName: displayName,
UserInfo: userInfoJson,

View File

@ -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.Data.ID)
roles, err = tx.GetUserRoles(auth.ID)
if err != nil {
return
}
appList, err = tx.GetAppList(auth.Data.ID, HasRole(roles, "lavender:admin"), offset)
appList, err = tx.GetAppList(auth.ID, 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.Data.ID)
roles, err = tx.GetUserRoles(auth.ID)
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.Data.ID, perms, public, sso, active)
return tx.InsertClientApp(name, domain, auth.ID, 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.Data.ID, name, domain, perms, hasPerms, public, sso, active)
return tx.UpdateClientApp(sub, auth.ID, 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.Data.ID)
secret, err = tx.ResetClientAppSecret(sub, auth.ID)
return err
}) {
return

View File

@ -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.Data.ID)
roles, err = tx.GetUserRoles(auth.ID)
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.Data.ID,
"CurrentAdmin": auth.ID,
}
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.Data.ID)
roles, err = tx.GetUserRoles(auth.ID)
return
}) {
return

View File

@ -87,7 +87,7 @@ func (h *HttpServer) authorizeEndpoint(rw http.ResponseWriter, req *http.Request
"ServiceName": h.conf.ServiceName,
"AppName": appName,
"AppDomain": appDomain,
"DisplayName": auth.Data.DisplayName,
"DisplayName": auth.DisplayName,
"WantsList": scope.FancyScopeList(scopeList),
"ResponseType": form.Get("response_type"),
"ResponseMode": form.Get("response_mode"),
@ -125,7 +125,7 @@ func (h *HttpServer) oauthUserAuthorization(rw http.ResponseWriter, req *http.Re
return "", err
}
auth, err := internalAuthenticationHandler(rw, req)
auth, err := h.internalAuthenticationHandler(req)
if err != nil {
return "", err
}
@ -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.Data.ID, nil
return auth.ID, nil
}

View File

@ -17,7 +17,6 @@ import (
"github.com/go-oauth2/oauth2/v4/manage"
"github.com/go-oauth2/oauth2/v4/server"
"github.com/go-oauth2/oauth2/v4/store"
"github.com/go-session/session"
"github.com/julienschmidt/httprouter"
"golang.org/x/oauth2"
"log"
@ -46,8 +45,6 @@ type flowStateData struct {
}
func NewHttpServer(conf Conf, db *database.DB, signingKey mjwt.Signer) *http.Server {
session.InitManager(session.SetCookieName("lavender_session"))
r := httprouter.New()
// remove last slash from baseUrl
@ -126,20 +123,14 @@ func NewHttpServer(conf Conf, db *database.DB, signingKey mjwt.Signer) *http.Ser
r.POST("/login", hs.OptionalAuthentication(hs.loginPost))
r.GET("/callback", hs.OptionalAuthentication(hs.loginCallback))
r.POST("/logout", hs.RequireAuthentication(func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, auth UserAuth) {
lNonce, ok := auth.Session.Get("action-nonce")
if !ok {
http.Error(rw, "Missing nonce", http.StatusInternalServerError)
cookie, err := req.Cookie("tulip-nonce")
if err != nil {
http.Error(rw, "Missing nonce", http.StatusBadRequest)
return
}
if subtle.ConstantTimeCompare([]byte(lNonce.(string)), []byte(req.PostFormValue("nonce"))) == 1 {
auth.Session.Delete("session-data")
if auth.Session.Save() != nil {
http.Error(rw, "Failed to save session", http.StatusInternalServerError)
return
}
if subtle.ConstantTimeCompare([]byte(cookie.Value), []byte(req.PostFormValue("nonce"))) == 1 {
http.SetCookie(rw, &http.Cookie{
Name: "lavender-login-data",
Name: "tulip-login-data",
Path: "/",
MaxAge: -1,
Secure: true,