Hello Dev Members,
I have updated the Secret Manager/Vault support in Apache OFBiz, and the
following changes have been made in my forked version.
Webtools Secret Manager Screen (EncryptValue)
----------------------------------------------
1. Single-entry form with dual storage targets
The EncryptValue screen supports two storage targets via a radio button:
SystemProperty entity and passwords.properties. For the SystemProperty
target,
Resource ID and Property ID are required; Lookup Key is optional and,
when
provided, wires the record to a remote secret provider. For the
passwords.properties target, two sub-cases are supported:
- Case A (Lookup Key only): writes jdbc-password.<key>=ENC(...) to
passwords.properties, covering the entityengine.xml
jdbc-password-lookup use case.
- Case B (all four fields): writes <key>=ENC(...) to
passwords.properties, sets
<propertyId>=LOOKUP(<key>) in the source .properties file on disk, and
flushes
the OFBiz property cache — no server restart needed.
2. Dynamic labels based on target selection
Field labels update dynamically when the target radio button is switched.
Selecting SystemProperty shows "System Resource ID (SystemProperty)" and
"System Property ID (SystemProperty)"; selecting passwords.properties
shows
"Resource ID (Property File)" and "Property ID (Property File)". This
avoids
confusion about which entity or file a field refers to.
3. Form field persistence on validation errors
When a submission fails validation, the radio button selection,
systemResourceId, systemPropertyId, and lookupKey fields are
pre-populated
with the values the user entered. The secretValue field is intentionally
cleared on error so the plain-text password is never re-rendered in the
page.
4. Stale lookup key cleanup
When Case B is used and the user changes the lookupKey for a property
that
already had a LOOKUP(...) reference in the source .properties file, the
old
key's entry is automatically removed from passwords.properties before
the new
one is written, preventing orphaned encrypted entries.
5. CSV bulk upload with security pre-scan
A CSV upload path allows multiple secrets to be created in one operation
(target, systemResourceId, systemPropertyId, lookupKey, secretValue
columns).
The entire file is validated before any writes occur — the file is
rejected
as a whole if any row contains unknown targets, invalid identifier
characters
(only letters, digits, dots, hyphens, and underscores are allowed in
identifier fields), path traversal sequences (..), or if the file exceeds
512 KB or 500 rows. The whole-file content scan uses OFBiz's existing
SecuredUpload.isValidTextContent() sandboxing mechanism to block null
bytes
and control characters at the Unicode code-point level.
6. Input validation on form and CSV upload
Server-side guards reject a LOOKUP(...) marker typed into the lookupKey
field
and an ENC(...) prefix typed into the secretValue field, on both the
single-entry form and the CSV bulk-upload path. A matching client-side
JavaScript validator highlights the offending field and shows an inline
error
before the form is even submitted, preventing accidental misuse.
Marker and Resolver Changes
----------------------------
7. Configurable secret marker (default: LOOKUP)
The marker used to identify secret references in property values
(previously
hardcoded as SECRET) is now configurable via secret.value.marker in
security.properties. The default has been changed to LOOKUP, which aligns
naturally with the existing systemPropertyLookup field name in the
SystemProperty
entity and the jdbc-password-lookup attribute in entityengine.xml.
Admins can
change it to any uppercase word if it collides with an existing property
value.
8. Plain key in SystemProperty.systemPropertyLookup
Previously the field stored a wrapped value like LOOKUP(smtp-key). It
now stores
the plain key (smtp-key) directly, since the field name already implies
lookup
semantics. EntityUtilProperties now calls the new
SecretValueResolver.resolveKey(String)
method to resolve the raw key without needing a marker wrapper.
Marker-based
resolution (LOOKUP(key)) in .properties files remains unchanged.
9. Marker config moved to security.properties
secret.value.marker and secret.cache.ttl.seconds are now documented in
security.properties rather than general.properties, which is the more
appropriate
home for secret-management configuration.
10. Fixed marker coupling in SecretManagerServices
All hardcoded "SECRET(" string literals in the writer and parser have
been
replaced with SecretValueResolver.MARKER_NAME (now public), ensuring the
writer and resolver always stay in sync when an admin changes the
configured
marker.
11. SecretValueResolver refactored
Cache and provider logic extracted into a new public resolveKey(String
key)
method; resolve(String) now delegates to it after parsing the marker.
This
eliminates duplicated cache logic and gives callers a clean API for
direct
raw-key resolution without constructing a synthetic LOOKUP(...) wrapper.
If you wish to review the code changes, please review the URL below:
https://github.com/ashishvijaywargiya/ofbiz-framework/tree/various-secret-manager
Thank you.
--
Kind Regards,
Ashish Vijaywargiya
Vice President of Operations
*HotWax Systems*
*Enterprise open source experts*
http://www.hotwaxsystems.com
On Mon, Jun 15, 2026 at 5:35 PM Ashish Vijaywargiya <
[email protected]> wrote:
> Dear Dev Community,
>
> Over the past few days, I've been working on adding optional Secret
> Manager/Vault integration to Apache OFBiz, and I'd like to share it with
> the Dev list for feedback before taking it further.
>
> The goal is simple: today, DB passwords, JWT/login keys, and various
> gateway credentials mostly live as plain text in .properties files or in
> the SystemProperty entity, which is a real concern for production and
> compliance-sensitive deployments (PCI-DSS, SOC2, etc.). This work gives
> adopters a way to back those values with an external secret manager of
> their choice, while leaving everything else untouched.
>
> To make this happen, I've added six provider plugins, each implementing a
> small SecretProvider SPI: AWS Secrets Manager, GCP(Google Cloud Platform)
> Secret Manager, Azure Key Vault, HashiCorp Vault by IBM, 1Password, and
> Bitwarden Secrets Manager.
>
> On the framework side, the key additions are a
> SecretProvider/SecretProviderFactory/FallbackSecretProvider setup in
> framework/base, a ConfigCryptoUtil class for local AES-256-GCM encryption
> of values as ENC(...) keyed by OFBIZ_MASTER_KEY, and a SecretValueResolver
> that resolves any SECRET(key) reference in a property file or
> SystemProperty row through the configured provider, with a configurable
> cache TTL.
>
> entityengine.xml now supports jdbc-password-lookup on inline-jdbc
> elements, which is resolved via SecretProviderFactory at startup, so the DB
> password never needs to appear in entityengine.xml at all. Previously,
> passwords.properties only supported plain-text values; now those same
> entries can be encrypted at rest using a new generateDBPassword gradle
> task, which prompts for the value via masked stdin and writes out the
> ENC(...) form, so no secret ever needs to be typed on the command line or
> land in shell history.
>
> SystemProperty also has a new systemPropertyLookup field so individual
> properties can opt into secret-manager resolution.
>
> I've also added unit tests (e.g. SecretValueResolverTest) covering plain
> values, ENC(...) values, cached lookups, and provider failure/fallback
> behavior, plus a generateDBPassword gradle task with the same masked-prompt
> approach for setting up the local DB password.
>
> On the impact to existing implementations/configurations, this is fully
> backward-compatible and opt-in only:
>
> 1) passwords.properties / entityengine.xml – Existing plain-text
> jdbc-password values continue to work exactly as before. Sites can
> optionally migrate to ENC(...) (AES-256-GCM, keyed by OFBIZ_MASTER_KEY,
> generated via generateEncryptedSecret) or to a jdbc-password-lookup key
> resolved via a secret manager, but if they do nothing, behavior is
> unchanged.
>
> 2) SystemProperty – The new systemPropertyLookup field is additive.
> Existing rows with only systemPropertyValue (plain or ENC(...)) behave as
> they do today. A site only sees secret-manager resolution if it explicitly
> sets systemPropertyLookup on a row.
>
> 3) Secret managers – All six provider plugins (AWS, Azure, HashiCorp, GCP,
> 1Password, Bitwarden) are optional. None are enabled by default, with no
> new mandatory dependencies in core. You can enable one plugin/provider at a
> time by changing values in the respective plugin's ofbiz-component.xml
> file.
>
> 4) Fallback chain – Lookup → remote secret manager (if configured) → local
> ENC(...)/plain value. If the remote provider is unreachable or
> unconfigured, OFBiz falls back to the existing file/entity-based value,
> resulting in no new failure mode for sites that do not adopt this
> functionality.
>
> 5) Testing – The new resolver, crypto utility, and provider fallback logic
> are all covered by unit tests, so existing config-loading paths remain
> verified alongside the new optional ones.
>
> Here is the source code for your kind review:
>
> Framework related changes:
>
> https://github.com/ashishvijaywargiya/ofbiz-framework/tree/various-secret-manager
>
> Plugins related changes:
>
> https://github.com/ashishvijaywargiya/ofbiz-plugins/tree/plugins-various-secret-manager
>
> Further work:
>
> 1) I am working on adding an Admin Screen in webtools where a user can
> create secrets either in the SystemProperty entity or the password.properties
> file. I will provide an option where a user can create multiple secrets
> in a CSV file and upload them in bulk. The AES algorithm will encrypt the
> password and put it either in SystemProperty or password.properties file.
>
> 2) Another item on the roadmap is secret key/password rotation handling
> from the remote service, with the corresponding values updated in
> passwords.properties or the relevant SystemProperty records. Many
> third-party services expect support for secret key rotation at regular
> intervals for security reasons. For now, we won't provide support for
> database password rotation in the Secret Manager, since that would also
> require handling of the user password rotation on the database side itself
> (MySQL, PostgreSQL, etc.).
>
> 3) Merge the generateDBPassword and generateEncryptedSecret Gradle tasks
> and make them one, i.e generateEncryptedSecret.
>
> Please let me know your thoughts/feedback on this work. I will quickly
> accommodate
> your thoughts/ideas in the current implementation.
>
> I'm planning to commit the current work to the Apache OFBiz project(trunk)
> in the next few days.
>
> Thank you.
>
> --
> Kind Regards,
> Ashish Vijaywargiya
> Vice President of Operations
> *HotWax Systems*
> *Enterprise open source experts*
> http://www.hotwaxsystems.com
>
>