]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/runtime/DirectoryConf.java
Prepare next development cycle
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / runtime / DirectoryConf.java
1 package org.argeo.cms.runtime;
2
3 import java.net.InetAddress;
4 import java.net.URI;
5 import java.net.URISyntaxException;
6 import java.net.UnknownHostException;
7 import java.util.Dictionary;
8 import java.util.Hashtable;
9 import java.util.List;
10 import java.util.Map;
11
12 import org.argeo.api.acr.ldap.NamingUtils;
13 import org.argeo.api.cms.directory.DirectoryDigestUtils;
14 import org.argeo.cms.directory.ldap.IpaUtils;
15
16 /** Properties used to configure user admins. */
17 public enum DirectoryConf {
18 /** Base DN (cannot be configured externally) */
19 baseDn(null),
20
21 /** URI of the underlying resource (cannot be configured externally) */
22 uri(null),
23
24 /** User objectClass */
25 userObjectClass("inetOrgPerson"),
26
27 /** Relative base DN for users */
28 userBase("ou=People"),
29
30 /** Groups objectClass */
31 groupObjectClass("groupOfNames"),
32
33 /** Relative base DN for users */
34 groupBase("ou=Groups"),
35
36 /** Relative base DN for users */
37 systemRoleBase("ou=Roles"),
38
39 /** Read-only source */
40 readOnly(null),
41
42 /** Disabled source */
43 disabled(null),
44
45 /** Authentication realm */
46 realm(null),
47
48 /** Override all passwords with this value (typically for testing purposes) */
49 forcedPassword(null);
50
51 public final static String FACTORY_PID = "org.argeo.osgi.useradmin.config";
52
53 public final static String SCHEME_LDAP = "ldap";
54 public final static String SCHEME_LDAPS = "ldaps";
55 public final static String SCHEME_FILE = "file";
56 public final static String SCHEME_OS = "os";
57 public final static String SCHEME_IPA = "ipa";
58
59 private final static String SECURITY_PRINCIPAL = "java.naming.security.principal";
60 private final static String SECURITY_CREDENTIALS = "java.naming.security.credentials";
61
62 /** The default value. */
63 private Object def;
64
65 DirectoryConf(Object def) {
66 this.def = def;
67 }
68
69 public Object getDefault() {
70 return def;
71 }
72
73 /**
74 * For use as Java property.
75 *
76 * @deprecated use {@link #name()} instead
77 */
78 @Deprecated
79 public String property() {
80 return name();
81 }
82
83 public String getValue(Dictionary<String, ?> properties) {
84 Object res = getRawValue(properties);
85 if (res == null)
86 return null;
87 return res.toString();
88 }
89
90 @SuppressWarnings("unchecked")
91 public <T> T getRawValue(Dictionary<String, ?> properties) {
92 Object res = properties.get(name());
93 if (res == null)
94 res = getDefault();
95 return (T) res;
96 }
97
98 /** @deprecated use {@link #valueOf(String)} instead */
99 @Deprecated
100 public static DirectoryConf local(String property) {
101 return DirectoryConf.valueOf(property);
102 }
103
104 /** Hides host and credentials. */
105 public static URI propertiesAsUri(Dictionary<String, ?> properties) {
106 StringBuilder query = new StringBuilder();
107
108 boolean first = true;
109 // for (Enumeration<String> keys = properties.keys(); keys.hasMoreElements();) {
110 // String key = keys.nextElement();
111 // // TODO clarify which keys are relevant (list only the enum?)
112 // if (!key.equals("service.factoryPid") && !key.equals("cn") && !key.equals("dn")
113 // && !key.equals(Constants.SERVICE_PID) && !key.startsWith("java") && !key.equals(baseDn.name())
114 // && !key.equals(uri.name()) && !key.equals(Constants.OBJECTCLASS)
115 // && !key.equals(Constants.SERVICE_ID) && !key.equals("bundle.id")) {
116 // if (first)
117 // first = false;
118 // else
119 // query.append('&');
120 // query.append(valueOf(key).name());
121 // query.append('=').append(properties.get(key).toString());
122 // }
123 // }
124
125 keys: for (DirectoryConf key : DirectoryConf.values()) {
126 if (key.equals(baseDn) || key.equals(uri))
127 continue keys;
128 Object value = properties.get(key.name());
129 if (value == null)
130 continue keys;
131 if (first)
132 first = false;
133 else
134 query.append('&');
135 query.append(key.name());
136 query.append('=').append(value.toString());
137
138 }
139
140 Object bDnObj = properties.get(baseDn.name());
141 String bDn = bDnObj != null ? bDnObj.toString() : null;
142 try {
143 return new URI(null, null, bDn != null ? '/' + bDn : null, query.length() != 0 ? query.toString() : null,
144 null);
145 } catch (URISyntaxException e) {
146 throw new IllegalArgumentException("Cannot create URI from properties", e);
147 }
148 }
149
150 public static Dictionary<String, Object> uriAsProperties(String uriStr) {
151 try {
152 Hashtable<String, Object> res = new Hashtable<String, Object>();
153 URI u = new URI(uriStr);
154 String scheme = u.getScheme();
155 if (scheme != null && scheme.equals(SCHEME_IPA)) {
156 return IpaUtils.convertIpaUri(u);
157 // scheme = u.getScheme();
158 }
159 String path = u.getPath();
160 // base DN
161 String bDn = path.substring(path.lastIndexOf('/') + 1, path.length());
162 if (bDn.equals("") && SCHEME_OS.equals(scheme)) {
163 bDn = getBaseDnFromHostname();
164 }
165
166 if (bDn.endsWith(".ldif"))
167 bDn = bDn.substring(0, bDn.length() - ".ldif".length());
168
169 // Normalize base DN as LDAP name
170 // bDn = new LdapName(bDn).toString();
171
172 String principal = null;
173 String credentials = null;
174 if (scheme != null)
175 if (scheme.equals(SCHEME_LDAP) || scheme.equals(SCHEME_LDAPS)) {
176 // TODO additional checks
177 if (u.getUserInfo() != null) {
178 String[] userInfo = u.getUserInfo().split(":");
179 principal = userInfo.length > 0 ? userInfo[0] : null;
180 credentials = userInfo.length > 1 ? userInfo[1] : null;
181 }
182 } else if (scheme.equals(SCHEME_FILE)) {
183 } else if (scheme.equals(SCHEME_IPA)) {
184 } else if (scheme.equals(SCHEME_OS)) {
185 } else
186 throw new IllegalArgumentException("Unsupported scheme " + scheme);
187 Map<String, List<String>> query = NamingUtils.queryToMap(u);
188 for (String key : query.keySet()) {
189 DirectoryConf ldapProp = DirectoryConf.valueOf(key);
190 List<String> values = query.get(key);
191 if (values.size() == 1) {
192 res.put(ldapProp.name(), values.get(0));
193 } else {
194 throw new IllegalArgumentException("Only single values are supported");
195 }
196 }
197 res.put(baseDn.name(), bDn);
198 if (SCHEME_OS.equals(scheme))
199 res.put(readOnly.name(), "true");
200 if (principal != null)
201 res.put(SECURITY_PRINCIPAL, principal);
202 if (credentials != null)
203 res.put(SECURITY_CREDENTIALS, credentials);
204 if (scheme != null) {// relative URIs are dealt with externally
205 if (SCHEME_OS.equals(scheme)) {
206 res.put(uri.name(), SCHEME_OS + ":///");
207 } else {
208 URI bareUri = new URI(scheme, null, u.getHost(), u.getPort(),
209 scheme.equals(SCHEME_FILE) ? u.getPath() : null, null, null);
210 res.put(uri.name(), bareUri.toString());
211 }
212 }
213 return res;
214 } catch (URISyntaxException e) {
215 throw new IllegalArgumentException("Cannot convert " + uri + " to properties", e);
216 }
217 }
218
219 private static String getBaseDnFromHostname() {
220 String hostname;
221 try {
222 hostname = InetAddress.getLocalHost().getHostName();
223 } catch (UnknownHostException e) {
224 hostname = "localhost.localdomain";
225 }
226 int dotIdx = hostname.indexOf('.');
227 if (dotIdx >= 0) {
228 String domain = hostname.substring(dotIdx + 1, hostname.length());
229 String bDn = ("." + domain).replaceAll("\\.", ",dc=");
230 bDn = bDn.substring(1, bDn.length());
231 return bDn;
232 } else {
233 return "dc=" + hostname;
234 }
235 }
236
237 /**
238 * Hash the base DN in order to have a deterministic string to be used as a cn
239 * for the underlying user directory.
240 */
241 public static String baseDnHash(Dictionary<String, Object> properties) {
242 String bDn = (String) properties.get(baseDn.name());
243 if (bDn == null)
244 throw new IllegalStateException("No baseDn in " + properties);
245 return DirectoryDigestUtils.sha1str(bDn);
246 }
247 }