1 package org.springframework.security.config;
2
3 import javax.naming.directory.Attribute;
4 import javax.naming.directory.Attributes;
5 import javax.naming.directory.BasicAttribute;
6 import javax.naming.directory.BasicAttributes;
7
8 import org.springframework.beans.factory.xml.BeanDefinitionParser;
9 import org.springframework.beans.factory.xml.ParserContext;
10 import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
11 import org.springframework.beans.factory.config.BeanDefinition;
12 import org.springframework.beans.factory.support.RootBeanDefinition;
13 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
14 import org.springframework.beans.factory.support.ManagedSet;
15 import org.springframework.util.StringUtils;
16
17 import org.w3c.dom.Element;
18 import org.apache.commons.logging.Log;
19 import org.apache.commons.logging.LogFactory;
20
21
22
23
24
25 public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
26 private static final String CONTEXT_SOURCE_CLASS="org.springframework.security.ldap.DefaultSpringSecurityContextSource";
27
28 private final Log logger = LogFactory.getLog(getClass());
29
30
31 private static final String ATT_URL = "url";
32
33 private static final String ATT_PRINCIPAL = "manager-dn";
34 private static final String ATT_PASSWORD = "manager-password";
35
36
37
38
39 public static final String ATT_ROOT_SUFFIX = "root";
40 private static final String OPT_DEFAULT_ROOT_SUFFIX = "dc=springframework,dc=org";
41
42
43
44
45 public static final String ATT_LDIF_FILE = "ldif";
46 private static final String OPT_DEFAULT_LDIF_FILE = "classpath*:*.ldif";
47
48
49 public static final String ATT_PORT = "port";
50 public static final String OPT_DEFAULT_PORT = "33389";
51
52
53 public BeanDefinition parse(Element elt, ParserContext parserContext) {
54 String url = elt.getAttribute(ATT_URL);
55
56 RootBeanDefinition contextSource;
57
58 if (!StringUtils.hasText(url)) {
59 contextSource = createEmbeddedServer(elt, parserContext);
60 } else {
61 contextSource = new RootBeanDefinition();
62 contextSource.setBeanClassName(CONTEXT_SOURCE_CLASS);
63 contextSource.getConstructorArgumentValues().addIndexedArgumentValue(0, url);
64 }
65
66 contextSource.setSource(parserContext.extractSource(elt));
67
68 String managerDn = elt.getAttribute(ATT_PRINCIPAL);
69 String managerPassword = elt.getAttribute(ATT_PASSWORD);
70
71 if (StringUtils.hasText(managerDn)) {
72 if(!StringUtils.hasText(managerPassword)) {
73 parserContext.getReaderContext().error("You must specify the " + ATT_PASSWORD +
74 " if you supply a " + managerDn, elt);
75 }
76
77 contextSource.getPropertyValues().addPropertyValue("userDn", managerDn);
78 contextSource.getPropertyValues().addPropertyValue("password", managerPassword);
79 }
80
81 String id = elt.getAttribute(AbstractBeanDefinitionParser.ID_ATTRIBUTE);
82
83 String contextSourceId = StringUtils.hasText(id) ? id : BeanIds.CONTEXT_SOURCE;
84
85 parserContext.getRegistry().registerBeanDefinition(contextSourceId, contextSource);
86
87 return null;
88 }
89
90
91
92
93
94
95
96
97
98
99 private RootBeanDefinition createEmbeddedServer(Element element, ParserContext parserContext) {
100 Object source = parserContext.extractSource(element);
101 BeanDefinitionBuilder configuration =
102 BeanDefinitionBuilder.rootBeanDefinition("org.apache.directory.server.configuration.MutableServerStartupConfiguration");
103 BeanDefinitionBuilder partition =
104 BeanDefinitionBuilder.rootBeanDefinition("org.apache.directory.server.core.partition.impl.btree.MutableBTreePartitionConfiguration");
105 configuration.setSource(source);
106 partition.setSource(source);
107
108 Attributes rootAttributes = new BasicAttributes("dc", "springsecurity");
109 Attribute a = new BasicAttribute("objectClass");
110 a.add("top");
111 a.add("domain");
112 a.add("extensibleObject");
113 rootAttributes.put(a);
114
115 partition.addPropertyValue("name", "springsecurity");
116 partition.addPropertyValue("contextEntry", rootAttributes);
117
118 String suffix = element.getAttribute(ATT_ROOT_SUFFIX);
119
120 if (!StringUtils.hasText(suffix)) {
121 suffix = OPT_DEFAULT_ROOT_SUFFIX;
122 }
123
124 partition.addPropertyValue("suffix", suffix);
125
126 ManagedSet partitions = new ManagedSet(1);
127 partitions.add(partition.getBeanDefinition());
128
129 String port = element.getAttribute(ATT_PORT);
130
131 if (!StringUtils.hasText(port)) {
132 port = OPT_DEFAULT_PORT;
133 }
134
135 configuration.addPropertyValue("ldapPort", port);
136
137
138
139 configuration.addPropertyValue("shutdownHookEnabled", Boolean.FALSE);
140 configuration.addPropertyValue("exitVmOnShutdown", Boolean.FALSE);
141 configuration.addPropertyValue("contextPartitionConfigurations", partitions);
142
143 String url = "ldap://127.0.0.1:" + port + "/" + suffix;
144
145 BeanDefinitionBuilder contextSource = BeanDefinitionBuilder.rootBeanDefinition(CONTEXT_SOURCE_CLASS);
146 contextSource.addConstructorArg(url);
147 contextSource.addPropertyValue("userDn", "uid=admin,ou=system");
148 contextSource.addPropertyValue("password", "secret");
149
150 RootBeanDefinition apacheContainer = new RootBeanDefinition("org.springframework.security.config.ApacheDSContainer", null, null);
151 apacheContainer.setSource(source);
152 apacheContainer.getConstructorArgumentValues().addGenericArgumentValue(configuration.getBeanDefinition());
153 apacheContainer.getConstructorArgumentValues().addGenericArgumentValue(contextSource.getBeanDefinition());
154
155 String ldifs = element.getAttribute(ATT_LDIF_FILE);
156 if (!StringUtils.hasText(ldifs)) {
157 ldifs = OPT_DEFAULT_LDIF_FILE;
158 }
159
160 apacheContainer.getConstructorArgumentValues().addGenericArgumentValue(ldifs);
161
162 logger.info("Embedded LDAP server bean created for URL: " + url);
163
164 if (parserContext.getRegistry().containsBeanDefinition(BeanIds.EMBEDDED_APACHE_DS)) {
165 parserContext.getReaderContext().error("Only one embedded server bean is allowed per application context",
166 element);
167 }
168
169 parserContext.getRegistry().registerBeanDefinition(BeanIds.EMBEDDED_APACHE_DS, apacheContainer);
170
171 return (RootBeanDefinition) contextSource.getBeanDefinition();
172 }
173 }