Skip to content

Commit eca4c5d

Browse files
committed
Refactor project service architecture and add authorization middleware
Major refactoring to improve code organization and maintainability: - Extract project endpoints into dedicated file (service_endpoint_project.go) - Move NATS infrastructure code into centralized package with proper interfaces - Add authorization middleware to capture auth headers in request context - Refactor main.go for better separation of concerns and graceful shutdown - Fix unsafe type assertions in context value extraction (prevent panics) - Restore health check endpoints (/livez, /readyz) to ingress route - Add comprehensive tests for authorization middleware - Update NATS message handling to use builder pattern Technical improvements: - Remove redundant slog.Default() calls after log initialization - Fix RequestIDMiddleware test to use correct context constant - Consolidate NATS KV store and messaging models - Improve error handling in NATS setup Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Andres Tobon <andrest2455@gmail.com>
1 parent 8d3e559 commit eca4c5d

File tree

23 files changed

+1646
-1338
lines changed

23 files changed

+1646
-1338
lines changed

README.md

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# LFX V2 Project Service
22

3-
This repository contains the source code the LFX v2 platform project service.
3+
This repository contains the source code for the LFX v2 platform project service.
4+
5+
## Overview
6+
7+
The LFX v2 Project Service is a RESTful API service that manages projects within the Linux Foundation's LFX platform. It provides endpoints for creating, reading, updating, and deleting projects with built-in authorization and audit capabilities.
48

59
## File Structure
610

@@ -10,13 +14,28 @@ This repository contains the source code the LFX v2 platform project service.
1014
├── charts/ # Helm charts for running the service in kubernetes
1115
├── cmd/ # Services (main packages)
1216
│ └── project-api/ # Project service code
17+
│ ├── gen/ # Generated code from Goa design
18+
│ └── design/ # API design specifications
1319
├── internal/ # Internal service packages
1420
│ ├── domain/ # Domain logic layer
1521
│ ├── service/ # Business logic layer
22+
│ ├── middleware/ # HTTP middleware components
1623
│ └── infrastructure/ # Infrastructure layer
17-
└── pkg/ # Shared packages for internal and external services
24+
│ └── nats/ # NATS messaging infrastructure
25+
└── pkg/ # Shared packages
26+
└── constants/ # Shared constants and configurations
1827
```
1928

29+
## Key Features
30+
31+
- **RESTful API**: Full CRUD operations for project management
32+
- **NATS Integration**: Event-driven architecture using NATS for messaging and key-value storage
33+
- **Authorization**: JWT-based authentication with Heimdall middleware integration
34+
- **OpenFGA Support**: Fine-grained authorization control (configurable)
35+
- **Health Checks**: Built-in `/livez` and `/readyz` endpoints
36+
- **Request Tracking**: Automatic request ID generation and propagation
37+
- **Structured Logging**: JSON-formatted logs with contextual information
38+
2039
## Contributing
2140

2241
To contribute to this repository:

charts/lfx-v2-project-service/templates/ingressroute.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ spec:
1616
- kind: Rule
1717
match: >-
1818
Host(`{{.Values.ingress.hostname}}`) &&
19-
(Path(`/projects`) || PathPrefix(`/projects/`))
19+
(Path(`/projects`) || PathPrefix(`/projects/`) || Path(`/livez`) || Path(`/readyz`))
2020
priority: 10
2121
middlewares:
2222
{{- if .Values.heimdall.enabled }}

cmd/project-api/README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ This service handles the following NATS subjects:
3535
├── gen/ # Goa generated implementation code (not committed)
3636
├── main.go # Dependency injection and startup
3737
├── service.go # Base service implementation
38-
├── service_endpoint.go # Service implementation of REST API endpoints
38+
├── service_endpoint.go # Service implementation of health check endpoints
39+
├── service_endpoint_project.go # Service implementation of project REST API endpoints
3940
├── service_handler.go # Service implementation of NATS handlers
4041
├── repo.go # Interface with data stores
4142
├── mock.go # Service mocks for tests
@@ -221,9 +222,9 @@ For local development without OpenFGA:
221222

222223
Note: follow the [Development Workflow](#4-development-workflow) section on how to run the service code
223224

224-
1. **Update design files**: Edit project file in `design/` to include specicification of the new endpoint with all of its supported parameters, responses, and errors, etc.
225+
1. **Update design files**: Edit project file in `design/` to include specification of the new endpoint with all of its supported parameters, responses, and errors, etc.
225226
2. **Regenerate code**: Run `make apigen` after design changes
226-
3. **Implement code**: Implement the new endpoint in `service_endpoint.go`. Follow similar standards of the other endpoint methods. Include tests for the new endpoint in `service_endpoint_test.go`.
227+
3. **Implement code**: Implement the new endpoint in `service_endpoint_project.go` (for project-related endpoints) or create a new `service_endpoint_*.go` file for other resource types. Follow similar standards of the other endpoint methods. Include tests for the new endpoint in the corresponding `*_test.go` file.
227228
4. **Update heimdall ruleset**: Ensure that `/charts/lfx-v2-project-service/templates/ruleset.yaml` has the route and method for the endpoint set so that authentication is configured when deployed. If the endpoint modifies data (PUT, DELETE, PATCH), consider adding OpenFGA authorization checks in the ruleset for proper access control
228229

229230
### Add new message handlers

cmd/project-api/jwt.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,22 +50,22 @@ type jwtAuth struct {
5050
validator *validator.Validator
5151
}
5252

53-
func setupJWTAuth(logger *slog.Logger) *jwtAuth {
53+
func setupJWTAuth() *jwtAuth {
5454
// Set up Heimdall JWKS key provider.
5555
jwksEnv := os.Getenv("JWKS_URL")
5656
if jwksEnv == "" {
5757
jwksEnv = "http://heimdall:4457/.well-known/jwks"
5858
}
5959
jwksURL, err := url.Parse(jwksEnv)
6060
if err != nil {
61-
logger.With(errKey, err).Error("invalid JWKS_URL")
61+
slog.With(errKey, err).Error("invalid JWKS_URL")
6262
os.Exit(1)
6363
}
6464
var issuer *url.URL
6565
issuer, err = url.Parse(defaultIssuer)
6666
if err != nil {
6767
// This shouldn't happen; a bare hostname is a valid URL.
68-
logger.Error("unexpected URL parsing of default issuer")
68+
slog.Error("unexpected URL parsing of default issuer")
6969
os.Exit(1)
7070
}
7171
provider := jwks.NewCachingProvider(issuer, 5*time.Minute, jwks.WithCustomJWKSURI(jwksURL))
@@ -84,7 +84,7 @@ func setupJWTAuth(logger *slog.Logger) *jwtAuth {
8484
validator.WithAllowedClockSkew(5*time.Second),
8585
)
8686
if err != nil {
87-
logger.With(errKey, err).Error("failed to set up the Heimdall JWT validator")
87+
slog.With(errKey, err).Error("failed to set up the Heimdall JWT validator")
8888
os.Exit(1)
8989
}
9090

0 commit comments

Comments
 (0)