diff --git a/docs/news/unreleased/weldr-image-type-denylist.md b/docs/news/unreleased/weldr-image-type-denylist.md index d56b9e079..14845f6eb 100644 --- a/docs/news/unreleased/weldr-image-type-denylist.md +++ b/docs/news/unreleased/weldr-image-type-denylist.md @@ -1,9 +1,9 @@ # Weldr API: introduce the ablility to limit exposed Image Types by configuration Extend Weldr API to accept a map of distribution-specific lists of denied -image types, which should not be exposed via API. A special name `*` can be -used to match any Distribution or any Image Type. This functionality is needed -to not expose image types which can't be successfully built outside +image types, which should not be exposed via API. It is allowed to use +globing patterns as Distribution and Image Type names. This functionality +is needed to not expose image types which can't be successfully built outside of Red Hat VPN. The list of denied Image Types is defined in `osbuild-composer` configuration, diff --git a/internal/weldr/api.go b/internal/weldr/api.go index 32d346fa0..c9f93ec99 100644 --- a/internal/weldr/api.go +++ b/internal/weldr/api.go @@ -24,6 +24,7 @@ import ( "time" "github.com/BurntSushi/toml" + "github.com/gobwas/glob" "github.com/google/uuid" "github.com/julienschmidt/httprouter" @@ -389,27 +390,40 @@ func (api *API) openImageFile(composeId uuid.UUID, compose store.Compose) (io.Re // the distro-specific ImageType Denylist provided to the API from configuration. // If the given ImageType is not allowed the method returns an `false`. // Otherwise `true` is returned. -func (api *API) isImageTypeAllowed(distroName, imageType string) bool { - anyImageType := "*" - anyDistro := "*" - +func (api *API) isImageTypeAllowed(distroName, imageType string) (bool, error) { for deniedDistro, deniedImgTypes := range api.distrosImageTypeDenylist { - if distroName == deniedDistro || deniedDistro == anyDistro { + deniedDistroPattern, err := glob.Compile(deniedDistro) + if err != nil { + // the bool return value here does not have any real meaning + return true, err + } + if deniedDistroPattern.Match(distroName) { for _, deniedImgType := range deniedImgTypes { - if imageType == deniedImgType || deniedImgType == anyImageType { - return false + deniedImageTypePattern, err := glob.Compile(deniedImgType) + if err != nil { + // the bool return value here does not have any real meaning + return true, err + } + + if deniedImageTypePattern.Match(imageType) { + return false, nil } } } } - return true + + return true, nil } // getImageType returns the ImageType for the selected distro // This is necessary because different distros support different image types, and the image // type may have a different package set than other distros. func (api *API) getImageType(distroName, imageType string) (distro.ImageType, error) { - if !api.isImageTypeAllowed(distroName, imageType) { + imgAllowed, err := api.isImageTypeAllowed(distroName, imageType) + if err != nil { + return nil, fmt.Errorf("error while checking if image type is allowed: %v", err) + } + if !imgAllowed { return nil, fmt.Errorf("image type %q for distro %q is denied by configuration", imageType, distroName) } @@ -2522,7 +2536,16 @@ func (api *API) composeTypesHandler(writer http.ResponseWriter, request *http.Re } for _, format := range arch.ListImageTypes() { - if !api.isImageTypeAllowed(distroName, format) { + imgAllowed, err := api.isImageTypeAllowed(distroName, format) + if err != nil { + errors := responseError{ + ID: "InternalError", + Msg: fmt.Sprintf("Error while checking if image type is allowed: %v", err), + } + statusResponseError(writer, http.StatusInternalServerError, errors) + return + } + if !imgAllowed { continue } reply.Types = append(reply.Types, composeType{format, true}) diff --git a/internal/weldr/api_test.go b/internal/weldr/api_test.go index dc7c028c4..c72101543 100644 --- a/internal/weldr/api_test.go +++ b/internal/weldr/api_test.go @@ -1691,6 +1691,26 @@ func TestComposePOST_ImageTypeDenylist(t *testing.T) { expectedComposeLocal, []string{"build_id"}, }, + { + "/api/v1/compose", + fmt.Sprintf(`{"blueprint_name": "test","compose_type": "%s","branch": "master"}`, test_distro.TestImageTypeName), + map[string][]string{fmt.Sprintf("%s*", test_distro.TestDistroName): {fmt.Sprintf("%s*", test_distro.TestImageTypeName)}}, + http.StatusBadRequest, + fmt.Sprintf(`{"status":false,"errors":[{"id":"ComposeError","msg":"Failed to get compose type \"%[1]s\": image type \"%[1]s\" for distro \"%[2]s\" is denied by configuration"}]}`, + test_distro.TestImageTypeName, test_distro.TestDistro2Name), + expectedComposeLocal, + []string{"build_id"}, + }, + { + "/api/v1/compose", + fmt.Sprintf(`{"blueprint_name": "test","compose_type": "%s","branch": "master"}`, test_distro.TestImageType2Name), + map[string][]string{fmt.Sprintf("%s*", test_distro.TestDistroName): {fmt.Sprintf("%s*", test_distro.TestImageTypeName)}}, + http.StatusBadRequest, + fmt.Sprintf(`{"status":false,"errors":[{"id":"ComposeError","msg":"Failed to get compose type \"%[1]s\": image type \"%[1]s\" for distro \"%[2]s\" is denied by configuration"}]}`, + test_distro.TestImageType2Name, test_distro.TestDistro2Name), + expectedComposeLocal, + []string{"build_id"}, + }, } tempdir, err := ioutil.TempDir("", "weldr-tests-")