Save current user in encrypted cookie to prevent repetative logging in

This commit is contained in:
Melon 2023-12-19 00:01:08 +00:00
parent 0f608d6b2f
commit 314e6f759c
Signed by: melon
GPG Key ID: 6C9D970C50D26A25
7 changed files with 146 additions and 58 deletions

View File

@ -8,6 +8,7 @@ import (
"errors" "errors"
"flag" "flag"
"fmt" "fmt"
"github.com/1f349/mjwt"
"github.com/1f349/tulip/database" "github.com/1f349/tulip/database"
"github.com/1f349/tulip/mail/templates" "github.com/1f349/tulip/mail/templates"
"github.com/1f349/tulip/pages" "github.com/1f349/tulip/pages"
@ -72,7 +73,10 @@ func (s *serveCmd) Execute(_ context.Context, _ *flag.FlagSet, _ ...any) subcomm
} }
func normalLoad(startUp server.Conf, wd string) { func normalLoad(startUp server.Conf, wd string) {
key := genHmacKey() signingKey, err := mjwt.NewMJwtSignerFromFileOrCreate(startUp.OtpIssuer, filepath.Join(wd, "tulip.key.pem"), rand.Reader, 4096)
if err != nil {
log.Fatal("[Tulip] Failed to open signing key file:", err)
}
db, err := database.Open(filepath.Join(wd, "tulip.db.sqlite")) db, err := database.Open(filepath.Join(wd, "tulip.db.sqlite"))
if err != nil { if err != nil {
@ -91,7 +95,7 @@ func normalLoad(startUp server.Conf, wd string) {
log.Fatal("[Tulip] Failed to load mail templates:", err) log.Fatal("[Tulip] Failed to load mail templates:", err)
} }
srv := server.NewHttpServer(startUp, db, key) srv := server.NewHttpServer(startUp, db, signingKey)
log.Printf("[Tulip] Starting HTTP server on '%s'\n", srv.Addr) log.Printf("[Tulip] Starting HTTP server on '%s'\n", srv.Addr)
go utils.RunBackgroundHttp("HTTP", srv) go utils.RunBackgroundHttp("HTTP", srv)

24
go.mod
View File

@ -1,27 +1,28 @@
module github.com/1f349/tulip module github.com/1f349/tulip
go 1.21.1 go 1.21.5
require ( require (
github.com/1f349/cache v0.0.2 github.com/1f349/cache v0.0.2
github.com/1f349/mjwt v0.2.1
github.com/1f349/overlapfs v0.0.1 github.com/1f349/overlapfs v0.0.1
github.com/1f349/violet v0.0.9 github.com/1f349/violet v0.0.12
github.com/MrMelon54/exit-reload v0.0.1 github.com/MrMelon54/exit-reload v0.0.1
github.com/MrMelon54/pronouns v1.0.1 github.com/MrMelon54/pronouns v1.0.1
github.com/emersion/go-message v0.17.0 github.com/emersion/go-message v0.17.0
github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43
github.com/emersion/go-smtp v0.18.1 github.com/emersion/go-smtp v0.19.0
github.com/go-oauth2/oauth2/v4 v4.5.2 github.com/go-oauth2/oauth2/v4 v4.5.2
github.com/go-session/session v3.1.2+incompatible github.com/go-session/session v3.1.2+incompatible
github.com/google/subcommands v1.2.0 github.com/google/subcommands v1.2.0
github.com/google/uuid v1.3.1 github.com/google/uuid v1.5.0
github.com/julienschmidt/httprouter v1.3.0 github.com/julienschmidt/httprouter v1.3.0
github.com/mattn/go-sqlite3 v1.14.17 github.com/mattn/go-sqlite3 v1.14.19
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.8.4
github.com/xlzd/gotp v0.1.0 github.com/xlzd/gotp v0.1.0
golang.org/x/crypto v0.13.0 golang.org/x/crypto v0.17.0
golang.org/x/text v0.13.0 golang.org/x/text v0.14.0
) )
require ( require (
@ -29,14 +30,17 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 // indirect github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/tidwall/btree v1.6.0 // indirect github.com/tidwall/btree v1.7.0 // indirect
github.com/tidwall/buntdb v1.3.0 // indirect github.com/tidwall/buntdb v1.3.0 // indirect
github.com/tidwall/gjson v1.16.0 // indirect github.com/tidwall/gjson v1.17.0 // indirect
github.com/tidwall/grect v0.1.4 // indirect github.com/tidwall/grect v0.1.4 // indirect
github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/rtred v0.1.2 // indirect github.com/tidwall/rtred v0.1.2 // indirect
github.com/tidwall/tinyqueue v0.1.1 // indirect github.com/tidwall/tinyqueue v0.1.1 // indirect
golang.org/x/net v0.19.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

26
go.sum
View File

@ -1,10 +1,14 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/1f349/cache v0.0.2 h1:27QD6zPd9xYyvh9V1qqWq+EAt5+N+qvyGWKfnjMrhP8= github.com/1f349/cache v0.0.2 h1:27QD6zPd9xYyvh9V1qqWq+EAt5+N+qvyGWKfnjMrhP8=
github.com/1f349/cache v0.0.2/go.mod h1:LibAMy13dF0KO1fQA9aEjZPBCB6Y4b5kKYEQJUqc2rQ= github.com/1f349/cache v0.0.2/go.mod h1:LibAMy13dF0KO1fQA9aEjZPBCB6Y4b5kKYEQJUqc2rQ=
github.com/1f349/mjwt v0.2.1 h1:REdiM/MaNjYQwHvI39LaMPhlvMg4Vy9SgomWMsKTNz8=
github.com/1f349/mjwt v0.2.1/go.mod h1:KEs6jd9JjWrQW+8feP2pGAU7pdA3aYTqjkT/YQr73PU=
github.com/1f349/overlapfs v0.0.1 h1:LAxBolrXFAgU0yqZtXg/C/aaPq3eoQSPpBc49BHuTp0= github.com/1f349/overlapfs v0.0.1 h1:LAxBolrXFAgU0yqZtXg/C/aaPq3eoQSPpBc49BHuTp0=
github.com/1f349/overlapfs v0.0.1/go.mod h1:I6aItQycr7nrzplmfNXp/QF9tTmKRSgY3fXmu/7Ky2o= github.com/1f349/overlapfs v0.0.1/go.mod h1:I6aItQycr7nrzplmfNXp/QF9tTmKRSgY3fXmu/7Ky2o=
github.com/1f349/violet v0.0.9 h1:eQfc5fDMKJXVFUjS2UiAGTkOVVBamppD5dguhmU4GeU= github.com/1f349/violet v0.0.9 h1:eQfc5fDMKJXVFUjS2UiAGTkOVVBamppD5dguhmU4GeU=
github.com/1f349/violet v0.0.9/go.mod h1:Uzu6I1pLBP5UEzcUCTQBbk/NTfI5TAABSrowa8DSpR0= github.com/1f349/violet v0.0.9/go.mod h1:Uzu6I1pLBP5UEzcUCTQBbk/NTfI5TAABSrowa8DSpR0=
github.com/1f349/violet v0.0.12 h1:VIiVYfKptCYJvwaJHFgtOyTUOURRMIltGp5Blw9+isY=
github.com/1f349/violet v0.0.12/go.mod h1:8xyh96shYiSBkwumvG/KkiY78tAhxiOomDlT7phZAbA=
github.com/MrMelon54/exit-reload v0.0.1 h1:sxHa59tNEQMcikwuX2+93lw6Vi1+R7oCRF8a0C3alXc= github.com/MrMelon54/exit-reload v0.0.1 h1:sxHa59tNEQMcikwuX2+93lw6Vi1+R7oCRF8a0C3alXc=
github.com/MrMelon54/exit-reload v0.0.1/go.mod h1:PLiSfmUzwdpTTQP3BBfUPhkqPwaIZjx0DuXBnM76Bug= github.com/MrMelon54/exit-reload v0.0.1/go.mod h1:PLiSfmUzwdpTTQP3BBfUPhkqPwaIZjx0DuXBnM76Bug=
github.com/MrMelon54/pronouns v1.0.1 h1:JOEA5Z1pEkNRTzs314quIDC0JW7vUWs4CT3wGtNMzR0= github.com/MrMelon54/pronouns v1.0.1 h1:JOEA5Z1pEkNRTzs314quIDC0JW7vUWs4CT3wGtNMzR0=
@ -23,8 +27,12 @@ github.com/emersion/go-message v0.17.0/go.mod h1:/9Bazlb1jwUNB0npYYBsdJ2EMOiiyN3
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead h1:fI1Jck0vUrXT8bnphprS1EoVRe2Q5CKCX8iDlpqjQ/Y= github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead h1:fI1Jck0vUrXT8bnphprS1EoVRe2Q5CKCX8iDlpqjQ/Y=
github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 h1:hH4PQfOndHDlpzYfLAAfl63E8Le6F2+EL/cdhlkyRJY=
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-smtp v0.18.1 h1:4DFV0jxKhq0Gqt/Br3BRHyKZy5TStk6NIMHAx6GE/LA= github.com/emersion/go-smtp v0.18.1 h1:4DFV0jxKhq0Gqt/Br3BRHyKZy5TStk6NIMHAx6GE/LA=
github.com/emersion/go-smtp v0.18.1/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ= github.com/emersion/go-smtp v0.18.1/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
github.com/emersion/go-smtp v0.19.0 h1:iVCDtR2/JY3RpKoaZ7u6I/sb52S3EzfNHO1fAWVHgng=
github.com/emersion/go-smtp v0.19.0/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 h1:IbFBtwoTQyw0fIM5xv1HF+Y+3ZijDR839WMulgxCcUY= github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 h1:IbFBtwoTQyw0fIM5xv1HF+Y+3ZijDR839WMulgxCcUY=
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U= github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
@ -41,6 +49,8 @@ github.com/go-session/session v3.1.2+incompatible/go.mod h1:8B3iivBQjrz/JtC68Np2
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= 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= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
@ -59,6 +69,8 @@ github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
@ -83,6 +95,8 @@ github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI=
github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs= github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs=
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
@ -91,6 +105,8 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108
github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
@ -113,6 +129,8 @@ github.com/tidwall/assert v0.1.0/go.mod h1:QLYtGyeqse53vuELQheYl9dngGCJQ+mTtlxck
github.com/tidwall/btree v0.0.0-20191029221954-400434d76274/go.mod h1:huei1BkDWJ3/sLXmO+bsCNELL+Bp2Kks9OLyQFkzvA8= github.com/tidwall/btree v0.0.0-20191029221954-400434d76274/go.mod h1:huei1BkDWJ3/sLXmO+bsCNELL+Bp2Kks9OLyQFkzvA8=
github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg= github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg=
github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY=
github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI=
github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY=
github.com/tidwall/buntdb v1.1.2/go.mod h1:xAzi36Hir4FarpSHyfuZ6JzPJdjRZ8QlLZSntE2mqlI= github.com/tidwall/buntdb v1.1.2/go.mod h1:xAzi36Hir4FarpSHyfuZ6JzPJdjRZ8QlLZSntE2mqlI=
github.com/tidwall/buntdb v1.3.0 h1:gdhWO+/YwoB2qZMeAU9JcWWsHSYU3OvcieYgFRS0zwA= github.com/tidwall/buntdb v1.3.0 h1:gdhWO+/YwoB2qZMeAU9JcWWsHSYU3OvcieYgFRS0zwA=
github.com/tidwall/buntdb v1.3.0/go.mod h1:lZZrZUWzlyDJKlLQ6DKAy53LnG7m5kHyrEHvvcDmBpU= github.com/tidwall/buntdb v1.3.0/go.mod h1:lZZrZUWzlyDJKlLQ6DKAy53LnG7m5kHyrEHvvcDmBpU=
@ -120,6 +138,8 @@ github.com/tidwall/gjson v1.3.4/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJH
github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.16.0 h1:SyXa+dsSPpUlcwEDuKuEBJEz5vzTvOea+9rjyYodQFg= github.com/tidwall/gjson v1.16.0 h1:SyXa+dsSPpUlcwEDuKuEBJEz5vzTvOea+9rjyYodQFg=
github.com/tidwall/gjson v1.16.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.16.0/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/grect v0.0.0-20161006141115-ba9a043346eb/go.mod h1:lKYYLFIr9OIgdgrtgkZ9zgRxRdvPYsExnYBsEAd8W5M= 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 h1:dA3oIgNgWdSspFzn1kS4S/RDpZFLrIxAZOdJKjYapOg=
github.com/tidwall/grect v0.1.4/go.mod h1:9FBsaYRaR0Tcy4UwefBX/UDcDcDy9V5jUcxHzv2jd5Q= github.com/tidwall/grect v0.1.4/go.mod h1:9FBsaYRaR0Tcy4UwefBX/UDcDcDy9V5jUcxHzv2jd5Q=
@ -164,6 +184,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -180,6 +202,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -215,6 +239,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=

View File

@ -1,6 +1,10 @@
package server package server
import ( import (
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"encoding/base64"
"fmt" "fmt"
"github.com/1f349/tulip/database" "github.com/1f349/tulip/database"
"github.com/go-session/session" "github.com/go-session/session"
@ -40,7 +44,7 @@ func (u UserAuth) SaveSessionData() error {
} }
func (h *HttpServer) RequireAdminAuthentication(next UserHandler) httprouter.Handle { func (h *HttpServer) RequireAdminAuthentication(next UserHandler) httprouter.Handle {
return RequireAuthentication(func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, auth UserAuth) { return h.RequireAuthentication(func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, auth UserAuth) {
var role database.UserRole var role database.UserRole
if h.DbTx(rw, func(tx *database.Tx) (err error) { if h.DbTx(rw, func(tx *database.Tx) (err error) {
role, err = tx.GetUserRole(auth.Data.ID) role, err = tx.GetUserRole(auth.Data.ID)
@ -56,8 +60,8 @@ func (h *HttpServer) RequireAdminAuthentication(next UserHandler) httprouter.Han
}) })
} }
func RequireAuthentication(next UserHandler) httprouter.Handle { func (h *HttpServer) RequireAuthentication(next UserHandler) httprouter.Handle {
return OptionalAuthentication(false, func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, auth UserAuth) { return h.OptionalAuthentication(false, func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, auth UserAuth) {
if auth.IsGuest() { if auth.IsGuest() {
redirectUrl := PrepareRedirectUrl("/login", req.URL) redirectUrl := PrepareRedirectUrl("/login", req.URL)
http.Redirect(rw, req, redirectUrl.String(), http.StatusFound) http.Redirect(rw, req, redirectUrl.String(), http.StatusFound)
@ -67,7 +71,7 @@ func RequireAuthentication(next UserHandler) httprouter.Handle {
}) })
} }
func OptionalAuthentication(flowPart bool, next UserHandler) httprouter.Handle { func (h *HttpServer) OptionalAuthentication(flowPart bool, next UserHandler) httprouter.Handle {
return func(rw http.ResponseWriter, req *http.Request, params httprouter.Params) { return func(rw http.ResponseWriter, req *http.Request, params httprouter.Params) {
auth, err := internalAuthenticationHandler(rw, req) auth, err := internalAuthenticationHandler(rw, req)
if err != nil { if err != nil {
@ -78,6 +82,20 @@ func OptionalAuthentication(flowPart bool, next UserHandler) httprouter.Handle {
http.Redirect(rw, req, n.String(), http.StatusFound) http.Redirect(rw, req, n.String(), http.StatusFound)
return return
} }
if auth.IsGuest() {
if loginCookie, err := req.Cookie("login-data"); err == nil {
if decryptedBytes, err := base64.RawStdEncoding.DecodeString(loginCookie.Value); err == nil {
if decryptedData, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, h.signingKey.PrivateKey(), decryptedBytes, []byte("login-data")); err == nil {
if len(decryptedData) == 16 {
var u uuid.UUID
copy(u[:], decryptedData[:])
auth.Data.ID = u
auth.Data.NeedOtp = false
}
}
}
}
}
next(rw, req, params, auth) next(rw, req, params, auth)
} }
} }

View File

@ -1,7 +1,11 @@
package server package server
import ( import (
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"database/sql" "database/sql"
"encoding/base64"
"errors" "errors"
"fmt" "fmt"
"github.com/1f349/tulip/database" "github.com/1f349/tulip/database"
@ -138,9 +142,30 @@ func (h *HttpServer) LoginPost(rw http.ResponseWriter, req *http.Request, _ http
return return
} }
if h.setLoginDataCookie(rw, auth.Data.ID) {
http.Error(rw, "Internal Server Error", http.StatusInternalServerError)
return
}
h.SafeRedirect(rw, req) h.SafeRedirect(rw, req)
} }
func (h *HttpServer) setLoginDataCookie(rw http.ResponseWriter, userId uuid.UUID) bool {
encryptedData, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, h.signingKey.PublicKey(), userId[:], []byte("login-data"))
if err != nil {
return true
}
encryptedString := base64.RawStdEncoding.EncodeToString(encryptedData)
http.SetCookie(rw, &http.Cookie{
Name: "login-data",
Value: encryptedString,
Path: "/",
Expires: time.Now().AddDate(0, 3, 0),
Secure: true,
SameSite: http.SameSiteStrictMode,
})
return false
}
func (h *HttpServer) LoginResetPasswordPost(rw http.ResponseWriter, req *http.Request, params httprouter.Params) { func (h *HttpServer) LoginResetPasswordPost(rw http.ResponseWriter, req *http.Request, params httprouter.Params) {
email := req.PostFormValue("email") email := req.PostFormValue("email")
address, err := mail.ParseAddress(email) address, err := mail.ParseAddress(email)

View File

@ -44,6 +44,7 @@ func (h *HttpServer) LoginOtpPost(rw http.ResponseWriter, req *http.Request, _ h
return return
} }
h.setLoginDataCookie(rw, auth.Data.ID)
h.SafeRedirect(rw, req) h.SafeRedirect(rw, req)
} }

View File

@ -7,6 +7,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/1f349/cache" "github.com/1f349/cache"
"github.com/1f349/mjwt"
clientStore "github.com/1f349/tulip/client-store" clientStore "github.com/1f349/tulip/client-store"
"github.com/1f349/tulip/database" "github.com/1f349/tulip/database"
"github.com/1f349/tulip/openid" "github.com/1f349/tulip/openid"
@ -34,7 +35,7 @@ type HttpServer struct {
oauthMgr *manage.Manager oauthMgr *manage.Manager
db *database.DB db *database.DB
conf Conf conf Conf
privKey []byte signingKey mjwt.Signer
// mailLinkCache contains a mapping of verify uuids to user uuids // mailLinkCache contains a mapping of verify uuids to user uuids
mailLinkCache *cache.Cache[mailLinkKey, uuid.UUID] mailLinkCache *cache.Cache[mailLinkKey, uuid.UUID]
@ -51,25 +52,7 @@ type mailLinkKey struct {
data uuid.UUID data uuid.UUID
} }
func (h *HttpServer) SafeRedirect(rw http.ResponseWriter, req *http.Request) { func NewHttpServer(conf Conf, db *database.DB, signingKey mjwt.Signer) *http.Server {
redirectUrl := req.FormValue("redirect")
if redirectUrl == "" {
http.Redirect(rw, req, "/", http.StatusFound)
return
}
parse, err := url.Parse(redirectUrl)
if err != nil {
http.Error(rw, "Failed to parse redirect url: "+redirectUrl, http.StatusBadRequest)
return
}
if parse.Scheme != "" && parse.Opaque != "" && parse.User != nil && parse.Host != "" {
http.Error(rw, "Invalid redirect url: "+redirectUrl, http.StatusBadRequest)
return
}
http.Redirect(rw, req, parse.String(), http.StatusFound)
}
func NewHttpServer(conf Conf, db *database.DB, privKey []byte) *http.Server {
r := httprouter.New() r := httprouter.New()
// remove last slash from baseUrl // remove last slash from baseUrl
@ -94,7 +77,7 @@ func NewHttpServer(conf Conf, db *database.DB, privKey []byte) *http.Server {
oauthMgr: oauthManager, oauthMgr: oauthManager,
db: db, db: db,
conf: conf, conf: conf,
privKey: privKey, signingKey: signingKey,
mailLinkCache: cache.New[mailLinkKey, uuid.UUID](), mailLinkCache: cache.New[mailLinkKey, uuid.UUID](),
} }
@ -136,8 +119,8 @@ func NewHttpServer(conf Conf, db *database.DB, privKey []byte) *http.Server {
rw.WriteHeader(http.StatusOK) rw.WriteHeader(http.StatusOK)
_, _ = rw.Write(openIdBytes) _, _ = rw.Write(openIdBytes)
}) })
r.GET("/", OptionalAuthentication(false, hs.Home)) r.GET("/", hs.OptionalAuthentication(false, hs.Home))
r.POST("/logout", RequireAuthentication(func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, auth UserAuth) { r.POST("/logout", hs.RequireAuthentication(func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, auth UserAuth) {
lNonce, ok := auth.Session.Get("action-nonce") lNonce, ok := auth.Session.Get("action-nonce")
if !ok { if !ok {
http.Error(rw, "Missing nonce", http.StatusInternalServerError) http.Error(rw, "Missing nonce", http.StatusInternalServerError)
@ -149,6 +132,15 @@ func NewHttpServer(conf Conf, db *database.DB, privKey []byte) *http.Server {
http.Error(rw, "Failed to save session", http.StatusInternalServerError) http.Error(rw, "Failed to save session", http.StatusInternalServerError)
return return
} }
http.SetCookie(rw, &http.Cookie{
Name: "login-data",
Path: "/",
MaxAge: -1,
Secure: true,
SameSite: http.SameSiteStrictMode,
})
http.Redirect(rw, req, "/", http.StatusFound) http.Redirect(rw, req, "/", http.StatusFound)
return return
} }
@ -161,10 +153,10 @@ func NewHttpServer(conf Conf, db *database.DB, privKey []byte) *http.Server {
}) })
// login steps // login steps
r.GET("/login", OptionalAuthentication(false, hs.LoginGet)) r.GET("/login", hs.OptionalAuthentication(false, hs.LoginGet))
r.POST("/login", OptionalAuthentication(false, hs.LoginPost)) r.POST("/login", hs.OptionalAuthentication(false, hs.LoginPost))
r.GET("/login/otp", OptionalAuthentication(true, hs.LoginOtpGet)) r.GET("/login/otp", hs.OptionalAuthentication(true, hs.LoginOtpGet))
r.POST("/login/otp", OptionalAuthentication(true, hs.LoginOtpPost)) r.POST("/login/otp", hs.OptionalAuthentication(true, hs.LoginOtpPost))
// mail codes // mail codes
r.GET("/mail/verify/:code", hs.MailVerify) r.GET("/mail/verify/:code", hs.MailVerify)
@ -173,9 +165,9 @@ func NewHttpServer(conf Conf, db *database.DB, privKey []byte) *http.Server {
r.GET("/mail/delete/:code", hs.MailDelete) r.GET("/mail/delete/:code", hs.MailDelete)
// edit profile pages // edit profile pages
r.GET("/edit", RequireAuthentication(hs.EditGet)) r.GET("/edit", hs.RequireAuthentication(hs.EditGet))
r.POST("/edit", RequireAuthentication(hs.EditPost)) r.POST("/edit", hs.RequireAuthentication(hs.EditPost))
r.POST("/edit/otp", RequireAuthentication(hs.EditOtpPost)) r.POST("/edit/otp", hs.RequireAuthentication(hs.EditOtpPost))
// management pages // management pages
r.GET("/manage/apps", hs.RequireAdminAuthentication(hs.ManageAppsGet)) r.GET("/manage/apps", hs.RequireAdminAuthentication(hs.ManageAppsGet))
@ -184,8 +176,8 @@ func NewHttpServer(conf Conf, db *database.DB, privKey []byte) *http.Server {
r.POST("/manage/users", hs.RequireAdminAuthentication(hs.ManageUsersPost)) r.POST("/manage/users", hs.RequireAdminAuthentication(hs.ManageUsersPost))
// oauth pages // oauth pages
r.GET("/authorize", RequireAuthentication(hs.authorizeEndpoint)) r.GET("/authorize", hs.RequireAuthentication(hs.authorizeEndpoint))
r.POST("/authorize", RequireAuthentication(hs.authorizeEndpoint)) r.POST("/authorize", hs.RequireAuthentication(hs.authorizeEndpoint))
r.POST("/token", func(rw http.ResponseWriter, req *http.Request, params httprouter.Params) { r.POST("/token", func(rw http.ResponseWriter, req *http.Request, params httprouter.Params) {
if err := oauthSrv.HandleTokenRequest(rw, req); err != nil { if err := oauthSrv.HandleTokenRequest(rw, req); err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError) http.Error(rw, err.Error(), http.StatusInternalServerError)
@ -266,6 +258,24 @@ func NewHttpServer(conf Conf, db *database.DB, privKey []byte) *http.Server {
} }
} }
func (h *HttpServer) SafeRedirect(rw http.ResponseWriter, req *http.Request) {
redirectUrl := req.FormValue("redirect")
if redirectUrl == "" {
http.Redirect(rw, req, "/", http.StatusFound)
return
}
parse, err := url.Parse(redirectUrl)
if err != nil {
http.Error(rw, "Failed to parse redirect url: "+redirectUrl, http.StatusBadRequest)
return
}
if parse.Scheme != "" && parse.Opaque != "" && parse.User != nil && parse.Host != "" {
http.Error(rw, "Invalid redirect url: "+redirectUrl, http.StatusBadRequest)
return
}
http.Redirect(rw, req, parse.String(), http.StatusFound)
}
func ParseClaims(claims string) map[string]bool { func ParseClaims(claims string) map[string]bool {
m := make(map[string]bool) m := make(map[string]bool)
for { for {