fleet/server/service/middleware/authzcheck/authzcheck.go
Victor Lyuboslavsky c88cc953fb
Refactor endpoint_utils for modularization (#36484)
Resolves #37192

Separating generic endpoint_utils middleware logic from domain-specific
business logic. New bounded contexts would share the generic logic and
implement their own domain-specific logic. The two approaches used in
this PR are:
- Use common `platform` types
- Use interfaces

In the next PR we will move `endpointer_utils`, `authzcheck` and
`ratelimit` into `platform` directory.

# Checklist for submitter

- [x] Added changes file

## Testing

- [x] Added/updated tests
- [x] QA'd all new/changed functionality manually



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Refactor**
* Restructured internal error handling and context management to support
bounded context architecture.
* Improved error context collection and telemetry observability through
a provider-based mechanism.
* Decoupled licensing and authentication concerns into interfaces for
better modularity.

* **Chores**
* Updated internal package dependencies to align with new architectural
boundaries.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-31 09:12:00 -06:00

60 lines
2.2 KiB
Go

// Package authzcheck implements a middleware that ensures that an authorization
// check was performed. This does not ensure that the correct authorization
// check was performed, but offers a backstop in case that a developer misses a
// check.
package authzcheck
import (
"context"
"errors"
authz_ctx "github.com/fleetdm/fleet/v4/server/contexts/authz"
platform_http "github.com/fleetdm/fleet/v4/server/platform/http"
"github.com/go-kit/kit/endpoint"
)
// Middleware is the authzcheck middleware type.
type Middleware struct{}
// NewMiddleware returns a new authzcheck middleware.
func NewMiddleware() *Middleware {
return &Middleware{}
}
func (m *Middleware) AuthzCheck() endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, req interface{}) (interface{}, error) {
authzctx := &authz_ctx.AuthorizationContext{}
ctx = authz_ctx.NewContext(ctx, authzctx)
response, err := next(ctx, req)
// If authentication check failed, return that error (so that we log
// appropriately).
var authFailedError *platform_http.AuthFailedError
var authRequiredError *platform_http.AuthRequiredError
var authHeaderRequiredError *platform_http.AuthHeaderRequiredError
if errors.As(err, &authFailedError) ||
errors.As(err, &authRequiredError) ||
errors.As(err, &authHeaderRequiredError) ||
errors.Is(err, platform_http.ErrPasswordResetRequired) {
return nil, err
}
// TODO(mna): currently, any error detected before an authorization check gets
// lost and the response is always Unauthorized because of the following condition.
// I _think_ it would be safe to check here of response.error() returns a non-nil
// error and if so, leave that error go through instead of returning a check missing
// authorization error. To look into when addressing #4406.
// If authorization was not checked, return a response that will
// marshal to a generic error and log that the check was missed.
if !authzctx.Checked() {
// Getting to here means there is an authorization-related bug in our code.
return nil, platform_http.CheckMissingWithResponse(response)
}
return response, err
}
}
}