// Copyright 2023 Harness, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package middleware import ( "context" "fmt" "net/http" "strings" "github.com/harness/gitness/app/api/render" "github.com/harness/gitness/app/api/request" "github.com/harness/gitness/app/auth" "github.com/harness/gitness/app/jwt" "github.com/harness/gitness/registry/app/api/handler/oci" registryauth "github.com/harness/gitness/registry/app/auth" "github.com/rs/zerolog/log" ) func OciCheckAuth(url string) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() session, _ := request.AuthSessionFrom(ctx) if session.Principal == auth.AnonymousPrincipal { scope := getScope(r) returnUnauthorised(ctx, w, url, scope) return } next.ServeHTTP(w, r) }, ) } } // BlockNonOciSourceToken blocks any request that doesn't have AccessPermissionMetadata. func BlockNonOciSourceToken(url string) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() if session, oks := request.AuthSessionFrom(ctx); oks { if metadata, okt := session.Metadata.(*auth.AccessPermissionMetadata); !okt || metadata.AccessPermissions.Source != jwt.OciSource { log.Ctx(ctx).Warn(). Msg("blocking request - non OCI source tokens are not allowed for usage with oci endpoints") scope := getScope(r) returnUnauthorised(ctx, w, url, scope) return } } next.ServeHTTP(w, r) }, ) } } func CheckAuth() func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() session, _ := request.AuthSessionFrom(ctx) if session.Principal == auth.AnonymousPrincipal { render.Unauthorized(ctx, w) return } next.ServeHTTP(w, r) }, ) } } func getRefsFromName(name string) (spaceRef, repoRef string) { name = strings.Trim(name, "/") refs := strings.Split(name, "/") spaceRef = refs[0] repoRef = refs[1] return } func getScope(r *http.Request) string { var scope string path := r.URL.Path if path != "/v2/" && path != "/v2/token" { paramMap := oci.GetQueryParamMap(r.URL.Query()) rootIdentifier, registryIdentifier, _, _, _, _ := oci.ExtractPathVars(path, paramMap) var access []registryauth.Access access = registryauth.AppendAccess(access, r.Method, rootIdentifier, registryIdentifier) if fromRepo := r.FormValue("from"); fromRepo != "" { space, repoName := getRefsFromName(fromRepo) access = registryauth.AppendAccess(access, http.MethodGet, space, repoName) } scope = registryauth.NewAccessSet(access...).ScopeParam() } return scope } func returnUnauthorised(ctx context.Context, w http.ResponseWriter, url string, scope string) { header := fmt.Sprintf(`Bearer realm="%s", service="gitness-registry"`, url) if scope != "" { header = fmt.Sprintf(`%s, scope="%s"`, header, scope) } w.Header().Set("WWW-Authenticate", header) render.Unauthorized(ctx, w) }