]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserAdminConf.java
Next development cycle
[lgpl/argeo-commons.git] / org.argeo.enterprise / src / org / argeo / osgi / useradmin / UserAdminConf.java
1 package org.argeo.osgi.useradmin;
2
3 import java.io.IOException;
4 import java.io.UnsupportedEncodingException;
5 import java.net.InetAddress;
6 import java.net.URI;
7 import java.net.URISyntaxException;
8 import java.net.URLDecoder;
9 import java.util.Dictionary;
10 import java.util.Enumeration;
11 import java.util.Hashtable;
12 import java.util.LinkedHashMap;
13 import java.util.LinkedList;
14 import java.util.List;
15 import java.util.Map;
16
17 import javax.naming.Context;
18 import javax.naming.NamingException;
19
20 import org.apache.commons.logging.Log;
21 import org.apache.commons.logging.LogFactory;
22 import org.argeo.naming.DnsBrowser;
23 import org.osgi.framework.Constants;
24
25 /** Properties used to configure user admins. */
26 public enum UserAdminConf {
27 /** Base DN (cannot be configured externally) */
28 baseDn("dc=example,dc=com"),
29
30 /** URI of the underlying resource (cannot be configured externally) */
31 uri("ldap://localhost:10389"),
32
33 /** User objectClass */
34 userObjectClass("inetOrgPerson"),
35
36 /** Relative base DN for users */
37 userBase("ou=People"),
38
39 /** Groups objectClass */
40 groupObjectClass("groupOfNames"),
41
42 /** Relative base DN for users */
43 groupBase("ou=Groups"),
44
45 /** Read-only source */
46 readOnly(null),
47
48 /** Authentication realm */
49 realm(null);
50
51 public final static String FACTORY_PID = "org.argeo.osgi.useradmin.config";
52 private final static Log log = LogFactory.getLog(UserAdminConf.class);
53
54 /** The default value. */
55 private Object def;
56
57 UserAdminConf(Object def) {
58 this.def = def;
59 }
60
61 public Object getDefault() {
62 return def;
63 }
64
65 /**
66 * For use as Java property.
67 *
68 * @deprecated use {@link #name()} instead
69 */
70 @Deprecated
71 public String property() {
72 return name();
73 }
74
75 public String getValue(Dictionary<String, ?> properties) {
76 Object res = getRawValue(properties);
77 if (res == null)
78 return null;
79 return res.toString();
80 }
81
82 @SuppressWarnings("unchecked")
83 public <T> T getRawValue(Dictionary<String, ?> properties) {
84 Object res = properties.get(name());
85 if (res == null)
86 res = getDefault();
87 return (T) res;
88 }
89
90 /** @deprecated use {@link #valueOf(String)} instead */
91 @Deprecated
92 public static UserAdminConf local(String property) {
93 return UserAdminConf.valueOf(property);
94 }
95
96 /** Hides host and credentials. */
97 public static URI propertiesAsUri(Dictionary<String, ?> properties) {
98 StringBuilder query = new StringBuilder();
99
100 boolean first = true;
101 for (Enumeration<String> keys = properties.keys(); keys.hasMoreElements();) {
102 String key = keys.nextElement();
103 // TODO clarify which keys are relevant (list only the enum?)
104 if (!key.equals("service.factoryPid") && !key.equals("cn") && !key.equals("dn")
105 && !key.equals(Constants.SERVICE_PID) && !key.startsWith("java") && !key.equals(baseDn.name())
106 && !key.equals(uri.name())) {
107 if (first)
108 first = false;
109 else
110 query.append('&');
111 query.append(valueOf(key).name());
112 query.append('=').append(properties.get(key).toString());
113 }
114 }
115
116 String bDn = (String) properties.get(baseDn.name());
117 try {
118 return new URI(null, null, bDn != null ? '/' + bDn : null, query.length() != 0 ? query.toString() : null,
119 null);
120 } catch (URISyntaxException e) {
121 throw new UserDirectoryException("Cannot create URI from properties", e);
122 }
123 }
124
125 public static Dictionary<String, Object> uriAsProperties(String uriStr) {
126 try {
127 Hashtable<String, Object> res = new Hashtable<String, Object>();
128 URI u = new URI(uriStr);
129 String scheme = u.getScheme();
130 if (scheme != null && scheme.equals("ipa")) {
131 u = convertIpaConfig(u);
132 scheme = u.getScheme();
133 }
134 String path = u.getPath();
135 String bDn = path.substring(path.lastIndexOf('/') + 1, path.length());
136 if (bDn.endsWith(".ldif"))
137 bDn = bDn.substring(0, bDn.length() - ".ldif".length());
138
139 String principal = null;
140 String credentials = null;
141 if (scheme != null)
142 if (scheme.equals("ldap") || scheme.equals("ldaps")) {
143 // TODO additional checks
144 if (u.getUserInfo() != null) {
145 String[] userInfo = u.getUserInfo().split(":");
146 principal = userInfo.length > 0 ? userInfo[0] : null;
147 credentials = userInfo.length > 1 ? userInfo[1] : null;
148 }
149 } else if (scheme.equals("file")) {
150 } else if (scheme.equals("ipa")) {
151 } else
152 throw new UserDirectoryException("Unsupported scheme " + scheme);
153 Map<String, List<String>> query = splitQuery(u.getQuery());
154 for (String key : query.keySet()) {
155 UserAdminConf ldapProp = UserAdminConf.valueOf(key);
156 List<String> values = query.get(key);
157 if (values.size() == 1) {
158 res.put(ldapProp.name(), values.get(0));
159 } else {
160 throw new UserDirectoryException("Only single values are supported");
161 }
162 }
163 res.put(baseDn.name(), bDn);
164 if (principal != null)
165 res.put(Context.SECURITY_PRINCIPAL, principal);
166 if (credentials != null)
167 res.put(Context.SECURITY_CREDENTIALS, credentials);
168 if (scheme != null) {
169 URI bareUri = new URI(scheme, null, u.getHost(), u.getPort(),
170 scheme.equals("file") ? u.getPath() : null, null, null);
171 res.put(uri.name(), bareUri.toString());
172 }
173 return res;
174 } catch (Exception e) {
175 throw new UserDirectoryException("Cannot convert " + uri + " to properties", e);
176 }
177 }
178
179 private static URI convertIpaConfig(URI uri) {
180 String path = uri.getPath();
181 String kerberosRealm;
182 if (path == null || path.length() <= 1) {
183 kerberosRealm = kerberosDomainFromDns();
184 } else {
185 kerberosRealm = path.substring(1);
186 }
187
188 if (kerberosRealm == null)
189 throw new UserDirectoryException("No Kerberos domain available for " + uri);
190 try (DnsBrowser dnsBrowser = new DnsBrowser()) {
191 String ldapHostsStr = uri.getHost();
192 if (ldapHostsStr == null || ldapHostsStr.trim().equals("")) {
193 List<String> ldapHosts = dnsBrowser.getSrvRecordsAsHosts("_ldap._tcp." + kerberosRealm.toLowerCase());
194 if (ldapHosts == null || ldapHosts.size() == 0) {
195 throw new UserDirectoryException("Cannot configure LDAP for IPA " + uri);
196 } else {
197 ldapHostsStr = ldapHosts.get(0);
198 }
199 }
200 URI convertedUri = new URI(
201 "ldap://" + ldapHostsStr + "/" + IpaUtils.domainToUserDirectoryConfigPath(kerberosRealm));
202 if (log.isDebugEnabled())
203 log.debug("Converted " + uri + " to " + convertedUri);
204 return convertedUri;
205 } catch (NamingException | IOException | URISyntaxException e) {
206 throw new UserDirectoryException("cannot convert IPA uri " + uri, e);
207 }
208 }
209
210 private static String kerberosDomainFromDns() {
211 String kerberosDomain;
212 try (DnsBrowser dnsBrowser = new DnsBrowser()) {
213 InetAddress localhost = InetAddress.getLocalHost();
214 String hostname = localhost.getHostName();
215 String dnsZone = hostname.substring(hostname.indexOf('.') + 1);
216 kerberosDomain = dnsBrowser.getRecord("_kerberos." + dnsZone, "TXT");
217 return kerberosDomain;
218 } catch (Exception e) {
219 throw new UserDirectoryException("Cannot determine Kerberos domain from DNS", e);
220 }
221
222 }
223
224 private static Map<String, List<String>> splitQuery(String query) throws UnsupportedEncodingException {
225 final Map<String, List<String>> query_pairs = new LinkedHashMap<String, List<String>>();
226 if (query == null)
227 return query_pairs;
228 final String[] pairs = query.split("&");
229 for (String pair : pairs) {
230 final int idx = pair.indexOf("=");
231 final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), "UTF-8") : pair;
232 if (!query_pairs.containsKey(key)) {
233 query_pairs.put(key, new LinkedList<String>());
234 }
235 final String value = idx > 0 && pair.length() > idx + 1
236 ? URLDecoder.decode(pair.substring(idx + 1), "UTF-8") : null;
237 query_pairs.get(key).add(value);
238 }
239 return query_pairs;
240 }
241
242 public static void main(String[] args) {
243 Dictionary<String, ?> props = uriAsProperties("ldap://" + "uid=admin,ou=system:secret@localhost:10389"
244 + "/dc=example,dc=com" + "?readOnly=false&userObjectClass=person");
245 System.out.println(props);
246 System.out.println(propertiesAsUri(props));
247
248 System.out.println(uriAsProperties("file://some/dir/dc=example,dc=com.ldif"));
249
250 props = uriAsProperties(
251 "/dc=example,dc=com.ldif?readOnly=true" + "&userBase=ou=CoWorkers,ou=People&groupBase=ou=Roles");
252 System.out.println(props);
253 System.out.println(propertiesAsUri(props));
254 }
255 }