emissary/internal/server/api/authorization.go

182 lines
3.6 KiB
Go
Raw Normal View History

2024-02-27 09:56:15 +01:00
package api
2023-03-13 10:44:58 +01:00
import (
"context"
"fmt"
"net/http"
"forge.cadoles.com/Cadoles/emissary/internal/auth"
"forge.cadoles.com/Cadoles/emissary/internal/auth/agent"
"forge.cadoles.com/Cadoles/emissary/internal/auth/thirdparty"
2024-02-26 18:20:40 +01:00
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
2023-03-13 10:44:58 +01:00
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/api"
"gitlab.com/wpetit/goweb/logger"
)
var ErrCodeForbidden api.ErrorCode = "forbidden"
func assertGlobalReadAccess(h http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
reqUser, ok := assertRequestUser(w, r)
if !ok {
return
}
switch user := reqUser.(type) {
case *thirdparty.User:
role := user.Role()
if role == thirdparty.RoleReader || role == thirdparty.RoleWriter {
h.ServeHTTP(w, r)
return
}
case *agent.User:
// Agents dont have global read access
default:
logUnexpectedUserType(r.Context(), reqUser)
}
forbidden(w, r)
}
return http.HandlerFunc(fn)
}
func assertAgentWriteAccess(h http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
reqUser, ok := assertRequestUser(w, r)
if !ok {
return
}
agentID, ok := getAgentID(w, r)
if !ok {
return
}
switch user := reqUser.(type) {
case *thirdparty.User:
role := user.Role()
if role == thirdparty.RoleWriter {
h.ServeHTTP(w, r)
return
}
case *agent.User:
if user.Agent().ID == agentID {
h.ServeHTTP(w, r)
return
}
default:
logUnexpectedUserType(r.Context(), reqUser)
}
forbidden(w, r)
}
return http.HandlerFunc(fn)
}
func assertAgentReadAccess(h http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
reqUser, ok := assertRequestUser(w, r)
if !ok {
return
}
agentID, ok := getAgentID(w, r)
if !ok {
return
}
switch user := reqUser.(type) {
case *thirdparty.User:
role := user.Role()
if role == thirdparty.RoleReader || role == thirdparty.RoleWriter {
h.ServeHTTP(w, r)
return
}
case *agent.User:
if user.Agent().ID == agentID {
h.ServeHTTP(w, r)
return
}
default:
logUnexpectedUserType(r.Context(), reqUser)
}
forbidden(w, r)
}
return http.HandlerFunc(fn)
}
func assertRequestUser(w http.ResponseWriter, r *http.Request) (auth.User, bool) {
ctx := r.Context()
user, err := auth.CtxUser(ctx)
if err != nil {
2023-10-13 12:30:52 +02:00
err = errors.WithStack(err)
2023-10-19 22:09:18 +02:00
logger.Error(ctx, "could not retrieve user", logger.CapturedE(err))
2023-03-13 10:44:58 +01:00
forbidden(w, r)
return nil, false
}
2024-02-26 18:20:40 +01:00
if user == nil || user.Tenant() == "" {
2023-03-13 10:44:58 +01:00
forbidden(w, r)
return nil, false
}
return user, true
}
2024-02-27 09:56:15 +01:00
func (m *Mount) assertTenantOwns(w http.ResponseWriter, r *http.Request, agentID datastore.AgentID) bool {
2024-02-26 18:20:40 +01:00
ctx := r.Context()
user, ok := assertRequestUser(w, r)
if !ok {
return false
}
2024-02-27 09:56:15 +01:00
agent, err := m.agentRepo.Get(ctx, agentID)
2024-02-26 18:20:40 +01:00
if err != nil {
err = errors.WithStack(err)
logger.Error(ctx, "could not get agent", logger.CapturedE(err))
api.ErrorResponse(w, http.StatusInternalServerError, ErrCodeUnknownError, nil)
}
if agent.TenantID != nil && *agent.TenantID == user.Tenant() {
return true
}
api.ErrorResponse(w, http.StatusForbidden, ErrCodeForbidden, nil)
return false
}
2023-03-13 10:44:58 +01:00
func forbidden(w http.ResponseWriter, r *http.Request) {
logger.Warn(r.Context(), "forbidden", logger.F("path", r.URL.Path))
api.ErrorResponse(w, http.StatusForbidden, ErrCodeForbidden, nil)
}
func logUnexpectedUserType(ctx context.Context, user auth.User) {
2023-10-13 12:30:52 +02:00
logger.Warn(
2023-03-13 10:44:58 +01:00
ctx, "unexpected user type",
logger.F("subject", user.Subject()),
logger.F("type", fmt.Sprintf("%T", user)),
)
}