...

Source file src/cuelabs.dev/go/oci/ociregistry/ociclient/auth_test.go

Documentation: cuelabs.dev/go/oci/ociregistry/ociclient

     1  package ociclient
     2  
     3  import (
     4  	"context"
     5  	"net/http"
     6  	"net/http/httptest"
     7  	"net/url"
     8  	"strings"
     9  	"testing"
    10  
    11  	"cuelabs.dev/go/oci/ociregistry"
    12  	"cuelabs.dev/go/oci/ociregistry/internal/exp/maps"
    13  	"cuelabs.dev/go/oci/ociregistry/ociauth"
    14  	"cuelabs.dev/go/oci/ociregistry/ocimem"
    15  	"cuelabs.dev/go/oci/ociregistry/ociserver"
    16  	"github.com/go-quicktest/qt"
    17  	"github.com/opencontainers/go-digest"
    18  )
    19  
    20  func TestAuthScopes(t *testing.T) {
    21  
    22  	// Test that we're passing the expected authorization scopes to the various parts of the API.
    23  	// All the call semantics themselves are tested elsewhere, but we want to be
    24  	// sure that we're passing the right required auth scopes to the authorizer.
    25  
    26  	srv := httptest.NewServer(ociserver.New(ocimem.New(), nil))
    27  	defer srv.Close()
    28  	srvURL, _ := url.Parse(srv.URL)
    29  
    30  	assertScope := func(scope string, f func(ctx context.Context, r ociregistry.Interface)) {
    31  		assertAuthScope(t, srvURL.Host, scope, f)
    32  	}
    33  
    34  	assertScope("repository:foo/bar:pull", func(ctx context.Context, r ociregistry.Interface) {
    35  		r.GetBlob(ctx, "foo/bar", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
    36  	})
    37  	assertScope("repository:foo/bar:pull", func(ctx context.Context, r ociregistry.Interface) {
    38  		r.GetBlobRange(ctx, "foo/bar", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 100, 200)
    39  	})
    40  	assertScope("repository:foo/bar:pull", func(ctx context.Context, r ociregistry.Interface) {
    41  		r.GetManifest(ctx, "foo/bar", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
    42  	})
    43  	assertScope("repository:foo/bar:pull", func(ctx context.Context, r ociregistry.Interface) {
    44  		r.GetTag(ctx, "foo/bar", "sometag")
    45  	})
    46  	assertScope("repository:foo/bar:pull", func(ctx context.Context, r ociregistry.Interface) {
    47  		r.ResolveBlob(ctx, "foo/bar", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
    48  	})
    49  	assertScope("repository:foo/bar:pull", func(ctx context.Context, r ociregistry.Interface) {
    50  		r.ResolveManifest(ctx, "foo/bar", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
    51  	})
    52  	assertScope("repository:foo/bar:pull", func(ctx context.Context, r ociregistry.Interface) {
    53  		r.ResolveTag(ctx, "foo/bar", "sometag")
    54  	})
    55  	assertScope("repository:foo/bar:push", func(ctx context.Context, r ociregistry.Interface) {
    56  		r.PushBlob(ctx, "foo/bar", ociregistry.Descriptor{
    57  			MediaType: "application/json",
    58  			Digest:    "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
    59  			Size:      3,
    60  		}, strings.NewReader("foo"))
    61  	})
    62  	assertScope("repository:foo/bar:push", func(ctx context.Context, r ociregistry.Interface) {
    63  		w, err := r.PushBlobChunked(ctx, "foo/bar", 0)
    64  		qt.Assert(t, qt.IsNil(err))
    65  		w.Write([]byte("foo"))
    66  		w.Close()
    67  
    68  		id := w.ID()
    69  		w, err = r.PushBlobChunkedResume(ctx, "foo/bar", id, 3, 0)
    70  		qt.Assert(t, qt.IsNil(err))
    71  		w.Write([]byte("bar"))
    72  		_, err = w.Commit(digest.FromString("foobar"))
    73  		qt.Assert(t, qt.IsNil(err))
    74  	})
    75  	assertScope("repository:x/y:pull repository:z/w:push", func(ctx context.Context, r ociregistry.Interface) {
    76  		r.MountBlob(ctx, "x/y", "z/w", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
    77  	})
    78  	assertScope("repository:foo/bar:push", func(ctx context.Context, r ociregistry.Interface) {
    79  		r.PushManifest(ctx, "foo/bar", "sometag", []byte("something"), "application/json")
    80  	})
    81  	assertScope("repository:foo/bar:push", func(ctx context.Context, r ociregistry.Interface) {
    82  		r.DeleteBlob(ctx, "foo/bar", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
    83  	})
    84  	assertScope("repository:foo/bar:push", func(ctx context.Context, r ociregistry.Interface) {
    85  		r.DeleteManifest(ctx, "foo/bar", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
    86  	})
    87  	assertScope("repository:foo/bar:push", func(ctx context.Context, r ociregistry.Interface) {
    88  		r.DeleteTag(ctx, "foo/bar", "sometag")
    89  	})
    90  	assertScope("registry:catalog:*", func(ctx context.Context, r ociregistry.Interface) {
    91  		ociregistry.All(r.Repositories(ctx, ""))
    92  	})
    93  	assertScope("repository:foo/bar:pull", func(ctx context.Context, r ociregistry.Interface) {
    94  		ociregistry.All(r.Tags(ctx, "foo/bar", ""))
    95  	})
    96  	assertScope("repository:foo/bar:pull", func(ctx context.Context, r ociregistry.Interface) {
    97  		ociregistry.All(r.Referrers(ctx, "foo/bar", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", ""))
    98  	})
    99  }
   100  
   101  // assertAuthScope asserts that the given function makes a client request with the
   102  // given scope to the given URL.
   103  func assertAuthScope(t *testing.T, host string, scope string, f func(ctx context.Context, r ociregistry.Interface)) {
   104  	requestedScopes := make(map[string]bool)
   105  
   106  	// Check that the context is passed through with values intact.
   107  	type foo struct{}
   108  	ctx := context.WithValue(context.Background(), foo{}, true)
   109  
   110  	client, err := New(host, &Options{
   111  		Insecure: true,
   112  		Transport: transportFunc(func(req *http.Request) (*http.Response, error) {
   113  			ctx := req.Context()
   114  			qt.Check(t, qt.Equals(ctx.Value(foo{}), true))
   115  			scope := ociauth.RequestInfoFromContext(ctx).RequiredScope
   116  			requestedScopes[scope.Canonical().String()] = true
   117  			return http.DefaultTransport.RoundTrip(req)
   118  		}),
   119  	})
   120  	qt.Assert(t, qt.IsNil(err))
   121  	f(ctx, client)
   122  	qt.Assert(t, qt.HasLen(requestedScopes, 1))
   123  	t.Logf("requested scopes: %v", requestedScopes)
   124  	qt.Assert(t, qt.Equals(maps.Keys(requestedScopes)[0], scope))
   125  }
   126  
   127  type transportFunc func(req *http.Request) (*http.Response, error)
   128  
   129  func (f transportFunc) RoundTrip(req *http.Request) (*http.Response, error) {
   130  	return f(req)
   131  }
   132  

View as plain text