]> git.argeo.org Git - lgpl/argeo-commons.git/blob - internal/kernel/InitUtils.java
Prepare next development cycle
[lgpl/argeo-commons.git] / internal / kernel / InitUtils.java
1 package org.argeo.cms.internal.kernel;
2
3 import static org.argeo.cms.internal.kernel.KernelUtils.getFrameworkProp;
4
5 import java.io.File;
6 import java.io.FileFilter;
7 import java.io.IOException;
8 import java.net.InetAddress;
9 import java.net.URI;
10 import java.net.URISyntaxException;
11 import java.nio.file.Files;
12 import java.nio.file.Path;
13 import java.security.KeyStore;
14 import java.util.ArrayList;
15 import java.util.Arrays;
16 import java.util.Dictionary;
17 import java.util.HashMap;
18 import java.util.Hashtable;
19 import java.util.List;
20 import java.util.Map;
21
22 import javax.jcr.Repository;
23 import javax.jcr.RepositoryException;
24 import javax.jcr.RepositoryFactory;
25 import javax.security.auth.x500.X500Principal;
26
27 import org.apache.commons.io.FileUtils;
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30 import org.argeo.api.NodeConstants;
31 import org.argeo.cms.internal.http.InternalHttpConstants;
32 import org.argeo.cms.internal.jcr.RepoConf;
33 import org.argeo.jackrabbit.client.ClientDavexRepositoryFactory;
34 import org.argeo.jcr.JcrException;
35 import org.argeo.naming.LdapAttrs;
36 import org.argeo.osgi.useradmin.UserAdminConf;
37 import org.osgi.framework.BundleContext;
38 import org.osgi.framework.Constants;
39
40 /**
41 * Interprets framework properties in order to generate the initial deploy
42 * configuration.
43 */
44 class InitUtils {
45 private final static Log log = LogFactory.getLog(InitUtils.class);
46
47 /** Override the provided config with the framework properties */
48 static Dictionary<String, Object> getNodeRepositoryConfig(Dictionary<String, Object> provided) {
49 Dictionary<String, Object> props = provided != null ? provided : new Hashtable<String, Object>();
50 for (RepoConf repoConf : RepoConf.values()) {
51 Object value = getFrameworkProp(NodeConstants.NODE_REPO_PROP_PREFIX + repoConf.name());
52 if (value != null) {
53 props.put(repoConf.name(), value);
54 if (log.isDebugEnabled())
55 log.debug("Set node repo configuration " + repoConf.name() + " to " + value);
56 }
57 }
58 props.put(NodeConstants.CN, NodeConstants.NODE_REPOSITORY);
59 return props;
60 }
61
62 static Dictionary<String, Object> getRepositoryConfig(String dataModelName, Dictionary<String, Object> provided) {
63 if (dataModelName.equals(NodeConstants.NODE_REPOSITORY) || dataModelName.equals(NodeConstants.EGO_REPOSITORY))
64 throw new IllegalArgumentException("Data model '" + dataModelName + "' is reserved.");
65 Dictionary<String, Object> props = provided != null ? provided : new Hashtable<String, Object>();
66 for (RepoConf repoConf : RepoConf.values()) {
67 Object value = getFrameworkProp(
68 NodeConstants.NODE_REPOS_PROP_PREFIX + dataModelName + '.' + repoConf.name());
69 if (value != null) {
70 props.put(repoConf.name(), value);
71 if (log.isDebugEnabled())
72 log.debug("Set " + dataModelName + " repo configuration " + repoConf.name() + " to " + value);
73 }
74 }
75 if (props.size() != 0)
76 props.put(NodeConstants.CN, dataModelName);
77 return props;
78 }
79
80 /** Override the provided config with the framework properties */
81 static Dictionary<String, Object> getHttpServerConfig(Dictionary<String, Object> provided) {
82 String httpPort = getFrameworkProp("org.osgi.service.http.port");
83 String httpsPort = getFrameworkProp("org.osgi.service.http.port.secure");
84 /// TODO make it more generic
85 String httpHost = getFrameworkProp(
86 InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.HTTP_HOST);
87 String httpsHost = getFrameworkProp(
88 InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.HTTPS_HOST);
89 String webSocketEnabled = getFrameworkProp(
90 InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.WEBSOCKET_ENABLED);
91
92 final Hashtable<String, Object> props = new Hashtable<String, Object>();
93 // try {
94 if (httpPort != null || httpsPort != null) {
95 boolean httpEnabled = httpPort != null;
96 props.put(InternalHttpConstants.HTTP_ENABLED, httpEnabled);
97 boolean httpsEnabled = httpsPort != null;
98 props.put(InternalHttpConstants.HTTPS_ENABLED, httpsEnabled);
99
100 if (httpEnabled) {
101 props.put(InternalHttpConstants.HTTP_PORT, httpPort);
102 if (httpHost != null)
103 props.put(InternalHttpConstants.HTTP_HOST, httpHost);
104 }
105
106 if (httpsEnabled) {
107 props.put(InternalHttpConstants.HTTPS_PORT, httpsPort);
108 if (httpsHost != null)
109 props.put(InternalHttpConstants.HTTPS_HOST, httpsHost);
110
111 // server certificate
112 Path keyStorePath = KernelUtils.getOsgiInstancePath(KernelConstants.DEFAULT_KEYSTORE_PATH);
113 String keyStorePassword = getFrameworkProp(
114 InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.SSL_PASSWORD);
115 if (keyStorePassword == null)
116 keyStorePassword = "changeit";
117 if (!Files.exists(keyStorePath))
118 createSelfSignedKeyStore(keyStorePath, keyStorePassword, PkiUtils.PKCS12);
119 props.put(InternalHttpConstants.SSL_KEYSTORETYPE, PkiUtils.PKCS12);
120 props.put(InternalHttpConstants.SSL_KEYSTORE, keyStorePath.toString());
121 props.put(InternalHttpConstants.SSL_PASSWORD, keyStorePassword);
122
123 // client certificate authentication
124 String wantClientAuth = getFrameworkProp(
125 InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.SSL_WANTCLIENTAUTH);
126 if (wantClientAuth != null)
127 props.put(InternalHttpConstants.SSL_WANTCLIENTAUTH, Boolean.parseBoolean(wantClientAuth));
128 String needClientAuth = getFrameworkProp(
129 InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.SSL_NEEDCLIENTAUTH);
130 if (needClientAuth != null)
131 props.put(InternalHttpConstants.SSL_NEEDCLIENTAUTH, Boolean.parseBoolean(needClientAuth));
132 }
133
134 // web socket
135 if (webSocketEnabled != null && webSocketEnabled.equals("true"))
136 props.put(InternalHttpConstants.WEBSOCKET_ENABLED, true);
137
138 props.put(NodeConstants.CN, NodeConstants.DEFAULT);
139 }
140 return props;
141 }
142
143 static List<Dictionary<String, Object>> getUserDirectoryConfigs() {
144 List<Dictionary<String, Object>> res = new ArrayList<>();
145 File nodeBaseDir = KernelUtils.getOsgiInstancePath(KernelConstants.DIR_NODE).toFile();
146 List<String> uris = new ArrayList<>();
147
148 // node roles
149 String nodeRolesUri = getFrameworkProp(NodeConstants.ROLES_URI);
150 String baseNodeRoleDn = NodeConstants.ROLES_BASEDN;
151 if (nodeRolesUri == null) {
152 nodeRolesUri = baseNodeRoleDn + ".ldif";
153 File nodeRolesFile = new File(nodeBaseDir, nodeRolesUri);
154 if (!nodeRolesFile.exists())
155 try {
156 FileUtils.copyInputStreamToFile(InitUtils.class.getResourceAsStream(baseNodeRoleDn + ".ldif"),
157 nodeRolesFile);
158 } catch (IOException e) {
159 throw new RuntimeException("Cannot copy demo resource", e);
160 }
161 // nodeRolesUri = nodeRolesFile.toURI().toString();
162 }
163 uris.add(nodeRolesUri);
164
165 // node tokens
166 String nodeTokensUri = getFrameworkProp(NodeConstants.TOKENS_URI);
167 String baseNodeTokensDn = NodeConstants.TOKENS_BASEDN;
168 if (nodeTokensUri == null) {
169 nodeTokensUri = baseNodeTokensDn + ".ldif";
170 File nodeRolesFile = new File(nodeBaseDir, nodeRolesUri);
171 if (!nodeRolesFile.exists())
172 try {
173 FileUtils.copyInputStreamToFile(InitUtils.class.getResourceAsStream(baseNodeTokensDn + ".ldif"),
174 nodeRolesFile);
175 } catch (IOException e) {
176 throw new RuntimeException("Cannot copy demo resource", e);
177 }
178 // nodeRolesUri = nodeRolesFile.toURI().toString();
179 }
180 uris.add(nodeTokensUri);
181
182 // Business roles
183 String userAdminUris = getFrameworkProp(NodeConstants.USERADMIN_URIS);
184 if (userAdminUris == null) {
185 String demoBaseDn = "dc=example,dc=com";
186 userAdminUris = demoBaseDn + ".ldif";
187 File businessRolesFile = new File(nodeBaseDir, userAdminUris);
188 File systemRolesFile = new File(nodeBaseDir, "ou=roles,ou=node.ldif");
189 if (!businessRolesFile.exists())
190 try {
191 FileUtils.copyInputStreamToFile(InitUtils.class.getResourceAsStream(demoBaseDn + ".ldif"),
192 businessRolesFile);
193 if (!systemRolesFile.exists())
194 FileUtils.copyInputStreamToFile(
195 InitUtils.class.getResourceAsStream("example-ou=roles,ou=node.ldif"), systemRolesFile);
196 } catch (IOException e) {
197 throw new RuntimeException("Cannot copy demo resources", e);
198 }
199 // userAdminUris = businessRolesFile.toURI().toString();
200 log.warn("## DEV Using dummy base DN " + demoBaseDn);
201 // TODO downgrade security level
202 }
203 for (String userAdminUri : userAdminUris.split(" "))
204 uris.add(userAdminUri);
205
206 // Interprets URIs
207 for (String uri : uris) {
208 URI u;
209 try {
210 u = new URI(uri);
211 if (u.getPath() == null)
212 throw new IllegalArgumentException(
213 "URI " + uri + " must have a path in order to determine base DN");
214 if (u.getScheme() == null) {
215 if (uri.startsWith("/") || uri.startsWith("./") || uri.startsWith("../"))
216 u = new File(uri).getCanonicalFile().toURI();
217 else if (!uri.contains("/")) {
218 // u = KernelUtils.getOsgiInstanceUri(KernelConstants.DIR_NODE + '/' + uri);
219 u = new URI(uri);
220 } else
221 throw new IllegalArgumentException("Cannot interpret " + uri + " as an uri");
222 } else if (u.getScheme().equals(UserAdminConf.SCHEME_FILE)) {
223 u = new File(u).getCanonicalFile().toURI();
224 }
225 } catch (Exception e) {
226 throw new RuntimeException("Cannot interpret " + uri + " as an uri", e);
227 }
228 Dictionary<String, Object> properties = UserAdminConf.uriAsProperties(u.toString());
229 res.add(properties);
230 }
231
232 return res;
233 }
234
235 /**
236 * Called before node initialisation, in order populate OSGi instance are with
237 * some files (typically LDIF, etc).
238 */
239 static void prepareFirstInitInstanceArea() {
240 String nodeInits = getFrameworkProp(NodeConstants.NODE_INIT);
241 if (nodeInits == null)
242 nodeInits = "../../init";
243
244 for (String nodeInit : nodeInits.split(",")) {
245
246 if (nodeInit.startsWith("http")) {
247 registerRemoteInit(nodeInit);
248 } else {
249
250 // TODO use java.nio.file
251 File initDir;
252 if (nodeInit.startsWith("."))
253 initDir = KernelUtils.getExecutionDir(nodeInit);
254 else
255 initDir = new File(nodeInit);
256 // TODO also uncompress archives
257 if (initDir.exists())
258 try {
259 FileUtils.copyDirectory(initDir, KernelUtils.getOsgiInstanceDir(), new FileFilter() {
260
261 @Override
262 public boolean accept(File pathname) {
263 if (pathname.getName().equals(".svn") || pathname.getName().equals(".git"))
264 return false;
265 return true;
266 }
267 });
268 log.info("CMS initialized from " + initDir.getCanonicalPath());
269 } catch (IOException e) {
270 throw new RuntimeException("Cannot initialize from " + initDir, e);
271 }
272 }
273 }
274 }
275
276 private static void registerRemoteInit(String uri) {
277 try {
278 BundleContext bundleContext = KernelUtils.getBundleContext();
279 Repository repository = createRemoteRepository(new URI(uri));
280 Hashtable<String, Object> properties = new Hashtable<>();
281 properties.put(NodeConstants.CN, NodeConstants.NODE_INIT);
282 properties.put(LdapAttrs.labeledURI.name(), uri);
283 properties.put(Constants.SERVICE_RANKING, -1000);
284 bundleContext.registerService(Repository.class, repository, properties);
285 } catch (RepositoryException e) {
286 throw new JcrException(e);
287 } catch (URISyntaxException e) {
288 throw new IllegalArgumentException(e);
289 }
290 }
291
292 private static Repository createRemoteRepository(URI uri) throws RepositoryException {
293 RepositoryFactory repositoryFactory = new ClientDavexRepositoryFactory();
294 Map<String, String> params = new HashMap<String, String>();
295 params.put(ClientDavexRepositoryFactory.JACKRABBIT_DAVEX_URI, uri.toString());
296 // TODO make it configurable
297 params.put(ClientDavexRepositoryFactory.JACKRABBIT_REMOTE_DEFAULT_WORKSPACE, NodeConstants.SYS_WORKSPACE);
298 return repositoryFactory.getRepository(params);
299 }
300
301 private static void createSelfSignedKeyStore(Path keyStorePath, String keyStorePassword, String keyStoreType) {
302 // for (Provider provider : Security.getProviders())
303 // System.out.println(provider.getName());
304 File keyStoreFile = keyStorePath.toFile();
305 char[] ksPwd = keyStorePassword.toCharArray();
306 char[] keyPwd = Arrays.copyOf(ksPwd, ksPwd.length);
307 if (!keyStoreFile.exists()) {
308 try {
309 keyStoreFile.getParentFile().mkdirs();
310 KeyStore keyStore = PkiUtils.getKeyStore(keyStoreFile, ksPwd, keyStoreType);
311 PkiUtils.generateSelfSignedCertificate(keyStore,
312 new X500Principal("CN=" + InetAddress.getLocalHost().getHostName() + ",OU=UNSECURE,O=UNSECURE"),
313 1024, keyPwd);
314 PkiUtils.saveKeyStore(keyStoreFile, ksPwd, keyStore);
315 if (log.isDebugEnabled())
316 log.debug("Created self-signed unsecure keystore " + keyStoreFile);
317 } catch (Exception e) {
318 if (keyStoreFile.length() == 0)
319 keyStoreFile.delete();
320 log.error("Cannot create keystore " + keyStoreFile, e);
321 }
322 } else {
323 throw new IllegalStateException("Keystore " + keyStorePath + " already exists");
324 }
325 }
326
327 }