@@ -15,6 +15,7 @@ import (
1515 "github.com/docker/distribution/reference"
1616 "github.com/docker/distribution/registry/api/errcode"
1717 "github.com/docker/distribution/registry/api/v2"
18+ "github.com/docker/distribution/registry/auth"
1819 "github.com/gorilla/handlers"
1920)
2021
@@ -269,6 +270,12 @@ func (imh *imageManifestHandler) PutImageManifest(w http.ResponseWriter, r *http
269270 if imh .Tag != "" {
270271 options = append (options , distribution .WithTag (imh .Tag ))
271272 }
273+
274+ if err := imh .applyResourcePolicy (manifest ); err != nil {
275+ imh .Errors = append (imh .Errors , err )
276+ return
277+ }
278+
272279 _ , err = manifests .Put (imh , manifest , options ... )
273280 if err != nil {
274281 // TODO(stevvooe): These error handling switches really need to be
@@ -339,6 +346,73 @@ func (imh *imageManifestHandler) PutImageManifest(w http.ResponseWriter, r *http
339346 w .WriteHeader (http .StatusCreated )
340347}
341348
349+ // applyResourcePolicy checks whether the resource class matches what has
350+ // been authorized and allowed by the policy configuration.
351+ func (imh * imageManifestHandler ) applyResourcePolicy (manifest distribution.Manifest ) error {
352+ allowedClasses := imh .App .Config .Policy .Repository .Classes
353+ if len (allowedClasses ) == 0 {
354+ return nil
355+ }
356+
357+ var class string
358+ switch m := manifest .(type ) {
359+ case * schema1.SignedManifest :
360+ class = "image"
361+ case * schema2.DeserializedManifest :
362+ switch m .Config .MediaType {
363+ case schema2 .MediaTypeConfig :
364+ class = "image"
365+ case schema2 .MediaTypePluginConfig :
366+ class = "plugin"
367+ default :
368+ message := fmt .Sprintf ("unknown manifest class for %s" , m .Config .MediaType )
369+ return errcode .ErrorCodeDenied .WithMessage (message )
370+ }
371+ }
372+
373+ if class == "" {
374+ return nil
375+ }
376+
377+ // Check to see if class is allowed in registry
378+ var allowedClass bool
379+ for _ , c := range allowedClasses {
380+ if class == c {
381+ allowedClass = true
382+ break
383+ }
384+ }
385+ if ! allowedClass {
386+ message := fmt .Sprintf ("registry does not allow %s manifest" , class )
387+ return errcode .ErrorCodeDenied .WithMessage (message )
388+ }
389+
390+ resources := auth .AuthorizedResources (imh )
391+ n := imh .Repository .Named ().Name ()
392+
393+ var foundResource bool
394+ for _ , r := range resources {
395+ if r .Name == n {
396+ if r .Class == "" {
397+ r .Class = "image"
398+ }
399+ if r .Class == class {
400+ return nil
401+ }
402+ foundResource = true
403+ }
404+ }
405+
406+ // resource was found but no matching class was found
407+ if foundResource {
408+ message := fmt .Sprintf ("repository not authorized for %s manifest" , class )
409+ return errcode .ErrorCodeDenied .WithMessage (message )
410+ }
411+
412+ return nil
413+
414+ }
415+
342416// DeleteImageManifest removes the manifest with the given digest from the registry.
343417func (imh * imageManifestHandler ) DeleteImageManifest (w http.ResponseWriter , r * http.Request ) {
344418 ctxu .GetLogger (imh ).Debug ("DeleteImageManifest" )
0 commit comments