Skip to content

Commit

Permalink
Merge pull request #9 from microsoft/feature/cae
Browse files Browse the repository at this point in the history
CAE support
  • Loading branch information
baywet authored May 19, 2022
2 parents c7d3ea8 + d4f06e6 commit c12a110
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 9 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

## [0.4.0] - 2022-05-18

### Added

- Adds support for continuous access evaluation.

## [0.3.0] - 2022-04-19

### Changed
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/microsoft/kiota-http-go
go 1.18

require (
github.com/microsoft/kiota-abstractions-go v0.6.0
github.com/microsoft/kiota-abstractions-go v0.7.0
github.com/stretchr/testify v1.7.1
)

Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/microsoft/kiota-abstractions-go v0.6.0 h1:e/mJM8+xidaTxOvuVLdY0uB/3GFHxyZB43vhjmxAuME=
github.com/microsoft/kiota-abstractions-go v0.6.0/go.mod h1:fL1Ni2uXdlRPGicO4Ut0aOLmkpunYuAAJwRBLgDhzw4=
github.com/microsoft/kiota-abstractions-go v0.7.0 h1:/yPcUaiTOUvq4stz8qrPi9UWljnCGBcksRYS2BZpUBk=
github.com/microsoft/kiota-abstractions-go v0.7.0/go.mod h1:fL1Ni2uXdlRPGicO4Ut0aOLmkpunYuAAJwRBLgDhzw4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
Expand Down
53 changes: 45 additions & 8 deletions nethttp_request_adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"errors"
"io/ioutil"
"regexp"
"strconv"
"strings"

Expand Down Expand Up @@ -88,18 +89,54 @@ func (a *NetHttpRequestAdapter) SetBaseUrl(baseUrl string) {
func (a *NetHttpRequestAdapter) GetBaseUrl() string {
return a.baseUrl
}
func (a *NetHttpRequestAdapter) getHttpResponseMessage(requestInfo *abs.RequestInformation) (*nethttp.Response, error) {
func (a *NetHttpRequestAdapter) getHttpResponseMessage(requestInfo *abs.RequestInformation, claims string) (*nethttp.Response, error) {
a.setBaseUrlForRequestInformation(requestInfo)
err := a.authenticationProvider.AuthenticateRequest(requestInfo)
additionalContext := make(map[string]interface{})
if claims != "" {
additionalContext[claimsKey] = claims
}
err := a.authenticationProvider.AuthenticateRequest(requestInfo, additionalContext)
if err != nil {
return nil, err
}
request, err := a.getRequestFromRequestInformation(requestInfo)
if err != nil {
return nil, err
}
return (*a.httpClient).Do(request)
response, err := (*a.httpClient).Do(request)
if err != nil {
return nil, err
}
return a.retryCAEResponseIfRequired(response, requestInfo, claims)
}

const claimsKey = "claims"

var reBearer = regexp.MustCompile(`(?i)^Bearer\s`)
var reClaims = regexp.MustCompile(`\"([^\"]*)\"`)

func (a *NetHttpRequestAdapter) retryCAEResponseIfRequired(response *nethttp.Response, requestInfo *abs.RequestInformation, claims string) (*nethttp.Response, error) {
if response.StatusCode == 401 &&
claims == "" { //avoid infinite loop, we only retry once
authenticateHeaderVal := response.Header.Get("WWW-Authenticate")
if authenticateHeaderVal != "" && reBearer.Match([]byte(authenticateHeaderVal)) {
responseClaims := ""
parametersRaw := string(reBearer.ReplaceAll([]byte(authenticateHeaderVal), []byte("")))
parameters := strings.Split(parametersRaw, ",")
for _, parameter := range parameters {
if strings.HasPrefix(strings.Trim(parameter, " "), claimsKey) {
responseClaims = reClaims.FindStringSubmatch(parameter)[1]
break
}
}
if responseClaims != "" {
return a.getHttpResponseMessage(requestInfo, responseClaims)
}
}
}
return response, nil
}

func (a *NetHttpRequestAdapter) getResponsePrimaryContentType(response *nethttp.Response) string {
if response.Header == nil {
return ""
Expand Down Expand Up @@ -143,7 +180,7 @@ func (a *NetHttpRequestAdapter) SendAsync(requestInfo *abs.RequestInformation, c
if requestInfo == nil {
return nil, errors.New("requestInfo cannot be nil")
}
response, err := a.getHttpResponseMessage(requestInfo)
response, err := a.getHttpResponseMessage(requestInfo, "")
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -178,7 +215,7 @@ func (a *NetHttpRequestAdapter) SendCollectionAsync(requestInfo *abs.RequestInfo
if requestInfo == nil {
return nil, errors.New("requestInfo cannot be nil")
}
response, err := a.getHttpResponseMessage(requestInfo)
response, err := a.getHttpResponseMessage(requestInfo, "")
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -213,7 +250,7 @@ func (a *NetHttpRequestAdapter) SendPrimitiveAsync(requestInfo *abs.RequestInfor
if requestInfo == nil {
return nil, errors.New("requestInfo cannot be nil")
}
response, err := a.getHttpResponseMessage(requestInfo)
response, err := a.getHttpResponseMessage(requestInfo, "")
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -269,7 +306,7 @@ func (a *NetHttpRequestAdapter) SendPrimitiveCollectionAsync(requestInfo *abs.Re
if requestInfo == nil {
return nil, errors.New("requestInfo cannot be nil")
}
response, err := a.getHttpResponseMessage(requestInfo)
response, err := a.getHttpResponseMessage(requestInfo, "")
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -303,7 +340,7 @@ func (a *NetHttpRequestAdapter) SendNoContentAsync(requestInfo *abs.RequestInfor
if requestInfo == nil {
return errors.New("requestInfo cannot be nil")
}
response, err := a.getHttpResponseMessage(requestInfo)
response, err := a.getHttpResponseMessage(requestInfo, "")
if err != nil {
return err
}
Expand Down
44 changes: 44 additions & 0 deletions nethttp_request_adapter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package nethttplibrary

import (
nethttp "net/http"
httptest "net/http/httptest"
"net/url"
"testing"

abs "github.com/microsoft/kiota-abstractions-go"
absauth "github.com/microsoft/kiota-abstractions-go/authentication"

"github.com/stretchr/testify/assert"
)

func TestItRetriesOnCAEResponse(t *testing.T) {
methodCallCount := 0

testServer := httptest.NewServer(nethttp.HandlerFunc(func(res nethttp.ResponseWriter, req *nethttp.Request) {
if methodCallCount > 0 {
res.WriteHeader(200)
} else {
res.Header().Set("WWW-Authenticate", "Bearer realm=\"\", authorization_uri=\"https://login.microsoftonline.com/common/oauth2/authorize\", client_id=\"00000003-0000-0000-c000-000000000000\", error=\"insufficient_claims\", claims=\"eyJhY2Nlc3NfdG9rZW4iOnsibmJmIjp7ImVzc2VudGlhbCI6dHJ1ZSwgInZhbHVlIjoiMTY1MjgxMzUwOCJ9fX0=\"")
res.WriteHeader(401)
}
methodCallCount++
res.Write([]byte("body"))
}))
defer func() { testServer.Close() }()
authProvider := &absauth.AnonymousAuthenticationProvider{}
adapter, err := NewNetHttpRequestAdapter(authProvider)
assert.Nil(t, err)
assert.NotNil(t, adapter)

uri, err := url.Parse(testServer.URL)
assert.Nil(t, err)
assert.NotNil(t, uri)
request := abs.NewRequestInformation()
request.SetUri(*uri)
request.Method = abs.GET

err2 := adapter.SendNoContentAsync(request, nil, nil)
assert.Nil(t, err2)
assert.Equal(t, 2, methodCallCount)
}

0 comments on commit c12a110

Please sign in to comment.