Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Agent license inspect command #4813

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

jamlo
Copy link
Contributor

@jamlo jamlo commented Jan 16, 2025

New Command: bacalhau agent license inspect

This PR introduces a new command that allows users to inspect the license information of a Bacalhau orchestrator node without exposing the license itself. The command provides a secure way to verify license status, capabilities, and metadata while maintaining the confidentiality of the underlying license file.

Features

  • New command: bacalhau agent license inspect
  • Supports multiple output formats (default, JSON, YAML)
  • Displays key license information:
    • Product name
    • License ID
    • Customer ID
    • Expiration date
    • Version
    • Capabilities
    • Custom metadata

Security Considerations

  • Does not expose the raw license file or cryptographic material
  • Only returns parsed, necessary information for verification
  • Maintains license confidentiality while providing essential details

License File Structure

The license file, fed to the orchestrator node config, uses a JSON format to support future extensibility and dynamic configuration. The structure is intentionally simple:

{
    "license": "your_license_token_here"
}

We chose JSON format for the license file because:

  • It allows for easy addition of future configuration options
  • Provides a structured way to include additional metadata if needed

Configuration

To add a license to an orchestrator, you need to configure your orchestrator node. Here's a sample configuration example:

NameProvider: "uuid"
API:
  Port: 1234
Orchestrator:
  Enabled: true
  Auth:
    Token: "your_secret_token_here"
  License:
    LocalPath: "/path/to/your/license.json"
Labels:
  label1: label1Value
  label2: label2Value

Key configuration points:

  • The Orchestrator.License.LocalPath field specifies the path to your license file
  • If no license is configured, the command will return an error message saying that no license was configured
  • If the license if expired, the inspect command will return an error message with this info. It will not display the expired licesne details, just a message saying it is expired.

Example Usage

# Default format
$ bacalhau agent license inspect

# JSON format
$ bacalhau agent license inspect --output=json

# YAML format
$ bacalhau agent license inspect --output=yaml

Example Output

For bacalhau agent license inspect:

Product      = Bacalhau
License ID   = 2d58c7c9-ec29-45a5-a5cd-cb8f7fee6678
Customer ID  = test-customer-id-123
Valid Until  = 2045-07-28
Version      = v1
Capabilities = max_nodes=1
Metadata     = someMetadata=valueOfSomeMetadata

For bacalhau agent license inspect --output=yaml:

capabilities:
  max_nodes: "1"
customer_id: test-customer-id-123
exp: 2384889682
iat: 1736889682
iss: https://expanso.io/
jti: 2d58c7c9-ec29-45a5-a5cd-cb8f7fee6678
license_id: 2d58c7c9-ec29-45a5-a5cd-cb8f7fee6678
license_type: standard
license_version: v1
metadata:
  someMetadata: valueOfSomeMetadata
product: Bacalhau
sub: test-customer-id-123

Documentation

  • Added command documentation with usage examples
  • Included field descriptions in help text

Linear: https://linear.app/expanso/issue/ENG-498/license-path-configuration

Summary by CodeRabbit

  • New Features

    • Added a new CLI command to inspect agent license information
    • Introduced a new API endpoint to retrieve agent license details
    • Implemented license configuration support for orchestrator nodes
  • Configuration

    • Added a new configuration option for specifying local license file path
  • API Enhancements

    • Created a new method to retrieve license information via API client
    • Updated Swagger documentation to include license-related endpoints
  • Testing

    • Added comprehensive integration tests for license inspection scenarios
    • Included test cases for various license configuration states

Copy link

linear bot commented Jan 16, 2025

Copy link
Contributor

coderabbitai bot commented Jan 16, 2025

Walkthrough

This pull request introduces a comprehensive license management feature for the Bacalhau project, focusing on agent license inspection and configuration. The changes span multiple components, including CLI commands, API endpoints, configuration types, and integration tests. The new functionality allows users to retrieve and inspect license details for the orchestrator, with support for various output formats and error handling scenarios.

Changes

File Change Summary
cmd/cli/agent/license/inspect.go Added CLI command for agent license inspection with support for different output formats
cmd/cli/agent/license/root.go Created root command for license-related CLI operations
cmd/cli/agent/root.go Integrated license command into the main CLI structure
pkg/config/types/* Added configuration constants and types for license management
pkg/publicapi/apimodels/agent.go Introduced new response type for agent license retrieval
pkg/publicapi/client/v2/api_agent.go Added License() method to API client
pkg/publicapi/endpoint/agent/endpoint.go Implemented license endpoint for retrieving license information
test_integration/* Added comprehensive test suites for license inspection scenarios

Sequence Diagram

sequenceDiagram
    participant CLI as Agent License CLI
    participant Endpoint as License Endpoint
    participant Config as Configuration
    participant Validator as License Validator

    CLI->>Endpoint: GET /api/v1/agent/license
    Endpoint->>Config: Check License Path
    Config-->>Endpoint: Return License Path
    Endpoint->>Endpoint: Read License File
    Endpoint->>Validator: Validate License Token
    Validator-->>Endpoint: Validation Result
    Endpoint-->>CLI: Return License Claims
Loading

Possibly related PRs

Suggested reviewers

  • wdbaruni
  • markkuhn

Poem

🐰 A License to Thrive

In the realm of Bacalhau's might,
A new command shines ever so bright
Inspect your license with grace
JSON, YAML, in every case
Code hopping with licensing delight! 🚀

Finishing Touches

  • 📝 Generate Docstrings (Beta)

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@jamlo jamlo requested review from wdbaruni and markkuhn January 16, 2025 18:58
@jamlo jamlo marked this pull request as ready for review January 16, 2025 20:53
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🔭 Outside diff range comments (1)
pkg/swagger/swagger.json (1)

Line range hint 1-3: Eliminate duplicate Swagger files.

The same Swagger definition exists in both:

  • webui/lib/api/schema/swagger.json
  • pkg/swagger/swagger.json

This duplication can lead to maintenance issues and inconsistencies. Consider:

  1. Keeping a single source of truth
  2. Generating the second file if needed
  3. Using symbolic links if both locations are required
🧹 Nitpick comments (15)
pkg/publicapi/apimodels/agent.go (1)

43-46: LGTM! Consider adding documentation.

The GetAgentLicenseResponse type follows the established pattern and correctly embeds the necessary types. Consider adding documentation comments to describe the purpose and fields of this type, similar to other response types in this file.

Add documentation like this:

+// GetAgentLicenseResponse is the response to the License request.
 type GetAgentLicenseResponse struct {
     BaseGetResponse
     *license.LicenseClaims
 }
cmd/cli/agent/license/root.go (2)

9-11: Consider enhancing the command description.

While the description is clear, it could be more detailed to help users understand the available subcommands and their purposes.

-    Short: "Commands to interact with the orchestrator license",
+    Short: "Commands to interact with the orchestrator license",
+    Long: `Commands to interact with the orchestrator license.
+
+Available Commands:
+  inspect     Inspect the orchestrator license information without revealing the license file`,

7-15: Consider adding PreRun and PostRun hooks.

For consistency with the parent command, consider adding the same hooks that are defined in the agent root command.

 func NewAgentLicenseRootCmd() *cobra.Command {
     cmd := &cobra.Command{
         Use:   "license",
         Short: "Commands to interact with the orchestrator license",
+        PersistentPreRunE:  hook.AfterParentPreRunHook(hook.RemoteCmdPreRunHooks),
+        PersistentPostRunE: hook.AfterParentPostRunHook(hook.RemoteCmdPostRunHooks),
     }
test_integration/15_agent_license_inspect_with_no_license_suite_test.go (1)

41-49: Enhance error handling test coverage.

Consider improving the test coverage by:

  1. Verifying the specific error type in addition to the error message.
  2. Testing different output formats (JSON, YAML) to ensure consistent error handling.
 func (s *AgentLicenseInspectWithNoLicenseSuite) TestValidateRemoteLicenseWithNoLicense() {
-	_, err := s.executeCommandInDefaultJumpbox(
+	// Test default output format
+	_, err := s.executeCommandInDefaultJumpbox(
 		[]string{
 			"bacalhau", "agent", "license", "inspect",
 		},
 	)
-
 	s.Require().ErrorContains(err, "Node license not configured")
+
+	// Test JSON output format
+	_, err = s.executeCommandInDefaultJumpbox(
+		[]string{
+			"bacalhau", "agent", "license", "inspect",
+			"--output=json",
+		},
+	)
+	s.Require().ErrorContains(err, "Node license not configured")
+
+	// Test YAML output format
+	_, err = s.executeCommandInDefaultJumpbox(
+		[]string{
+			"bacalhau", "agent", "license", "inspect",
+			"--output=yaml",
+		},
+	)
+	s.Require().ErrorContains(err, "Node license not configured")
 }
test_integration/13_agent_license_inspect_suite_test.go (2)

49-57: Consider using relative dates for test data.

The test uses a hardcoded far-future date (2045-07-28). Consider using a relative date calculation to make the test more maintainable.

+	// Calculate a date 20 years in the future for testing
+	futureDate := time.Now().AddDate(20, 0, 0).Format("2006-01-02")
 	expectedOutput := `Product      = Bacalhau
 License ID   = e66d1f3a-a8d8-4d57-8f14-00722844afe2
 Customer ID  = test-customer-id-123
-Valid Until  = 2045-07-28
+Valid Until  = ` + futureDate + `
 Version      = v1
 Capabilities = max_nodes=1
 Metadata     = {}`

72-94: Consider using structured JSON parsing for more robust tests.

Instead of using string queries which can be fragile, consider parsing the JSON response into a struct for more reliable validation.

+	type LicenseResponse struct {
+		Product       string            `json:"product"`
+		LicenseID     string            `json:"license_id"`
+		CustomerID    string            `json:"customer_id"`
+		LicenseVersion string           `json:"license_version"`
+		Capabilities  map[string]string `json:"capabilities"`
+	}
+
-	output, err := s.convertStringToDynamicJSON(agentLicenseInspectionOutput)
+	var response LicenseResponse
+	err = json.Unmarshal([]byte(agentLicenseInspectionOutput), &response)
 	s.Require().NoError(err)
 
-	productName, err := output.Query("$.product")
-	s.Require().NoError(err)
-	s.Require().Equal("Bacalhau", productName.String())
+	s.Require().Equal("Bacalhau", response.Product)
+	s.Require().Equal("e66d1f3a-a8d8-4d57-8f14-00722844afe2", response.LicenseID)
+	s.Require().Equal("test-customer-id-123", response.CustomerID)
+	s.Require().Equal("v1", response.LicenseVersion)
+	s.Require().Equal("1", response.Capabilities["max_nodes"])
cmd/cli/agent/license/inspect.go (2)

31-46: Add usage examples to command help text.

The command would benefit from usage examples in the help text to improve user experience.

 	licenseCmd := &cobra.Command{
 		Use:   "inspect",
 		Short: "Get the agent license information",
+		Long: `Get the agent license information.
+
+Examples:
+  # Get license information in default format
+  bacalhau agent license inspect
+
+  # Get license information in JSON format
+  bacalhau agent license inspect --output=json
+
+  # Get license information in YAML format
+  bacalhau agent license inspect --output=yaml`,
 		Args:  cobra.NoArgs,

73-99: Reduce code duplication in map formatting logic.

The code duplicates the map iteration logic for capabilities and metadata. Consider extracting this into a helper function.

+	// Helper function to format map as string
+	formatMapAsString := func(m map[string]string) string {
+		if len(m) == 0 {
+			return "{}"
+		}
+		var items []string
+		for k, v := range m {
+			items = append(items, fmt.Sprintf("%s=%s", k, v))
+		}
+		return strings.Join(items, ", ")
+	}
+
-	// Always show Capabilities
-	capabilitiesStr := "{}"
-	if len(response.Capabilities) > 0 {
-		var caps []string
-		for k, v := range response.Capabilities {
-			caps = append(caps, fmt.Sprintf("%s=%s", k, v))
-		}
-		capabilitiesStr = strings.Join(caps, ", ")
-	}
 	headerData = append(headerData, collections.Pair[string, any]{
 		Left:  "Capabilities",
-		Right: capabilitiesStr,
+		Right: formatMapAsString(response.Capabilities),
 	})
 
-	// Always show Metadata
-	metadataStr := "{}"
-	if len(response.Metadata) > 0 {
-		var meta []string
-		for k, v := range response.Metadata {
-			meta = append(meta, fmt.Sprintf("%s=%s", k, v))
-		}
-		metadataStr = strings.Join(meta, ", ")
-	}
 	headerData = append(headerData, collections.Pair[string, any]{
 		Left:  "Metadata",
-		Right: metadataStr,
+		Right: formatMapAsString(response.Metadata),
 	})
test_integration/14_agent_license_inspect_with_metadata_suite_test.go (1)

60-84: Use structured YAML parsing for more robust tests.

Instead of using multiple string contains assertions which can be fragile, consider parsing the YAML response into a struct for more reliable validation.

+	type LicenseResponse struct {
+		Metadata       map[string]string `yaml:"metadata"`
+		JTI            string            `yaml:"jti"`
+		Issuer         string            `yaml:"iss"`
+		IssuedAt       int64             `yaml:"iat"`
+		ExpiresAt      int64             `yaml:"exp"`
+		CustomerID     string            `yaml:"customer_id"`
+		LicenseID      string            `yaml:"license_id"`
+		LicenseType    string            `yaml:"license_type"`
+		Subject        string            `yaml:"sub"`
+		Product        string            `yaml:"product"`
+		LicenseVersion string            `yaml:"license_version"`
+		Capabilities   map[string]string `yaml:"capabilities"`
+	}
+
 	agentLicenseInspectionOutput, err := s.executeCommandInDefaultJumpbox(
 		[]string{
 			"bacalhau",
@@ -69,16 +83,21 @@
 	)
 	s.Require().NoErrorf(err, "Error inspecting license: %q", err)
 
-	s.Require().Contains(agentLicenseInspectionOutput, "someMetadata: valueOfSomeMetadata")
-	s.Require().Contains(agentLicenseInspectionOutput, "jti: 2d58c7c9-ec29-45a5-a5cd-cb8f7fee6678")
-	s.Require().Contains(agentLicenseInspectionOutput, "iss: https://expanso.io/")
-	s.Require().Contains(agentLicenseInspectionOutput, "iat: 1736889682")
-	s.Require().Contains(agentLicenseInspectionOutput, "exp: 2384889682")
-	s.Require().Contains(agentLicenseInspectionOutput, "customer_id: test-customer-id-123")
-	s.Require().Contains(agentLicenseInspectionOutput, "license_id: 2d58c7c9-ec29-45a5-a5cd-cb8f7fee6678")
-	s.Require().Contains(agentLicenseInspectionOutput, "license_type: standard")
-	s.Require().Contains(agentLicenseInspectionOutput, "sub: test-customer-id-123")
-	s.Require().Contains(agentLicenseInspectionOutput, "product: Bacalhau")
-	s.Require().Contains(agentLicenseInspectionOutput, "license_version: v1")
-	s.Require().Contains(agentLicenseInspectionOutput, "max_nodes: \"1\"")
+	var response LicenseResponse
+	err = yaml.Unmarshal([]byte(agentLicenseInspectionOutput), &response)
+	s.Require().NoError(err)
+
+	s.Require().Equal("valueOfSomeMetadata", response.Metadata["someMetadata"])
+	s.Require().Equal("2d58c7c9-ec29-45a5-a5cd-cb8f7fee6678", response.JTI)
+	s.Require().Equal("https://expanso.io/", response.Issuer)
+	s.Require().Equal(int64(1736889682), response.IssuedAt)
+	s.Require().Equal(int64(2384889682), response.ExpiresAt)
+	s.Require().Equal("test-customer-id-123", response.CustomerID)
+	s.Require().Equal("2d58c7c9-ec29-45a5-a5cd-cb8f7fee6678", response.LicenseID)
+	s.Require().Equal("standard", response.LicenseType)
+	s.Require().Equal("test-customer-id-123", response.Subject)
+	s.Require().Equal("Bacalhau", response.Product)
+	s.Require().Equal("v1", response.LicenseVersion)
+	s.Require().Equal("1", response.Capabilities["max_nodes"])
pkg/publicapi/endpoint/agent/endpoint.go (2)

164-175: Add file size limit and improve JSON handling.

Consider these improvements for robustness:

  1. Add a size limit when reading the license file to prevent memory issues
  2. Use a more strict JSON decoder with DisallowUnknownFields()
 // Read license file
+const maxLicenseSize = 1 << 20 // 1MB limit
+fi, err := os.Stat(licensePath)
+if err != nil {
+    return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("failed to stat license file: %s", err))
+}
+if fi.Size() > maxLicenseSize {
+    return echo.NewHTTPError(http.StatusInternalServerError, "license file too large")
+}
 licenseData, err := os.ReadFile(licensePath)

 // Parse license JSON
 var licenseFile struct {
     License string `json:"license"`
 }
-if err := json.Unmarshal(licenseData, &licenseFile); err != nil {
+decoder := json.NewDecoder(bytes.NewReader(licenseData))
+decoder.DisallowUnknownFields()
+if err := decoder.Decode(&licenseFile); err != nil {

156-191: Improve error handling consistency.

The error handling is good but could be more consistent. Consider wrapping all errors with a common error type that includes:

  • Error code
  • User-friendly message
  • Internal error details (for logging)
+type licenseError struct {
+    Code    int
+    Message string
+    Err     error
+}
+
+func (e *licenseError) Error() string {
+    return e.Message
+}
+
 func (e *Endpoint) license(c echo.Context) error {
     // Get license path from config
     licensePath := e.bacalhauConfig.Orchestrator.License.LocalPath
     if licensePath == "" {
-        return echo.NewHTTPError(http.StatusNotFound, "Node license not configured")
+        return &licenseError{
+            Code:    http.StatusNotFound,
+            Message: "Node license not configured",
+        }
     }
pkg/publicapi/endpoint/agent/endpoint_test.go (1)

60-268: LGTM! Comprehensive test coverage with room for enhancement.

The test suite thoroughly covers the main scenarios. Consider adding these edge cases:

  1. License file with valid JSON but empty license string
  2. License file with permissions issues
  3. License file exceeding size limit (after implementing the size limit)

Would you like me to provide the additional test cases?

pkg/swagger/docs.go (1)

3178-3179: Consider documenting the reason for template delimiter changes.

While the changes to SwaggerInfo's template delimiters look correct, it would be helpful to document why these specific delimiters were chosen.

test_integration/common_assets/nodes_configs/14_orchestrator_config_with_license_with_metadata.yaml (1)

9-9: Use relative paths in test configurations.

Using absolute paths in test configurations can cause portability issues across different environments. Consider using a path relative to the repository root:

-    LocalPath: "/bacalhau_integration_tests/common_assets/licenses/test-license-with-metadata.json"
+    LocalPath: "test_integration/common_assets/licenses/test-license-with-metadata.json"
webui/lib/api/schema/swagger.json (1)

1223-1298: Enhance license claims schema validation.

Consider adding these validations to the license.LicenseClaims schema:

  1. Required fields
  2. String format validations
  3. Enum constraints for license_type

Apply these schema enhancements:

         "license.LicenseClaims": {
             "type": "object",
+            "required": [
+                "license_id",
+                "customer_id",
+                "license_type",
+                "license_version",
+                "product"
+            ],
             "properties": {
                 "license_type": {
-                    "type": "string"
+                    "type": "string",
+                    "enum": ["standard", "enterprise", "trial"]
                 },
                 "license_version": {
-                    "type": "string"
+                    "type": "string",
+                    "pattern": "^v\\d+$"
                 }
             }
         }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bba0b6b and 1d3b71d.

📒 Files selected for processing (20)
  • cmd/cli/agent/license/inspect.go (1 hunks)
  • cmd/cli/agent/license/root.go (1 hunks)
  • cmd/cli/agent/root.go (2 hunks)
  • pkg/config/types/generated_constants.go (1 hunks)
  • pkg/config/types/generated_descriptions.go (1 hunks)
  • pkg/config/types/orchestrator.go (2 hunks)
  • pkg/publicapi/apimodels/agent.go (2 hunks)
  • pkg/publicapi/client/v2/api_agent.go (1 hunks)
  • pkg/publicapi/endpoint/agent/endpoint.go (3 hunks)
  • pkg/publicapi/endpoint/agent/endpoint_test.go (3 hunks)
  • pkg/swagger/docs.go (6 hunks)
  • pkg/swagger/swagger.json (4 hunks)
  • test_integration/13_agent_license_inspect_suite_test.go (1 hunks)
  • test_integration/14_agent_license_inspect_with_metadata_suite_test.go (1 hunks)
  • test_integration/15_agent_license_inspect_with_no_license_suite_test.go (1 hunks)
  • test_integration/common_assets/licenses/test-license-with-metadata.json (1 hunks)
  • test_integration/common_assets/nodes_configs/13_orchestrator_config_with_license.yaml (1 hunks)
  • test_integration/common_assets/nodes_configs/14_orchestrator_config_with_license_with_metadata.yaml (1 hunks)
  • test_integration/common_assets/nodes_configs/15_orchestrator_config_with_no_license.yaml (1 hunks)
  • webui/lib/api/schema/swagger.json (4 hunks)
🧰 Additional context used
🪛 Gitleaks (8.21.2)
test_integration/common_assets/licenses/test-license-with-metadata.json

2-2: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)

pkg/publicapi/endpoint/agent/endpoint_test.go

23-23: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: testcontainers-suite / tests
🔇 Additional comments (15)
pkg/publicapi/client/v2/api_agent.go (1)

40-45: LGTM! Implementation follows established patterns.

The License method is well-implemented, following the same patterns as other methods in this file. The documentation and error handling are appropriate.

cmd/cli/agent/root.go (1)

4-4: LGTM! Command registration is correct.

The license command is properly integrated into the agent CLI command structure, following the established pattern.

Also applies to: 21-21

pkg/config/types/orchestrator.go (2)

22-23: LGTM! Well-structured configuration type.

The License field is properly documented and follows the established patterns for configuration types.


82-85: LGTM! Clean and extensible struct design.

The License struct is minimal yet allows for future extension. The omitempty tags ensure backward compatibility.

pkg/publicapi/endpoint/agent/endpoint.go (2)

50-50: LGTM! Clean route registration.

The license endpoint is properly registered with the API group.


146-155: LGTM! Well-documented endpoint.

The Swagger documentation is comprehensive and accurately describes the endpoint's behavior, including success and error responses.

pkg/config/types/generated_constants.go (1)

89-89: LGTM! Consistent constant definition.

The new constant follows the established naming pattern and integrates well with existing constants.

pkg/publicapi/endpoint/agent/endpoint_test.go (1)

22-23: Review the test license token.

The hardcoded JWT token was flagged by security scanning. While it appears to be a test token, please verify that:

  1. It's not a real production license
  2. The expiration date (2384881638 = Jan 19, 2045) is intentionally far in the future

Consider moving test tokens to a separate test data file or using a helper function to generate test tokens.

🧰 Tools
🪛 Gitleaks (8.21.2)

23-23: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)

pkg/config/types/generated_descriptions.go (1)

91-91: LGTM! Description follows the established pattern.

The description for OrchestratorLicenseLocalPathKey is clear, concise, and follows the same style as other configuration descriptions in the map.

pkg/swagger/docs.go (2)

116-147: LGTM! Well-defined API endpoint with appropriate response codes.

The endpoint is properly documented with:

  • Clear summary and operationId
  • Appropriate response codes (200, 404, 500)
  • Proper schema references

1227-1302: LGTM! Comprehensive license claims schema.

The schema includes all essential JWT fields with proper descriptions and references to RFC 7519, plus custom license-specific fields:

  • Standard JWT claims (aud, exp, iat, iss, jti, nbf, sub)
  • License-specific claims (capabilities, customer_id, license_id, license_type, license_version, metadata, product)
test_integration/common_assets/nodes_configs/15_orchestrator_config_with_no_license.yaml (1)

1-10: LGTM! Valid test configuration for no-license scenario.

The configuration provides all necessary fields for testing the orchestrator without a license:

  • Unique node identification (uuid)
  • API configuration
  • Authentication token
  • Test labels
test_integration/common_assets/nodes_configs/13_orchestrator_config_with_license.yaml (2)

1-12: LGTM! Valid test configuration for license scenario.

The configuration includes all necessary fields including the license configuration.


8-9: Verify the license file path in the test environment.

The license file path is hardcoded to /bacalhau_integration_tests/common_assets/licenses/test-license.json. Ensure this path is correctly set up in the test environment.

✅ Verification successful

License file path is correctly configured

The path follows the established pattern where test_integration is mounted as /bacalhau_integration_tests in the test environment, and the license file exists at the expected location.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the existence of the test license file
# Test: Check if the license file exists in the test assets
fd -t f "test-license.json" test_integration/common_assets/licenses/

Length of output: 126


Script:

#!/bin/bash
# Search for similar path patterns in yaml files
rg -g "*.yaml" -g "*.yml" "/bacalhau_integration_tests" test_integration/

Length of output: 2980

test_integration/common_assets/licenses/test-license-with-metadata.json (1)

1-3: Verify test JWT token validity and expiration.

The file contains a JWT token that could pose a security risk if valid. Please ensure:

  1. This is a test-only token with no production credentials
  2. The token is expired or invalidated
  3. Consider using a shorter token or placeholder in test files

Run the following script to check the token's validity and expiration:

✅ Verification successful

Confirmed: Test-only license token verified

This is a valid test license file based on:

  • Located in test assets directory
  • Contains test-specific identifiers (bacalhau-integration-tests, test-customer-id-123)
  • Properly isolated in test fixtures
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify if the JWT token is expired and test-only
# Note: This script only checks the token structure and claims, not its cryptographic validity

echo "Decoding JWT token to verify claims..."
TOKEN=$(cat test_integration/common_assets/licenses/test-license-with-metadata.json | jq -r .license)
echo $TOKEN | awk -F. '{print $2}' | base64 -d 2>/dev/null | jq .

Length of output: 2274


Script:

#!/bin/bash
# Description: Verify JWT token with proper base64 padding
echo "Decoding JWT token to verify claims..."
TOKEN=$(cat test_integration/common_assets/licenses/test-license-with-metadata.json | jq -r .license)
echo $TOKEN | awk -F. '{print $2}' | base64 -d 2>/dev/null | python3 -m json.tool 2>/dev/null || \
(echo $TOKEN | awk -F. '{print $2 "=="}' | base64 -d 2>/dev/null | python3 -m json.tool)

Length of output: 3475


Script:

#!/bin/bash
# Description: Simple JWT token decode using only base64
echo "Decoding JWT token to verify claims..."
TOKEN=$(cat test_integration/common_assets/licenses/test-license-with-metadata.json | jq -r .license)
echo "JWT Payload:"
echo $TOKEN | cut -d"." -f2 | { base64 -d 2>/dev/null || base64 -D 2>/dev/null; }

Length of output: 2305


Script:

#!/bin/bash
# Description: Decode URL-safe base64 JWT token
echo "Decoding JWT token to verify claims..."
TOKEN=$(cat test_integration/common_assets/licenses/test-license-with-metadata.json | jq -r .license)
echo "JWT Payload:"
echo $TOKEN | cut -d"." -f2 | tr '_-' '/+' | tr -d '\n' | fold -w 4 | sed '$ s/$/==/' | head -c -4 | base64 -d

Length of output: 2552

🧰 Tools
🪛 Gitleaks (8.21.2)

2-2: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)

Comment on lines +112 to +143
"/api/v1/agent/license": {
"get": {
"produces": [
"application/json"
],
"tags": [
"Ops"
],
"summary": "Returns the details of the current configured orchestrator license.",
"operationId": "agent/license",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/license.LicenseClaims"
}
},
"404": {
"description": "Node license not configured",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "string"
}
}
}
}
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add authentication requirement to license endpoint.

The license endpoint may expose sensitive information. Consider:

  1. Adding authentication requirement
  2. Implementing rate limiting
  3. Adding appropriate security headers

Apply this diff to add authentication:

         "/api/v1/agent/license": {
             "get": {
                 "produces": [
                     "application/json"
                 ],
+                "security": [
+                    {
+                        "ApiKeyAuth": []
+                    }
+                ],
                 "tags": [
                     "Ops"
                 ],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"/api/v1/agent/license": {
"get": {
"produces": [
"application/json"
],
"tags": [
"Ops"
],
"summary": "Returns the details of the current configured orchestrator license.",
"operationId": "agent/license",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/license.LicenseClaims"
}
},
"404": {
"description": "Node license not configured",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "string"
}
}
}
}
},
"/api/v1/agent/license": {
"get": {
"produces": [
"application/json"
],
"security": [
{
"ApiKeyAuth": []
}
],
"tags": [
"Ops"
],
"summary": "Returns the details of the current configured orchestrator license.",
"operationId": "agent/license",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/license.LicenseClaims"
}
},
"404": {
"description": "Node license not configured",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "string"
}
}
}
}
},

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant