Skip to content

chore: add unit tests for controller authz failures#262

Open
niazkhansap wants to merge 1 commit intomainfrom
task/authz-failures-tests
Open

chore: add unit tests for controller authz failures#262
niazkhansap wants to merge 1 commit intomainfrom
task/authz-failures-tests

Conversation

@niazkhansap
Copy link
Copy Markdown
Contributor

@niazkhansap niazkhansap commented Apr 22, 2026

Summary by CodeRabbit

Release Notes

  • Tests
    • Added authorization test suite with integration capabilities for verifying API access control across endpoints.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 22, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7a3f8909-62c1-4b9a-8255-ac11781a809e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

This change adds authorization testing infrastructure consisting of two components. First, a new integration test in authz_test.go that starts a test database and API server, then validates that restricted endpoints deny access with 403 Forbidden responses for unauthorized roles. Second, a test utility layer in internal/testutils/authz.go provides the RunAuthzFailureTests function and supporting helpers that derive authorization restrictions from policy data, enumerate blocked roles for each endpoint, and execute per-role authorization failure subtests. Together, these additions establish a framework for verifying authorization policy enforcement across the API.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@niazkhansap niazkhansap force-pushed the task/authz-failures-tests branch 4 times, most recently from 925a0ea to d8a8264 Compare April 22, 2026 15:46
@niazkhansap
Copy link
Copy Markdown
Contributor Author

@coderabbitai summary

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 23, 2026

✅ Actions performed

Summary regeneration triggered.

Comment thread internal/controllers/cmk/authz_test.go Outdated
}

// randomUUID returns a new random UUID string for use in endpoint paths
func randomUUID() string {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to be overkill to me. Can we not use uuid.New().String() directly below?

Comment thread internal/controllers/cmk/authz_test.go Outdated
}

// jsonBody returns a Body function that provides the given JSON string
func jsonBody(s string) func(testing.TB) io.Reader {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this not be in the testutils function? We can declare JSON strings in the test and let the utility do the json marshalling

Comment thread internal/testutils/authz.go Outdated

for _, ep := range endpoints {
pattern := ep.Pattern
if pattern == "" {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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}

@niazkhansap niazkhansap force-pushed the task/authz-failures-tests branch 3 times, most recently from bf27f31 to aea82f6 Compare April 23, 2026 17:46
minh-nghia
minh-nghia previously approved these changes Apr 24, 2026
Comment thread internal/controllers/cmk/authz_test.go Outdated

sv, ok := testutils.NewAPIServer(t, db, testutils.TestAPIServerConfig{
Config: config.Config{Database: dbCfg},
}).(*daemon.ServeMux)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Np: Can be split to 2 steps for clarity

minh-nghia
minh-nghia previously approved these changes Apr 24, 2026
Comment thread internal/controllers/cmk/authz_test.go Outdated
sv, ok := server.(*daemon.ServeMux)
if !ok {
t.Fatal("expected *daemon.ServeMux")
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use assertify to remain consistant

Comment thread internal/controllers/cmk/authz_test.go Outdated
Config: config.Config{Database: dbCfg},
})

sv, ok := server.(*daemon.ServeMux)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the cast?

Comment thread internal/testutils/authz.go Outdated
Comment on lines +271 to +351
// 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())
})
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be on testutils, these are the tests themselves

@niazkhansap niazkhansap force-pushed the task/authz-failures-tests branch 2 times, most recently from 24f8659 to 1340f0b Compare April 24, 2026 14:11
@niazkhansap niazkhansap force-pushed the task/authz-failures-tests branch from 1340f0b to 6e5b102 Compare April 27, 2026 08:38
minh-nghia
minh-nghia previously approved these changes Apr 27, 2026

continue
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be worth adding, for sanity, a single test for allowed role?

@@ -0,0 +1,359 @@
//go:build !unit
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@niazkhansap niazkhansap force-pushed the task/authz-failures-tests branch 2 times, most recently from 04166df to 5068e70 Compare April 27, 2026 19:49
anettlippert
anettlippert previously approved these changes Apr 28, 2026
@niazkhansap niazkhansap force-pushed the task/authz-failures-tests branch from a1973f1 to a02c4ec Compare April 28, 2026 09:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants