2023-06-22 06:29:45 -05:00
|
|
|
package common
|
|
|
|
|
|
|
|
import (
|
|
|
|
"net/http"
|
2023-07-10 11:40:14 -05:00
|
|
|
"net/url"
|
2023-07-07 11:27:10 -05:00
|
|
|
"strconv"
|
2023-06-22 06:29:45 -05:00
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/gorilla/mux"
|
2023-07-07 11:27:10 -05:00
|
|
|
"github.com/gorilla/sessions"
|
2023-06-22 06:29:45 -05:00
|
|
|
jsoniter "github.com/json-iterator/go"
|
|
|
|
|
|
|
|
"zotregistry.io/zot/pkg/api/constants"
|
|
|
|
apiErr "zotregistry.io/zot/pkg/api/errors"
|
2023-07-07 11:27:10 -05:00
|
|
|
"zotregistry.io/zot/pkg/log"
|
2023-06-22 06:29:45 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
func AllowedMethods(methods ...string) []string {
|
|
|
|
return append(methods, http.MethodOptions)
|
|
|
|
}
|
|
|
|
|
|
|
|
func AddExtensionSecurityHeaders() mux.MiddlewareFunc { //nolint:varnamelen
|
|
|
|
return func(next http.Handler) http.Handler {
|
|
|
|
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
|
|
|
resp.Header().Set("X-Content-Type-Options", "nosniff")
|
|
|
|
|
|
|
|
next.ServeHTTP(resp, req)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func ACHeadersHandler(allowedMethods ...string) mux.MiddlewareFunc {
|
|
|
|
headerValue := strings.Join(allowedMethods, ",")
|
|
|
|
|
|
|
|
return func(next http.Handler) http.Handler {
|
|
|
|
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
|
|
|
resp.Header().Set("Access-Control-Allow-Methods", headerValue)
|
2023-07-07 11:27:10 -05:00
|
|
|
resp.Header().Set("Access-Control-Allow-Headers", "Authorization,content-type,"+constants.SessionClientHeaderName)
|
|
|
|
resp.Header().Set("Access-Control-Allow-Credentials", "true")
|
2023-06-22 06:29:45 -05:00
|
|
|
|
|
|
|
if req.Method == http.MethodOptions {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
next.ServeHTTP(resp, req)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-07 11:27:10 -05:00
|
|
|
func AuthzFail(w http.ResponseWriter, r *http.Request, realm string, delay int) {
|
2023-06-22 06:29:45 -05:00
|
|
|
time.Sleep(time.Duration(delay) * time.Second)
|
2023-07-07 11:27:10 -05:00
|
|
|
|
|
|
|
// don't send auth headers if request is coming from UI
|
|
|
|
if r.Header.Get(constants.SessionClientHeaderName) != constants.SessionClientHeaderValue {
|
|
|
|
if realm == "" {
|
|
|
|
realm = "Authorization Required"
|
|
|
|
}
|
|
|
|
|
|
|
|
realm = "Basic realm=" + strconv.Quote(realm)
|
|
|
|
|
|
|
|
w.Header().Set("WWW-Authenticate", realm)
|
|
|
|
}
|
|
|
|
|
2023-06-22 06:29:45 -05:00
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
WriteJSON(w, http.StatusForbidden, apiErr.NewErrorList(apiErr.NewError(apiErr.DENIED)))
|
|
|
|
}
|
|
|
|
|
|
|
|
func WriteJSON(response http.ResponseWriter, status int, data interface{}) {
|
|
|
|
json := jsoniter.ConfigCompatibleWithStandardLibrary
|
|
|
|
|
|
|
|
body, err := json.Marshal(data)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
WriteData(response, status, constants.DefaultMediaType, body)
|
|
|
|
}
|
|
|
|
|
|
|
|
func WriteData(w http.ResponseWriter, status int, mediaType string, data []byte) {
|
|
|
|
w.Header().Set("Content-Type", mediaType)
|
|
|
|
w.WriteHeader(status)
|
|
|
|
_, _ = w.Write(data)
|
|
|
|
}
|
2023-07-07 11:27:10 -05:00
|
|
|
|
2023-07-10 11:40:14 -05:00
|
|
|
func QueryHasParams(values url.Values, params []string) bool {
|
|
|
|
for _, param := range params {
|
|
|
|
if !values.Has(param) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2023-07-07 11:27:10 -05:00
|
|
|
/*
|
|
|
|
GetAuthUserFromRequestSession returns identity
|
|
|
|
and auth status if on the request's cookie session is a logged in user.
|
|
|
|
*/
|
|
|
|
func GetAuthUserFromRequestSession(cookieStore sessions.Store, request *http.Request, log log.Logger,
|
|
|
|
) (string, bool) {
|
|
|
|
session, err := cookieStore.Get(request, "session")
|
|
|
|
if err != nil {
|
|
|
|
log.Error().Err(err).Msg("can not decode existing session")
|
|
|
|
// expired cookie, no need to return err
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
|
|
|
|
// at this point we should have a session set on cookie.
|
|
|
|
// if created in the earlier Get() call then user is not logged in with sessions.
|
|
|
|
if session.IsNew {
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
|
|
|
|
authenticated := session.Values["authStatus"]
|
|
|
|
if authenticated != true {
|
|
|
|
log.Error().Msg("can not get `user` session value")
|
|
|
|
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
|
|
|
|
identity, ok := session.Values["user"].(string)
|
|
|
|
if !ok {
|
|
|
|
log.Error().Msg("can not get `user` session value")
|
|
|
|
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
|
|
|
|
return identity, true
|
|
|
|
}
|