add insufficient auth to graph, errs (#4776)
#### Does this PR need a docs update or release note? - [x] ⛔ No #### Type of change - [x] 🌻 Feature #### Test Plan - [x] ⚡ Unit test
This commit is contained in:
parent
79a37bfb91
commit
37dcb952fb
@ -13,12 +13,13 @@ import (
|
|||||||
type errEnum string
|
type errEnum string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ApplicationThrottled errEnum = "application-throttled"
|
ApplicationThrottled errEnum = "application-throttled"
|
||||||
BackupNotFound errEnum = "backup-not-found"
|
BackupNotFound errEnum = "backup-not-found"
|
||||||
RepoAlreadyExists errEnum = "repository-already-exists"
|
InsufficientAuthorization errEnum = "insufficient-authorization"
|
||||||
ResourceNotAccessible errEnum = "resource-not-accesible"
|
RepoAlreadyExists errEnum = "repository-already-exists"
|
||||||
ResourceOwnerNotFound errEnum = "resource-owner-not-found"
|
ResourceNotAccessible errEnum = "resource-not-accesible"
|
||||||
ServiceNotEnabled errEnum = "service-not-enabled"
|
ResourceOwnerNotFound errEnum = "resource-owner-not-found"
|
||||||
|
ServiceNotEnabled errEnum = "service-not-enabled"
|
||||||
)
|
)
|
||||||
|
|
||||||
// map of enums to errors. We might want to re-use an enum for multiple
|
// map of enums to errors. We might want to re-use an enum for multiple
|
||||||
@ -41,9 +42,10 @@ type ErrCheck func(error) bool
|
|||||||
// checks. This allows us to apply those comparison checks instead of relying
|
// checks. This allows us to apply those comparison checks instead of relying
|
||||||
// only on sentinels.
|
// only on sentinels.
|
||||||
var externalToInternalCheck = map[errEnum][]ErrCheck{
|
var externalToInternalCheck = map[errEnum][]ErrCheck{
|
||||||
ApplicationThrottled: {graph.IsErrApplicationThrottled},
|
ApplicationThrottled: {graph.IsErrApplicationThrottled},
|
||||||
ResourceNotAccessible: {graph.IsErrResourceLocked},
|
ResourceNotAccessible: {graph.IsErrResourceLocked},
|
||||||
ResourceOwnerNotFound: {graph.IsErrItemNotFound},
|
ResourceOwnerNotFound: {graph.IsErrItemNotFound},
|
||||||
|
InsufficientAuthorization: {graph.IsErrInsufficientAuthorization},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal returns the internal errors and error checking functions which
|
// Internal returns the internal errors and error checking functions which
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/alcionai/corso/src/internal/tester"
|
"github.com/alcionai/corso/src/internal/tester"
|
||||||
"github.com/alcionai/corso/src/pkg/repository"
|
"github.com/alcionai/corso/src/pkg/repository"
|
||||||
"github.com/alcionai/corso/src/pkg/services/m365/api/graph"
|
"github.com/alcionai/corso/src/pkg/services/m365/api/graph"
|
||||||
|
graphTD "github.com/alcionai/corso/src/pkg/services/m365/api/graph/testdata"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ErrUnitSuite struct {
|
type ErrUnitSuite struct {
|
||||||
@ -103,6 +104,12 @@ func (suite *ErrUnitSuite) TestInternal_checks() {
|
|||||||
expectHasChecks: assert.NotEmpty,
|
expectHasChecks: assert.NotEmpty,
|
||||||
expect: assert.True,
|
expect: assert.True,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
get: InsufficientAuthorization,
|
||||||
|
err: graphTD.ODataErr(string(graph.AuthorizationRequestDenied)),
|
||||||
|
expectHasChecks: assert.NotEmpty,
|
||||||
|
expect: assert.True,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, test := range table {
|
for _, test := range table {
|
||||||
suite.Run(string(test.get), func() {
|
suite.Run(string(test.get), func() {
|
||||||
@ -155,6 +162,10 @@ func (suite *ErrUnitSuite) TestIs() {
|
|||||||
target: ResourceNotAccessible,
|
target: ResourceNotAccessible,
|
||||||
err: graph.ErrResourceLocked,
|
err: graph.ErrResourceLocked,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
target: InsufficientAuthorization,
|
||||||
|
err: graphTD.ODataErr(string(graph.AuthorizationRequestDenied)),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, test := range table {
|
for _, test := range table {
|
||||||
suite.Run(string(test.target), func() {
|
suite.Run(string(test.target), func() {
|
||||||
|
|||||||
@ -30,9 +30,12 @@ type errorCode string
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
applicationThrottled errorCode = "ApplicationThrottled"
|
applicationThrottled errorCode = "ApplicationThrottled"
|
||||||
// this auth error is a catch-all used by graph in a variety of cases:
|
// this authN error is a catch-all used by graph in a variety of cases:
|
||||||
// users without licenses, bad jwts, missing account permissions, etc.
|
// users without licenses, bad jwts, missing account permissions, etc.
|
||||||
AuthenticationError errorCode = "AuthenticationError"
|
AuthenticationError errorCode = "AuthenticationError"
|
||||||
|
// on the other hand, authZ errors apply specifically to authenticated,
|
||||||
|
// but unauthorized, user requests
|
||||||
|
AuthorizationRequestDenied errorCode = "Authorization_RequestDenied"
|
||||||
// cannotOpenFileAttachment happen when an attachment is
|
// cannotOpenFileAttachment happen when an attachment is
|
||||||
// inaccessible. The error message is usually "OLE conversion
|
// inaccessible. The error message is usually "OLE conversion
|
||||||
// failed for an attachment."
|
// failed for an attachment."
|
||||||
@ -137,6 +140,10 @@ func IsErrAuthenticationError(err error) bool {
|
|||||||
return hasErrorCode(err, AuthenticationError)
|
return hasErrorCode(err, AuthenticationError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsErrInsufficientAuthorization(err error) bool {
|
||||||
|
return hasErrorCode(err, AuthorizationRequestDenied)
|
||||||
|
}
|
||||||
|
|
||||||
func IsErrDeletedInFlight(err error) bool {
|
func IsErrDeletedInFlight(err error) bool {
|
||||||
if errors.Is(err, ErrDeletedInFlight) {
|
if errors.Is(err, ErrDeletedInFlight) {
|
||||||
return true
|
return true
|
||||||
|
|||||||
@ -124,6 +124,40 @@ func (suite *GraphErrorsUnitSuite) TestIsErrAuthenticationError() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *GraphErrorsUnitSuite) TestIsErrInsufficientAuthorization() {
|
||||||
|
table := []struct {
|
||||||
|
name string
|
||||||
|
err error
|
||||||
|
expect assert.BoolAssertionFunc
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nil",
|
||||||
|
err: nil,
|
||||||
|
expect: assert.False,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "non-matching",
|
||||||
|
err: assert.AnError,
|
||||||
|
expect: assert.False,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "non-matching oDataErr",
|
||||||
|
err: graphTD.ODataErr("fnords"),
|
||||||
|
expect: assert.False,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AuthorizationRequestDenied oDataErr",
|
||||||
|
err: graphTD.ODataErr(string(AuthorizationRequestDenied)),
|
||||||
|
expect: assert.True,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range table {
|
||||||
|
suite.Run(test.name, func() {
|
||||||
|
test.expect(suite.T(), IsErrInsufficientAuthorization(test.err))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *GraphErrorsUnitSuite) TestIsErrDeletedInFlight() {
|
func (suite *GraphErrorsUnitSuite) TestIsErrDeletedInFlight() {
|
||||||
table := []struct {
|
table := []struct {
|
||||||
name string
|
name string
|
||||||
|
|||||||
@ -22,9 +22,7 @@ const (
|
|||||||
func shouldLogRespBody(resp *http.Response) bool {
|
func shouldLogRespBody(resp *http.Response) bool {
|
||||||
return logger.DebugAPIFV ||
|
return logger.DebugAPIFV ||
|
||||||
os.Getenv(logGraphRequestsEnvKey) != "" ||
|
os.Getenv(logGraphRequestsEnvKey) != "" ||
|
||||||
resp.StatusCode == http.StatusBadRequest ||
|
resp.StatusCode > 399
|
||||||
resp.StatusCode == http.StatusForbidden ||
|
|
||||||
resp.StatusCode == http.StatusConflict
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func logResp(ctx context.Context, resp *http.Response) {
|
func logResp(ctx context.Context, resp *http.Response) {
|
||||||
@ -47,6 +45,7 @@ func logResp(ctx context.Context, resp *http.Response) {
|
|||||||
// Log api calls according to api debugging configurations.
|
// Log api calls according to api debugging configurations.
|
||||||
switch respClass {
|
switch respClass {
|
||||||
case 2:
|
case 2:
|
||||||
|
// only log 2xx's if we want the full response body.
|
||||||
if logBody {
|
if logBody {
|
||||||
// only dump the body if it's under a size limit. We don't want to copy gigs into memory for a log.
|
// only dump the body if it's under a size limit. We don't want to copy gigs into memory for a log.
|
||||||
dump := getRespDump(ctx, resp, os.Getenv(log2xxGraphResponseEnvKey) != "" && resp.ContentLength < logMBLimit)
|
dump := getRespDump(ctx, resp, os.Getenv(log2xxGraphResponseEnvKey) != "" && resp.ContentLength < logMBLimit)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user