1 package org
.argeo
.cms
.internal
.runtime
;
4 import java
.io
.FileFilter
;
5 import java
.io
.IOException
;
7 import java
.net
.InetAddress
;
9 import java
.net
.UnknownHostException
;
10 import java
.nio
.charset
.StandardCharsets
;
11 import java
.nio
.file
.Files
;
12 import java
.nio
.file
.Path
;
13 import java
.nio
.file
.Paths
;
14 import java
.security
.KeyStore
;
15 import java
.util
.ArrayList
;
16 import java
.util
.Arrays
;
17 import java
.util
.Collections
;
18 import java
.util
.HashMap
;
19 import java
.util
.List
;
20 import java
.util
.Locale
;
22 import java
.util
.Objects
;
23 import java
.util
.StringJoiner
;
24 import java
.util
.UUID
;
26 import javax
.security
.auth
.login
.Configuration
;
28 import org
.apache
.commons
.io
.FileUtils
;
29 import org
.argeo
.api
.cms
.CmsConstants
;
30 import org
.argeo
.api
.cms
.CmsLog
;
31 import org
.argeo
.api
.cms
.CmsState
;
32 import org
.argeo
.api
.uuid
.UuidFactory
;
33 import org
.argeo
.cms
.CmsDeployProperty
;
34 import org
.argeo
.cms
.auth
.ident
.IdentClient
;
37 * Implementation of a {@link CmsState}, initialising the required services.
39 public class CmsStateImpl
implements CmsState
{
40 private final static CmsLog log
= CmsLog
.getLog(CmsStateImpl
.class);
43 private Long availableSince
;
46 // private final boolean cleanState;
47 private String hostname
;
49 private UuidFactory uuidFactory
;
51 private final Map
<CmsDeployProperty
, String
> deployPropertyDefaults
;
53 public CmsStateImpl() {
54 Map
<CmsDeployProperty
, String
> deployPropertyDefaults
= new HashMap
<>();
55 deployPropertyDefaults
.put(CmsDeployProperty
.NODE_INIT
, "../../init");
56 deployPropertyDefaults
.put(CmsDeployProperty
.LOCALE
, Locale
.getDefault().toString());
59 deployPropertyDefaults
.put(CmsDeployProperty
.SSL_KEYSTORETYPE
, PkiUtils
.PKCS12
);
60 deployPropertyDefaults
.put(CmsDeployProperty
.SSL_PASSWORD
, PkiUtils
.DEFAULT_KEYSTORE_PASSWORD
);
61 Path keyStorePath
= getDataPath(PkiUtils
.DEFAULT_KEYSTORE_PATH
);
62 deployPropertyDefaults
.put(CmsDeployProperty
.SSL_KEYSTORE
, keyStorePath
.toAbsolutePath().toString());
64 Path trustStorePath
= getDataPath(PkiUtils
.DEFAULT_TRUSTSTORE_PATH
);
65 deployPropertyDefaults
.put(CmsDeployProperty
.SSL_TRUSTSTORETYPE
, PkiUtils
.PKCS12
);
66 deployPropertyDefaults
.put(CmsDeployProperty
.SSL_TRUSTSTOREPASSWORD
, PkiUtils
.DEFAULT_KEYSTORE_PASSWORD
);
67 deployPropertyDefaults
.put(CmsDeployProperty
.SSL_TRUSTSTORE
, trustStorePath
.toAbsolutePath().toString());
69 this.deployPropertyDefaults
= Collections
.unmodifiableMap(deployPropertyDefaults
);
73 // Runtime.getRuntime().addShutdownHook(new CmsShutdown());
79 if (log
.isTraceEnabled())
80 log
.trace("CMS State started");
82 // String stateUuidStr = KernelUtils.getFrameworkProp(Constants.FRAMEWORK_UUID);
83 // this.uuid = UUID.fromString(stateUuidStr);
84 this.uuid
= uuidFactory
.timeUUID();
85 // this.cleanState = stateUuid.equals(frameworkUuid);
87 this.hostname
= InetAddress
.getLocalHost().getHostName();
88 } catch (UnknownHostException e
) {
89 log
.error("Cannot set hostname: " + e
);
92 availableSince
= System
.currentTimeMillis();
93 if (log
.isDebugEnabled()) {
94 // log.debug("## CMS starting... stateUuid=" + this.stateUuid + (cleanState ? "
95 // (clean state) " : " "));
96 StringJoiner sb
= new StringJoiner("\n");
97 CmsDeployProperty
[] deployProperties
= CmsDeployProperty
.values();
98 Arrays
.sort(deployProperties
, (o1
, o2
) -> o1
.name().compareTo(o2
.name()));
99 for (CmsDeployProperty deployProperty
: deployProperties
) {
100 List
<String
> values
= getDeployProperties(deployProperty
);
101 for (int i
= 0; i
< values
.size(); i
++) {
102 String value
= values
.get(i
);
104 boolean isDefault
= deployPropertyDefaults
.containsKey(deployProperty
)
105 && value
.equals(deployPropertyDefaults
.get(deployProperty
));
106 String line
= deployProperty
.getProperty() + (i
== 0 ?
"" : "." + i
) + "=" + value
107 + (isDefault ?
" (default)" : "");
112 log
.debug("## CMS starting... (" + uuid
+ ")\n" + sb
+ "\n");
117 if (!Files
.exists(getDataPath(CmsConstants
.NODE
))) {// first init
121 } catch (RuntimeException
| IOException e
) {
122 log
.error("## FATAL: CMS activator failed", e
);
126 private void initSecurity() {
127 if (getDeployProperty(CmsDeployProperty
.JAVA_LOGIN_CONFIG
) == null) {
128 String jaasConfig
= KernelConstants
.JAAS_CONFIG
;
129 URL url
= getClass().getResource(jaasConfig
);
130 // System.setProperty(KernelConstants.JAAS_CONFIG_PROP,
131 // url.toExternalForm());
132 KernelUtils
.setJaasConfiguration(url
);
134 // explicitly load JAAS configuration
135 Configuration
.getConfiguration();
137 boolean initSsl
= getDeployProperty(CmsDeployProperty
.HTTPS_PORT
) != null;
143 private void initCertificates() {
144 // server certificate
145 Path keyStorePath
= Paths
.get(getDeployProperty(CmsDeployProperty
.SSL_KEYSTORE
));
146 Path pemKeyPath
= getDataPath(PkiUtils
.DEFAULT_PEM_KEY_PATH
);
147 Path pemCertPath
= getDataPath(PkiUtils
.DEFAULT_PEM_CERT_PATH
);
148 char[] keyStorePassword
= getDeployProperty(CmsDeployProperty
.SSL_PASSWORD
).toCharArray();
151 // if PEM files both exists, update the PKCS12 file
152 if (Files
.exists(pemCertPath
) && Files
.exists(pemKeyPath
)) {
153 // TODO check certificate update time? monitor changes?
154 KeyStore keyStore
= PkiUtils
.getKeyStore(keyStorePath
, keyStorePassword
,
155 getDeployProperty(CmsDeployProperty
.SSL_KEYSTORETYPE
));
156 try (Reader key
= Files
.newBufferedReader(pemKeyPath
, StandardCharsets
.US_ASCII
);
157 Reader cert
= Files
.newBufferedReader(pemCertPath
, StandardCharsets
.US_ASCII
);) {
158 PkiUtils
.loadPem(keyStore
, key
, keyStorePassword
, cert
);
159 Files
.createDirectories(keyStorePath
.getParent());
160 PkiUtils
.saveKeyStore(keyStorePath
, keyStorePassword
, keyStore
);
161 if (log
.isDebugEnabled())
162 log
.debug("PEM certificate stored in " + keyStorePath
);
163 } catch (IOException e
) {
164 log
.error("Cannot read PEM files " + pemKeyPath
+ " and " + pemCertPath
, e
);
169 Path trustStorePath
= Paths
.get(getDeployProperty(CmsDeployProperty
.SSL_TRUSTSTORE
));
170 char[] trustStorePassword
= getDeployProperty(CmsDeployProperty
.SSL_TRUSTSTOREPASSWORD
).toCharArray();
173 Path ipaCaCertPath
= Paths
.get(PkiUtils
.IPA_PEM_CA_CERT_PATH
);
174 if (Files
.exists(ipaCaCertPath
)) {
175 KeyStore trustStore
= PkiUtils
.getKeyStore(trustStorePath
, trustStorePassword
,
176 getDeployProperty(CmsDeployProperty
.SSL_TRUSTSTORETYPE
));
177 try (Reader cert
= Files
.newBufferedReader(ipaCaCertPath
, StandardCharsets
.US_ASCII
);) {
178 PkiUtils
.loadPem(trustStore
, null, trustStorePassword
, cert
);
179 Files
.createDirectories(keyStorePath
.getParent());
180 PkiUtils
.saveKeyStore(trustStorePath
, trustStorePassword
, trustStore
);
181 if (log
.isDebugEnabled())
182 log
.debug("IPA CA certificate stored in " + trustStorePath
);
183 } catch (IOException e
) {
184 log
.error("Cannot trust CA certificate", e
);
188 if (!Files
.exists(keyStorePath
))
189 PkiUtils
.createSelfSignedKeyStore(keyStorePath
, keyStorePassword
, PkiUtils
.PKCS12
);
190 // props.put(JettyHttpConstants.SSL_KEYSTORETYPE, PkiUtils.PKCS12);
191 // props.put(JettyHttpConstants.SSL_KEYSTORE, keyStorePath.toString());
192 // props.put(JettyHttpConstants.SSL_PASSWORD, new String(keyStorePassword));
194 // props.put(InternalHttpConstants.SSL_KEYSTORETYPE, "PKCS11");
195 // props.put(InternalHttpConstants.SSL_KEYSTORE, "../../nssdb");
196 // props.put(InternalHttpConstants.SSL_PASSWORD, keyStorePassword);
201 if (log
.isDebugEnabled())
202 log
.debug("CMS stopping... (" + this.uuid
+ ")");
204 long duration
= ((System
.currentTimeMillis() - availableSince
) / 1000) / 60;
205 log
.info("## ARGEO CMS STOPPED after " + (duration
/ 60) + "h " + (duration
% 60) + "min uptime ##");
208 private void firstInit() throws IOException
{
209 log
.info("## FIRST INIT ##");
210 List
<String
> nodeInits
= getDeployProperties(CmsDeployProperty
.NODE_INIT
);
211 // if (nodeInits == null)
212 // nodeInits = "../../init";
213 CmsStateImpl
.prepareFirstInitInstanceArea(nodeInits
);
217 public String
getDeployProperty(String property
) {
218 CmsDeployProperty deployProperty
= CmsDeployProperty
.find(property
);
219 if (deployProperty
== null) {
221 if (property
.startsWith("argeo.node.")) {
222 return doGetDeployProperty(property
);
224 if (property
.equals("argeo.i18n.locales")) {
225 String value
= doGetDeployProperty(property
);
227 log
.warn("Property " + property
+ " was ignored (value=" + value
+ ")");
232 throw new IllegalArgumentException("Unsupported deploy property " + property
);
234 int index
= CmsDeployProperty
.getPropertyIndex(property
);
235 return getDeployProperty(deployProperty
, index
);
239 public List
<String
> getDeployProperties(String property
) {
240 CmsDeployProperty deployProperty
= CmsDeployProperty
.find(property
);
241 if (deployProperty
== null)
242 return new ArrayList
<>();
243 return getDeployProperties(deployProperty
);
246 public static List
<String
> getDeployProperties(CmsState cmsState
, CmsDeployProperty deployProperty
) {
247 return ((CmsStateImpl
) cmsState
).getDeployProperties(deployProperty
);
250 public List
<String
> getDeployProperties(CmsDeployProperty deployProperty
) {
251 List
<String
> res
= new ArrayList
<>(deployProperty
.getMaxCount());
252 for (int i
= 0; i
< deployProperty
.getMaxCount(); i
++) {
253 // String propertyName = i == 0 ? deployProperty.getProperty() :
254 // deployProperty.getProperty() + "." + i;
255 String value
= getDeployProperty(deployProperty
, i
);
261 public static String
getDeployProperty(CmsState cmsState
, CmsDeployProperty deployProperty
) {
262 return ((CmsStateImpl
) cmsState
).getDeployProperty(deployProperty
);
265 public String
getDeployProperty(CmsDeployProperty deployProperty
) {
266 String value
= getDeployProperty(deployProperty
, 0);
270 public String
getDeployProperty(CmsDeployProperty deployProperty
, int index
) {
271 String propertyName
= deployProperty
.getProperty() + (index
== 0 ?
"" : "." + index
);
272 String value
= doGetDeployProperty(propertyName
);
273 if (value
== null && index
== 0) {
275 if (deployPropertyDefaults
.containsKey(deployProperty
)) {
276 value
= deployPropertyDefaults
.get(deployProperty
);
277 if (deployProperty
.isSystemPropertyOnly())
278 System
.setProperty(deployProperty
.getProperty(), value
);
282 // try legacy properties
283 String legacyProperty
= switch (deployProperty
) {
284 case DIRECTORY
-> "argeo.node.useradmin.uris";
285 case DB_URL
-> "argeo.node.dburl";
286 case DB_USER
-> "argeo.node.dbuser";
287 case DB_PASSWORD
-> "argeo.node.dbpassword";
288 case HTTP_PORT
-> "org.osgi.service.http.port";
289 case HTTPS_PORT
-> "org.osgi.service.http.port.secure";
290 case HOST
-> "org.eclipse.equinox.http.jetty.http.host";
291 case LOCALE
-> "argeo.i18n.defaultLocale";
295 if (legacyProperty
!= null) {
296 value
= doGetDeployProperty(legacyProperty
);
298 log
.warn("Retrieved deploy property " + deployProperty
.getProperty()
299 + " through deprecated property " + legacyProperty
);
304 if (index
== 0 && deployProperty
.isSystemPropertyOnly()) {
305 String systemPropertyValue
= System
.getProperty(deployProperty
.getProperty());
306 if (!Objects
.equals(value
, systemPropertyValue
))
307 throw new IllegalStateException(
308 "Property " + deployProperty
+ " must be a ssystem property, but its value is " + value
309 + ", while the system property value is " + systemPropertyValue
);
311 return value
!= null ? value
.toString() : null;
314 protected String
getLegacyProperty(String legacyProperty
, CmsDeployProperty deployProperty
) {
315 String value
= doGetDeployProperty(legacyProperty
);
317 log
.warn("Retrieved deploy property " + deployProperty
.getProperty() + " through deprecated property "
318 + legacyProperty
+ ".");
323 protected String
doGetDeployProperty(String property
) {
324 return KernelUtils
.getFrameworkProp(property
);
328 public Path
getDataPath(String relativePath
) {
329 return KernelUtils
.getOsgiInstancePath(relativePath
);
333 public Long
getAvailableSince() {
334 return availableSince
;
340 public String
getHostname() {
345 public UUID
getUuid() {
349 public void setUuidFactory(UuidFactory uuidFactory
) {
350 this.uuidFactory
= uuidFactory
;
354 * Called before node initialisation, in order populate OSGi instance are with
355 * some files (typically LDIF, etc).
357 public static void prepareFirstInitInstanceArea(List
<String
> nodeInits
) {
359 for (String nodeInit
: nodeInits
) {
363 if (nodeInit
.startsWith("http")) {
365 // registerRemoteInit(nodeInit);
368 // TODO use java.nio.file
370 if (nodeInit
.startsWith("."))
371 initDir
= KernelUtils
.getExecutionDir(nodeInit
);
373 initDir
= new File(nodeInit
);
374 // TODO also uncompress archives
375 if (initDir
.exists())
377 // TODO use NIO utilities
378 FileUtils
.copyDirectory(initDir
, KernelUtils
.getOsgiInstancePath("").toFile(),
382 public boolean accept(File pathname
) {
383 if (pathname
.getName().equals(".svn") || pathname
.getName().equals(".git"))
388 log
.info("CMS initialized from " + initDir
.getCanonicalPath());
389 } catch (IOException e
) {
390 throw new RuntimeException("Cannot initialize from " + initDir
, e
);
399 public static IdentClient
getIdentClient(String remoteAddr
) {
400 if (!IdentClient
.isDefaultAuthdPassphraseFileAvailable())
402 // TODO make passphrase more configurable
403 return new IdentClient(remoteAddr
);