Spring, property file, empty values
Asked Answered
L

5

10

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

Liminal answered 7/5, 2012 at 13:34 Comment(1)
What version of Spring do you use?Lactalbumin
L
2

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 ;)

Liminal answered 28/5, 2012 at 8:2 Comment(4)
This is not a bug. The url attribute should not be used if you are going to create embedded server. See static.springsource.org/spring-security/site/docs/3.1.x/…Discipline
What I call a bug is that using an empty string runs and using a variable does not run because spring security checks that url is not empty by not replacing the variable by its value (which can be empty !). This is a poor design.Liminal
In that case, I think it make sense to report an issue in Spring bug tracker, right ? jira.springsource.org/secure/Dashboard.jspaPedology
Thanks to have submit the issue, I would do it tonight or tomorrow ;)Liminal
M
1

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.

Matrix answered 7/5, 2012 at 17:50 Comment(3)
thanks for answer, but I've always the same bug using the default value tip.Liminal
I don't understand it. If default value is '' I think it should be the same that setting it to ''.Matrix
Yes, I was also thinking so... It's very strange... Maybe, the setter of url is called only when we use a spring expression, and calling the setter with an empty value causes the bug. In case of not specifying the expression, the setter is maybe not called ?Liminal
G
1

I think that url="" works because url attribute is of type xs:token in spring-security XSD and empty string is converted to null (xs:token is removing any leading or trailing spaces, so "" can be recognized as no value). Maybe the value of ${ldap.server.url} is resolved as empty string and that is why you've got an error.

You can try use Spring profiles to define different configurations of ldap server (see Spring Team Blog for details about profiles)

Guelph answered 25/5, 2012 at 12:53 Comment(3)
LdapServerBeanDefinitionParser is already taking care of it. Somehow it managed to create bean definition of org.springframework.security.ldap.DefaultSpringSecurityContextSource with blank URL in constructor even though all checks seem to be in place to prevent it.Discipline
I think I've found why Ritesh has right and why it does not run : When I debug the launch of the server (in method parse of LdapServerBeanDefinition), all placeholders are not replaced. So url is : ${ldap.server.url}. The resolution of place holder is done after the creation of the context source.Liminal
As per the documentation static.springsource.org/spring-security/site/docs/3.1.x/…, the url attribute should not be used at all if we want to create embedded server.Discipline
W
1

I believe there is an issue here while using place holders. The following will most probably solve the problem:

Create a class which extends PropertyPlaceHolderConfigurer and override its method convertPropertyValue() in the method you can return the property as empty string if you find anything other than a string which is of type LDAP url i.e. ldap://myldapserver.com/dc=springframeworg,dc=org

Also you need to configure your new specialization of class PropertyPlaceHolderConfigurer in the context file.

Hope this helps.

Weighted answered 28/5, 2012 at 7:44 Comment(0)
D
0

You can define empty String in the application.properties file as following:

com.core.estimation.stopwords=\ \ enter image description here

Daren answered 6/2, 2020 at 4:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.