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