mirror of
https://github.com/1f349/lavender.git
synced 2025-04-15 15:27:55 +01:00
150 lines
3.6 KiB
Go
150 lines
3.6 KiB
Go
package providers
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"github.com/1f349/lavender/auth"
|
|
"github.com/1f349/lavender/auth/authContext"
|
|
"github.com/1f349/lavender/auth/process"
|
|
"github.com/1f349/lavender/database"
|
|
"github.com/1f349/lavender/issuer"
|
|
"github.com/1f349/lavender/logger"
|
|
"github.com/1f349/lavender/utils"
|
|
"github.com/google/uuid"
|
|
"golang.org/x/oauth2"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const memoryCookieName = "lavender-user-memory"
|
|
|
|
var _ auth.Provider = (*Base)(nil)
|
|
var _ auth.Form = (*Base)(nil)
|
|
|
|
type Base struct {
|
|
DB *database.Queries
|
|
MyNamespace string
|
|
Manager *issuer.Manager
|
|
OAuth *OAuthLogin
|
|
}
|
|
|
|
func (b *Base) AccessState() process.State { return process.StateUnauthorized }
|
|
|
|
func (b *Base) Name() string { return "base" }
|
|
|
|
func (b *Base) String() string { return "%Provider(base)" }
|
|
|
|
func (b *Base) RenderTemplate(ctx authContext.TemplateContext) error {
|
|
type s struct {
|
|
LoginNameMemory bool
|
|
UserEmail string
|
|
Redirect string
|
|
}
|
|
|
|
req := ctx.Request()
|
|
q := req.URL.Query()
|
|
|
|
userEmail := ctx.LoginProcessData().Email
|
|
if userEmail == "" {
|
|
cookie, err := req.Cookie(memoryCookieName)
|
|
if err == nil && cookie.Valid() == nil {
|
|
userEmail = cookie.Value
|
|
}
|
|
}
|
|
|
|
ctx.Render(s{
|
|
UserEmail: userEmail,
|
|
Redirect: q.Get("redirect"),
|
|
})
|
|
return nil
|
|
}
|
|
|
|
func (b *Base) AttemptLogin(ctx authContext.FormContext) error {
|
|
req := ctx.Request()
|
|
rw := ctx.ResponseWriter()
|
|
|
|
if req.FormValue("not-you") != "" {
|
|
http.SetCookie(rw, &http.Cookie{
|
|
Name: memoryCookieName,
|
|
Value: "",
|
|
Path: "/",
|
|
MaxAge: -1,
|
|
Secure: true,
|
|
HttpOnly: true,
|
|
SameSite: http.SameSiteLaxMode,
|
|
})
|
|
return auth.RedirectError{
|
|
Target: "/login",
|
|
Code: http.StatusFound,
|
|
}
|
|
}
|
|
|
|
loginName := req.FormValue("email")
|
|
if loginName == "" {
|
|
return errors.New("email required")
|
|
}
|
|
|
|
rememberMe := req.FormValue("remember-me") != ""
|
|
logger.Logger.Debug("Hi", "login", loginName, "remember", rememberMe)
|
|
|
|
// Set the remember me cookie
|
|
if rememberMe {
|
|
now := time.Now()
|
|
future := now.AddDate(1, 0, 0)
|
|
http.SetCookie(rw, &http.Cookie{
|
|
Name: memoryCookieName,
|
|
Value: loginName,
|
|
Path: "/",
|
|
Expires: future,
|
|
MaxAge: int(future.Sub(now).Seconds()),
|
|
Secure: true,
|
|
HttpOnly: true,
|
|
SameSite: http.SameSiteLaxMode,
|
|
})
|
|
}
|
|
|
|
// append local namespace if @ is missing
|
|
if !strings.ContainsRune(loginName, '@') {
|
|
loginName += "@" + b.MyNamespace
|
|
}
|
|
|
|
user, namespace, err := utils.ParseLoginName(loginName)
|
|
if err != nil {
|
|
http.Error(rw, "Invalid login name", http.StatusBadRequest)
|
|
return fmt.Errorf("invalid login name %s", loginName)
|
|
}
|
|
|
|
login := b.Manager.GetService(namespace)
|
|
if login == nil {
|
|
http.Error(rw, "No login service defined for this username", http.StatusBadRequest)
|
|
return errors.New("no login service defined for this username")
|
|
}
|
|
|
|
// the login is not for this namespace
|
|
if login != issuer.MeWellKnown {
|
|
// TODO: this is oauth request code start oauth request
|
|
// TODO: this could all be 1 function call into oauth
|
|
|
|
// save state for use later
|
|
state := login.Config.Namespace + ":" + uuid.NewString()
|
|
b.OAuth.flow.Set(state, flowStateData{loginName, login, req.PostFormValue("redirect")}, time.Now().Add(15*time.Minute))
|
|
|
|
// generate oauth2 config and redirect to authorize URL
|
|
oa2conf := login.OAuth2Config
|
|
oa2conf.RedirectURL = b.OAuth.BaseUrl.JoinPath("callback").String()
|
|
nextUrl := oa2conf.AuthCodeURL(state, oauth2.SetAuthURLParam("login_name", user))
|
|
return auth.RedirectError{
|
|
Target: nextUrl,
|
|
Code: http.StatusFound,
|
|
}
|
|
}
|
|
|
|
ctx.UpdateSession(process.LoginProcessData{
|
|
Email: loginName,
|
|
State: process.StateBase,
|
|
})
|
|
|
|
return nil
|
|
}
|