diff --git a/auth/auth.go b/auth/auth.go
index 90d76f1..4d30deb 100644
--- a/auth/auth.go
+++ b/auth/auth.go
@@ -25,13 +25,9 @@ const (
StateSudo
)
-func IsLoggedIn(s State) bool {
- return s >= StateExtended
-}
+func (s State) IsLoggedIn() bool { return s >= StateExtended }
-func IsSudoAvailable(s State) bool {
- return s == StateSudo
-}
+func (s State) IsSudoAvailable() bool { return s == StateSudo }
type Provider interface {
// AccessState defines the state at which the provider is allowed to show.
diff --git a/auth/providers/login.go b/auth/providers/login.go
index 11b1c10..e21c222 100644
--- a/auth/providers/login.go
+++ b/auth/providers/login.go
@@ -4,8 +4,10 @@ import (
"context"
"database/sql"
"errors"
+ "fmt"
"github.com/1f349/lavender/auth"
"github.com/1f349/lavender/database"
+ "html/template"
"net/http"
)
@@ -20,13 +22,13 @@ type BasicLogin struct {
DB basicLoginDB
}
-func (b *BasicLogin) Factor() auth.State { return FactorBasic }
+func (b *BasicLogin) AccessState() auth.State { return auth.StateUnauthorized }
func (b *BasicLogin) Name() string { return "basic" }
-func (b *BasicLogin) RenderData(ctx context.Context, req *http.Request, user *database.User, data map[string]any) error {
- data["username"] = req.FormValue("username")
- return nil
+func (b *BasicLogin) RenderTemplate(ctx context.Context, req *http.Request, user *database.User) (template.HTML, error) {
+ // TODO(melon): rewrite this
+ return template.HTML(fmt.Sprintf("
%s
", req.FormValue("username"))), nil
}
func (b *BasicLogin) AttemptLogin(ctx context.Context, req *http.Request, user *database.User) error {
@@ -39,7 +41,7 @@ func (b *BasicLogin) AttemptLogin(ctx context.Context, req *http.Request, user *
login, err := b.DB.CheckLogin(ctx, un, pw)
switch {
case err == nil:
- return auth.lookupUser(ctx, b.DB, login.Subject, false, user)
+ return auth.LookupUser(ctx, b.DB, login.Subject, user)
case errors.Is(err, sql.ErrNoRows):
return auth.BasicUserSafeError(http.StatusForbidden, "Username or password is invalid")
default:
diff --git a/auth/providers/oauth.go b/auth/providers/oauth.go
index 862d069..855ffd5 100644
--- a/auth/providers/oauth.go
+++ b/auth/providers/oauth.go
@@ -9,6 +9,7 @@ import (
"github.com/1f349/lavender/issuer"
"github.com/google/uuid"
"golang.org/x/oauth2"
+ "html/template"
"net/http"
"time"
)
@@ -33,13 +34,12 @@ func (o OAuthLogin) Init() {
o.flow = cache.New[string, flowStateData]()
}
-func (o OAuthLogin) Factor() auth.State { return FactorBasic }
+func (o OAuthLogin) AccessState() auth.State { return auth.StateUnauthorized }
func (o OAuthLogin) Name() string { return "oauth" }
-func (o OAuthLogin) RenderData(ctx context.Context, req *http.Request, user *database.User, data map[string]any) error {
- //TODO implement me
- panic("implement me")
+func (o OAuthLogin) RenderTemplate(ctx context.Context, req *http.Request, user *database.User) (template.HTML, error) {
+ return "OAuth Login Template
", nil
}
func (o OAuthLogin) AttemptLogin(ctx context.Context, req *http.Request, user *database.User) error {
diff --git a/auth/providers/otp.go b/auth/providers/otp.go
index 14d6f47..ed1279b 100644
--- a/auth/providers/otp.go
+++ b/auth/providers/otp.go
@@ -3,9 +3,11 @@ package providers
import (
"context"
"errors"
+ "fmt"
"github.com/1f349/lavender/auth"
"github.com/1f349/lavender/database"
"github.com/xlzd/gotp"
+ "html/template"
"net/http"
"time"
)
@@ -24,28 +26,28 @@ type OtpLogin struct {
DB otpLoginDB
}
-func (o *OtpLogin) Factor() auth.State { return FactorExtended }
+func (o *OtpLogin) AccessState() auth.State { return auth.StateBasic }
func (o *OtpLogin) Name() string { return "basic" }
-func (o *OtpLogin) RenderData(_ context.Context, _ *http.Request, user *database.User, data map[string]any) error {
+func (o *OtpLogin) RenderTemplate(_ context.Context, _ *http.Request, user *database.User) (template.HTML, error) {
if user == nil || user.Subject == "" {
- return ErrRequiresPreviousFactor
+ return "", fmt.Errorf("requires previous factor")
}
if user.OtpSecret == "" || !isDigitsSupported(user.OtpDigits) {
- return auth.ErrUserDoesNotSupportFactor
+ return "", fmt.Errorf("user does not support factor")
}
// no need to provide render data
- return nil
+ return "OTP login template
", nil
}
func (o *OtpLogin) AttemptLogin(ctx context.Context, req *http.Request, user *database.User) error {
if user == nil || user.Subject == "" {
- return ErrRequiresPreviousFactor
+ return fmt.Errorf("requires previous factor")
}
if user.OtpSecret == "" || !isDigitsSupported(user.OtpDigits) {
- return auth.ErrUserDoesNotSupportFactor
+ return fmt.Errorf("user does not support factor")
}
code := req.FormValue("code")
diff --git a/auth/providers/passkey.go b/auth/providers/passkey.go
index 623bae9..642f5a4 100644
--- a/auth/providers/passkey.go
+++ b/auth/providers/passkey.go
@@ -2,13 +2,15 @@ package providers
import (
"context"
+ "fmt"
"github.com/1f349/lavender/auth"
"github.com/1f349/lavender/database"
+ "html/template"
"net/http"
)
type passkeyLoginDB interface {
- auth.lookupUserDB
+ auth.LookupUserDB
}
var _ auth.Provider = (*PasskeyLogin)(nil)
@@ -17,19 +19,18 @@ type PasskeyLogin struct {
DB passkeyLoginDB
}
-func (p *PasskeyLogin) Factor() auth.State { return FactorBasic }
+func (p *PasskeyLogin) AccessState() auth.State { return auth.StateUnauthorized }
func (p *PasskeyLogin) Name() string { return "passkey" }
-func (p *PasskeyLogin) RenderData(ctx context.Context, req *http.Request, user *database.User, data map[string]any) error {
+func (p *PasskeyLogin) RenderTemplate(ctx context.Context, req *http.Request, user *database.User) (template.HTML, error) {
if user == nil || user.Subject == "" {
- return ErrRequiresPreviousFactor
+ return "", fmt.Errorf("requires previous factor")
}
if user.OtpSecret == "" {
- return auth.ErrUserDoesNotSupportFactor
+ return "", fmt.Errorf("user does not support factor")
}
- //TODO implement me
panic("implement me")
}
@@ -41,7 +42,7 @@ func init() {
func (p *PasskeyLogin) AttemptLogin(ctx context.Context, req *http.Request, user *database.User) error {
if user.Subject == "" && !passkeyShortcut {
- return ErrRequiresPreviousFactor
+ return fmt.Errorf("requires previous factor")
}
//TODO implement me
diff --git a/auth/userauth.go b/auth/userauth.go
index eff31c6..9d42b53 100644
--- a/auth/userauth.go
+++ b/auth/userauth.go
@@ -22,7 +22,7 @@ func (u UserAuth) NextFlowUrl(origin *url.URL) *url.URL {
if origin.Path == "/login" || origin.Path == "/callback" {
return nil
}
- if u.Factor < FactorAuthorized {
+ if !u.Factor.IsLoggedIn() {
return PrepareRedirectUrl("/login", origin)
}
return nil
diff --git a/server/auth_test.go b/server/auth_test.go
index 384e90c..5a8f6a0 100644
--- a/server/auth_test.go
+++ b/server/auth_test.go
@@ -18,7 +18,7 @@ func TestUserAuth_NextFlowUrl(t *testing.T) {
assert.Equal(t, url.URL{Path: "/login"}, *u.NextFlowUrl(&url.URL{}))
assert.Equal(t, url.URL{Path: "/login", RawQuery: url.Values{"redirect": {"/hello"}}.Encode()}, *u.NextFlowUrl(&url.URL{Path: "/hello"}))
assert.Equal(t, url.URL{Path: "/login", RawQuery: url.Values{"redirect": {"/hello?a=A"}}.Encode()}, *u.NextFlowUrl(&url.URL{Path: "/hello", RawQuery: url.Values{"a": {"A"}}.Encode()}))
- u.Factor = auth.FactorAuthorized
+ u.Factor = auth.StateExtended
assert.Nil(t, u.NextFlowUrl(&url.URL{}))
}
diff --git a/server/login.go b/server/login.go
index a1b8b17..992700c 100644
--- a/server/login.go
+++ b/server/login.go
@@ -47,10 +47,11 @@ func (h *httpServer) testAuthSources(req *http.Request, user *database.User, fac
data := make(map[string]any)
for _, i := range h.authSources {
// ignore not-supported factors
- if i.State()&factor == 0 {
+ if i.AccessState() != factor {
continue
}
- err := i.RenderTemplate(req.Context(), req, user, data)
+ page, err := i.RenderTemplate(req.Context(), req, user)
+ _ = page
authSource[i.Name()] = err == nil
clear(data)
}
@@ -77,14 +78,14 @@ func (h *httpServer) loginGet(rw http.ResponseWriter, req *http.Request, _ httpr
return
}
- fmt.Printf("%#v\n", h.testAuthSources(req, userPtr, auth2.FactorBasic))
+ fmt.Printf("%#v\n", h.testAuthSources(req, userPtr, auth2.StateBasic))
web.RenderPageTemplate(rw, "login-memory", map[string]any{
"ServiceName": h.conf.ServiceName,
"LoginName": cookie.Value,
"Redirect": req.URL.Query().Get("redirect"),
"Source": "start",
- "Auth": h.testAuthSources(req, userPtr, auth2.FactorBasic),
+ "Auth": h.testAuthSources(req, userPtr, auth2.StateBasic),
})
return
}
@@ -95,7 +96,7 @@ func (h *httpServer) loginGet(rw http.ResponseWriter, req *http.Request, _ httpr
"LoginName": "",
"Redirect": req.URL.Query().Get("redirect"),
"Source": "start",
- "Auth": h.testAuthSources(req, nil, auth2.FactorBasic),
+ "Auth": h.testAuthSources(req, nil, auth2.StateBasic),
})
}
@@ -207,7 +208,7 @@ func (h *httpServer) updateExternalUserInfo(req *http.Request, sso *issuer.WellK
})
return auth2.UserAuth{
Subject: userSubject,
- Factor: auth2.FactorAuthorized,
+ Factor: auth2.StateExtended,
UserInfo: sessionData.UserInfo,
}, err
case errors.Is(err, sql.ErrNoRows):
@@ -263,7 +264,7 @@ func (h *httpServer) updateExternalUserInfo(req *http.Request, sso *issuer.WellK
// TODO(melon): this feels bad
sessionData = auth2.UserAuth{
Subject: userSubject,
- Factor: auth2.FactorAuthorized,
+ Factor: auth2.StateExtended,
UserInfo: sessionData.UserInfo,
}
@@ -453,7 +454,7 @@ func (h *httpServer) fetchUserInfo(sso *issuer.WellKnownOIDC, token *oauth2.Toke
return auth2.UserAuth{
Subject: subject,
- Factor: auth2.FactorAuthorized,
+ Factor: auth2.StateExtended,
UserInfo: userInfoJson,
}, nil
}
diff --git a/web/src/layouts/Layout.astro b/web/src/layouts/Layout.astro
index 39204d2..b7b035b 100644
--- a/web/src/layouts/Layout.astro
+++ b/web/src/layouts/Layout.astro
@@ -1,5 +1,7 @@
---
import Header from "../components/Header.astro";
+
+const {title} = Astro.props;
---
@@ -9,7 +11,7 @@ import Header from "../components/Header.astro";
- [[.ServiceName]]
+ [[ renderTitle "{title}" .ServiceName ]]
@@ -36,6 +38,16 @@ import Header from "../components/Header.astro";
background: #13151a;
}
+ main {
+ margin: auto;
+ padding: 1rem;
+ width: 800px;
+ max-width: calc(100% - 2rem);
+ color: white;
+ font-size: 20px;
+ line-height: 1.6;
+ }
+
code {
font-family: Menlo,
Monaco,
diff --git a/web/src/pages/edit.go.html b/web/src/pages/edit.astro
similarity index 97%
rename from web/src/pages/edit.go.html
rename to web/src/pages/edit.astro
index ef67d3d..5021351 100644
--- a/web/src/pages/edit.go.html
+++ b/web/src/pages/edit.astro
@@ -1,3 +1,12 @@
+---
+import Layout from "../layouts/Layout.astro";
+---
+
+
+
+
+
+
diff --git a/web/src/pages/index.astro b/web/src/pages/index.astro
index fb62628..0760d27 100644
--- a/web/src/pages/index.astro
+++ b/web/src/pages/index.astro
@@ -1,123 +1,21 @@
---
import Layout from '../layouts/Layout.astro';
-import Card from '../components/Card.astro';
---
-
-
- Welcome to Astro
-
- To get started, open the directory src/pages
in your project.
- Code Challenge: Tweak the "Welcome to Astro" message above.
-
-
-
+
+
Logged in as: [[ .Auth.UserInfo.name ]] ([[ .Auth.Subject ]])
+
+ [[ if .IsAdmin ]]
+
+ [[ end ]]
+
+
-
-
diff --git a/web/web.go b/web/web.go
index cc79f32..d567d9d 100644
--- a/web/web.go
+++ b/web/web.go
@@ -11,6 +11,7 @@ import (
"io/fs"
"net/http"
"os"
+ "path"
"path/filepath"
"strings"
)
@@ -45,14 +46,20 @@ func LoadPages(wd string) error {
pageTemplates, err = template.New("web").Delims("[[", "]]").Funcs(template.FuncMap{
"emailHide": utils.EmailHide,
- }).ParseFS(webCombinedDir, "*.html")
+ "renderTitle":
+ }).ParseFS(webCombinedDir, "*/index.html")
return err
})
}
+func renderTitle(title string, service string) string {
+
+}
+
func RenderPageTemplate(wr io.Writer, name string, data any) {
- err := pageTemplates.ExecuteTemplate(wr, name+".html", data)
+ p := path.Join(name, "index.html")
+ err := pageTemplates.ExecuteTemplate(wr, p, data)
if err != nil {
logger.Logger.Warn("Failed to render page", "name", name, "err", err)
}