From 6fc1f5276d325d94e62081a3e0f0748fc298178a Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Mon, 4 Sep 2023 19:39:14 +0300 Subject: [PATCH] refactor(coderd/httpapi): remove database, dbauthz and rbac imports (#9481) Ref: #9380 --- coderd/database/dbauthz/dbauthz.go | 9 +++++++++ coderd/httpapi/httpapi.go | 11 ++++++++--- .../httpapi/httpapiconstraints/httpapiconstraints.go | 10 ++++++++++ coderd/httpapi/queryparams.go | 7 +++---- coderd/rbac/error.go | 10 ++++++++++ 5 files changed, 40 insertions(+), 7 deletions(-) create mode 100644 coderd/httpapi/httpapiconstraints/httpapiconstraints.go diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index aa17940603..e4b802092f 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -17,6 +17,7 @@ import ( "cdr.dev/slog" "github.com/coder/coder/v2/coderd/database" "github.com/coder/coder/v2/coderd/database/dbtime" + "github.com/coder/coder/v2/coderd/httpapi/httpapiconstraints" "github.com/coder/coder/v2/coderd/rbac" "github.com/coder/coder/v2/coderd/util/slice" ) @@ -36,10 +37,18 @@ type NotAuthorizedError struct { Err error } +// Ensure we implement the IsUnauthorized interface. +var _ httpapiconstraints.IsUnauthorizedError = (*NotAuthorizedError)(nil) + func (e NotAuthorizedError) Error() string { return fmt.Sprintf("unauthorized: %s", e.Err.Error()) } +// IsUnauthorized implements the IsUnauthorized interface. +func (NotAuthorizedError) IsUnauthorized() bool { + return true +} + // Unwrap will always unwrap to a sql.ErrNoRows so the API returns a 404. // So 'errors.Is(err, sql.ErrNoRows)' will always be true. func (e NotAuthorizedError) Unwrap() error { diff --git a/coderd/httpapi/httpapi.go b/coderd/httpapi/httpapi.go index 0691edd8e9..cf89f6e509 100644 --- a/coderd/httpapi/httpapi.go +++ b/coderd/httpapi/httpapi.go @@ -16,8 +16,7 @@ import ( "github.com/go-playground/validator/v10" "golang.org/x/xerrors" - "github.com/coder/coder/v2/coderd/database/dbauthz" - "github.com/coder/coder/v2/coderd/rbac" + "github.com/coder/coder/v2/coderd/httpapi/httpapiconstraints" "github.com/coder/coder/v2/coderd/tracing" "github.com/coder/coder/v2/codersdk" ) @@ -90,7 +89,13 @@ func Is404Error(err error) bool { if err == nil { return false } - return xerrors.Is(err, sql.ErrNoRows) || dbauthz.IsNotAuthorizedError(err) || rbac.IsUnauthorizedError(err) + + // This tests for dbauthz.IsNotAuthorizedError and rbac.IsUnauthorizedError. + var unauthorized httpapiconstraints.IsUnauthorizedError + if errors.As(err, &unauthorized) && unauthorized.IsUnauthorized() { + return true + } + return xerrors.Is(err, sql.ErrNoRows) } // Convenience error functions don't take contexts since their responses are diff --git a/coderd/httpapi/httpapiconstraints/httpapiconstraints.go b/coderd/httpapi/httpapiconstraints/httpapiconstraints.go new file mode 100644 index 0000000000..e4aeed1f11 --- /dev/null +++ b/coderd/httpapi/httpapiconstraints/httpapiconstraints.go @@ -0,0 +1,10 @@ +// Package httpapiconstraints contain types that can be used and implemented +// across the application to return specific HTTP status codes without pulling +// in large dependency trees. +package httpapiconstraints + +// IsUnauthorizedError is an interface that can be implemented in other packages +// in order to return 404. +type IsUnauthorizedError interface { + IsUnauthorized() bool +} diff --git a/coderd/httpapi/queryparams.go b/coderd/httpapi/queryparams.go index 1ff9abc796..77991ac019 100644 --- a/coderd/httpapi/queryparams.go +++ b/coderd/httpapi/queryparams.go @@ -10,7 +10,6 @@ import ( "github.com/google/uuid" "golang.org/x/xerrors" - "github.com/coder/coder/v2/coderd/database" "github.com/coder/coder/v2/codersdk" ) @@ -158,10 +157,10 @@ func (p *QueryParamParser) Strings(vals url.Values, def []string, queryParam str }) } -// ValidEnum parses enum query params. Add more to the list as needed. +// ValidEnum represents an enum that can be parsed and validated. type ValidEnum interface { - database.ResourceType | database.AuditAction | database.BuildReason | database.UserStatus | - database.WorkspaceStatus + // Add more types as needed (avoid importing large dependency trees). + ~string // Valid is required on the enum type to be used with ParseEnum. Valid() bool diff --git a/coderd/rbac/error.go b/coderd/rbac/error.go index 36ff664d67..37c83f759e 100644 --- a/coderd/rbac/error.go +++ b/coderd/rbac/error.go @@ -9,6 +9,8 @@ import ( "github.com/open-policy-agent/opa/rego" "github.com/open-policy-agent/opa/topdown" "golang.org/x/xerrors" + + "github.com/coder/coder/v2/coderd/httpapi/httpapiconstraints" ) const ( @@ -33,6 +35,14 @@ type UnauthorizedError struct { output rego.ResultSet } +// Ensure we implement the IsUnauthorized interface. +var _ httpapiconstraints.IsUnauthorizedError = (*UnauthorizedError)(nil) + +// IsUnauthorized implements the IsUnauthorized interface. +func (UnauthorizedError) IsUnauthorized() bool { + return true +} + // IsUnauthorizedError is a convenience function to check if err is UnauthorizedError. // It is equivalent to errors.As(err, &UnauthorizedError{}). func IsUnauthorizedError(err error) bool {