mirror of
https://github.com/1f349/lavender.git
synced 2024-12-21 23:24:09 +00:00
Remove session library
This commit is contained in:
parent
894149305b
commit
374a708899
5
go.mod
5
go.mod
@ -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
9
go.sum
@ -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=
|
||||
|
@ -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)
|
||||
if err != nil {
|
||||
return UserAuth{}, fmt.Errorf("failed to start session")
|
||||
}
|
||||
|
||||
// get auth object
|
||||
userIdRaw, ok := ss.Get("session-data")
|
||||
if !ok {
|
||||
return UserAuth{Session: ss}, nil
|
||||
}
|
||||
userData, ok := userIdRaw.(SessionData)
|
||||
if !ok {
|
||||
ss.Delete("session-data")
|
||||
err := ss.Save()
|
||||
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{Session: ss}, fmt.Errorf("failed to reset invalid session data")
|
||||
return UserAuth{}, err
|
||||
}
|
||||
return UserAuth{ID: b.Subject}, nil
|
||||
}
|
||||
|
||||
return UserAuth{Session: ss, Data: userData}, nil
|
||||
// not logged in
|
||||
return UserAuth{}, nil
|
||||
}
|
||||
|
||||
func PrepareRedirectUrl(targetPath string, origin *url.URL) *url.URL {
|
||||
|
@ -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,
|
||||
})
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user