chore: add unit tests for controller authz failures#262
chore: add unit tests for controller authz failures#262niazkhansap wants to merge 1 commit intomainfrom
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughThis change adds authorization testing infrastructure consisting of two components. First, a new integration test in Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
925a0ea to
d8a8264
Compare
|
@coderabbitai summary |
✅ Actions performedSummary regeneration triggered. |
| } | ||
|
|
||
| // randomUUID returns a new random UUID string for use in endpoint paths | ||
| func randomUUID() string { |
There was a problem hiding this comment.
Seems to be overkill to me. Can we not use uuid.New().String() directly below?
| } | ||
|
|
||
| // jsonBody returns a Body function that provides the given JSON string | ||
| func jsonBody(s string) func(testing.TB) io.Reader { |
There was a problem hiding this comment.
Should this not be in the testutils function? We can declare JSON strings in the test and let the utility do the json marshalling
|
|
||
| for _, ep := range endpoints { | ||
| pattern := ep.Pattern | ||
| if pattern == "" { |
There was a problem hiding this comment.
sv is created by startAPIServer so it's an instance of daemon.ServeMux which wraps http.ServeMux.
There is a function in http.ServeMux called Handler which should know how to do the pattern matching, we shouldn't have to do it again here
I would remove Pattern from the test fixtue and extend daemon.ServeMux with
func (m *ServeMux) Handler(r *http.Request) (http.Handler, string) {
return m.httpServeMux.Handler(r)
}
and derive the pattern as
func RunAuthzFailureTests(
t *testing.T,
sv *daemon.ServeMux,
tenant string,
r repo.Repo,
ctx context.Context,
endpoints []AuthzTestEndpoint,
) {
t.Helper()
for _, ep := range endpoints {
req := NewHTTPRequest(t, RequestOptions{ //nolint:contextcheck
Method: ep.Method,
Endpoint: ep.Endpoint,
})
_, pattern := sv.Handler(req)
pattern = strings.Replace(pattern, sv.BaseURL, "", 1) // strip /cmk/v1/{tenant}
bf27f31 to
aea82f6
Compare
|
|
||
| sv, ok := testutils.NewAPIServer(t, db, testutils.TestAPIServerConfig{ | ||
| Config: config.Config{Database: dbCfg}, | ||
| }).(*daemon.ServeMux) |
There was a problem hiding this comment.
Np: Can be split to 2 steps for clarity
aea82f6 to
83a3515
Compare
| sv, ok := server.(*daemon.ServeMux) | ||
| if !ok { | ||
| t.Fatal("expected *daemon.ServeMux") | ||
| } |
There was a problem hiding this comment.
Use assertify to remain consistant
| Config: config.Config{Database: dbCfg}, | ||
| }) | ||
|
|
||
| sv, ok := server.(*daemon.ServeMux) |
| // RunAuthzFailureTests runs authorization failure tests for the provided | ||
| // endpoints. For each endpoint it uses the ServeMux to resolve the registered | ||
| // pattern, determines which roles should be blocked based on the policy data, | ||
| // and asserts that each blocked role receives 403 Forbidden. | ||
| func RunAuthzFailureTests( | ||
| t *testing.T, | ||
| sv *daemon.ServeMux, | ||
| tenant string, | ||
| r repo.Repo, | ||
| ctx context.Context, | ||
| endpoints []AuthzTestEndpoint, | ||
| ) { | ||
| t.Helper() | ||
|
|
||
| for _, ep := range endpoints { | ||
| req := NewHTTPRequest(t, RequestOptions{ //nolint:contextcheck | ||
| Method: ep.Method, | ||
| Endpoint: ep.Endpoint, | ||
| Tenant: tenant, | ||
| }) | ||
|
|
||
| _, pattern := sv.Handler(req) | ||
| pattern = strings.Replace(pattern, sv.BaseURL, "", 1) | ||
|
|
||
| restriction, exists := authz.RestrictionsByAPI[pattern] | ||
| if !exists { | ||
| t.Fatalf( | ||
| "no authz restriction found for pattern %q on %s %s", | ||
| pattern, ep.Method, ep.Endpoint, | ||
| ) | ||
| } | ||
|
|
||
| blockedRoles := getBlockedRoles( | ||
| restriction.APIResourceTypeName, restriction.APIAction, | ||
| ) | ||
| if len(blockedRoles) == 0 { | ||
| t.Logf("all roles are allowed for %q (%s:%s), skipping", | ||
| pattern, restriction.APIResourceTypeName, restriction.APIAction) | ||
|
|
||
| continue | ||
| } | ||
|
|
||
| for _, role := range blockedRoles { | ||
| runAuthzBlockedSubtest(t, sv, tenant, r, ctx, ep, role) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // runAuthzBlockedSubtest executes a single subtest asserting that the given | ||
| // role is blocked (403 Forbidden) for the endpoint. | ||
| func runAuthzBlockedSubtest( | ||
| t *testing.T, | ||
| sv *daemon.ServeMux, | ||
| tenant string, | ||
| r repo.Repo, | ||
| ctx context.Context, | ||
| ep AuthzTestEndpoint, | ||
| role constants.Role, | ||
| ) { | ||
| t.Helper() | ||
|
|
||
| testName := fmt.Sprintf( | ||
| "%s_%s_blocked_for_%s", ep.Method, cleanPath(ep.Endpoint), role, | ||
| ) | ||
|
|
||
| t.Run(testName, func(t *testing.T) { | ||
| authClient := NewAuthClient(ctx, t, r, roleAuthClientOpt(role)) | ||
|
|
||
| w := MakeHTTPRequest(t, sv, RequestOptions{ //nolint:contextcheck | ||
| Method: ep.Method, | ||
| Endpoint: ep.Endpoint, | ||
| Tenant: tenant, | ||
| Body: WithBody(t, ep.Body), | ||
| AdditionalContext: authClient.GetClientMap(), | ||
| }) | ||
|
|
||
| assert.Equal(t, http.StatusForbidden, w.Code, | ||
| "expected 403 for role %s on %s %s, got %d: %s", | ||
| role, ep.Method, ep.Endpoint, w.Code, w.Body.String()) | ||
| }) | ||
| } |
There was a problem hiding this comment.
This shouldn't be on testutils, these are the tests themselves
24f8659 to
1340f0b
Compare
1340f0b to
6e5b102
Compare
|
|
||
| continue | ||
| } | ||
|
|
There was a problem hiding this comment.
Would it be worth adding, for sanity, a single test for allowed role?
| @@ -0,0 +1,359 @@ | |||
| //go:build !unit | |||
There was a problem hiding this comment.
Maybe unauthz_test.go would be a better name?
If the case be leave a commit for the sanity test suggested in other comment to leave a code comment to indicate that positive authz is for sanity test.
04166df to
5068e70
Compare
5068e70 to
836a61d
Compare
836a61d to
a1973f1
Compare
a1973f1 to
a02c4ec
Compare
Summary by CodeRabbit
Release Notes