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