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
23
24
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
102
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
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