We use JAAS in a heavily loaded web server. The configuration file is loaded from a file,
System.setProperty("java.security.auth.login.config", "/config/jaas.config");
During profiling, we noticed that the configuration is loaded from file for every login attempt. This is an I/O operation we try to avoid. Is there anyway to store the JAAS configuration in memory?
Following code snippet connects to a PostgreSQL database (using pgjdbc and HikariCP) with an in-memory JAAS configuration, that is, no Configuration files are required:
package com.vlkan.kerberos.auth;
import com.google.common.collect.ImmutableMap;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
import java.util.Properties;
import static com.google.common.base.Preconditions.checkArgument;
public enum Main {
private static final String JAAS_CONFIG_NAME = "pgjdbc";
public static void main(String[] args) throws LoginException, SQLException {
String jdbcUrl = "jdbc:postgresql://host/dbname";
String jdbcDriver = "org.postgresql.Driver";
String username = "user";
String password = "pass";
Configuration jaasConfig = createJaasConfig();
Configuration.setConfiguration(jaasConfig);
HikariConfig hikariConfig = createHikariConfig(jdbcUrl, jdbcDriver, username, password);
HikariDataSource dataSource = new HikariDataSource(hikariConfig);
try (Connection connection = dataSource.getConnection()) {
try (PreparedStatement statement = connection.prepareStatement("SELECT 1")) {
try (ResultSet resultSet = statement.executeQuery()) {
boolean next = resultSet.next();
checkArgument(next, "no results");
int result = resultSet.getInt(1);
checkArgument(result == 1, "expecting: 1, found: %s", result);
System.out.println("ok");
}
}
}
}
private static HikariConfig createHikariConfig(String jdbcUrl, String jdbcDriver, String username, String password) {
HikariConfig config = new HikariConfig();
config.setDriverClassName(jdbcDriver);
config.setJdbcUrl(jdbcUrl);
config.setUsername(username);
config.setPassword(password);
fixKerberosProperties(config, username, password);
return config;
}
private static void fixKerberosProperties(HikariConfig config, String username, String password) {
Properties properties = new Properties();
properties.setProperty("user", username);
properties.setProperty("password", password);
properties.setProperty("JAASConfigName", JAAS_CONFIG_NAME);
config.setDataSourceProperties(properties);
}
private static Configuration createJaasConfig() {
// Create entry options.
Map<String, Object> options = ImmutableMap.of(
"useFirstPass", "false", // Do *not* use javax.security.auth.login.{name,password} from shared state.
"debug", "true" // Output debug (including plain text username and password!) messages.
);
// Create entries.
AppConfigurationEntry[] entries = {
new AppConfigurationEntry(
"com.sun.security.auth.module.Krb5LoginModule",
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
options)
};
// Create configuration.
return new Configuration() {
#Override
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
checkArgument(JAAS_CONFIG_NAME.equals(name));
return entries;
}
};
}
}
You could implement your own Configuration. The javadoc says:
The default Configuration
implementation can be changed by
setting the value of the
"login.configuration.provider"
security property (in the Java
security properties file) to the fully
qualified name of the desired
Configuration implementation class.
The default implementation com.sun.security.auth.login.ConfigFile (source) appears to load the file each time the class is instantiated. You could cache the contents. No comment on the security aspects either way.
Related
I was trying to create a dynamic JDBC connection in java to connect to snowflake.
I am stuck at a point ,how can i pass the parameter from my property file into snowflake connection file
Please find the attached code
package com.cisco.export.utils;
import java.sql.*;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
import com.cisco.config.Configuration;
public class SFDbConnection {
public Connection getConnection(Configuration config) throws SQLException{
Connection connection=null;
try {
System.out.println(config.getProp("sf.driverclass"));
System.out.println(config.getProp("sf.url"));
System.out.println(config.getProp("sf.account"));
System.out.println(config.getProp("sf.username"));
System.out.println(config.getProp("sf.password"));
System.out.println(config.getProp("sf.warehouse"));
System.out.println(config.getProp("sf.db"));
System.out.println(config.getProp("sf.schema"));
System.out.println(config.getProp("sf.role"));
Class.forName(config.getProp("sf.driverclass"));
String connectStr = "jdbc:snowflake://mysnowflakeaccount.us-east-1.snowflakecomputing.com";
connection = DriverManager.getConnection()
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return connection;
}
Can some one Help me how can i make the parameters inside the getConnection() dynamic.
Appreciate your help.
Thanks,
Nikhil
The Snowflake JDBC Driver accepts connection properties via the connection-string or via a java.util.Properties class object.
Using the properties in a connection string:
String sfAccount = config.getProp("sf.account");
String sfUsername = config.getProp("sf.username");
String sfPassword = config.getProp("sf.password");
String sfWarehouse = config.getProp("sf.warehouse");
String sfDatabase = config.getProp("sf.db");
String sfSchema = config.getProp("sf.schema");
String sfRole = config.getProp("sf.role");
String connectionString =
String.format("jdbc:snowflake://%s.snowflakecomputing.com/?role=%s&warehouse=%s&db=%s&schema=%s",
sfAccount,
sfRole,
sfWarehouse,
sfDatabase,
sfSchema
);
return DriverManager.getConnection(connectionString, sfUsername, sfPassword);
The com.cisco.config.Configuration class is not a known public API type, but if it can be translated to a java.util.Properties object, you can pass it when building a connection. Here's a direct conversion:
java.util.Properties props = new java.util.Properties();
String connectionString =
String.format(
"jdbc:snowflake://%s.snowflakecomputing.com",
config.getProp("sf.account")
);
props.setProperty("user", config.getProp("sf.username"));
props.setProperty("password", config.getProp("sf.password"));
props.setProperty("role", config.getProp("sf.role"));
props.setProperty("warehouse", config.getProp("sf.warehouse"));
props.setProperty("db", config.getProp("sf.db"));
props.setProperty("schema", config.getProp("sf.schema"));
return DriverManager.getConnection(connectionString, props);
I recently started working in a Spring boot project. Here all the application or environment level properties are stored in the DB (Oracle). This includes all the URLs, paths etc used in the application.
These properties are fetched at the startup, stored in a static map and then used across the entire application (perhaps the idea is that the client can update any property in DB and restart the particular environment and all should work fine).
This works fine accept the fact that the path for server logs is still picked from a logging.properties file. The logging is done via spring-logback xml configuration.
I believe that the logging is the first thing done by the Spring boot even before the DB connections and hence it is difficult to fetch the path from DB and then provide it to the logback file.
Is there any possible way to configure the logging so that the path for the logs can be picked up from the DB as well.
I cerebrate you require to store the application.properties file data into the DB.
you can configure this and if you optate to store the appication.properties into DB withal you can do that by utilizing the DB appender.
<configuration debug="true">
<appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
<connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource">
<dataSource class="${dataSourceClass}">
<!-- Joran cannot substitute variables
that are not attribute values. Therefore, we cannot
declare the next parameter like the others.
-->
<param name="${url-key:-url}" value="${url_value}"/>
<serverName>${serverName}</serverName>
<databaseName>${databaseName}</databaseName>
</dataSource>
<user>${user}</user>
<password>${password}</password>
</connectionSource>
</appender>
<root level="INFO">
<appender-ref ref="DB" />
</root>
</configuration>
But need to take the data from DB then you can utilize the below code.
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
public class ReadDBPropertiesInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final Logger LOG = LoggerFactory.getLogger(ReadDBPropertiesInitializer.class);
/**
* Name of the custom property source added by this post processor class
*/
private static final String PROPERTY_SOURCE_NAME = "databaseProperties";
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment configEnv = ((ConfigurableEnvironment) applicationContext.getEnvironment());
LOG.info("Load properties from database");
Map<String, Object> propertySource = new HashMap<>();
try {
final String url = getEnv(configEnv, "spring.datasource.url");
String driverClassName = getProperty(configEnv, "spring.datasource.driver-class-name");
final String username = getEnv(configEnv, "spring.datasource.username");
final String password = getEnv(configEnv, "spring.datasource.password");
DataSource ds = DataSourceBuilder.create().url(url).username(username).password(password)
.driverClassName(driverClassName).build();
// Fetch all properties
PreparedStatement preparedStatement = ds.getConnection()
.prepareStatement("SELECT config_key as name, config_value as value, config_label as label FROM TB_CONFIGURATION");
ResultSet rs = preparedStatement.executeQuery();
// Populate all properties into the property source
while (rs.next()) {
final String propName = rs.getString("name");
final String propValue = rs.getString("value");
final String propLabel = rs.getString("label");
LOG.info(String.format("Property: %s | Label: %s", propName, propLabel));
LOG.info(String.format("Value: %s", propValue));
propertySource.put(propName, propValue);
}
// Create a custom property source with the highest precedence and add it to
// Spring Environment
applicationContext.getEnvironment().getPropertySources()
.addFirst(new MapPropertySource(PROPERTY_SOURCE_NAME, propertySource));
} catch (Exception e) {
throw new RuntimeException("Error fetching properties from db");
}
}
private String getEnv(ConfigurableEnvironment configEnv, final String property) {
MutablePropertySources propertySources = configEnv.getPropertySources();
PropertySource<?> appConfigProp = propertySources.get("applicationConfigurationProperties");
return System.getenv().get(((String) appConfigProp.getProperty(property)).replace("${", "").replace("}", ""));
}
private String getProperty(ConfigurableEnvironment configEnv, final String property) {
MutablePropertySources propertySources = configEnv.getPropertySources();
PropertySource<?> appConfigProp = propertySources.get("applicationConfigurationProperties");
return (String) appConfigProp.getProperty(property);
}
I am struggling with the following issue while trying to retrieve all GPO of a domain with Java. I was able to create a connection to Active Directory and get the policy objects, however I am not able to retrieve their settings in which I am interested in.
I was only able to retrieve the following properties:
CanonicalName
CN
Created
createTimeStamp
Deleted
Description
DisplayName
DistinguishedName
dSCorePropagationData
flags
gPCFileSysPath
gPCFunctionalityVersion
gPCMachineExtensionNames
gPCUserExtensionNames
instanceType
isCriticalSystemObject
isDeleted
LastKnownParent
Modified
modifyTimeStamp
Name
nTSecurityDescriptor
ObjectCategory
ObjectClass
ObjectGUID
ProtectedFromAccidentalDeletion
sDRightsEffective
showInAdvancedViewOnly
systemFlags
uSNChanged
uSNCreated
versionNumber
whenChanged
whenCreated
Do you know how should I face this issue? Is there any extended property from which I can retrieve the settings of each GPO?
I do not know if the code would be useful as it is just a connection and a ldap query:
colAttributes = {"*"};
strSearchRoot = "DC=xx,DC=xx";
this.getActiveDirectoryConnection().setRequestControl(null, Control.NONCRITICAL);
colSearchResult = this.getActiveDirectoryConnection().getQuery(colAttributes, "(ObjectClass=groupPolicyContainer)", strSearchRoot);
while (colSearchResult.hasMoreElements())
{
objSearchResult = (SearchResult) colSearchResult.nextElement();
objAttributes = objSearchResult.getAttributes();
}
private void getActiveDirectoryConnection()
{
return new ActiveDirectory(strDomain, strUsername, strPassword);
}
An example of I am trying to get is the Default Domain Policy, not only this but all policies. And settings goes through Password Settings such as maxPwdAge, lockoutThreshold, etc Screen and Power Settings, between others
import java.io.File;
import java.io.IOException;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.Control;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import javax.naming.ldap.PagedResultsControl;
import javax.naming.ldap.PagedResultsResponseControl;
public class ActiveDirectory
{
private LdapContext objLDAPContext;
public ActiveDirectory(String strURL, String strUserName, String strPassword) throws NamingException
{
Hashtable<String, Object> objEnvironment;
objEnvironment = new Hashtable<String, Object>(11);
objEnvironment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
objEnvironment.put(Context.PROVIDER_URL, strURL);
objEnvironment.put(Context.SECURITY_AUTHENTICATION, "simple");
objEnvironment.put(Context.SECURITY_PRINCIPAL, strUserName);
objEnvironment.put(Context.SECURITY_CREDENTIALS, strPassword);
objEnvironment.put("java.naming.ldap.attributes.binary", "objectGUID");
try
{
this.objLDAPContext = new InitialLdapContext(objEnvironment, null);
}
catch (NamingException objException)
{
System.setProperty("javax.net.ssl.trustStore", "certificates".concat(File.separator).concat("cacerts"));
objEnvironment.put(Context.PROVIDER_URL, strURL.replace("LDAP:", "LDAPS:").replace(":389", ":636"));
}
this.objLDAPContext = new InitialLdapContext(objEnvironment, null);
}
private LdapContext getContext()
{
return this.objLDAPContext;
}
public NamingEnumeration<SearchResult> getQuery(String[] colAttributes, String strLDAPFilter, String strSearchRoot) throws NamingException
{
NamingEnumeration<SearchResult> objAnswer;
SearchControls objSearchControls = new SearchControls();
objSearchControls.setReturningAttributes(colAttributes);
objSearchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
objAnswer = this.getContext().search(strSearchRoot, strLDAPFilter, objSearchControls);
return objAnswer;
}
public void close() throws NamingException
{
this.getContext().close();
}
public void setRequestControl(byte[] objCookie, boolean bolControl)
{
int intPageSize;
intPageSize = 1000;
try
{
this.getContext().setRequestControls(new Control[]
{
new PagedResultsControl(intPageSize, objCookie, bolControl)
});
}
catch(NamingException | IOException objException)
{
//No more pages could be recovered
}
}
public byte[] getCookie()
{
byte[] objCookie;
objCookie = null;
try
{
Control[] objControl = this.getContext().getResponseControls();
if (objControl != null)
{
for (int intCounter = 0; intCounter < objControl.length; intCounter++)
{
if (objControl[intCounter] instanceof PagedResultsResponseControl)
{
PagedResultsResponseControl objPagedControl = (PagedResultsResponseControl) objControl[intCounter];
objCookie = objPagedControl.getCookie();
}
}
}
}
catch(NamingException objException)
{
//Skip errors null cookie will be handled
}
return objCookie;
}
}
You can't get gpo settings through LDAP, because gpo templates has been stored in sysvol folder in domain controller.It is a shared folder across the domain. We can get the path of GPO templates by GPO container attribute gPLink. If you access the file you can get all applied gpo settings
I use SimpleDriverDataSource and JdbcTemplate to connect to my drill server. In order to make this connection successful, I have to set the correct jdbcUrl. The problem is, I can set the drill server's port to any number, even larger than 65535, and the result is correct every time. I don't know why. Here's my code.
import org.apache.log4j.Logger;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
import org.apache.drill.jdbc.Driver;
import java.util.List;
import java.util.Map;
public class TestDrill {
private final static Logger logger = Logger.getLogger(TestDrill.class);
public static void main(String[] args) {
Driver driver = new Driver();
String jdbcUrl = "jdbc:drill:drillbit=server1:66666,server2:66666,server3:66666";
SimpleDriverDataSource drillDs = new SimpleDriverDataSource(driver, jdbcUrl, "username", "password");
JdbcTemplate drillTemplate = new JdbcTemplate(drillDs);
try {
List<Map<String, Object>> dbs = drillTemplate.queryForList("show schemas");
logger.debug(dbs.toString());
logger.debug("show schemas succeeded");
} catch(DataAccessException e) {
logger.debug("show schemas failed");
}
}
}
P.S. If I set the wrong server hostname, the query would fail. This is normal. I just have no idea why I can set any port number and the query would still be successful.
I have a Maven controller Spring web app and it runs fine on the command line with mvn clean tomcat:run but I can't get it to work with a Run/Debug Configuration. I get a long list of autowire dependencies failures ending with:
...Instantiation of bean failed; nested exception is
org.springframework.beans.BeanInstantiationException: Could not
instantiate bean class
[com.mycompany.config.DataSourceConfig$$EnhancerByCGLIB$$543b87de]:
Constructor threw exception; nested exception is
java.lang.NumberFormatException: null
Here is the offending class:
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.beans.PropertyVetoException;
import java.net.URISyntaxException;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean;
#Configuration
public class DataSourceConfig {
//change PACKAGE_TO_SCAN
private static final String PACKAGE_TO_SCAN = "com.mycompany";
private static final int MODE_DEV = 0;
private static final int MODE_STG = 1;
private static final int MODE_PROD = 2;
private Logger log = LoggerFactory.getLogger(this.getClass());
private int environment = Integer.parseInt(System.getenv("ENVIRONMENT"));
private String dburl = System.getenv("UMWORKFLOW_DATABASE_URL");
private String dbuser = System.getenv("UMWORKFLOW_DATABASE_USER");
private String dbpass = System.getenv("UMWORKFLOW_DATABASE_PASSWORD");
public DataSourceConfig(){
}
#Bean(destroyMethod="close")
public ComboPooledDataSource dataSource() throws URISyntaxException, PropertyVetoException {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass("org.postgresql.Driver");
ds.setMinPoolSize(1);
ds.setMaxPoolSize(10);
ds.setAcquireIncrement(1);
ds.setIdleConnectionTestPeriod(300);
ds.setMaxStatements(0);
ds.setCheckoutTimeout(100);
ds.setJdbcUrl(dburl);
ds.setUser(dbuser);
ds.setPassword(dbpass);
return ds;
}
#Bean
public AnnotationSessionFactoryBean sessionFactory() throws URISyntaxException, PropertyVetoException {
AnnotationSessionFactoryBean sf = new AnnotationSessionFactoryBean();
sf.setDataSource(dataSource());
String[] packageToScan = new String[1];
packageToScan[0] = PACKAGE_TO_SCAN;
sf.setPackagesToScan(packageToScan);
Properties hibProp = new Properties();
hibProp.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
//modes create, create-drop, update, validate
if( environment == MODE_DEV) {
hibProp.put("hibernate.hbm2ddl.auto", "update");
} else if ( environment == MODE_STG) {
hibProp.put("hibernate.hbm2ddl.auto", "update");
} else {
hibProp.put("hibernate.hbm2ddl.auto", "update");
}
sf.setHibernateProperties(hibProp);
return sf;
}
}
The system property ENVIRONMENT seems to be custom.
private int environment = Integer.parseInt(System.getenv("ENVIRONMENT"));
So System.getenv() is returning null and causing the IllegalArgumentException. You will need to set that property in your run/debug configuration.