mirror of
https://github.com/abhinavxd/libredesk.git
synced 2025-11-01 12:33:42 +00:00
- feat: button to show/hide quoted text of incoming emails. - fix: logout redirect - remove hardcoded user login info. - add libredesk.io to webtemplate footer
173 lines
5.3 KiB
Go
173 lines
5.3 KiB
Go
package main
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
|
|
amodels "github.com/abhinavxd/artemis/internal/auth/models"
|
|
"github.com/abhinavxd/artemis/internal/envelope"
|
|
"github.com/valyala/fasthttp"
|
|
"github.com/zerodha/fastglue"
|
|
)
|
|
|
|
// tryAuth is a middleware that attempts to authenticate the user and add them to the context
|
|
// but doesn't enforce authentication. Handlers can check if user exists in context optionally.
|
|
func tryAuth(handler fastglue.FastRequestHandler) fastglue.FastRequestHandler {
|
|
return func(r *fastglue.Request) error {
|
|
app := r.Context.(*App)
|
|
|
|
// Try to validate session without returning error.
|
|
userSession, err := app.auth.ValidateSession(r)
|
|
if err != nil || userSession.ID <= 0 {
|
|
return handler(r)
|
|
}
|
|
|
|
// Try to get user.
|
|
user, err := app.user.Get(userSession.ID)
|
|
if err != nil {
|
|
return handler(r)
|
|
}
|
|
|
|
// Set user in context if found.
|
|
r.RequestCtx.SetUserValue("user", amodels.User{
|
|
ID: user.ID,
|
|
Email: user.Email.String,
|
|
FirstName: user.FirstName,
|
|
LastName: user.LastName,
|
|
})
|
|
|
|
return handler(r)
|
|
}
|
|
}
|
|
|
|
// auth makes sure the user is logged in.
|
|
func auth(handler fastglue.FastRequestHandler) fastglue.FastRequestHandler {
|
|
return func(r *fastglue.Request) error {
|
|
var (
|
|
app = r.Context.(*App)
|
|
)
|
|
|
|
// Validate session and fetch user.
|
|
userSession, err := app.auth.ValidateSession(r)
|
|
if err != nil || userSession.ID <= 0 {
|
|
app.lo.Error("error validating session", "error", err)
|
|
return r.SendErrorEnvelope(http.StatusUnauthorized, "Invalid or expired session", nil, envelope.PermissionError)
|
|
}
|
|
|
|
// Set user in the request context.
|
|
user, err := app.user.Get(userSession.ID)
|
|
if err != nil {
|
|
return sendErrorEnvelope(r, err)
|
|
}
|
|
r.RequestCtx.SetUserValue("user", amodels.User{
|
|
ID: user.ID,
|
|
Email: user.Email.String,
|
|
FirstName: user.FirstName,
|
|
LastName: user.LastName,
|
|
})
|
|
|
|
return handler(r)
|
|
}
|
|
}
|
|
|
|
// perm does session validation, CSRF, and permission enforcement.
|
|
func perm(handler fastglue.FastRequestHandler, perm string) fastglue.FastRequestHandler {
|
|
return func(r *fastglue.Request) error {
|
|
var (
|
|
app = r.Context.(*App)
|
|
cookieToken = string(r.RequestCtx.Request.Header.Cookie("csrf_token"))
|
|
hdrToken = string(r.RequestCtx.Request.Header.Peek("X-CSRFTOKEN"))
|
|
)
|
|
|
|
if cookieToken == "" || hdrToken == "" || cookieToken != hdrToken {
|
|
app.lo.Error("csrf token mismatch", "cookie_token", cookieToken, "header_token", hdrToken)
|
|
return r.SendErrorEnvelope(http.StatusForbidden, "Invalid CSRF token", nil, envelope.PermissionError)
|
|
}
|
|
|
|
// Validate session and fetch user.
|
|
sessUser, err := app.auth.ValidateSession(r)
|
|
if err != nil || sessUser.ID <= 0 {
|
|
app.lo.Error("error validating session", "error", err)
|
|
return r.SendErrorEnvelope(http.StatusUnauthorized, "Invalid or expired session", nil, envelope.PermissionError)
|
|
}
|
|
|
|
// Get user from DB.
|
|
user, err := app.user.Get(sessUser.ID)
|
|
if err != nil {
|
|
return sendErrorEnvelope(r, err)
|
|
}
|
|
|
|
// Split the permission string into object and action and enforce it.
|
|
parts := strings.Split(perm, ":")
|
|
if len(parts) != 2 {
|
|
return r.SendErrorEnvelope(http.StatusInternalServerError, "Invalid permission format", nil, envelope.GeneralError)
|
|
}
|
|
object, action := parts[0], parts[1]
|
|
ok, err := app.authz.Enforce(user, object, action)
|
|
if err != nil {
|
|
return r.SendErrorEnvelope(http.StatusInternalServerError, "Error checking permissions", nil, envelope.GeneralError)
|
|
}
|
|
if !ok {
|
|
return r.SendErrorEnvelope(http.StatusForbidden, "Permission denied", nil, envelope.PermissionError)
|
|
}
|
|
|
|
// Set user in the request context.
|
|
r.RequestCtx.SetUserValue("user", amodels.User{
|
|
ID: user.ID,
|
|
Email: user.Email.String,
|
|
FirstName: user.FirstName,
|
|
LastName: user.LastName,
|
|
})
|
|
|
|
return handler(r)
|
|
}
|
|
}
|
|
|
|
// authPage ensures the user is logged in; otherwise, redirects to the login page.
|
|
func authPage(handler fastglue.FastRequestHandler) fastglue.FastRequestHandler {
|
|
return func(r *fastglue.Request) error {
|
|
app := r.Context.(*App)
|
|
|
|
// Validate session.
|
|
user, err := app.auth.ValidateSession(r)
|
|
if err != nil {
|
|
app.lo.Error("error validating session", "error", err)
|
|
return r.SendErrorEnvelope(http.StatusUnauthorized, "Invalid or expired session", nil, envelope.PermissionError)
|
|
}
|
|
if user.ID > 0 {
|
|
return handler(r)
|
|
}
|
|
|
|
nextURI := r.RequestCtx.QueryArgs().Peek("next")
|
|
if len(nextURI) == 0 {
|
|
nextURI = r.RequestCtx.RequestURI()
|
|
}
|
|
return r.RedirectURI("/", fasthttp.StatusFound, map[string]interface{}{
|
|
"next": string(nextURI),
|
|
}, "")
|
|
}
|
|
}
|
|
|
|
// notAuthPage allows access only if the user is not authenticated; otherwise, redirects to the user inbox.
|
|
func notAuthPage(handler fastglue.FastRequestHandler) fastglue.FastRequestHandler {
|
|
return func(r *fastglue.Request) error {
|
|
app := r.Context.(*App)
|
|
|
|
// Validate session.
|
|
user, err := app.auth.ValidateSession(r)
|
|
if err != nil {
|
|
app.lo.Error("error validating session", "error", err)
|
|
return r.SendErrorEnvelope(http.StatusUnauthorized, "Invalid or expired session", nil, envelope.PermissionError)
|
|
}
|
|
|
|
if user.ID != 0 {
|
|
nextURI := string(r.RequestCtx.QueryArgs().Peek("next"))
|
|
if nextURI == "" {
|
|
nextURI = "/inboxes/assigned"
|
|
}
|
|
return r.RedirectURI(nextURI, fasthttp.StatusFound, nil, "")
|
|
}
|
|
return handler(r)
|
|
}
|
|
}
|