1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package downscope
16
17 import (
18 "context"
19 "io"
20 "net/http"
21 "net/http/httptest"
22 "testing"
23
24 "cloud.google.com/go/auth"
25 )
26
27 var (
28 standardReqBody = "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange&options=%7B%22accessBoundary%22%3A%7B%22accessBoundaryRules%22%3A%5B%7B%22availableResource%22%3A%22test1%22%2C%22availablePermissions%22%3A%5B%22Perm1%22%2C%22Perm2%22%5D%7D%5D%7D%7D&requested_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token&subject_token=token_base&subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token"
29 standardRespBody = `{"access_token":"fake_token","expires_in":42,"token_type":"Bearer"}`
30 )
31
32 func staticCredentials(tok string) *auth.Credentials {
33 return auth.NewCredentials(&auth.CredentialsOptions{
34 TokenProvider: staticTokenProvider(tok),
35 })
36 }
37
38 type staticTokenProvider string
39
40 func (s staticTokenProvider) Token(context.Context) (*auth.Token, error) {
41 return &auth.Token{Value: string(s)}, nil
42 }
43
44 func TestNewTokenProvider(t *testing.T) {
45 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
46 if r.Method != "POST" {
47 t.Errorf("Unexpected request method, %v is found", r.Method)
48 }
49 if r.URL.String() != "/" {
50 t.Errorf("Unexpected request URL, %v is found", r.URL)
51 }
52 body, err := io.ReadAll(r.Body)
53 if err != nil {
54 t.Fatalf("Failed to read request body: %v", err)
55 }
56 if got, want := string(body), standardReqBody; got != want {
57 t.Errorf("Unexpected exchange payload: got %v but want %v,", got, want)
58 }
59 w.Header().Set("Content-Type", "application/json")
60 w.Write([]byte(standardRespBody))
61
62 }))
63 defer ts.Close()
64 creds, err := NewCredentials(&Options{
65 Credentials: staticCredentials("token_base"),
66 Rules: []AccessBoundaryRule{
67 {
68 AvailableResource: "test1",
69 AvailablePermissions: []string{"Perm1", "Perm2"},
70 },
71 },
72 })
73 if err != nil {
74 t.Fatalf("NewTokenProvider() = %v", err)
75 }
76
77 creds.TokenProvider.(*downscopedTokenProvider).identityBindingEndpoint = ts.URL
78
79 tok, err := creds.Token(context.Background())
80 if err != nil {
81 t.Fatalf("Token failed with error: %v", err)
82 }
83 if want := "fake_token"; tok.Value != want {
84 t.Fatalf("got %v, want %v", tok.Value, want)
85 }
86 }
87
88 func TestNewCredentials_Validations(t *testing.T) {
89 tests := []struct {
90 name string
91 opts *Options
92 }{
93 {
94 name: "no opts",
95 opts: nil,
96 },
97 {
98 name: "no provider",
99 opts: &Options{},
100 },
101 {
102 name: "no rules",
103 opts: &Options{
104 Credentials: staticCredentials("token_base"),
105 },
106 },
107 {
108 name: "too many rules",
109 opts: &Options{
110 Credentials: staticCredentials("token_base"),
111 Rules: []AccessBoundaryRule{{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}},
112 },
113 },
114 {
115 name: "no resource",
116 opts: &Options{
117 Credentials: staticCredentials("token_base"),
118 Rules: []AccessBoundaryRule{{}},
119 },
120 },
121 {
122 name: "no perm",
123 opts: &Options{
124 Credentials: staticCredentials("token_base"),
125 Rules: []AccessBoundaryRule{{
126 AvailableResource: "resource",
127 }},
128 },
129 },
130 }
131 for _, test := range tests {
132 t.Run(test.name, func(t *testing.T) {
133 if _, err := NewCredentials(test.opts); err == nil {
134 t.Fatal("want non-nil err")
135 }
136 })
137 }
138 }
139
140 func TestOptions_UniverseDomain(t *testing.T) {
141 tests := []struct {
142 universeDomain string
143 want string
144 }{
145 {"", "https://sts.googleapis.com/v1/token"},
146 {"googleapis.com", "https://sts.googleapis.com/v1/token"},
147 {"example.com", "https://sts.example.com/v1/token"},
148 }
149 for _, tt := range tests {
150 c := Options{
151 UniverseDomain: tt.universeDomain,
152 }
153 if got := c.identityBindingEndpoint(); got != tt.want {
154 t.Errorf("got %q, want %q", got, tt.want)
155 }
156 }
157 }
158
View as plain text