Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring, property file, empty values

I have configured spring security with a ldap server (but continue reading, it's not a problem if you have no knowledge about it, this is really a spring problem). All runs like a charm. Here is the line I use for that:

<ldap-server ldif="" root="" manager-dn="" manager-password="" url=""  id="ldapServer" />

If I fill ldif and root attributes, it will run an embeded server:

<ldap-server ldif="classpath://ldap.ldif" root="dc=springframework,dc=org" manager-dn="" manager-password="" url=""  id="ldapServer" />

If I fill other fields, it will run a distant server:

<ldap-server ldif="" root="" manager-dn="dc=admin,dc=springframeworg,dc=org" manager-password="password" url="ldap://myldapserver.com/dc=springframeworg,dc=org" id="ldapServer" />

All this stuff run correctly. Now I want to use Spring mechanism to load such parameters from a property file:

So I replace attribute values like this:

<ldap-server ldif="${ldap.ldif.path}" root="${ldap.ldif.root}" manager-dn="${ldap.server.manager.dn}" manager-password="${ldap.server.manager.password}" url="${ldap.server.url}"  id="ldapServer" />

and create a property file with:

ldap.server.url=
ldap.server.manager.dn=
ldap.server.manager.password=

ldap.ldif.path= 
ldap.ldif.root= 

Now, the funny part of the problem. If I fill the following properties in the file:

ldap.server.url=ldap://myldapserver.com/dc=springframeworg,dc=org
ldap.server.manager.dn=dc=admin,dc=springframeworg,dc=org
ldap.server.manager.password=password

ldap.ldif.path= 
ldap.ldif.root= 

It runs a distant server as expected.

If I fill the property file like this:

ldap.server.url=
ldap.server.manager.dn=
ldap.server.manager.password=

ldap.ldif.path= classpath:ldap.ldif
ldap.ldif.root= dc=springframeworg,dc=org

It does not run, complaining that the ldap url is missing. But the problem is that if I change the spring configuration from:

<ldap-server ldif="${ldap.ldif.path}" root="${ldap.ldif.root}" manager-dn="${ldap.server.manager.dn}" manager-password="${ldap.server.manager.password}" url="${ldap.server.url}"  id="ldapServer" />

to (by just removing the reference to the variable ${ldap.server.url})

<ldap-server ldif="${ldap.ldif.path}" root="${ldap.ldif.root}" manager-dn="${ldap.server.manager.dn}" manager-password="${ldap.server.manager.password}" url=""  id="ldapServer" />

It runs !

My thoughs are that spring does not replace the attribute value with the property config one if this one is empty. But I find it strange.

Can you give me some clue to understand that ? And what's the best to do to configure my ldap server via a property file ?

EDIT: this is due to a poor design choice (look at accepted answer), an issue has been opened on jira : https://jira.springsource.org/browse/SEC-1966

like image 830
Jerome Cance Avatar asked May 07 '12 13:05

Jerome Cance


2 Answers

Ok, I think this is a spring security bug.

If I debug and look at the class LdapServerBeanDefinition, there is a method called "parse". Here is an extract:

public BeanDefinition parse(Element elt, ParserContext parserContext) {
    String url = elt.getAttribute(ATT_URL);

    RootBeanDefinition contextSource;

    if (!StringUtils.hasText(url)) {
        contextSource = createEmbeddedServer(elt, parserContext);
    } else {
        contextSource = new RootBeanDefinition();
        contextSource.setBeanClassName(CONTEXT_SOURCE_CLASS);
        contextSource.getConstructorArgumentValues().addIndexedArgumentValue(0, url);
    }

    contextSource.setSource(parserContext.extractSource(elt));

    String managerDn = elt.getAttribute(ATT_PRINCIPAL);
    String managerPassword = elt.getAttribute(ATT_PASSWORD);

    if (StringUtils.hasText(managerDn)) {
        if(!StringUtils.hasText(managerPassword)) {
            parserContext.getReaderContext().error("You must specify the " + ATT_PASSWORD +
                    " if you supply a " + managerDn, elt);
        }

        contextSource.getPropertyValues().addPropertyValue("userDn", managerDn);
        contextSource.getPropertyValues().addPropertyValue("password", managerPassword);
    }

    ...
}

If I debug here, all variables (url, managerDn, managerPassword...) are not replaced by the value specified in the property file. And so, url has the value ${ldap.server.url}, managerDn has the value ${ldap.server.manager.dn} and so on.

The method parse creates a bean, a context source that will be used further. And when this bean will be used, place holders will be replaced.

Here, we got the bug. The parse method check if url is empty or not. The problem is that url is not empty here because it has the value ${ldap.server.url}. So, the parse method creates a context source as a distant server.

When the created source will be used, it will replace the ${ldap.server.url} by empty value (like specified in the property file). And....... Bug !

I don't know really how to solve this for the moment, but I now understand why it bugs ;)

like image 82
Jerome Cance Avatar answered Dec 17 '22 22:12

Jerome Cance


I cannot explain it, but I think you can fix your problem using defaulting syntax, available since Spring 3.0.0.RC1 (see).

In the chageg log you can read: PropertyPlaceholderConfigurer supports "${myKey:myDefaultValue}" defaulting syntax

Anyway, I think that the problem is because "" is valid value, but no value in the property file don't.

like image 23
jddsantaella Avatar answered Dec 18 '22 00:12

jddsantaella