Skip to content

Commit

Permalink
add permHandler
Browse files Browse the repository at this point in the history
  • Loading branch information
zyxkad committed Jun 28, 2024
1 parent e17d8c7 commit f2dbcbf
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 103 deletions.
2 changes: 1 addition & 1 deletion api/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const (
SubscribePerm
// LogPerm allows the user to view non-debug logs & download access logs
LogPerm
// DebugPerm allows the user to access debug settings and download debug logs
// DebugPerm allows the user to access debug settings & download debug logs
DebugPerm
// FullConfigPerm allows the user to access all config values
FullConfigPerm
Expand Down
123 changes: 25 additions & 98 deletions api/v0/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,22 +60,27 @@ func apiGetClientId(req *http.Request) (id string) {
}

type Handler struct {
handler *utils.HttpMiddleWareHandler
router *http.ServeMux
userManager api.UserManager
tokenManager api.TokenManager
subManager api.SubscriptionManager
handler *utils.HttpMiddleWareHandler
router *http.ServeMux
users api.UserManager
tokens api.TokenManager
subscriptions api.SubscriptionManager
}

var _ http.Handler = (*Handler)(nil)

func NewHandler(verifier TokenVerifier, subManager api.SubscriptionManager) *Handler {
func NewHandler(
users api.UserManager,
tokenManager api.TokenManager,
subManager api.SubscriptionManager,
) *Handler {
mux := http.NewServeMux()
h := &Handler{
router: mux,
handler: utils.NewHttpMiddleWareHandler(mux),
verifier: verifier,
subManager: subManager,
router: mux,
handler: utils.NewHttpMiddleWareHandler(mux),
users: users,
tokens: tokenManager,
subscriptions: subManager,
}
h.buildRoute()
h.handler.Use(cliIdMiddleWare)
Expand Down Expand Up @@ -107,115 +112,37 @@ func (h *Handler) buildRoute() {
mux.Handle("/logout", authHandleFunc(h.routeLogout))

mux.HandleFunc("/log.io", h.routeLogIO)
mux.Handle("/pprof", authHandleFunc(h.routePprof))
mux.Handle("/pprof", permHandleFunc(api.DebugPerm, h.routePprof))
mux.HandleFunc("/subscribeKey", h.routeSubscribeKey)
mux.Handle("/subscribe", authHandle(&utils.HttpMethodHandler{
mux.Handle("/subscribe", permHandleFunc(api.SubscribePerm, &utils.HttpMethodHandler{
Get: h.routeSubscribeGET,
Post: h.routeSubscribePOST,
Delete: h.routeSubscribeDELETE,
}))
mux.Handle("/subscribe_email", authHandle(&utils.HttpMethodHandler{
mux.Handle("/subscribe_email", permHandleFunc(api.SubscribePerm, &utils.HttpMethodHandler{
Get: h.routeSubscribeEmailGET,
Post: h.routeSubscribeEmailPOST,
Patch: h.routeSubscribeEmailPATCH,
Delete: h.routeSubscribeEmailDELETE,
}))
mux.Handle("/webhook", authHandle(&utils.HttpMethodHandler{
mux.Handle("/webhook", permHandleFunc(api.SubscribePerm, &utils.HttpMethodHandler{
Get: h.routeWebhookGET,
Post: h.routeWebhookPOST,
Patch: h.routeWebhookPATCH,
Delete: h.routeWebhookDELETE,
}))

mux.Handle("/log_files", authHandleFunc(h.routeLogFiles))
mux.Handle("/log_file/", authHandle(http.StripPrefix("/log_file/", (http.HandlerFunc)(h.routeLogFile))))
mux.Handle("/log_files", permHandleFunc(api.LogPerm, h.routeLogFiles))
mux.Handle("/log_file/", permHandle(api.LogPerm, http.StripPrefix("/log_file/", (http.HandlerFunc)(h.routeLogFile))))

mux.Handle("/configure/cluster", authHandleFunc(h.routeConfigureCluster))
mux.Handle("/configure/cluster", permHandleFunc(api.ClusterPerm, h.routeConfigureCluster))
}

func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
h.handler.ServeHTTP(rw, req)
}

func cliIdMiddleWare(rw http.ResponseWriter, req *http.Request, next http.Handler) {
var id string
if cid, _ := req.Cookie(clientIdCookieName); cid != nil {
id = cid.Value
} else {
var err error
id, err = utils.GenRandB64(16)
if err != nil {
http.Error(rw, "cannot generate random number", http.StatusInternalServerError)
return
}
http.SetCookie(rw, &http.Cookie{
Name: clientIdCookieName,
Value: id,
Expires: time.Now().Add(time.Hour * 24 * 365 * 16),
Secure: true,
HttpOnly: true,
})
}
req = req.WithContext(context.WithValue(req.Context(), clientIdKey, utils.AsSha256(id)))
next.ServeHTTP(rw, req)
}

func (h *Handler) authMiddleWare(rw http.ResponseWriter, req *http.Request, next http.Handler) {
cli := apiGetClientId(req)

ctx := req.Context()

var (
id string
uid string
err error
)
if req.Method == http.MethodGet {
if tk := req.URL.Query().Get("_t"); tk != "" {
path := GetRequestRealPath(req)
if id, uid, err = h.verifier.verifyAPIToken(cli, tk, path, req.URL.Query()); err == nil {
ctx = context.WithValue(ctx, tokenTypeKey, tokenTypeAPI)
}
}
}
if id == "" {
auth := req.Header.Get("Authorization")
tk, ok := strings.CutPrefix(auth, "Bearer ")
if !ok {
if err == nil {
err = ErrUnsupportAuthType
}
} else if id, uid, err = h.verifier.VerifyAuthToken(cli, tk); err != nil {
id = ""
} else {
ctx = context.WithValue(ctx, tokenTypeKey, tokenTypeAuth)
}
}
if id != "" {
ctx = context.WithValue(ctx, loggedUserKey, uid)
ctx = context.WithValue(ctx, tokenIdKey, id)
req = req.WithContext(ctx)
}
next.ServeHTTP(rw, req)
}

func authHandle(next http.Handler) http.Handler {
return (http.HandlerFunc)(func(rw http.ResponseWriter, req *http.Request) {
if req.Context().Value(tokenTypeKey) == nil {
writeJson(rw, http.StatusUnauthorized, Map{
"error": "403 Unauthorized",
})
return
}
next.ServeHTTP(rw, req)
})
}

func authHandleFunc(next http.HandlerFunc) http.Handler {
return authHandle(next)
}

func (cr *Cluster) routePing(rw http.ResponseWriter, req *http.Request) {
func (h *Handler) routePing(rw http.ResponseWriter, req *http.Request) {
if req.Method != http.MethodGet {
errorMethodNotAllowed(rw, req, http.MethodGet)
return
Expand Down Expand Up @@ -331,13 +258,13 @@ func (h *Handler) routeLogin(rw http.ResponseWriter, req *http.Request) {
return
}

if err := h.verifier.VerifyChallengeToken(cli, "login", data.Challenge); err != nil {
if err := h.tokens.VerifyChallengeToken(cli, "login", data.Challenge); err != nil {
writeJson(rw, http.StatusUnauthorized, Map{
"error": "Invalid challenge",
})
return
}
if err := h.verifier.VerifyUserPassword(data.User, func(password string) bool {
if err := h.tokens.VerifyUserPassword(data.User, func(password string) bool {
expectSignature := utils.HMACSha256HexBytes(password, data.Challenge)
return subtle.ConstantTimeCompare(expectSignature, ([]byte)(data.Signature)) == 0
}); err != nil {
Expand Down
103 changes: 100 additions & 3 deletions api/v0/api_token.go → api/v0/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,108 @@ func getRequestTokenType(req *http.Request) string {
return ""
}

func getLoggedUser(req *http.Request) string {
if user, ok := req.Context().Value(loggedUserKey).(string); ok {
func getLoggedUser(req *http.Request) *api.User {
if user, ok := req.Context().Value(loggedUserKey).(*api.User); ok {
return user
}
return ""
return nil
}

func cliIdMiddleWare(rw http.ResponseWriter, req *http.Request, next http.Handler) {
var id string
if cid, _ := req.Cookie(clientIdCookieName); cid != nil {
id = cid.Value
} else {
var err error
id, err = utils.GenRandB64(16)
if err != nil {
http.Error(rw, "cannot generate random number", http.StatusInternalServerError)
return
}
http.SetCookie(rw, &http.Cookie{
Name: clientIdCookieName,
Value: id,
Expires: time.Now().Add(time.Hour * 24 * 365 * 16),
Secure: true,
HttpOnly: true,
})
}
req = req.WithContext(context.WithValue(req.Context(), clientIdKey, utils.AsSha256(id)))
next.ServeHTTP(rw, req)
}

func (h *Handler) authMiddleWare(rw http.ResponseWriter, req *http.Request, next http.Handler) {
cli := apiGetClientId(req)

ctx := req.Context()

var (
typ string
id string
uid string
err error
)
if req.Method == http.MethodGet {
if tk := req.URL.Query().Get("_t"); tk != "" {
path := GetRequestRealPath(req)
if id, uid, err = h.tokens.VerifyAPIToken(cli, tk, path, req.URL.Query()); err == nil {
typ = tokenTypeAPI
}
}
}
if id == "" {
auth := req.Header.Get("Authorization")
tk, ok := strings.CutPrefix(auth, "Bearer ")
if !ok {
if err == nil {
err = ErrUnsupportAuthType
}
} else if id, uid, err = h.tokens.VerifyAuthToken(cli, tk); err == nil {
typ = tokenTypeAuth
}
}
if typ != "" {
user, err := h.users.GetUser(uid)
if err == nil {
ctx = context.WithValue(ctx, tokenTypeKey, typ)
ctx = context.WithValue(ctx, loggedUserKey, user)
ctx = context.WithValue(ctx, tokenIdKey, id)
req = req.WithContext(ctx)
}
}
next.ServeHTTP(rw, req)
}

func authHandle(next http.Handler) http.Handler {
return permHandle(api.BasicPerm, next)
}

func authHandleFunc(next http.HandlerFunc) http.Handler {
return authHandle(next)
}

func permHandle(perm api.PermissionFlag, next http.Handler) http.Handler {
perm |= api.BasicPerm
return (http.HandlerFunc)(func(rw http.ResponseWriter, req *http.Request) {
user := getLoggedUser(req)
if user == nil {
writeJson(rw, http.StatusUnauthorized, Map{
"error": "403 Unauthorized",
})
return
}
if user.Permissions & perm != perm {
writeJson(rw, http.StatusForbidden, Map{
"error": "Permission denied",
})
return
}
next.ServeHTTP(rw, req)
})
}

func permHandleFunc(perm api.PermissionFlag, next http.HandlerFunc) http.Handler {
return permHandle(perm, next)
}

var (
Expand Down
10 changes: 9 additions & 1 deletion api/v0/configure_cluster.go → api/v0/configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@

package v0

func (h *Handler) apiConfigureCluster() {
import (
"net/http"
)

func (h *Handler) apiConfigureCluster(rw http.ResponseWriter, req *http.Request) {
//
}

func (h *Handler) apiConfigureStorage(rw http.ResponseWriter, req *http.Request) {
//
}

0 comments on commit f2dbcbf

Please sign in to comment.