github.com/verrazzano/verrazzano@v1.7.0/authproxy/src/auth/token_test.go (about) 1 // Copyright (c) 2023, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 4 package auth 5 6 import ( 7 "context" 8 "encoding/base64" 9 "encoding/json" 10 "fmt" 11 "net/http" 12 "testing" 13 14 "github.com/coreos/go-oidc/v3/oidc" 15 "github.com/stretchr/testify/assert" 16 "go.uber.org/zap" 17 ) 18 19 // TestAuthenticateToken tests that tokens are properly processed and validated 20 // GIVEN a request to authenticate a token 21 // WHEN the request is processed 22 // THEN the proper validation result is returned 23 func TestAuthenticateToken(t *testing.T) { 24 validToken := "token-valid" 25 issuer := "test-issuer" 26 authenticator := OIDCAuthenticator{ 27 Log: zap.S(), 28 oidcConfig: &OIDCConfiguration{ 29 ServiceURL: issuer, 30 }, 31 } 32 verifier := newMockVerifier(issuer, validToken) 33 authenticator.verifier.Store(verifier) 34 35 tests := []struct { 36 name string 37 token string 38 expectValidation bool 39 }{ 40 { 41 name: "valid token provided", 42 token: validToken, 43 expectValidation: true, 44 }, 45 { 46 name: "invalid token provided", 47 token: "token-invalid", 48 expectValidation: false, 49 }, 50 } 51 for _, tt := range tests { 52 t.Run(tt.name, func(t *testing.T) { 53 validated, err := authenticator.AuthenticateToken(context.TODO(), tt.token) 54 if tt.expectValidation { 55 assert.NoError(t, err) 56 assert.True(t, validated) 57 return 58 } 59 assert.Error(t, err) 60 assert.False(t, validated) 61 }) 62 } 63 } 64 65 // TestGetTokenFromAuthHeader tests that the token can be extracted from an auth header 66 // GIVEN an auth header 67 // WHEN the bearer token is properly formatted 68 // THEN the token value is returned 69 func TestGetTokenFromAuthHeader(t *testing.T) { 70 tests := []struct { 71 name string 72 authHeader string 73 expectedToken string 74 expectError bool 75 }{ 76 { 77 name: "empty auth header", 78 expectError: true, 79 }, 80 { 81 name: "no token", 82 authHeader: "Bearer", 83 expectError: true, 84 }, 85 { 86 name: "valid token", 87 authHeader: "Bearer token", 88 expectedToken: "token", 89 }, 90 { 91 name: "token with params", 92 authHeader: "Bearer token param1 param2", 93 expectedToken: "token", 94 }, 95 } 96 for _, tt := range tests { 97 t.Run(tt.name, func(t *testing.T) { 98 token, err := getTokenFromAuthHeader(tt.authHeader) 99 if tt.expectError { 100 assert.Error(t, err) 101 return 102 } 103 104 assert.NoError(t, err) 105 assert.Equal(t, tt.expectedToken, token) 106 }) 107 } 108 } 109 110 // TestInitServiceOIDCVerifier tests that the OIDC verifier can be properly initialized 111 func TestInitServiceOIDCVerifier(t *testing.T) { 112 authenticator := OIDCAuthenticator{ 113 Log: zap.S(), 114 } 115 116 // GIVEN a valid configuration 117 // WHEN the service URL is not set 118 // THEN an error is returned 119 err := authenticator.initServiceOIDCVerifier() 120 assert.Error(t, err) 121 122 issuer := "test-issuer" 123 authenticator.oidcConfig = &OIDCConfiguration{ 124 ServiceURL: issuer, 125 } 126 // GIVEN a valid configuration 127 // WHEN the service URL is set 128 // THEN no error is returned 129 err = authenticator.initServiceOIDCVerifier() 130 assert.Error(t, err) 131 132 } 133 134 // TestLoadVerifier tests loading the verifier object from the atomic source 135 func TestLoadVerifier(t *testing.T) { 136 authenticator := OIDCAuthenticator{ 137 Log: zap.S(), 138 } 139 140 // GIVEN an Authenticator object 141 // WHEN the verifier is not set 142 // THEN an error is returned 143 _, err := authenticator.loadVerifier() 144 assert.Error(t, err) 145 146 // GIVEN an Authenticator object 147 // WHEN the verifier is not the correct value 148 // THEN an error is returned 149 authenticator.verifier.Store("incorrect value") 150 _, err = authenticator.loadVerifier() 151 assert.Error(t, err) 152 153 // GIVEN an Authenticator object 154 // WHEN the verifier is correctly set 155 // THEN no error is returned 156 authenticator = OIDCAuthenticator{ 157 Log: zap.S(), 158 } 159 authenticator.verifier.Store(newMockVerifier("", "")) 160 v, err := authenticator.loadVerifier() 161 assert.NoError(t, err) 162 assert.NotNil(t, v) 163 assert.Implements(t, (*verifier)(nil), v) 164 } 165 166 // TestGetImpersonationHeadersFromRequest tests that the impersonation user and groups can be collected from a request 167 func TestGetImpersonationHeadersFromRequest(t *testing.T) { 168 testUser := "user" 169 testGroups := []string{ 170 "group1", 171 "group2", 172 } 173 174 testImp := ImpersonationHeaders{ 175 User: testUser, 176 Groups: testGroups, 177 } 178 179 impJSON, err := json.Marshal(testImp) 180 assert.NoError(t, err) 181 validToken := fmt.Sprintf("info.%s.info", base64.RawURLEncoding.EncodeToString(impJSON)) 182 183 tests := []struct { 184 name string 185 token string 186 expectedUser string 187 expectedGroups []string 188 expectError bool 189 }{ 190 // GIVEN a request with a valid token 191 // WHEN the request is evaluated 192 // THEN the expected users and groups are populated 193 { 194 name: "valid token provided", 195 token: validToken, 196 expectedUser: testUser, 197 expectedGroups: testGroups, 198 }, 199 // GIVEN a request with a bad JWT token 200 // WHEN the request is evaluated 201 // THEN an error is returned 202 { 203 name: "malformed token provided", 204 token: "token-invalid", 205 expectError: true, 206 }, 207 // GIVEN a request with an empty token body 208 // WHEN the request is evaluated 209 // THEN no error is returned 210 { 211 name: "empty token provided", 212 token: fmt.Sprintf("info.%s.info", base64.RawURLEncoding.EncodeToString([]byte("{}"))), 213 }, 214 } 215 for _, tt := range tests { 216 t.Run(tt.name, func(t *testing.T) { 217 req := http.Request{ 218 Header: map[string][]string{ 219 authHeaderKey: {"Bearer " + tt.token}, 220 }, 221 } 222 imp, err := GetImpersonationHeadersFromRequest(&req) 223 if tt.expectError { 224 assert.Error(t, err) 225 return 226 } 227 assert.NoError(t, err) 228 assert.Equal(t, tt.expectedUser, imp.User) 229 assert.ElementsMatch(t, tt.expectedGroups, imp.Groups) 230 }) 231 } 232 } 233 234 func (m mockVerifier) Verify(_ context.Context, rawIDToken string) (*oidc.IDToken, error) { 235 if rawIDToken != m.token { 236 return nil, fmt.Errorf("provided token does not match the mocked token") 237 } 238 return &oidc.IDToken{Issuer: m.issuer}, nil 239 } 240 241 func newMockVerifier(issuer, token string) *mockVerifier { 242 return &mockVerifier{ 243 issuer: issuer, 244 token: token, 245 } 246 }