565 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			565 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Go
		
	
	
	
| package util
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"encoding/base64"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"net/http"
 | |
| 	"net/http/httptest"
 | |
| 	"net/url"
 | |
| 	"sync/atomic"
 | |
| 
 | |
| 	. "github.com/onsi/ginkgo"
 | |
| 	. "github.com/onsi/ginkgo/extensions/table"
 | |
| 	. "github.com/onsi/gomega"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	emptyJSON             = "{}"
 | |
| 	profilePath           = "/userinfo"
 | |
| 	authorizedAccessToken = "valid_access_token"
 | |
| 	basicIDTokenPayload   = `{
 | |
|       "user": "idTokenUser",
 | |
|       "email": "idTokenEmail",
 | |
|       "groups": [
 | |
|         "idTokenGroup1",
 | |
|         "idTokenGroup2"
 | |
|       ],
 | |
|       "https://groups.test": [
 | |
|         "fqdnGroup1",
 | |
|         "fqdnGroup2"
 | |
|       ]
 | |
|     }`
 | |
| 	basicProfileURLPayload = `{
 | |
|         "user": "profileUser",
 | |
|         "email": "profileEmail",
 | |
|         "groups": [
 | |
|           "profileGroup1",
 | |
|           "profileGroup2"
 | |
|         ]
 | |
|       }`
 | |
| 	nestedClaimPayload = `{
 | |
|       "auth": {
 | |
|         "user": {
 | |
|           "username": "nestedUser"
 | |
|         }
 | |
|       }
 | |
|     }`
 | |
| 	complexGroupsPayload = `{
 | |
|       "groups": [
 | |
|         {
 | |
|           "groupID": "group1",
 | |
|           "roles": ["admin"]
 | |
|         },
 | |
|         {
 | |
|           "groupID": "group2",
 | |
|           "roles": ["user", "employee"]
 | |
|         }
 | |
|       ]
 | |
|     }`
 | |
| )
 | |
| 
 | |
| var _ = Describe("Claim Extractor Suite", func() {
 | |
| 	Context("Claim Extractor", func() {
 | |
| 		type newClaimExtractorTableInput struct {
 | |
| 			idToken       string
 | |
| 			expectedError error
 | |
| 		}
 | |
| 
 | |
| 		DescribeTable("NewClaimExtractor",
 | |
| 			func(in newClaimExtractorTableInput) {
 | |
| 				_, err := NewClaimExtractor(context.Background(), in.idToken, nil, nil)
 | |
| 				if in.expectedError != nil {
 | |
| 					Expect(err).To(MatchError(in.expectedError))
 | |
| 				} else {
 | |
| 					Expect(err).ToNot(HaveOccurred())
 | |
| 				}
 | |
| 			},
 | |
| 			Entry("with a valid JWT", newClaimExtractorTableInput{
 | |
| 				idToken:       createJWTFromPayload(basicIDTokenPayload),
 | |
| 				expectedError: nil,
 | |
| 			}),
 | |
| 			Entry("with a JWT with a non-json payload", newClaimExtractorTableInput{
 | |
| 				idToken:       createJWTFromPayload("this is not JSON"),
 | |
| 				expectedError: errors.New("failed to parse ID Token payload: invalid character 'h' in literal true (expecting 'r')"),
 | |
| 			}),
 | |
| 			Entry("with an IDToken with the wrong number of parts", newClaimExtractorTableInput{
 | |
| 				idToken:       "eyJeyJ",
 | |
| 				expectedError: errors.New("failed to parse ID Token: oidc: malformed jwt, expected 3 parts got 1"),
 | |
| 			}),
 | |
| 			Entry("with an non-base64 IDToken", newClaimExtractorTableInput{
 | |
| 				idToken:       "{metadata}.{payload}.{signature}",
 | |
| 				expectedError: errors.New("failed to parse ID Token: oidc: malformed jwt payload: illegal base64 data at input byte 0"),
 | |
| 			}),
 | |
| 		)
 | |
| 
 | |
| 		type getClaimTableInput struct {
 | |
| 			testClaimExtractorOpts
 | |
| 			claim         string
 | |
| 			expectedValue interface{}
 | |
| 			expectExists  bool
 | |
| 			expectedError error
 | |
| 		}
 | |
| 
 | |
| 		DescribeTable("GetClaim",
 | |
| 			func(in getClaimTableInput) {
 | |
| 				claimExtractor, serverClose, err := newTestClaimExtractor(in.testClaimExtractorOpts)
 | |
| 				Expect(err).ToNot(HaveOccurred())
 | |
| 				if serverClose != nil {
 | |
| 					defer serverClose()
 | |
| 				}
 | |
| 
 | |
| 				value, exists, err := claimExtractor.GetClaim(in.claim)
 | |
| 				if in.expectedError != nil {
 | |
| 					Expect(err).To(MatchError(in.expectedError))
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				Expect(err).ToNot(HaveOccurred())
 | |
| 				if in.expectedValue != nil {
 | |
| 					Expect(value).To(Equal(in.expectedValue))
 | |
| 				} else {
 | |
| 					Expect(value).To(BeNil())
 | |
| 				}
 | |
| 
 | |
| 				Expect(exists).To(Equal(in.expectExists))
 | |
| 			},
 | |
| 			Entry("retrieves a string claim from ID Token when present", getClaimTableInput{
 | |
| 				testClaimExtractorOpts: testClaimExtractorOpts{
 | |
| 					idTokenPayload:        basicIDTokenPayload,
 | |
| 					setProfileURL:         true,
 | |
| 					profileRequestHeaders: newAuthorizedHeader(),
 | |
| 					profileRequestHandler: shouldNotBeRequestedProfileHandler,
 | |
| 				},
 | |
| 				claim:         "user",
 | |
| 				expectExists:  true,
 | |
| 				expectedValue: "idTokenUser",
 | |
| 				expectedError: nil,
 | |
| 			}),
 | |
| 			Entry("retrieves a slice claim from ID Token when present", getClaimTableInput{
 | |
| 				testClaimExtractorOpts: testClaimExtractorOpts{
 | |
| 					idTokenPayload:        basicIDTokenPayload,
 | |
| 					setProfileURL:         true,
 | |
| 					profileRequestHeaders: newAuthorizedHeader(),
 | |
| 					profileRequestHandler: shouldNotBeRequestedProfileHandler,
 | |
| 				},
 | |
| 				claim:         "groups",
 | |
| 				expectExists:  true,
 | |
| 				expectedValue: []interface{}{"idTokenGroup1", "idTokenGroup2"},
 | |
| 				expectedError: nil,
 | |
| 			}),
 | |
| 			Entry("when the requested claim is the empty string", getClaimTableInput{
 | |
| 				testClaimExtractorOpts: testClaimExtractorOpts{
 | |
| 					idTokenPayload: basicIDTokenPayload,
 | |
| 				},
 | |
| 				claim:         "",
 | |
| 				expectExists:  false,
 | |
| 				expectedValue: nil,
 | |
| 				expectedError: nil,
 | |
| 			}),
 | |
| 			Entry("when the requested claim is the not found (with no profile URL)", getClaimTableInput{
 | |
| 				testClaimExtractorOpts: testClaimExtractorOpts{
 | |
| 					idTokenPayload:        basicIDTokenPayload,
 | |
| 					profileRequestHeaders: newAuthorizedHeader(),
 | |
| 				},
 | |
| 				claim:         "not_found",
 | |
| 				expectExists:  false,
 | |
| 				expectedValue: nil,
 | |
| 				expectedError: nil,
 | |
| 			}),
 | |
| 			Entry("when the requested claim is the not found (with profile URL)", getClaimTableInput{
 | |
| 				testClaimExtractorOpts: testClaimExtractorOpts{
 | |
| 					idTokenPayload:        basicIDTokenPayload,
 | |
| 					setProfileURL:         true,
 | |
| 					profileRequestHeaders: newAuthorizedHeader(),
 | |
| 					profileRequestHandler: requiresAuthProfileHandler,
 | |
| 				},
 | |
| 				claim:         "not_found",
 | |
| 				expectExists:  false,
 | |
| 				expectedValue: nil,
 | |
| 				expectedError: nil,
 | |
| 			}),
 | |
| 			Entry("when the requested claim is the not found (with no profile Headers)", getClaimTableInput{
 | |
| 				testClaimExtractorOpts: testClaimExtractorOpts{
 | |
| 					idTokenPayload:        basicIDTokenPayload,
 | |
| 					setProfileURL:         true,
 | |
| 					profileRequestHeaders: nil,
 | |
| 					profileRequestHandler: shouldNotBeRequestedProfileHandler,
 | |
| 				},
 | |
| 				claim:         "not_found",
 | |
| 				expectExists:  false,
 | |
| 				expectedValue: nil,
 | |
| 				expectedError: nil,
 | |
| 			}),
 | |
| 			Entry("when the profile URL is unauthorized", getClaimTableInput{
 | |
| 				testClaimExtractorOpts: testClaimExtractorOpts{
 | |
| 					idTokenPayload:        emptyJSON,
 | |
| 					setProfileURL:         true,
 | |
| 					profileRequestHeaders: make(http.Header),
 | |
| 					profileRequestHandler: requiresAuthProfileHandler,
 | |
| 				},
 | |
| 				claim:         "user",
 | |
| 				expectExists:  false,
 | |
| 				expectedValue: nil,
 | |
| 				expectedError: errors.New("failed to fetch claims from profile URL: error making request to profile URL: unexpected status \"403\": Unauthorized"),
 | |
| 			}),
 | |
| 			Entry("retrieves a string claim from profile URL when not present in the ID Token", getClaimTableInput{
 | |
| 				testClaimExtractorOpts: testClaimExtractorOpts{
 | |
| 					idTokenPayload:        emptyJSON,
 | |
| 					setProfileURL:         true,
 | |
| 					profileRequestHeaders: newAuthorizedHeader(),
 | |
| 					profileRequestHandler: requiresAuthProfileHandler,
 | |
| 				},
 | |
| 				claim:         "user",
 | |
| 				expectExists:  true,
 | |
| 				expectedValue: "profileUser",
 | |
| 				expectedError: nil,
 | |
| 			}),
 | |
| 			Entry("retrieves a string claim from a nested path", getClaimTableInput{
 | |
| 				testClaimExtractorOpts: testClaimExtractorOpts{
 | |
| 					idTokenPayload:        nestedClaimPayload,
 | |
| 					setProfileURL:         true,
 | |
| 					profileRequestHeaders: newAuthorizedHeader(),
 | |
| 					profileRequestHandler: shouldNotBeRequestedProfileHandler,
 | |
| 				},
 | |
| 				claim:         "auth.user.username",
 | |
| 				expectExists:  true,
 | |
| 				expectedValue: "nestedUser",
 | |
| 				expectedError: nil,
 | |
| 			}),
 | |
| 			Entry("retrieves claim for with FQDN", getClaimTableInput{
 | |
| 				testClaimExtractorOpts: testClaimExtractorOpts{
 | |
| 					idTokenPayload:        basicIDTokenPayload,
 | |
| 					setProfileURL:         true,
 | |
| 					profileRequestHeaders: newAuthorizedHeader(),
 | |
| 					profileRequestHandler: shouldNotBeRequestedProfileHandler,
 | |
| 				},
 | |
| 				claim:         "https://groups.test",
 | |
| 				expectExists:  true,
 | |
| 				expectedValue: []interface{}{"fqdnGroup1", "fqdnGroup2"},
 | |
| 				expectedError: nil,
 | |
| 			}),
 | |
| 		)
 | |
| 	})
 | |
| 
 | |
| 	It("GetClaim should only call the profile URL once", func() {
 | |
| 		var counter int32
 | |
| 		countRequestsHandler := func(rw http.ResponseWriter, _ *http.Request) {
 | |
| 			atomic.AddInt32(&counter, 1)
 | |
| 			rw.Write([]byte(basicProfileURLPayload))
 | |
| 		}
 | |
| 
 | |
| 		claimExtractor, serverClose, err := newTestClaimExtractor(testClaimExtractorOpts{
 | |
| 			idTokenPayload:        "{}",
 | |
| 			setProfileURL:         true,
 | |
| 			profileRequestHeaders: newAuthorizedHeader(),
 | |
| 			profileRequestHandler: countRequestsHandler,
 | |
| 		})
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		if serverClose != nil {
 | |
| 			defer serverClose()
 | |
| 		}
 | |
| 
 | |
| 		value, exists, err := claimExtractor.GetClaim("user")
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		Expect(exists).To(BeTrue())
 | |
| 		Expect(value).To(Equal("profileUser"))
 | |
| 		Expect(counter).To(BeEquivalentTo(1))
 | |
| 
 | |
| 		// Check a different claim, but expect the count not to increase
 | |
| 		value, exists, err = claimExtractor.GetClaim("email")
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		Expect(exists).To(BeTrue())
 | |
| 		Expect(value).To(Equal("profileEmail"))
 | |
| 		Expect(counter).To(BeEquivalentTo(1))
 | |
| 	})
 | |
| 
 | |
| 	It("GetClaim should not return an error with a non-nil empty ProfileURL", func() {
 | |
| 		claims, serverClose, err := newTestClaimExtractor(testClaimExtractorOpts{
 | |
| 			idTokenPayload:        "{}",
 | |
| 			profileRequestHeaders: newAuthorizedHeader(),
 | |
| 		})
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		if serverClose != nil {
 | |
| 			defer serverClose()
 | |
| 		}
 | |
| 		// Set the ProfileURL to be empty, but not nil
 | |
| 		claims.(*claimExtractor).profileURL = &url.URL{}
 | |
| 
 | |
| 		value, exists, err := claims.GetClaim("user")
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		Expect(exists).To(BeFalse())
 | |
| 		Expect(value).To(BeNil())
 | |
| 	})
 | |
| 
 | |
| 	type getClaimIntoTableInput struct {
 | |
| 		testClaimExtractorOpts
 | |
| 		into          interface{}
 | |
| 		claim         string
 | |
| 		expectedValue interface{}
 | |
| 		expectExists  bool
 | |
| 		expectedError error
 | |
| 	}
 | |
| 
 | |
| 	DescribeTable("GetClaimInto",
 | |
| 		func(in getClaimIntoTableInput) {
 | |
| 			claimExtractor, serverClose, err := newTestClaimExtractor(in.testClaimExtractorOpts)
 | |
| 			Expect(err).ToNot(HaveOccurred())
 | |
| 			if serverClose != nil {
 | |
| 				defer serverClose()
 | |
| 			}
 | |
| 
 | |
| 			exists, err := claimExtractor.GetClaimInto(in.claim, in.into)
 | |
| 			if in.expectedError != nil {
 | |
| 				Expect(err).To(MatchError(in.expectedError))
 | |
| 				return
 | |
| 			}
 | |
| 
 | |
| 			Expect(err).ToNot(HaveOccurred())
 | |
| 			if in.expectedValue != nil {
 | |
| 				Expect(in.into).To(Equal(in.expectedValue))
 | |
| 			} else {
 | |
| 				Expect(in.into).To(BeEmpty())
 | |
| 			}
 | |
| 
 | |
| 			Expect(exists).To(Equal(in.expectExists))
 | |
| 		},
 | |
| 		Entry("retrieves a string claim from ID Token when present into a string", getClaimIntoTableInput{
 | |
| 			testClaimExtractorOpts: testClaimExtractorOpts{
 | |
| 				idTokenPayload:        basicIDTokenPayload,
 | |
| 				setProfileURL:         true,
 | |
| 				profileRequestHeaders: newAuthorizedHeader(),
 | |
| 				profileRequestHandler: shouldNotBeRequestedProfileHandler,
 | |
| 			},
 | |
| 			claim:         "user",
 | |
| 			into:          stringPointer(""),
 | |
| 			expectExists:  true,
 | |
| 			expectedValue: stringPointer("idTokenUser"),
 | |
| 			expectedError: nil,
 | |
| 		}),
 | |
| 		Entry("retrieves a string claim from ID Token when present into a string slice", getClaimIntoTableInput{
 | |
| 			testClaimExtractorOpts: testClaimExtractorOpts{
 | |
| 				idTokenPayload:        basicIDTokenPayload,
 | |
| 				setProfileURL:         true,
 | |
| 				profileRequestHeaders: newAuthorizedHeader(),
 | |
| 				profileRequestHandler: shouldNotBeRequestedProfileHandler,
 | |
| 			},
 | |
| 			claim:         "user",
 | |
| 			into:          stringSlicePointer([]string{}),
 | |
| 			expectExists:  true,
 | |
| 			expectedValue: stringSlicePointer([]string{"idTokenUser"}),
 | |
| 			expectedError: nil,
 | |
| 		}),
 | |
| 		Entry("retrieves a string slice claim from ID Token when present into a string slice", getClaimIntoTableInput{
 | |
| 			testClaimExtractorOpts: testClaimExtractorOpts{
 | |
| 				idTokenPayload:        basicIDTokenPayload,
 | |
| 				setProfileURL:         true,
 | |
| 				profileRequestHeaders: newAuthorizedHeader(),
 | |
| 				profileRequestHandler: shouldNotBeRequestedProfileHandler,
 | |
| 			},
 | |
| 			claim:         "groups",
 | |
| 			into:          stringSlicePointer([]string{}),
 | |
| 			expectExists:  true,
 | |
| 			expectedValue: stringSlicePointer([]string{"idTokenGroup1", "idTokenGroup2"}),
 | |
| 			expectedError: nil,
 | |
| 		}),
 | |
| 		Entry("retrieves a string slice claim from ID Token when present into a string", getClaimIntoTableInput{
 | |
| 			testClaimExtractorOpts: testClaimExtractorOpts{
 | |
| 				idTokenPayload:        basicIDTokenPayload,
 | |
| 				setProfileURL:         true,
 | |
| 				profileRequestHeaders: newAuthorizedHeader(),
 | |
| 				profileRequestHandler: shouldNotBeRequestedProfileHandler,
 | |
| 			},
 | |
| 			claim:         "groups",
 | |
| 			into:          stringPointer(""),
 | |
| 			expectExists:  true,
 | |
| 			expectedValue: stringPointer("[\"idTokenGroup1\",\"idTokenGroup2\"]"),
 | |
| 			expectedError: nil,
 | |
| 		}),
 | |
| 		Entry("returns an error when a non-pointer is passed for the destination", getClaimIntoTableInput{
 | |
| 			testClaimExtractorOpts: testClaimExtractorOpts{
 | |
| 				idTokenPayload:        basicIDTokenPayload,
 | |
| 				setProfileURL:         true,
 | |
| 				profileRequestHeaders: newAuthorizedHeader(),
 | |
| 				profileRequestHandler: shouldNotBeRequestedProfileHandler,
 | |
| 			},
 | |
| 			claim:         "user",
 | |
| 			into:          "",
 | |
| 			expectExists:  false,
 | |
| 			expectedValue: "",
 | |
| 			expectedError: errors.New("could no coerce claim: unknown type for destination: string"),
 | |
| 		}),
 | |
| 		Entry("flattens a complex claim value into a JSON string", getClaimIntoTableInput{
 | |
| 			testClaimExtractorOpts: testClaimExtractorOpts{
 | |
| 				idTokenPayload:        complexGroupsPayload,
 | |
| 				setProfileURL:         true,
 | |
| 				profileRequestHeaders: newAuthorizedHeader(),
 | |
| 				profileRequestHandler: shouldNotBeRequestedProfileHandler,
 | |
| 			},
 | |
| 			claim:        "groups",
 | |
| 			into:         stringSlicePointer([]string{}),
 | |
| 			expectExists: true,
 | |
| 			expectedValue: stringSlicePointer([]string{
 | |
| 				"{\"groupID\":\"group1\",\"roles\":[\"admin\"]}",
 | |
| 				"{\"groupID\":\"group2\",\"roles\":[\"user\",\"employee\"]}",
 | |
| 			}),
 | |
| 			expectedError: nil,
 | |
| 		}),
 | |
| 		Entry("does not return an error when the claim does not exist", getClaimIntoTableInput{
 | |
| 			testClaimExtractorOpts: testClaimExtractorOpts{
 | |
| 				idTokenPayload:        basicIDTokenPayload,
 | |
| 				setProfileURL:         true,
 | |
| 				profileRequestHeaders: newAuthorizedHeader(),
 | |
| 				profileRequestHandler: requiresAuthProfileHandler,
 | |
| 			},
 | |
| 			claim:         "not_found",
 | |
| 			into:          stringPointer(""),
 | |
| 			expectExists:  false,
 | |
| 			expectedValue: stringPointer(""),
 | |
| 			expectedError: nil,
 | |
| 		}),
 | |
| 		Entry("returns an error when the profile request is unauthorized", getClaimIntoTableInput{
 | |
| 			testClaimExtractorOpts: testClaimExtractorOpts{
 | |
| 				idTokenPayload:        emptyJSON,
 | |
| 				setProfileURL:         true,
 | |
| 				profileRequestHeaders: make(http.Header),
 | |
| 				profileRequestHandler: requiresAuthProfileHandler,
 | |
| 			},
 | |
| 			claim:         "user",
 | |
| 			into:          stringPointer(""),
 | |
| 			expectExists:  false,
 | |
| 			expectedValue: stringPointer(""),
 | |
| 			expectedError: errors.New("could not get claim \"user\": failed to fetch claims from profile URL: error making request to profile URL: unexpected status \"403\": Unauthorized"),
 | |
| 		}),
 | |
| 	)
 | |
| 
 | |
| 	type coerceClaimTableInput struct {
 | |
| 		value         interface{}
 | |
| 		dst           interface{}
 | |
| 		expectedDst   interface{}
 | |
| 		expectedError error
 | |
| 	}
 | |
| 
 | |
| 	DescribeTable("coerceClaim",
 | |
| 		func(in coerceClaimTableInput) {
 | |
| 			err := coerceClaim(in.value, in.dst)
 | |
| 			if in.expectedError != nil {
 | |
| 				Expect(err).To(MatchError(in.expectedError))
 | |
| 				return
 | |
| 			}
 | |
| 
 | |
| 			Expect(err).ToNot(HaveOccurred())
 | |
| 			Expect(in.dst).To(Equal(in.expectedDst))
 | |
| 		},
 | |
| 		Entry("coerces a string to a string", coerceClaimTableInput{
 | |
| 			value:       "some_string",
 | |
| 			dst:         stringPointer(""),
 | |
| 			expectedDst: stringPointer("some_string"),
 | |
| 		}),
 | |
| 		Entry("coerces a slice to a string slice", coerceClaimTableInput{
 | |
| 			value:       []interface{}{"a", "b"},
 | |
| 			dst:         stringSlicePointer([]string{}),
 | |
| 			expectedDst: stringSlicePointer([]string{"a", "b"}),
 | |
| 		}),
 | |
| 		Entry("coerces a bool to a bool", coerceClaimTableInput{
 | |
| 			value:       true,
 | |
| 			dst:         boolPointer(false),
 | |
| 			expectedDst: boolPointer(true),
 | |
| 		}),
 | |
| 		Entry("coerces a string to a bool", coerceClaimTableInput{
 | |
| 			value:       "true",
 | |
| 			dst:         boolPointer(false),
 | |
| 			expectedDst: boolPointer(true),
 | |
| 		}),
 | |
| 		Entry("coerces a map to a string", coerceClaimTableInput{
 | |
| 			value: map[string]interface{}{
 | |
| 				"foo": []interface{}{"bar", "baz"},
 | |
| 			},
 | |
| 			dst:         stringPointer(""),
 | |
| 			expectedDst: stringPointer("{\"foo\":[\"bar\",\"baz\"]}"),
 | |
| 		}),
 | |
| 	)
 | |
| })
 | |
| 
 | |
| // ******************************************
 | |
| // Helpers for setting up the claim extractor
 | |
| // ******************************************
 | |
| 
 | |
| type testClaimExtractorOpts struct {
 | |
| 	idTokenPayload        string
 | |
| 	setProfileURL         bool
 | |
| 	profileRequestHeaders http.Header
 | |
| 	profileRequestHandler http.HandlerFunc
 | |
| }
 | |
| 
 | |
| func newTestClaimExtractor(in testClaimExtractorOpts) (ClaimExtractor, func(), error) {
 | |
| 	var profileURL *url.URL
 | |
| 	var closeServer func()
 | |
| 	if in.setProfileURL {
 | |
| 		server := httptest.NewServer(http.HandlerFunc(in.profileRequestHandler))
 | |
| 		closeServer = server.Close
 | |
| 
 | |
| 		var err error
 | |
| 		profileURL, err = url.Parse("http://" + server.Listener.Addr().String() + profilePath)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 	}
 | |
| 
 | |
| 	rawIDToken := createJWTFromPayload(in.idTokenPayload)
 | |
| 
 | |
| 	claimExtractor, err := NewClaimExtractor(context.Background(), rawIDToken, profileURL, in.profileRequestHeaders)
 | |
| 	return claimExtractor, closeServer, err
 | |
| }
 | |
| 
 | |
| func createJWTFromPayload(payload string) string {
 | |
| 	header := base64.RawURLEncoding.EncodeToString([]byte(emptyJSON))
 | |
| 	payloadJSON := base64.RawURLEncoding.EncodeToString([]byte(payload))
 | |
| 
 | |
| 	return fmt.Sprintf("%s.%s.%s", header, payloadJSON, header)
 | |
| }
 | |
| 
 | |
| func newAuthorizedHeader() http.Header {
 | |
| 	headers := make(http.Header)
 | |
| 	headers.Add("Authorization", "Bearer "+authorizedAccessToken)
 | |
| 	return headers
 | |
| }
 | |
| 
 | |
| func hasAuthorizedHeader(headers http.Header) bool {
 | |
| 	return headers.Get("Authorization") == "Bearer "+authorizedAccessToken
 | |
| }
 | |
| 
 | |
| // ***********************
 | |
| // Typed Pointer Functions
 | |
| // ***********************
 | |
| 
 | |
| func stringPointer(in string) *string {
 | |
| 	return &in
 | |
| }
 | |
| 
 | |
| func stringSlicePointer(in []string) *[]string {
 | |
| 	return &in
 | |
| }
 | |
| 
 | |
| func boolPointer(in bool) *bool {
 | |
| 	return &in
 | |
| }
 | |
| 
 | |
| // ******************************
 | |
| // Different profile URL handlers
 | |
| // ******************************
 | |
| 
 | |
| func shouldNotBeRequestedProfileHandler(_ http.ResponseWriter, _ *http.Request) {
 | |
| 	defer GinkgoRecover()
 | |
| 	Expect(true).To(BeFalse(), "Unexpected request to profile URL")
 | |
| }
 | |
| 
 | |
| func requiresAuthProfileHandler(rw http.ResponseWriter, req *http.Request) {
 | |
| 	if !hasAuthorizedHeader(req.Header) {
 | |
| 		rw.WriteHeader(403)
 | |
| 		rw.Write([]byte("Unauthorized"))
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	rw.Write([]byte(basicProfileURLPayload))
 | |
| }
 |