Skip to content
Open
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
6 changes: 3 additions & 3 deletions src/integration/frameworks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ func testFrameworks(platform switchblade.Platform, fixtures string) func(*testin
deployment, logs, err := platform.Deploy.
WithEnv(map[string]string{
"BP_JAVA_VERSION": "11",
"JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER": "'{enabled: true}'",
"JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER": "{ enabled: true }",
}).
Execute(name, filepath.Join(fixtures, "containers", "tomcat"))
Expect(err).NotTo(HaveOccurred(), logs.String)
Expand All @@ -550,7 +550,7 @@ func testFrameworks(platform switchblade.Platform, fixtures string) func(*testin
deployment, logs, err := platform.Deploy.
WithEnv(map[string]string{
"BP_JAVA_VERSION": "11",
"JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER": "'{enabled: false}'",
"JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER": "{ enabled: false }",
}).
Execute(name, filepath.Join(fixtures, "containers", "tomcat"))
Expect(err).NotTo(HaveOccurred(), logs.String)
Expand Down Expand Up @@ -851,7 +851,7 @@ func testFrameworks(platform switchblade.Platform, fixtures string) func(*testin
deployment, logs, err := platform.Deploy.
WithEnv(map[string]string{
"BP_JAVA_VERSION": "11",
"JBP_CONFIG_JPROFILER_PROFILER": "'{enabled: true}'",
"JBP_CONFIG_JPROFILER_PROFILER": "{ enabled: true }",
}).
Execute(name, filepath.Join(fixtures, "containers", "spring_boot_staged"))
Expect(err).NotTo(HaveOccurred(), logs.String)
Expand Down
3 changes: 1 addition & 2 deletions src/java/common/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ package common
import (
"encoding/json"
"fmt"
"github.com/cloudfoundry/libbuildpack"
"os"
"path/filepath"
"strconv"
"strings"

"github.com/cloudfoundry/libbuildpack"
)

// Context holds shared dependencies for buildpack components
Expand Down
26 changes: 26 additions & 0 deletions src/java/common/yaml_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package common

import (
"bytes"
"go.yaml.in/yaml/v3"
)

// YamlHandler provides a thin wrapper around yaml.v3's Marshal and Unmarshal.
type YamlHandler struct{}

// Unmarshal decodes the YAML data into the provided destination.
func (h YamlHandler) Unmarshal(data []byte, out any) error {
return yaml.Unmarshal(data, out)
}

// Marshal encodes the given value into YAML.
func (h YamlHandler) Marshal(in any) ([]byte, error) {
return yaml.Marshal(in)
}

// ValidateFields is used to detect unknown fields during parsing of JBP_CONFIG* configurations
func (h YamlHandler) ValidateFields(data []byte, out interface{}) error {
dec := yaml.NewDecoder(bytes.NewReader(data))
dec.KnownFields(true)
return dec.Decode(out)
}
46 changes: 29 additions & 17 deletions src/java/frameworks/client_certificate_mapper.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package frameworks

import (
"github.com/cloudfoundry/java-buildpack/src/java/common"
"fmt"
"github.com/cloudfoundry/java-buildpack/src/java/common"
"os"
"path/filepath"
)
Expand All @@ -23,7 +23,13 @@ func NewClientCertificateMapperFramework(ctx *common.Context) *ClientCertificate
// Enabled by default to support mTLS scenarios, can be disabled via configuration
func (c *ClientCertificateMapperFramework) Detect() (string, error) {
// Check if explicitly disabled via configuration
if !c.isEnabled() {
config, err := c.loadConfig()
if err != nil {
c.context.Log.Warning("Failed to load ccm config: %s", err.Error())
return "", nil // Don't fail the build
}

if !config.isEnabled() {
return "", nil
}

Expand Down Expand Up @@ -77,25 +83,31 @@ func (c *ClientCertificateMapperFramework) Finalize() error {
return nil
}

// isEnabled checks if client certificate mapper is enabled
// Default is true (enabled) to support mTLS scenarios unless explicitly disabled
func (c *ClientCertificateMapperFramework) isEnabled() bool {
// Check JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER environment variable
func (c *ClientCertificateMapperFramework) loadConfig() (*ccmConfig, error) {
// initialize default values
mapperConfig := ccmConfig{
Enabled: true,
}
config := os.Getenv("JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER")

// Parse the config to check for enabled: false
// For simplicity, if JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER is set and contains "enabled", check its value
// A more robust implementation would parse YAML
if config != "" {
// Simple check: if it contains "enabled: false" or "'enabled': false"
if contains(config, "enabled: false") || contains(config, "'enabled': false") {
return false
yamlHandler := common.YamlHandler{}
err := yamlHandler.ValidateFields([]byte(config), &mapperConfig)
if err != nil {
c.context.Log.Warning("Unknown user config values: %s", err.Error())
}
if contains(config, "enabled: true") || contains(config, "'enabled': true") {
return true
// overlay JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER over default values
if err = yamlHandler.Unmarshal([]byte(config), &mapperConfig); err != nil {
return nil, fmt.Errorf("failed to parse JBP_CONFIG_CLIENT_CERTIFICATE_MAPPER: %w", err)
}
}
return &mapperConfig, nil
}

// Default to enabled (to support mTLS client certificate authentication)
return true
type ccmConfig struct {
Enabled bool `yaml:"enabled"`
}

// isEnabled checks if client certificate mapper is enabled
func (c *ccmConfig) isEnabled() bool {
return c.Enabled
}
64 changes: 31 additions & 33 deletions src/java/frameworks/container_security_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,17 @@ func (c *ContainerSecurityProviderFramework) Finalize() error {
return fmt.Errorf("failed to write security properties: %w", err)
}

config, err := c.loadConfig()
if err != nil {
c.context.Log.Warning("Failed to load container security provider config: %s", err.Error())
}
// Add key manager and trust manager configuration if specified
keyManagerEnabled := c.getKeyManagerEnabled()
keyManagerEnabled := config.getKeyManagerEnabled()
if keyManagerEnabled != "" {
javaOpts += fmt.Sprintf(" -Dorg.cloudfoundry.security.keymanager.enabled=%s", keyManagerEnabled)
}

trustManagerEnabled := c.getTrustManagerEnabled()
trustManagerEnabled := config.getTrustManagerEnabled()
if trustManagerEnabled != "" {
javaOpts += fmt.Sprintf(" -Dorg.cloudfoundry.security.trustmanager.enabled=%s", trustManagerEnabled)
}
Expand Down Expand Up @@ -214,44 +218,38 @@ func (c *ContainerSecurityProviderFramework) getDefaultSecurityProviders() []str
}
}

// getKeyManagerEnabled returns the key_manager_enabled configuration value
func (c *ContainerSecurityProviderFramework) getKeyManagerEnabled() string {
config := os.Getenv("JBP_CONFIG_CONTAINER_SECURITY_PROVIDER")
if config == "" {
return ""
func (c *ContainerSecurityProviderFramework) loadConfig() (*cspConfig, error) {
// initialize default values
secConfig := cspConfig{
KeyManagerEnabled: "",
TrustManagerEnabled: "",
}

// Parse configuration for key_manager_enabled
// Format: {key_manager_enabled: true} or {'key_manager_enabled': 'true'}
if contains(config, "key_manager_enabled") {
if contains(config, "true") {
return "true"
config := os.Getenv("JBP_CONFIG_CONTAINER_SECURITY_PROVIDER")
if config != "" {
yamlHandler := common.YamlHandler{}
err := yamlHandler.ValidateFields([]byte(config), &secConfig)
if err != nil {
c.context.Log.Warning("Unknown user config values: %s", err.Error())
}
if contains(config, "false") {
return "false"
// overlay JBP_CONFIG_CONTAINER_SECURITY_PROVIDER over default values
if err = yamlHandler.Unmarshal([]byte(config), &secConfig); err != nil {
return nil, fmt.Errorf("failed to parse JBP_CONFIG_CONTAINER_SECURITY_PROVIDER: %w", err)
}
}
return &secConfig, nil
}

return ""
// getKeyManagerEnabled returns the key_manager_enabled configuration value
func (c *cspConfig) getKeyManagerEnabled() string {
return c.KeyManagerEnabled
}

// getTrustManagerEnabled returns the trust_manager_enabled configuration value
func (c *ContainerSecurityProviderFramework) getTrustManagerEnabled() string {
config := os.Getenv("JBP_CONFIG_CONTAINER_SECURITY_PROVIDER")
if config == "" {
return ""
}

// Parse configuration for trust_manager_enabled
// Format: {trust_manager_enabled: true} or {'trust_manager_enabled': 'true'}
if contains(config, "trust_manager_enabled") {
if contains(config, "true") {
return "true"
}
if contains(config, "false") {
return "false"
}
}
func (c *cspConfig) getTrustManagerEnabled() string {
return c.TrustManagerEnabled
}

return ""
type cspConfig struct {
KeyManagerEnabled string `yaml:"key_manager_enabled"`
TrustManagerEnabled string `yaml:"trust_manager_enabled"`
}
104 changes: 53 additions & 51 deletions src/java/frameworks/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,32 @@ func NewDebugFramework(ctx *common.Context) *DebugFramework {
// Detect checks if debugging should be enabled
func (d *DebugFramework) Detect() (string, error) {
// Check if debug is enabled in configuration
enabled := d.isEnabled()
if !enabled {
config, err := d.loadConfig()
if err != nil {
d.context.Log.Warning("Failed to load debug config: %s", err.Error())
return "", nil // Don't fail the build
}
if !config.isEnabled() {
return "", nil
}

port := d.getPort()
port := config.getPort()
return fmt.Sprintf("debug=%d", port), nil
}

// Supply performs debug setup during supply phase
func (d *DebugFramework) Supply() error {
if !d.isEnabled() {
config, err := d.loadConfig()
if err != nil {
d.context.Log.Warning("Failed to load debug config: %s", err.Error())
return nil // Don't fail the build
}
if !config.isEnabled() {
return nil
}

port := d.getPort()
suspend := d.getSuspend()
port := config.getPort()
suspend := config.getSuspend()

suspendMsg := ""
if suspend {
Expand All @@ -50,12 +59,17 @@ func (d *DebugFramework) Supply() error {

// Finalize adds debug options to JAVA_OPTS
func (d *DebugFramework) Finalize() error {
if !d.isEnabled() {
config, err := d.loadConfig()
if err != nil {
d.context.Log.Warning("Failed to load debug config: %s", err.Error())
return nil // Don't fail the build
}
if !config.isEnabled() {
return nil
}

port := d.getPort()
suspend := d.getSuspend()
port := config.getPort()
suspend := config.getSuspend()

// Build JDWP agent string
suspendValue := "n"
Expand All @@ -74,7 +88,7 @@ func (d *DebugFramework) Finalize() error {
}

// isEnabled checks if debugging is enabled
func (d *DebugFramework) isEnabled() bool {
func (d *debugConfig) isEnabled() bool {
// Check BPL_DEBUG_ENABLED first (Cloud Native Buildpacks convention)
bplEnabled := os.Getenv("BPL_DEBUG_ENABLED")
if bplEnabled == "true" || bplEnabled == "1" {
Expand All @@ -84,28 +98,11 @@ func (d *DebugFramework) isEnabled() bool {
return false
}

// Check JBP_CONFIG_DEBUG environment variable (Java Buildpack convention)
config := os.Getenv("JBP_CONFIG_DEBUG")

// Parse the config to check for enabled: true
// For simplicity, if JBP_CONFIG_DEBUG is set and contains "enabled", check its value
// A more robust implementation would parse YAML
if config != "" {
// Simple check: if it contains "enabled: true" or just "true"
if contains(config, "enabled: true") || contains(config, "'enabled': true") {
return true
}
if contains(config, "enabled: false") || contains(config, "'enabled': false") {
return false
}
}

// Default to disabled (matches Ruby buildpack default)
return false
return d.Enabled
}

// getPort returns the debug port
func (d *DebugFramework) getPort() int {
func (d *debugConfig) getPort() int {
// Check BPL_DEBUG_PORT first (Cloud Native Buildpacks convention)
bplPort := os.Getenv("BPL_DEBUG_PORT")
if bplPort != "" {
Expand All @@ -114,35 +111,40 @@ func (d *DebugFramework) getPort() int {
}
}

// Check JBP_CONFIG_DEBUG for port setting (Java Buildpack convention)
config := os.Getenv("JBP_CONFIG_DEBUG")
if config != "" {
// Simple parsing - look for port: XXXX
// A more robust implementation would parse YAML
if idx := findInString(config, "port:"); idx != -1 {
portStr := extractNumber(config[idx:])
if port, err := strconv.Atoi(portStr); err == nil && port > 0 {
return port
}
}
}

// Default port
return 8000
return d.Port
}

// getSuspend returns whether to suspend on start
func (d *DebugFramework) getSuspend() bool {
// Check JBP_CONFIG_DEBUG for suspend setting
func (d *debugConfig) getSuspend() bool {
return d.Suspend
}

type debugConfig struct {
Enabled bool `yaml:"enabled"`
Port int `yaml:"port"`
Suspend bool `yaml:"suspend"`
}

func (d *DebugFramework) loadConfig() (*debugConfig, error) {
// initialize default values
dbgConfig := debugConfig{
Enabled: false,
Port: 8000,
Suspend: false,
}
config := os.Getenv("JBP_CONFIG_DEBUG")
if config != "" {
if contains(config, "suspend: true") || contains(config, "'suspend': true") {
return true
yamlHandler := common.YamlHandler{}
err := yamlHandler.ValidateFields([]byte(config), &dbgConfig)
if err != nil {
d.context.Log.Warning("Unknown user config values: %s", err.Error())
}
// overlay JBP_CONFIG_DEBUG over default values
if err = yamlHandler.Unmarshal([]byte(config), &dbgConfig); err != nil {
return nil, fmt.Errorf("failed to parse JBP_CONFIG_DEBUG: %w", err)
}
}

// Default to false
return false
return &dbgConfig, nil
}

// Helper function to check if string contains substring
Expand Down
Loading