Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
207 changes: 107 additions & 100 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,40 +1,26 @@
# golangci-lint configuration
# golangci-lint v2 configuration
# https://golangci-lint.run/usage/configuration/

version: "2"
run:
timeout: 5m
issues-exit-code: 1
tests: true

output:
formats:
- format: colored-line-number
print-issued-lines: true
print-linter-name: true

text:
path: stdout
print-linter-name: true
print-issued-lines: true
linters:
enable:
# Default linters
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- unused
# Additional linters
- bodyclose
- dogsled
- dupl
- errorlint
- exhaustive
- exportloopref
- gochecknoinits
- goconst
- gocritic
- gocyclo
- godot
- gofmt
- goimports
- goprintffuncname
- gosec
- misspell
Expand All @@ -47,92 +33,113 @@ linters:
- revive
- rowserrcheck
- sqlclosecheck
- stylecheck
- staticcheck
- tparallel
- unconvert
- unparam
- whitespace

linters-settings:
errcheck:
check-type-assertions: true
check-blank: true

govet:
enable-all: true
disable:
- fieldalignment

gocyclo:
min-complexity: 15

goconst:
min-len: 3
min-occurrences: 3

misspell:
locale: US

revive:
settings:
dupl:
threshold: 150
exhaustive:
# A switch with a default branch has made its catch-all explicit;
# don't also require every enum member to be spelled out.
default-signifies-exhaustive: true
gocritic:
disabled-checks:
- dupImport
- ifElseChain
- octalLiteral
- whyNoLint
- wrapperFunc
# controller-runtime passes spec structs by value all over; flagging
# every 160-byte struct copy is noise for an operator codebase.
- hugeParam
enabled-tags:
- diagnostic
- experimental
- opinionated
- performance
- style
gocyclo:
min-complexity: 20
gosec:
excludes:
- G104
- G304
govet:
disable:
- fieldalignment
# `if err := f(); err != nil` inside a scope that already has an err
# is idiomatic Go; shadow flags all of them.
- shadow
enable-all: true
misspell:
locale: US
revive:
rules:
- name: blank-imports
- name: context-as-argument
- name: context-keys-type
- name: dot-imports
- name: error-return
- name: error-strings
- name: error-naming
- name: exported
- name: if-return
- name: increment-decrement
- name: var-naming
- name: var-declaration
- name: package-comments
- name: range
- name: receiver-naming
- name: time-naming
- name: unexported-return
- name: indent-error-flow
- name: errorf
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
rules:
- name: blank-imports
- name: context-as-argument
- name: context-keys-type
- name: dot-imports
- name: error-return
- name: error-strings
- name: error-naming
- name: exported
- name: if-return
- name: increment-decrement
- name: var-naming
- name: var-declaration
- name: package-comments
- name: range
- name: receiver-naming
- name: time-naming
- name: unexported-return
- name: indent-error-flow
- name: errorf

gosec:
excludes:
- G104 # Audit errors not checked
- G304 # File path provided as taint input

dupl:
threshold: 150

gocritic:
enabled-tags:
- diagnostic
- experimental
- opinionated
- performance
- style
disabled-checks:
- dupImport
- ifElseChain
- octalLiteral
- whyNoLint
- wrapperFunc

- linters:
- dogsled
- dupl
- errcheck
- gocyclo
- gosec
path: _test\.go
# kubebuilder scaffolds init() for scheme registration in api packages
# and manager entrypoints — that's the supported pattern, not a smell.
- linters:
- gochecknoinits
path: ^(api|cmd)/
# Reconcile loops are long state machines by design; the complexity
# budget elsewhere stays at the default.
- linters:
- gocyclo
path: ^internal/controller/
- linters:
- all
path: (.*)\.gen\.go
paths:
- third_party$
- builtin$
- examples$
issues:
exclude-rules:
# Exclude some linters from running on tests files
- path: _test\.go
linters:
- gocyclo
- errcheck
- dupl
- gosec
- goconst

# Exclude known issues in generated files
- path: "(.*)\\.gen\\.go"
linters:
- all

max-issues-per-linter: 0
max-same-issues: 0
new: false
formatters:
enable:
- gofmt
- goimports
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$
1 change: 1 addition & 0 deletions api/v1alpha1/groupversion_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ var (
GroupVersion = schema.GroupVersion{Group: "dploy.dev", Version: "v1alpha1"}

// SchemeBuilder is used to add go types to the GroupVersionKind scheme.
//nolint:staticcheck // scheme.Builder is the kubebuilder-scaffolded pattern; SA1019 is aimed at hand-written api packages.
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}

// AddToScheme adds the types in this group-version to the given scheme.
Expand Down
11 changes: 11 additions & 0 deletions charts/dploy/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,19 @@ auth:
jwtUsernameClaim: preferred_username
oidcClientID: dploy
oidcClientSecret: ""

# oidcIssuer is the URL the API uses for discovery, token exchange and
# JWKS — typically the in-cluster Service URL in a Kubernetes deployment.
oidcIssuer: ""

# oidcPublicIssuer is OPTIONAL — only set it for split-horizon deployments
# where the IdP is reached via two distinct URLs: an in-cluster one for
# backend traffic (oidcIssuer above) and a public one served via the
# ingress/gateway for browser redirects. When equal to oidcIssuer (or
# empty), the API uses a single URL for everything and the split-horizon
# code path is a no-op.
oidcPublicIssuer: ""

oidcRedirectURL: ""

service:
Expand Down
2 changes: 1 addition & 1 deletion cmd/api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ func main() {

// Admin endpoints — gated by MANAGER_ENABLED + the admin claim/value pair.
// 404 when disabled, 403 to non-admin requesters. Shared manager-gate
// middleware so both routes get the same 404 behaviour off-feature.
// middleware so both routes get the same 404 behavior off-feature.
managerGate := func(c *fiber.Ctx) error {
if !cfg.ManagerEnabled {
return c.Status(fiber.StatusNotFound).JSON(models.ErrorResponse{Error: "manager disabled"})
Expand Down
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ require (
github.com/go-openapi/swag/typeutils v0.25.4 // indirect
github.com/go-openapi/swag/yamlutils v0.25.4 // indirect
github.com/google/gnostic-models v0.7.0 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.18.0 // indirect
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,6 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
Expand Down
12 changes: 6 additions & 6 deletions internal/auth/jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,16 @@ func NewJWTValidator(jwksURL, issuer, audience, usernameClaim string) *JWTValida
return &JWTValidator{verifier: verifier, usernameClaim: usernameClaim}
}

// Validate verifies the token and returns (sanitizedUsername, raw claims, err).
// All cryptographic and standard-claim checks live inside Verify; the only
// dploy-specific work is pulling the configured username claim and sanitizing
// it for use as a Kubernetes label.
func (v *JWTValidator) Validate(tokenString string) (string, map[string]any, error) {
// Validate verifies the token and returns the sanitized username plus the raw
// claims. All cryptographic and standard-claim checks live inside Verify; the
// only dploy-specific work is pulling the configured username claim and
// sanitizing it for use as a Kubernetes label.
func (v *JWTValidator) Validate(tokenString string) (username string, claims map[string]any, err error) {
idToken, err := v.verifier.Verify(context.Background(), tokenString)
if err != nil {
return "", nil, fmt.Errorf("token parsing failed: %w", err)
}
claims := map[string]any{}
claims = map[string]any{}
if err := idToken.Claims(&claims); err != nil {
return "", nil, fmt.Errorf("decode claims: %w", err)
}
Expand Down
Loading
Loading