2021-03-26 15:10:10 +03:00
|
|
|
package middlewares
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"github.com/muety/wakapi/models"
|
|
|
|
"net/http"
|
|
|
|
)
|
|
|
|
|
|
|
|
const keyPrincipal = "principal"
|
|
|
|
|
|
|
|
type PrincipalContainer struct {
|
|
|
|
principal *models.User
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *PrincipalContainer) SetPrincipal(user *models.User) {
|
|
|
|
c.principal = user
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *PrincipalContainer) GetPrincipal() *models.User {
|
|
|
|
return c.principal
|
|
|
|
}
|
|
|
|
|
2023-01-02 12:55:57 +03:00
|
|
|
func (c *PrincipalContainer) GetPrincipalIdentity() string {
|
|
|
|
return c.principal.Identity()
|
|
|
|
}
|
|
|
|
|
2021-03-26 15:10:10 +03:00
|
|
|
// This middleware is a bit of a dirty workaround to the fact that a http.Request's context
|
|
|
|
// does not allow to pass values from an inner to an outer middleware. Calling WithContext() on a
|
|
|
|
// request shallow-copies the whole request itself and therefore, in a chain of handler1(handler2()),
|
|
|
|
// handler 1 will not have access to values handler 2 writes to its context. In addition, Context.WithValue
|
|
|
|
// returns a new context with the old context as a parent.
|
|
|
|
//
|
|
|
|
// As a concrete example, SentryMiddleware as well as LoggingMiddleware should be quite the outer layers,
|
|
|
|
// while AuthenticationMiddleware is on the very inside of the chain. However, we still want sentry or the
|
|
|
|
// logger to have access to the user object populated by the auth. middleware, if present.
|
|
|
|
//
|
|
|
|
// This middleware shall be included as the outermost layers and it injects a stateful container that does
|
|
|
|
// nothing but conditionally hold a reference to an authenticated user object.
|
|
|
|
//
|
|
|
|
// Other reference: https://stackoverflow.com/questions/55972869/send-errors-to-sentry-with-golang-and-mux
|
|
|
|
|
|
|
|
type PrincipalMiddleware struct {
|
|
|
|
handler http.Handler
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewPrincipalMiddleware() func(handler http.Handler) http.Handler {
|
|
|
|
return func(h http.Handler) http.Handler {
|
|
|
|
return &PrincipalMiddleware{handler: h}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *PrincipalMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := context.WithValue(r.Context(), keyPrincipal, &PrincipalContainer{})
|
|
|
|
p.handler.ServeHTTP(w, r.WithContext(ctx))
|
|
|
|
}
|
|
|
|
|
|
|
|
func SetPrincipal(r *http.Request, user *models.User) {
|
|
|
|
if p := r.Context().Value(keyPrincipal); p != nil {
|
|
|
|
p.(*PrincipalContainer).SetPrincipal(user)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetPrincipal(r *http.Request) *models.User {
|
|
|
|
if p := r.Context().Value(keyPrincipal); p != nil {
|
|
|
|
return p.(*PrincipalContainer).GetPrincipal()
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|