]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserAdminConf.java
Add JGit to client.
[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.net.InetAddress;
5 import java.net.URI;
6 import java.net.URISyntaxException;
7 import java.net.UnknownHostException;
8 import java.util.Dictionary;
9 import java.util.Enumeration;
10 import java.util.Hashtable;
11 import java.util.List;
12 import java.util.Map;
13
14 import javax.naming.Context;
15 import javax.naming.NamingException;
16 import javax.naming.ldap.LdapName;
17
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 /** Disabled source */
46 disabled(null),
47
48 /** Authentication realm */
49 realm(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_FILE = "file";
55 public final static String SCHEME_OS = "os";
56 public final static String SCHEME_IPA = "ipa";
57
58 /** The default value. */
59 private Object def;
60
61 UserAdminConf(Object def) {
62 this.def = def;
63 }
64
65 public Object getDefault() {
66 return def;
67 }
68
69 /**
70 * For use as Java property.
71 *
72 * @deprecated use {@link #name()} instead
73 */
74 @Deprecated
75 public String property() {
76 return name();
77 }
78
79 public String getValue(Dictionary<String, ?> properties) {
80 Object res = getRawValue(properties);
81 if (res == null)
82 return null;
83 return res.toString();
84 }
85
86 @SuppressWarnings("unchecked")
87 public <T> T getRawValue(Dictionary<String, ?> properties) {
88 Object res = properties.get(name());
89 if (res == null)
90 res = getDefault();
91 return (T) res;
92 }
93
94 /** @deprecated use {@link #valueOf(String)} instead */
95 @Deprecated
96 public static UserAdminConf local(String property) {
97 return UserAdminConf.valueOf(property);
98 }
99
100 /** Hides host and credentials. */
101 public static URI propertiesAsUri(Dictionary<String, ?> properties) {
102 StringBuilder query = new StringBuilder();
103
104 boolean first = true;
105 for (Enumeration<String> keys = properties.keys(); keys.hasMoreElements();) {
106 String key = keys.nextElement();
107 // TODO clarify which keys are relevant (list only the enum?)
108 if (!key.equals("service.factoryPid") && !key.equals("cn") && !key.equals("dn")
109 && !key.equals(Constants.SERVICE_PID) && !key.startsWith("java") && !key.equals(baseDn.name())
110 && !key.equals(uri.name())) {
111 if (first)
112 first = false;
113 else
114 query.append('&');
115 query.append(valueOf(key).name());
116 query.append('=').append(properties.get(key).toString());
117 }
118 }
119
120 String bDn = (String) properties.get(baseDn.name());
121 try {
122 return new URI(null, null, bDn != null ? '/' + bDn : null, query.length() != 0 ? query.toString() : null,
123 null);
124 } catch (URISyntaxException e) {
125 throw new UserDirectoryException("Cannot create URI from properties", e);
126 }
127 }
128
129 public static Dictionary<String, Object> uriAsProperties(String uriStr) {
130 try {
131 Hashtable<String, Object> res = new Hashtable<String, Object>();
132 URI u = new URI(uriStr);
133 String scheme = u.getScheme();
134 if (scheme != null && scheme.equals(SCHEME_IPA)) {
135 u = convertIpaConfig(u);
136 scheme = u.getScheme();
137 }
138 String path = u.getPath();
139 // base DN
140 String bDn = path.substring(path.lastIndexOf('/') + 1, path.length());
141 if (bDn.equals("") && SCHEME_OS.equals(scheme)) {
142 bDn = getBaseDnFromHostname();
143 }
144
145 if (bDn.endsWith(".ldif"))
146 bDn = bDn.substring(0, bDn.length() - ".ldif".length());
147
148 // Normalize base DN as LDAP name
149 bDn = new LdapName(bDn).toString();
150
151 String principal = null;
152 String credentials = null;
153 if (scheme != null)
154 if (scheme.equals(SCHEME_LDAP) || scheme.equals("ldaps")) {
155 // TODO additional checks
156 if (u.getUserInfo() != null) {
157 String[] userInfo = u.getUserInfo().split(":");
158 principal = userInfo.length > 0 ? userInfo[0] : null;
159 credentials = userInfo.length > 1 ? userInfo[1] : null;
160 }
161 } else if (scheme.equals(SCHEME_FILE)) {
162 } else if (scheme.equals(SCHEME_IPA)) {
163 } else if (scheme.equals(SCHEME_OS)) {
164 } else
165 throw new UserDirectoryException("Unsupported scheme " + scheme);
166 Map<String, List<String>> query = NamingUtils.queryToMap(u);
167 for (String key : query.keySet()) {
168 UserAdminConf ldapProp = UserAdminConf.valueOf(key);
169 List<String> values = query.get(key);
170 if (values.size() == 1) {
171 res.put(ldapProp.name(), values.get(0));
172 } else {
173 throw new UserDirectoryException("Only single values are supported");
174 }
175 }
176 res.put(baseDn.name(), bDn);
177 if (SCHEME_OS.equals(scheme))
178 res.put(readOnly.name(), "true");
179 if (principal != null)
180 res.put(Context.SECURITY_PRINCIPAL, principal);
181 if (credentials != null)
182 res.put(Context.SECURITY_CREDENTIALS, credentials);
183 if (scheme != null) {// relative URIs are dealt with externally
184 if (SCHEME_OS.equals(scheme)) {
185 res.put(uri.name(), SCHEME_OS + ":///");
186 } else {
187 URI bareUri = new URI(scheme, null, u.getHost(), u.getPort(),
188 scheme.equals(SCHEME_FILE) ? u.getPath() : null, null, null);
189 res.put(uri.name(), bareUri.toString());
190 }
191 }
192 return res;
193 } catch (Exception e) {
194 throw new UserDirectoryException("Cannot convert " + uri + " to properties", e);
195 }
196 }
197
198 private static URI convertIpaConfig(URI uri) {
199 String path = uri.getPath();
200 String kerberosRealm;
201 if (path == null || path.length() <= 1) {
202 kerberosRealm = kerberosDomainFromDns();
203 } else {
204 kerberosRealm = path.substring(1);
205 }
206
207 if (kerberosRealm == null)
208 throw new UserDirectoryException("No Kerberos domain available for " + uri);
209 try (DnsBrowser dnsBrowser = new DnsBrowser()) {
210 String ldapHostsStr = uri.getHost();
211 if (ldapHostsStr == null || ldapHostsStr.trim().equals("")) {
212 List<String> ldapHosts = dnsBrowser.getSrvRecordsAsHosts("_ldap._tcp." + kerberosRealm.toLowerCase());
213 if (ldapHosts == null || ldapHosts.size() == 0) {
214 throw new UserDirectoryException("Cannot configure LDAP for IPA " + uri);
215 } else {
216 ldapHostsStr = ldapHosts.get(0);
217 }
218 }
219 URI convertedUri = new URI(
220 SCHEME_LDAP + "://" + ldapHostsStr + "/" + IpaUtils.domainToUserDirectoryConfigPath(kerberosRealm));
221 return convertedUri;
222 } catch (NamingException | IOException | URISyntaxException e) {
223 throw new UserDirectoryException("cannot convert IPA uri " + uri, e);
224 }
225 }
226
227 private static String kerberosDomainFromDns() {
228 String kerberosDomain;
229 try (DnsBrowser dnsBrowser = new DnsBrowser()) {
230 InetAddress localhost = InetAddress.getLocalHost();
231 String hostname = localhost.getHostName();
232 String dnsZone = hostname.substring(hostname.indexOf('.') + 1);
233 kerberosDomain = dnsBrowser.getRecord("_kerberos." + dnsZone, "TXT");
234 return kerberosDomain;
235 } catch (Exception e) {
236 throw new UserDirectoryException("Cannot determine Kerberos domain from DNS", e);
237 }
238
239 }
240
241 private static String getBaseDnFromHostname() {
242 String hostname;
243 try {
244 hostname = InetAddress.getLocalHost().getHostName();
245 } catch (UnknownHostException e) {
246 hostname = "localhost.localdomain";
247 }
248 int dotIdx = hostname.indexOf('.');
249 if (dotIdx >= 0) {
250 String domain = hostname.substring(dotIdx + 1, hostname.length());
251 String bDn = ("." + domain).replaceAll("\\.", ",dc=");
252 bDn = bDn.substring(1, bDn.length());
253 return bDn;
254 } else {
255 return "dc=" + hostname;
256 }
257 }
258
259 /**
260 * Hash the base DN in order to have a deterministic string to be used as a cn
261 * for the underlying user directory.
262 */
263 public static String baseDnHash(Dictionary<String, Object> properties) {
264 String bDn = (String) properties.get(baseDn.name());
265 if (bDn == null)
266 throw new UserDirectoryException("No baseDn in " + properties);
267 return DigestUtils.sha1str(bDn);
268 }
269 }