Fix all golangci-lint warnings

- errorlint: use errors.Is for db.ErrNotFound comparisons
  in accountservice.go, credentialservice.go, tokenservice.go
- gofmt/goimports: move mciasv1 alias into internal import group
  in auth.go, credentialservice.go, grpcserver.go, grpcserver_test.go
- gosec G115: add nolint annotation on int32 port conversions
  in mciasgrpcctl/main.go and credentialservice.go (port validated
  as [1,65535] on input; overflow not reachable)
- govet fieldalignment: reorder Server, grpcRateLimiter,
  grpcRateLimitEntry, testEnv structs to reduce GC bitmap size
  (96 -> 80 pointer bytes each)
- ineffassign: remove intermediate grpcSrv = GRPCServer() call
  in cmd/mciassrv/main.go (immediately overwritten by TLS build)
- staticcheck SA9003: replace empty if-body with _ = Serve(lis)
  in grpcserver_test.go
0 golangci-lint issues; 137 tests pass (go test -race ./...)
This commit is contained in:
2026-03-11 15:24:07 -07:00
parent 941c71f2d1
commit f34e9a69a0
8 changed files with 35 additions and 37 deletions

View File

@@ -506,7 +506,7 @@ func (c *controller) pgCredsSet(args []string) {
Id: *id, Id: *id,
Creds: &mciasv1.PGCreds{ Creds: &mciasv1.PGCreds{
Host: *host, Host: *host,
Port: int32(*port), Port: int32(*port), //nolint:gosec // G115: port validated as [1,65535] by flag parsing
Database: *dbName, Database: *dbName,
Username: *username, Username: *username,
Password: *password, Password: *password,

View File

@@ -132,11 +132,8 @@ func run(configPath string, logger *slog.Logger) error {
} }
grpcSrvImpl := grpcserver.New(database, cfg, privKey, pubKey, masterKey, logger) grpcSrvImpl := grpcserver.New(database, cfg, privKey, pubKey, masterKey, logger)
grpcSrv = grpcSrvImpl.GRPCServer() // Build server directly with TLS credentials. GRPCServerWithCreds builds
// Apply TLS to the gRPC server by wrapping options. // the server with transport credentials at construction time per gRPC idiom.
// We reconstruct the server with TLS credentials since GRPCServer()
// returns an already-built server; instead, build with creds directly.
// Re-create with TLS option.
grpcSrv = rebuildGRPCServerWithTLS(grpcSrvImpl, grpcTLSCreds) grpcSrv = rebuildGRPCServerWithTLS(grpcSrvImpl, grpcTLSCreds)
grpcListener, err = net.Listen("tcp", cfg.Server.GRPCAddr) grpcListener, err = net.Listen("tcp", cfg.Server.GRPCAddr)

View File

@@ -4,16 +4,17 @@ package grpcserver
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb" "google.golang.org/protobuf/types/known/timestamppb"
mciasv1 "git.wntrmute.dev/kyle/mcias/gen/mcias/v1"
"git.wntrmute.dev/kyle/mcias/internal/auth" "git.wntrmute.dev/kyle/mcias/internal/auth"
"git.wntrmute.dev/kyle/mcias/internal/db" "git.wntrmute.dev/kyle/mcias/internal/db"
"git.wntrmute.dev/kyle/mcias/internal/model" "git.wntrmute.dev/kyle/mcias/internal/model"
mciasv1 "git.wntrmute.dev/kyle/mcias/gen/mcias/v1"
) )
type accountServiceServer struct { type accountServiceServer struct {
@@ -101,7 +102,7 @@ func (a *accountServiceServer) GetAccount(ctx context.Context, req *mciasv1.GetA
} }
acct, err := a.s.db.GetAccountByUUID(req.Id) acct, err := a.s.db.GetAccountByUUID(req.Id)
if err != nil { if err != nil {
if err == db.ErrNotFound { if errors.Is(err, db.ErrNotFound) {
return nil, status.Error(codes.NotFound, "account not found") return nil, status.Error(codes.NotFound, "account not found")
} }
return nil, status.Error(codes.Internal, "internal error") return nil, status.Error(codes.Internal, "internal error")
@@ -119,7 +120,7 @@ func (a *accountServiceServer) UpdateAccount(ctx context.Context, req *mciasv1.U
} }
acct, err := a.s.db.GetAccountByUUID(req.Id) acct, err := a.s.db.GetAccountByUUID(req.Id)
if err != nil { if err != nil {
if err == db.ErrNotFound { if errors.Is(err, db.ErrNotFound) {
return nil, status.Error(codes.NotFound, "account not found") return nil, status.Error(codes.NotFound, "account not found")
} }
return nil, status.Error(codes.Internal, "internal error") return nil, status.Error(codes.Internal, "internal error")
@@ -149,7 +150,7 @@ func (a *accountServiceServer) DeleteAccount(ctx context.Context, req *mciasv1.D
} }
acct, err := a.s.db.GetAccountByUUID(req.Id) acct, err := a.s.db.GetAccountByUUID(req.Id)
if err != nil { if err != nil {
if err == db.ErrNotFound { if errors.Is(err, db.ErrNotFound) {
return nil, status.Error(codes.NotFound, "account not found") return nil, status.Error(codes.NotFound, "account not found")
} }
return nil, status.Error(codes.Internal, "internal error") return nil, status.Error(codes.Internal, "internal error")
@@ -174,7 +175,7 @@ func (a *accountServiceServer) GetRoles(ctx context.Context, req *mciasv1.GetRol
} }
acct, err := a.s.db.GetAccountByUUID(req.Id) acct, err := a.s.db.GetAccountByUUID(req.Id)
if err != nil { if err != nil {
if err == db.ErrNotFound { if errors.Is(err, db.ErrNotFound) {
return nil, status.Error(codes.NotFound, "account not found") return nil, status.Error(codes.NotFound, "account not found")
} }
return nil, status.Error(codes.Internal, "internal error") return nil, status.Error(codes.Internal, "internal error")
@@ -199,7 +200,7 @@ func (a *accountServiceServer) SetRoles(ctx context.Context, req *mciasv1.SetRol
} }
acct, err := a.s.db.GetAccountByUUID(req.Id) acct, err := a.s.db.GetAccountByUUID(req.Id)
if err != nil { if err != nil {
if err == db.ErrNotFound { if errors.Is(err, db.ErrNotFound) {
return nil, status.Error(codes.NotFound, "account not found") return nil, status.Error(codes.NotFound, "account not found")
} }
return nil, status.Error(codes.Internal, "internal error") return nil, status.Error(codes.Internal, "internal error")

View File

@@ -12,11 +12,11 @@ import (
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb" "google.golang.org/protobuf/types/known/timestamppb"
mciasv1 "git.wntrmute.dev/kyle/mcias/gen/mcias/v1"
"git.wntrmute.dev/kyle/mcias/internal/auth" "git.wntrmute.dev/kyle/mcias/internal/auth"
"git.wntrmute.dev/kyle/mcias/internal/crypto" "git.wntrmute.dev/kyle/mcias/internal/crypto"
"git.wntrmute.dev/kyle/mcias/internal/model" "git.wntrmute.dev/kyle/mcias/internal/model"
"git.wntrmute.dev/kyle/mcias/internal/token" "git.wntrmute.dev/kyle/mcias/internal/token"
mciasv1 "git.wntrmute.dev/kyle/mcias/gen/mcias/v1"
) )
type authServiceServer struct { type authServiceServer struct {

View File

@@ -4,14 +4,15 @@ package grpcserver
import ( import (
"context" "context"
"errors"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
mciasv1 "git.wntrmute.dev/kyle/mcias/gen/mcias/v1"
"git.wntrmute.dev/kyle/mcias/internal/crypto" "git.wntrmute.dev/kyle/mcias/internal/crypto"
"git.wntrmute.dev/kyle/mcias/internal/db" "git.wntrmute.dev/kyle/mcias/internal/db"
"git.wntrmute.dev/kyle/mcias/internal/model" "git.wntrmute.dev/kyle/mcias/internal/model"
mciasv1 "git.wntrmute.dev/kyle/mcias/gen/mcias/v1"
) )
type credentialServiceServer struct { type credentialServiceServer struct {
@@ -31,7 +32,7 @@ func (c *credentialServiceServer) GetPGCreds(ctx context.Context, req *mciasv1.G
} }
acct, err := c.s.db.GetAccountByUUID(req.Id) acct, err := c.s.db.GetAccountByUUID(req.Id)
if err != nil { if err != nil {
if err == db.ErrNotFound { if errors.Is(err, db.ErrNotFound) {
return nil, status.Error(codes.NotFound, "account not found") return nil, status.Error(codes.NotFound, "account not found")
} }
return nil, status.Error(codes.Internal, "internal error") return nil, status.Error(codes.Internal, "internal error")
@@ -39,7 +40,7 @@ func (c *credentialServiceServer) GetPGCreds(ctx context.Context, req *mciasv1.G
cred, err := c.s.db.ReadPGCredentials(acct.ID) cred, err := c.s.db.ReadPGCredentials(acct.ID)
if err != nil { if err != nil {
if err == db.ErrNotFound { if errors.Is(err, db.ErrNotFound) {
return nil, status.Error(codes.NotFound, "no credentials stored") return nil, status.Error(codes.NotFound, "no credentials stored")
} }
return nil, status.Error(codes.Internal, "internal error") return nil, status.Error(codes.Internal, "internal error")
@@ -59,7 +60,7 @@ func (c *credentialServiceServer) GetPGCreds(ctx context.Context, req *mciasv1.G
Database: cred.PGDatabase, Database: cred.PGDatabase,
Username: cred.PGUsername, Username: cred.PGUsername,
Password: string(password), // security: returned only on explicit admin request Password: string(password), // security: returned only on explicit admin request
Port: int32(cred.PGPort), Port: int32(cred.PGPort), //nolint:gosec // G115: PGPort is validated as [1,65535] on write
}, },
}, nil }, nil
} }
@@ -87,7 +88,7 @@ func (c *credentialServiceServer) SetPGCreds(ctx context.Context, req *mciasv1.S
acct, err := c.s.db.GetAccountByUUID(req.Id) acct, err := c.s.db.GetAccountByUUID(req.Id)
if err != nil { if err != nil {
if err == db.ErrNotFound { if errors.Is(err, db.ErrNotFound) {
return nil, status.Error(codes.NotFound, "account not found") return nil, status.Error(codes.NotFound, "account not found")
} }
return nil, status.Error(codes.Internal, "internal error") return nil, status.Error(codes.Internal, "internal error")

View File

@@ -31,10 +31,10 @@ import (
"google.golang.org/grpc/peer" "google.golang.org/grpc/peer"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
mciasv1 "git.wntrmute.dev/kyle/mcias/gen/mcias/v1"
"git.wntrmute.dev/kyle/mcias/internal/config" "git.wntrmute.dev/kyle/mcias/internal/config"
"git.wntrmute.dev/kyle/mcias/internal/db" "git.wntrmute.dev/kyle/mcias/internal/db"
"git.wntrmute.dev/kyle/mcias/internal/token" "git.wntrmute.dev/kyle/mcias/internal/token"
mciasv1 "git.wntrmute.dev/kyle/mcias/gen/mcias/v1"
) )
// contextKey is the unexported context key type for this package. // contextKey is the unexported context key type for this package.
@@ -55,10 +55,10 @@ func claimsFromContext(ctx context.Context) *token.Claims {
type Server struct { type Server struct {
db *db.DB db *db.DB
cfg *config.Config cfg *config.Config
logger *slog.Logger
privKey ed25519.PrivateKey privKey ed25519.PrivateKey
pubKey ed25519.PublicKey pubKey ed25519.PublicKey
masterKey []byte masterKey []byte
logger *slog.Logger
} }
// New creates a Server with the given dependencies (same as the REST Server). // New creates a Server with the given dependencies (same as the REST Server).
@@ -217,17 +217,17 @@ func (s *Server) requireAdmin(ctx context.Context) error {
// grpcRateLimiter is a per-IP token bucket for gRPC, sharing the same // grpcRateLimiter is a per-IP token bucket for gRPC, sharing the same
// algorithm as the REST RateLimit middleware. // algorithm as the REST RateLimit middleware.
type grpcRateLimiter struct { type grpcRateLimiter struct {
mu sync.Mutex
ips map[string]*grpcRateLimitEntry ips map[string]*grpcRateLimitEntry
rps float64 rps float64
burst float64 burst float64
ttl time.Duration ttl time.Duration
mu sync.Mutex
} }
type grpcRateLimitEntry struct { type grpcRateLimitEntry struct {
mu sync.Mutex
lastSeen time.Time lastSeen time.Time
tokens float64 tokens float64
mu sync.Mutex
} }
func newGRPCRateLimiter(rps float64, burst int) *grpcRateLimiter { func newGRPCRateLimiter(rps float64, burst int) *grpcRateLimiter {

View File

@@ -22,12 +22,12 @@ import (
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
"google.golang.org/grpc/test/bufconn" "google.golang.org/grpc/test/bufconn"
mciasv1 "git.wntrmute.dev/kyle/mcias/gen/mcias/v1"
"git.wntrmute.dev/kyle/mcias/internal/auth" "git.wntrmute.dev/kyle/mcias/internal/auth"
"git.wntrmute.dev/kyle/mcias/internal/config" "git.wntrmute.dev/kyle/mcias/internal/config"
"git.wntrmute.dev/kyle/mcias/internal/db" "git.wntrmute.dev/kyle/mcias/internal/db"
"git.wntrmute.dev/kyle/mcias/internal/model" "git.wntrmute.dev/kyle/mcias/internal/model"
"git.wntrmute.dev/kyle/mcias/internal/token" "git.wntrmute.dev/kyle/mcias/internal/token"
mciasv1 "git.wntrmute.dev/kyle/mcias/gen/mcias/v1"
) )
const ( const (
@@ -38,11 +38,11 @@ const (
// testEnv holds all resources for a single test's gRPC server. // testEnv holds all resources for a single test's gRPC server.
type testEnv struct { type testEnv struct {
db *db.DB db *db.DB
cfg *config.Config
conn *grpc.ClientConn
priv ed25519.PrivateKey priv ed25519.PrivateKey
pub ed25519.PublicKey pub ed25519.PublicKey
masterKey []byte masterKey []byte
cfg *config.Config
conn *grpc.ClientConn
} }
// newTestEnv spins up an in-process gRPC server using bufconn and returns // newTestEnv spins up an in-process gRPC server using bufconn and returns
@@ -76,9 +76,7 @@ func newTestEnv(t *testing.T) *testEnv {
lis := bufconn.Listen(bufConnSize) lis := bufconn.Listen(bufConnSize)
go func() { go func() {
if err := grpcSrv.Serve(lis); err != nil { _ = grpcSrv.Serve(lis) // returns on listener close; error ignored
// Serve returns when the listener is closed; ignore that error.
}
}() }()
conn, err := grpc.NewClient( conn, err := grpc.NewClient(

View File

@@ -3,16 +3,17 @@ package grpcserver
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb" "google.golang.org/protobuf/types/known/timestamppb"
mciasv1 "git.wntrmute.dev/kyle/mcias/gen/mcias/v1"
"git.wntrmute.dev/kyle/mcias/internal/db" "git.wntrmute.dev/kyle/mcias/internal/db"
"git.wntrmute.dev/kyle/mcias/internal/model" "git.wntrmute.dev/kyle/mcias/internal/model"
"git.wntrmute.dev/kyle/mcias/internal/token" "git.wntrmute.dev/kyle/mcias/internal/token"
mciasv1 "git.wntrmute.dev/kyle/mcias/gen/mcias/v1"
) )
type tokenServiceServer struct { type tokenServiceServer struct {
@@ -110,7 +111,7 @@ func (ts *tokenServiceServer) RevokeToken(ctx context.Context, req *mciasv1.Revo
} }
if err := ts.s.db.RevokeToken(req.Jti, "admin revocation"); err != nil { if err := ts.s.db.RevokeToken(req.Jti, "admin revocation"); err != nil {
if err == db.ErrNotFound { if errors.Is(err, db.ErrNotFound) {
return nil, status.Error(codes.NotFound, "token not found or already revoked") return nil, status.Error(codes.NotFound, "token not found or already revoked")
} }
return nil, status.Error(codes.Internal, "internal error") return nil, status.Error(codes.Internal, "internal error")