Overview
Accounts in TNB are Java objects that contain all the information required to connect to a service, including credentials, endpoints, and configuration parameters.
The Account interface
The Account interface is minimal, providing utility methods for account manipulation:
public interface Account {
/**
* Create a Properties instance from this account.
*
* @return properties instance
*/
default Properties toProperties () {
Properties properties = new Properties ();
for ( Field field : ReflectionUtil . getAllFields ( new ArrayList <>(), this . getClass ())) {
try {
field . setAccessible ( true );
// Null values can't be stored in properties
if ( field . get ( this ) != null ) {
properties . put ( StringUtils . replaceUnderscoreWithCamelCase ( field . getName ()), field . get ( this ));
}
} catch ( IllegalAccessException e ) {
throw new RuntimeException ( "Unable to get field " + field . getName () + " value: " , e);
}
}
return properties;
}
}
The toProperties() method automatically converts all account fields into a Java Properties object, useful for configuring clients.
Account types
For self-hosted services, account values are typically hardcoded since the service is deployed by TNB. Example: KafkaAccount public class KafkaAccount implements Account {
private String basicUser = "testuser" ;
private String basicPassword = "testpassword" ;
private String trustStore = "target/kafka-truststore.p12" ;
private String trustStorePassword ;
public String basicUser () {
return "testuser" ;
}
public String basicPassword () {
return "testpassword" ;
}
public String trustStore () {
return trustStore;
}
public String trustStorePassword () {
return trustStorePassword;
}
public void setTrustStorePassword ( String password ) {
this . trustStorePassword = password;
}
}
Self-hosted service accounts use default credentials since TNB controls the deployment.
For remote services, accounts must implement the WithId interface to enable credential loading from external sources. Example: AWS Account public class AWSAccount implements Account , WithId {
private String accessKey ;
private String secretKey ;
private String region ;
private String accountId ;
@ Override
public String credentialsId () {
return "aws" ;
}
// Getters and setters...
}
The WithId interface enables automatic credential loading: public interface WithId {
String SYSTEM_PROPERTY_FORMAT = "tnb.%s.id" ;
/**
* Get the id from system property if set, the system property is for example "tnb.awsaccount.id".
*
* @return value of system property or default id
*/
default String getId () {
return TestConfiguration . getProperty (
String . format (SYSTEM_PROPERTY_FORMAT, this . getClass (). getSimpleName (). toLowerCase ()),
credentialsId ()
);
}
/**
* Default id.
*
* @return id
*/
String credentialsId ();
}
Creating accounts with AccountFactory
The AccountFactory class creates account instances and populates them with credentials:
T instance = AccountFactory . create ( YourAccount . class );
How AccountFactory works
From AccountFactory.java:35-51:
public static < T extends Account > T create ( Class < T > accountClass) {
T instance = createInstance (accountClass);
if (instance instanceof WithId) {
LOG . debug ( "Loading {} account" , accountClass . getSimpleName ());
if (loader == null ) {
try {
loader = defaultLoader ();
} catch ( Exception e ) {
fail ( "Could not load credentials" , e);
}
}
return loader . get ( getCredentialsIds (instance), accountClass);
} else {
LOG . debug ( "Initialization of {}. No credentials loading needed." , accountClass . getSimpleName ());
return instance;
}
}
Create instance
Instantiate the account class using reflection
Check for WithId
If the account implements WithId, proceed with credential loading
Initialize loader
Create a credentials loader based on configuration
Load credentials
Populate account fields from the credentials source
Credential loading strategies
TNB supports multiple credential sources, checked in the following order:
Overriding account IDs
You can override the default credentials ID for any account using system properties:
tnb. <accountClassName>.id=custom-id
Example
For an AWSAccount class with a default ID of “aws”:
# Use credentials from "aws-production" instead of "aws"
tnb.awsaccount.id =aws-production
The property name is the account class name in lowercase.
Composite accounts
Accounts can extend other accounts and inherit credentials, enabling a credential extension mechanism.
How it works
When creating an account instance, AccountFactory checks all parent classes for the WithId interface and loads credentials from all IDs.
Example
public class ParentAccount implements Account , WithId {
private String parentKey ;
@ Override
public String credentialsId () {
return "parent" ;
}
}
public class ChildAccount extends ParentAccount {
private String childKey ;
@ Override
public String credentialsId () {
return "child" ;
}
}
Credentials file
services :
parent :
credentials :
parentKey : parentValue
child :
credentials :
childKey : childValue
When you create a ChildAccount:
ChildAccount account = AccountFactory . create ( ChildAccount . class );
The resulting account has both parentKey and childKey populated.
Credentials are merged in order from parent to child, with child values overriding parent values if there are conflicts.
Partial composite accounts
At least one of the credentials IDs must exist. This allows for flexible credential structures:
services :
child :
credentials :
parentKey : parentValue
childKey : childValue
This is valid even though the “parent” entry doesn’t exist, as long as all required fields are in the “child” entry.
Best practices
Never commit secrets Use external credential sources (Vault or YAML files) and add them to .gitignore.
Use environment-specific IDs Override account IDs per environment (dev, staging, production) using system properties.
Implement WithId for remote services Always implement WithId for accounts that need external credentials.
Use composite accounts for reuse Share common credentials across multiple accounts using inheritance.
Next steps
Services Learn about the Service abstraction
Validation Understand validation classes