package org.argeo.api.cms;
+import java.util.List;
+import java.util.Locale;
+
/**
* A logical view on this CMS instance, independently of a particular launch or
* deployment.
*/
public final static String WORKGROUP = "workgroup";
+ Locale getDefaultLocale();
+
+ List<Locale> getLocales();
+
+ Long getAvailableSince();
+
+
/** Mark this group as a workgroup */
void createWorkgroup(String groupDn);
}
/** A configured node deployment. */
public interface CmsDeployment {
- Long getAvailableSince();
-
+
void addFactoryDeployConfig(String factoryPid, Dictionary<String, Object> props);
- Dictionary<String, Object> getProps(String factoryPid, String cn);
+ Dictionary<String, Object> getProps(String factoryPid, String cn);
}
package org.argeo.api.cms;
-import java.util.List;
-import java.util.Locale;
-
/** A running node process. */
public interface CmsState {
- Locale getDefaultLocale();
-
- List<Locale> getLocales();
-
String getHostname();
Long getAvailableSince();
import java.util.GregorianCalendar;
import java.util.TimeZone;
-import org.argeo.api.cms.CmsState;
import org.argeo.api.cms.CmsConstants;
+import org.argeo.api.cms.CmsContext;
import org.argeo.api.cms.CmsDeployment;
+import org.argeo.api.cms.CmsState;
import org.argeo.cms.swt.CmsSwtUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
if (nodeStateRef == null)
throw new IllegalStateException("No CMS state available");
CmsState nodeState = bc.getService(nodeStateRef);
- ServiceReference<CmsDeployment> nodeDeploymentRef = bc.getServiceReference(CmsDeployment.class);
+ ServiceReference<CmsContext> nodeDeploymentRef = bc.getServiceReference(CmsContext.class);
Label label = new Label(composite, SWT.WRAP);
CmsSwtUtils.markup(label);
if (nodeDeploymentRef == null) {
label.setText("Not yet deployed on <br>" + nodeState.getHostname() + "</br>, please configure below.");
} else {
Object stateUuid = nodeStateRef.getProperty(CmsConstants.CN);
- CmsDeployment nodeDeployment = bc.getService(nodeDeploymentRef);
+ CmsContext nodeDeployment = bc.getService(nodeDeploymentRef);
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTimeInMillis(nodeDeployment.getAvailableSince());
calendar.setTimeZone(TimeZone.getDefault());
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
-import org.argeo.api.cms.CmsView;
-import org.argeo.api.cms.CmsLog;
import org.argeo.api.cms.CmsAuth;
-import org.argeo.api.cms.CmsState;
+import org.argeo.api.cms.CmsContext;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.CmsView;
import org.argeo.cms.CmsMsg;
import org.argeo.cms.LocaleUtils;
import org.argeo.cms.auth.RemoteAuthCallback;
public CmsLogin(CmsView cmsView) {
this.cmsView = cmsView;
- CmsState nodeState = null;// = Activator.getNodeState();
+ CmsContext nodeState = null;// = Activator.getNodeState();
+ // FIXME reactivate locales
if (nodeState != null) {
defaultLocale = nodeState.getDefaultLocale();
List<Locale> locales = nodeState.getLocales();
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" immediate="false" name="CMS Context">
+ <implementation class="org.argeo.cms.internal.runtime.CmsContextImpl"/>
+ <reference bind="setCmsDeployment" cardinality="1..1" interface="org.argeo.api.cms.CmsDeployment" name="CmsDeployment" policy="static"/>
+ <service>
+ <provide interface="org.argeo.api.cms.CmsContext"/>
+ </service>
+ <reference bind="setCmsState" cardinality="1..1" interface="org.argeo.api.cms.CmsState" name="CmsState" policy="static"/>
+ <reference bind="setUserAdmin" cardinality="1..1" interface="org.osgi.service.useradmin.UserAdmin" name="UserAdmin" policy="static"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" immediate="false" name="CMS Deployment">
+ <reference bind="setDeployConfig" cardinality="1..1" interface="org.argeo.cms.internal.osgi.DeployConfig" name="DeployConfig" policy="static"/>
+ <implementation class="org.argeo.cms.internal.runtime.CmsDeploymentImpl"/>
+ <reference bind="setCmsState" cardinality="1..1" interface="org.argeo.api.cms.CmsState" name="CmsState" policy="static"/>
+ <service>
+ <provide interface="org.argeo.api.cms.CmsDeployment"/>
+ </service>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" immediate="false" name="CMS State">
+ <implementation class="org.argeo.cms.internal.runtime.CmsStateImpl"/>
+ <service>
+ <provide interface="org.argeo.api.cms.CmsState"/>
+ </service>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="Deploy Config">
+ <implementation class="org.argeo.cms.internal.osgi.DeployConfig"/>
+ <service>
+ <provide interface="org.argeo.cms.internal.osgi.DeployConfig"/>
+ </service>
+ <reference bind="setConfigurationAdmin" cardinality="1..1" interface="org.osgi.service.cm.ConfigurationAdmin" name="ConfigurationAdmin" policy="static"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="Node User Admin">
+ <implementation class="org.argeo.cms.internal.osgi.NodeUserAdmin"/>
+ <property name="service.pid" type="String" value="org.argeo.api.userAdmin"/>
+ <reference bind="setTransactionManager" cardinality="1..1" interface="org.argeo.osgi.transaction.WorkControl" name="WorkControl" policy="static"/>
+ <service>
+ <provide interface="org.osgi.service.cm.ManagedServiceFactory"/>
+ </service>
+ <reference bind="setUserTransaction" cardinality="1..1" interface="org.argeo.osgi.transaction.WorkTransaction" name="WorkTransaction" policy="static"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Simple Transaction Manager">
+ <implementation class="org.argeo.osgi.transaction.SimpleTransactionManager"/>
+ <service>
+ <provide interface="org.argeo.osgi.transaction.WorkControl"/>
+ <provide interface="org.argeo.osgi.transaction.WorkTransaction"/>
+ </service>
+</scr:component>
-Bundle-Activator: org.argeo.cms.internal.kernel.Activator
+Bundle-Activator: org.argeo.cms.internal.osgi.CmsActivator
-Import-Package: org.apache.commons.httpclient.cookie;resolution:=optional,\
+Import-Package: \
+org.argeo.osgi.transaction, \
+org.apache.commons.httpclient.cookie;resolution:=optional,\
!com.sun.security.jgss,\
org.osgi.*;version=0.0.0,\
*
-#Service-Component:\
-#OSGI-INF/cmsUserManager.xml,\
+Service-Component:\
+OSGI-INF/cmsState.xml,\
+OSGI-INF/simpleTransactionManager.xml,\
+OSGI-INF/nodeUserAdmin.xml,\
+OSGI-INF/cmsUserManager.xml,\
+OSGI-INF/deployConfig.xml,\
+OSGI-INF/cmsDeployment.xml,\
+OSGI-INF/cmsContext.xml,\
bin.includes = META-INF/,\
.,\
bin/,\
- OSGI-INF/
+ OSGI-INF/,\
+ OSGI-INF/simpleTransactionManager.xml,\
+ OSGI-INF/cmsState.xml,\
+ OSGI-INF/nodeUserAdmin.xml,\
+ OSGI-INF/deployConfig.xml,\
+ OSGI-INF/cmsDeployment.xml,\
+ OSGI-INF/cmsContext.xml
source.. = src/
import javax.security.auth.Subject;
import javax.security.auth.x500.X500Principal;
+import org.argeo.api.cms.CmsConstants;
import org.argeo.api.cms.CmsSession;
import org.argeo.api.cms.CmsSessionId;
-import org.argeo.api.cms.CmsConstants;
import org.argeo.cms.internal.auth.CmsSessionImpl;
import org.argeo.cms.internal.auth.ImpliedByPrincipal;
-import org.argeo.cms.internal.kernel.Activator;
+import org.argeo.cms.internal.runtime.CmsContextImpl;
import org.osgi.service.useradmin.Authorization;
/**
public final static Locale locale(Subject subject) {
Set<Locale> locales = subject.getPublicCredentials(Locale.class);
if (locales.isEmpty()) {
- Locale defaultLocale = Activator.getNodeState().getDefaultLocale();
+ Locale defaultLocale = CmsContextImpl.getCmsContext().getDefaultLocale();
return defaultLocale;
} else
return locales.iterator().next();
else
return false;
CmsSessionImpl cmsSession = CmsSessionImpl.getByUuid(nodeSessionId.toString());
-
+
// FIXME logout all views
// TODO check why it is sometimes null
if (cmsSession != null)
import org.argeo.api.cms.CmsLog;
import org.argeo.cms.auth.ident.IdentClient;
-import org.argeo.cms.internal.kernel.Activator;
+import org.argeo.cms.internal.runtime.CmsStateImpl;
/** Use an ident service to identify. */
public class IdentLoginModule implements LoginModule {
RemoteAuthRequest request = httpCallback.getRequest();
if (request == null)
return false;
- IdentClient identClient = Activator.getIdentClient(request.getRemoteAddr());
+ IdentClient identClient = CmsStateImpl.getIdentClient(request.getRemoteAddr());
if (identClient == null)
return false;
String identUsername;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
+import org.argeo.api.cms.CmsConstants;
import org.argeo.api.cms.CmsLog;
import org.argeo.cms.internal.auth.CmsSessionImpl;
-import org.argeo.cms.internal.kernel.Activator;
+import org.argeo.cms.internal.runtime.KernelUtils;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.service.http.HttpContext;
if (log.isDebugEnabled())
log.debug("Client certificate " + certDn + " verified by servlet container");
} // Reverse proxy verified the client certificate
- String clientDnHttpHeader = Activator.getHttpProxySslHeader();
+ String clientDnHttpHeader = KernelUtils.getFrameworkProp(CmsConstants.HTTP_PROXY_SSL_DN);
if (clientDnHttpHeader != null) {
String certDn = req.getHeader(clientDnHttpHeader);
// TODO retrieve more cf. https://httpd.apache.org/docs/current/mod/mod_ssl.html
import javax.security.auth.spi.LoginModule;
import org.argeo.api.cms.CmsLog;
-import org.argeo.cms.internal.kernel.Activator;
+import org.argeo.cms.internal.runtime.CmsContextImpl;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
private GSSContext checkToken(byte[] authToken) {
GSSManager manager = GSSManager.getInstance();
try {
- GSSContext gContext = manager.createContext(Activator.getAcceptorCredentials());
+ GSSContext gContext = manager.createContext(CmsContextImpl.getAcceptorCredentials());
if (gContext == null) {
log.debug("SpnegoUserRealm: failed to establish GSSContext");
}
+ @Deprecated
public static boolean hasAcceptorCredentials() {
- return Activator.getAcceptorCredentials() != null;
+ return CmsContextImpl.getAcceptorCredentials() != null;
}
}
import org.argeo.api.cms.CmsConstants;
import org.argeo.api.cms.CmsLog;
-import org.argeo.cms.internal.kernel.Activator;
+import org.argeo.cms.internal.osgi.NodeUserAdmin;
+import org.argeo.cms.internal.runtime.CmsContextImpl;
import org.argeo.cms.security.CryptoKeyring;
import org.argeo.osgi.useradmin.AuthenticatingUser;
import org.argeo.osgi.useradmin.IpaUtils;
@Override
public boolean login() throws LoginException {
- UserAdmin userAdmin = Activator.getUserAdmin();
+ UserAdmin userAdmin = CmsContextImpl.getUserAdmin();
final String username;
final char[] password;
Object certificateChain = null;
// if (singleUser) {
// OsUserUtils.loginAsSystemUser(subject);
// }
- UserAdmin userAdmin = Activator.getUserAdmin();
+ UserAdmin userAdmin = CmsContextImpl.getUserAdmin();
Authorization authorization;
if (callbackHandler == null) {// anonymous
authorization = userAdmin.getAuthorization(null);
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.DefaultHttpParams;
import org.apache.commons.httpclient.params.HttpParams;
-import org.argeo.cms.internal.kernel.KernelConstants;
+import org.argeo.cms.internal.runtime.KernelConstants;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSManager;
+++ /dev/null
-package org.argeo.cms.internal.kernel;
-
-import java.io.IOException;
-import java.net.URL;
-import java.security.AllPermission;
-import java.util.Dictionary;
-import java.util.List;
-import java.util.Locale;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-import javax.security.auth.login.Configuration;
-
-import org.argeo.api.cms.CmsState;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.api.cms.CmsContext;
-import org.argeo.api.cms.CmsDeployment;
-import org.argeo.cms.ArgeoLogger;
-import org.argeo.cms.auth.ident.IdentClient;
-import org.ietf.jgss.GSSCredential;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleActivator;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.service.condpermadmin.BundleLocationCondition;
-import org.osgi.service.condpermadmin.ConditionInfo;
-import org.osgi.service.condpermadmin.ConditionalPermissionAdmin;
-import org.osgi.service.condpermadmin.ConditionalPermissionInfo;
-import org.osgi.service.condpermadmin.ConditionalPermissionUpdate;
-import org.osgi.service.log.LogReaderService;
-import org.osgi.service.permissionadmin.PermissionInfo;
-import org.osgi.service.useradmin.UserAdmin;
-import org.osgi.util.tracker.ServiceTracker;
-
-/**
- * Activates the kernel. Gives access to kernel information for the rest of the
- * bundle (and only it)
- */
-public class Activator implements BundleActivator {
- private final static CmsLog log = CmsLog.getLog(Activator.class);
-
- private static Activator instance;
-
- // TODO make it configurable
- private boolean hardened = false;
-
- private static BundleContext bundleContext;
-
- private LogReaderService logReaderService;
-
- private NodeLogger logger;
- private CmsStateImpl nodeState;
- private CmsDeploymentImpl nodeDeployment;
- private CmsContextImpl nodeInstance;
-
- private ServiceTracker<UserAdmin, NodeUserAdmin> userAdminSt;
- private ExecutorService internalExecutorService;
-
- static {
- Bundle bundle = FrameworkUtil.getBundle(Activator.class);
- if (bundle != null) {
- bundleContext = bundle.getBundleContext();
- }
- }
-
- void init() {
- Runtime.getRuntime().addShutdownHook(new CmsShutdown());
- instance = this;
-// this.bc = bundleContext;
- if (bundleContext != null)
- this.logReaderService = getService(LogReaderService.class);
- this.internalExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
-
- try {
- initSecurity();
-// initArgeoLogger();
- initNode();
-
- if (log.isTraceEnabled())
- log.trace("Kernel bundle started");
- } catch (Throwable e) {
- log.error("## FATAL: CMS activator failed", e);
- }
- }
-
- void destroy() {
- try {
- if (nodeInstance != null)
- nodeInstance.shutdown();
- if (nodeDeployment != null)
- nodeDeployment.shutdown();
- if (nodeState != null)
- nodeState.shutdown();
-
- if (userAdminSt != null)
- userAdminSt.close();
-
- internalExecutorService.shutdown();
- instance = null;
- bundleContext = null;
- this.logReaderService = null;
- // this.configurationAdmin = null;
- } catch (Exception e) {
- log.error("CMS activator shutdown failed", e);
- }
- }
-
- private void initSecurity() {
- if (System.getProperty(KernelConstants.JAAS_CONFIG_PROP) == null) {
- String jaasConfig = KernelConstants.JAAS_CONFIG;
- URL url = getClass().getResource(jaasConfig);
- // System.setProperty(KernelConstants.JAAS_CONFIG_PROP,
- // url.toExternalForm());
- KernelUtils.setJaasConfiguration(url);
- }
- // explicitly load JAAS configuration
- Configuration.getConfiguration();
-
- // code-level permissions
- String osgiSecurity = KernelUtils.getFrameworkProp(Constants.FRAMEWORK_SECURITY);
- if (osgiSecurity != null && Constants.FRAMEWORK_SECURITY_OSGI.equals(osgiSecurity)) {
- // TODO rather use a tracker?
- ConditionalPermissionAdmin permissionAdmin = bundleContext
- .getService(bundleContext.getServiceReference(ConditionalPermissionAdmin.class));
- if (!hardened) {
- // All permissions to all bundles
- ConditionalPermissionUpdate update = permissionAdmin.newConditionalPermissionUpdate();
- update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
- new ConditionInfo[] {
- new ConditionInfo(BundleLocationCondition.class.getName(), new String[] { "*" }) },
- new PermissionInfo[] { new PermissionInfo(AllPermission.class.getName(), null, null) },
- ConditionalPermissionInfo.ALLOW));
- // TODO data admin permission
-// PermissionInfo dataAdminPerm = new PermissionInfo(AuthPermission.class.getName(),
-// "createLoginContext." + NodeConstants.LOGIN_CONTEXT_DATA_ADMIN, null);
-// update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
-// new ConditionInfo[] {
-// new ConditionInfo(BundleLocationCondition.class.getName(), new String[] { "*" }) },
-// new PermissionInfo[] { dataAdminPerm }, ConditionalPermissionInfo.DENY));
-// update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
-// new ConditionInfo[] {
-// new ConditionInfo(BundleSignerCondition.class.getName(), new String[] { "CN=\"Eclipse.org Foundation, Inc.\", OU=IT, O=\"Eclipse.org Foundation, Inc.\", L=Nepean, ST=Ontario, C=CA" }) },
-// new PermissionInfo[] { dataAdminPerm }, ConditionalPermissionInfo.ALLOW));
- update.commit();
- } else {
- SecurityProfile securityProfile = new SecurityProfile() {
- };
- securityProfile.applySystemPermissions(permissionAdmin);
- }
- }
-
- }
-
- private void initArgeoLogger() {
- logger = new NodeLogger(logReaderService);
- if (bundleContext != null)
- bundleContext.registerService(ArgeoLogger.class, logger, null);
- }
-
- private void initNode() throws IOException {
- // Node state
- nodeState = new CmsStateImpl();
- registerService(CmsState.class, nodeState, null);
-
- // Node deployment
- nodeDeployment = new CmsDeploymentImpl();
-// registerService(NodeDeployment.class, nodeDeployment, null);
-
- // Node instance
- nodeInstance = new CmsContextImpl();
- registerService(CmsContext.class, nodeInstance, null);
- }
-
- public static <T> void registerService(Class<T> clss, T service, Dictionary<String, ?> properties) {
- if (bundleContext != null) {
- bundleContext.registerService(clss, service, properties);
- }
-
- }
-
- public static <T> T getService(Class<T> clss) {
- if (bundleContext != null) {
- return bundleContext.getService(bundleContext.getServiceReference(clss));
- } else {
- return null;
- }
- }
-
- /*
- * OSGi
- */
-
- @Override
- public void start(BundleContext bc) throws Exception {
- if (!bc.getBundle().equals(bundleContext.getBundle()))
- throw new IllegalStateException(
- "Bundle " + bc.getBundle() + " is not consistent with " + bundleContext.getBundle());
- init();
- userAdminSt = new ServiceTracker<>(bundleContext, UserAdmin.class, null);
- userAdminSt.open();
- }
-
- @Override
- public void stop(BundleContext bc) throws Exception {
- if (!bc.getBundle().equals(bundleContext.getBundle()))
- throw new IllegalStateException(
- "Bundle " + bc.getBundle() + " is not consistent with " + bundleContext.getBundle());
- destroy();
- }
-
-// private <T> T getService(Class<T> clazz) {
-// ServiceReference<T> sr = bundleContext.getServiceReference(clazz);
-// if (sr == null)
-// throw new IllegalStateException("No service available for " + clazz);
-// return bundleContext.getService(sr);
-// }
-
- public static CmsState getNodeState() {
- return instance.nodeState;
- }
-
- public static GSSCredential getAcceptorCredentials() {
- return getNodeUserAdmin().getAcceptorCredentials();
- }
-
- @Deprecated
- public static boolean isSingleUser() {
- return getNodeUserAdmin().isSingleUser();
- }
-
- public static UserAdmin getUserAdmin() {
- return (UserAdmin) getNodeUserAdmin();
- }
-
- public static String getHttpProxySslHeader() {
- return KernelUtils.getFrameworkProp(CmsConstants.HTTP_PROXY_SSL_DN);
- }
-
- public static IdentClient getIdentClient(String remoteAddr) {
- if (!IdentClient.isDefaultAuthdPassphraseFileAvailable())
- return null;
- // TODO make passphrase more configurable
- return new IdentClient(remoteAddr);
- }
-
- private static NodeUserAdmin getNodeUserAdmin() {
- NodeUserAdmin res;
- try {
- res = instance.userAdminSt.waitForService(60000);
- } catch (InterruptedException e) {
- throw new IllegalStateException("Cannot retrieve Node user admin", e);
- }
- if (res == null)
- throw new IllegalStateException("No Node user admin found");
-
- return res;
- // ServiceReference<UserAdmin> sr =
- // instance.bc.getServiceReference(UserAdmin.class);
- // NodeUserAdmin userAdmin = (NodeUserAdmin) instance.bc.getService(sr);
- // return userAdmin;
-
- }
-
- static ExecutorService getInternalExecutorService() {
- return instance.internalExecutorService;
- }
-
- // static CmsSecurity getCmsSecurity() {
- // return instance.nodeSecurity;
- // }
-
- public String[] getLocales() {
- // TODO optimize?
- List<Locale> locales = getNodeState().getLocales();
- String[] res = new String[locales.size()];
- for (int i = 0; i < locales.size(); i++)
- res[i] = locales.get(i).toString();
- return res;
- }
-
- static BundleContext getBundleContext() {
- return bundleContext;
- }
-
- public static void main(String[] args) {
- instance = new Activator();
- instance.init();
- }
-
-}
+++ /dev/null
-package org.argeo.cms.internal.kernel;
-
-import org.argeo.api.cms.CmsContext;
-import org.argeo.api.cms.CmsLog;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-
-public class CmsContextImpl implements CmsContext {
- private final CmsLog log = CmsLog.getLog(getClass());
- private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
-
-// private EgoRepository egoRepository;
-
- public CmsContextImpl() {
- initTrackers();
- }
-
- private void initTrackers() {
- // node repository
-// new ServiceTracker<Repository, Repository>(bc, Repository.class, null) {
-// @Override
-// public Repository addingService(ServiceReference<Repository> reference) {
-// Object cn = reference.getProperty(NodeConstants.CN);
-// if (cn != null && cn.equals(NodeConstants.EGO_REPOSITORY)) {
-//// egoRepository = (EgoRepository) bc.getService(reference);
-// if (log.isTraceEnabled())
-// log.trace("Home repository is available");
-// }
-// return super.addingService(reference);
-// }
-//
-// @Override
-// public void removedService(ServiceReference<Repository> reference, Repository service) {
-// super.removedService(reference, service);
-//// egoRepository = null;
-// }
-//
-// }.open();
- }
-
- public void shutdown() {
-
- }
-
- @Override
- public void createWorkgroup(String dn) {
-// if (egoRepository == null)
-// throw new CmsException("Ego repository is not available");
-// // TODO add check that the group exists
-// egoRepository.createWorkgroup(dn);
- throw new UnsupportedOperationException();
- }
-
-}
+++ /dev/null
-package org.argeo.cms.internal.kernel;
-
-import java.io.IOException;
-import java.lang.management.ManagementFactory;
-import java.net.URL;
-import java.util.Dictionary;
-
-import org.argeo.api.cms.CmsState;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.api.cms.CmsDeployment;
-import org.argeo.osgi.transaction.WorkTransaction;
-import org.argeo.osgi.useradmin.UserAdminConf;
-import org.eclipse.equinox.http.jetty.JettyConfigurator;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.cm.Configuration;
-import org.osgi.service.cm.ConfigurationAdmin;
-import org.osgi.service.http.HttpService;
-import org.osgi.service.useradmin.Group;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.UserAdmin;
-import org.osgi.util.tracker.ServiceTracker;
-
-/** Implementation of a CMS deployment. */
-public class CmsDeploymentImpl implements CmsDeployment {
- private final CmsLog log = CmsLog.getLog(getClass());
- private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
-
- private DeployConfig deployConfig;
-
- private Long availableSince;
-
- // Readiness
- private boolean nodeAvailable = false;
- private boolean userAdminAvailable = false;
- private boolean httpExpected = false;
- private boolean httpAvailable = false;
-
- public CmsDeploymentImpl() {
-// ServiceReference<NodeState> nodeStateSr = bc.getServiceReference(NodeState.class);
-// if (nodeStateSr == null)
-// throw new CmsException("No node state available");
-
-// NodeState nodeState = bc.getService(nodeStateSr);
-// cleanState = nodeState.isClean();
-
-// nodeHttp = new NodeHttp();
- initTrackers();
- }
-
- private void initTrackers() {
- ServiceTracker<?, ?> httpSt = new ServiceTracker<HttpService, HttpService>(bc, HttpService.class, null) {
-
- @Override
- public HttpService addingService(ServiceReference<HttpService> sr) {
- httpAvailable = true;
- Object httpPort = sr.getProperty("http.port");
- Object httpsPort = sr.getProperty("https.port");
- log.info(httpPortsMsg(httpPort, httpsPort));
- checkReadiness();
- return super.addingService(sr);
- }
- };
- // httpSt.open();
- KernelUtils.asyncOpen(httpSt);
-
- ServiceTracker<?, ?> userAdminSt = new ServiceTracker<UserAdmin, UserAdmin>(bc, UserAdmin.class, null) {
- @Override
- public UserAdmin addingService(ServiceReference<UserAdmin> reference) {
- UserAdmin userAdmin = super.addingService(reference);
- addStandardSystemRoles(userAdmin);
- userAdminAvailable = true;
- checkReadiness();
- return userAdmin;
- }
- };
- // userAdminSt.open();
- KernelUtils.asyncOpen(userAdminSt);
-
- ServiceTracker<?, ?> confAdminSt = new ServiceTracker<ConfigurationAdmin, ConfigurationAdmin>(bc,
- ConfigurationAdmin.class, null) {
- @Override
- public ConfigurationAdmin addingService(ServiceReference<ConfigurationAdmin> reference) {
- ConfigurationAdmin configurationAdmin = bc.getService(reference);
- boolean isClean;
- try {
- Configuration[] confs = configurationAdmin
- .listConfigurations("(service.factoryPid=" + CmsConstants.NODE_USER_ADMIN_PID + ")");
- isClean = confs == null || confs.length == 0;
- } catch (Exception e) {
- throw new IllegalStateException("Cannot analyse clean state", e);
- }
- deployConfig = new DeployConfig(configurationAdmin, isClean);
- Activator.registerService(CmsDeployment.class, CmsDeploymentImpl.this, null);
-// JcrInitUtils.addToDeployment(CmsDeployment.this);
- httpExpected = deployConfig.getProps(KernelConstants.JETTY_FACTORY_PID, "default") != null;
- try {
- Configuration[] configs = configurationAdmin
- .listConfigurations("(service.factoryPid=" + CmsConstants.NODE_USER_ADMIN_PID + ")");
-
- boolean hasDomain = false;
- for (Configuration config : configs) {
- Object realm = config.getProperties().get(UserAdminConf.realm.name());
- if (realm != null) {
- log.debug("Found realm: " + realm);
- hasDomain = true;
- }
- }
- if (hasDomain) {
- loadIpaJaasConfiguration();
- }
- } catch (Exception e) {
- throw new IllegalStateException("Cannot initialize config", e);
- }
- return super.addingService(reference);
- }
- };
- // confAdminSt.open();
- KernelUtils.asyncOpen(confAdminSt);
- }
-
- public void addFactoryDeployConfig(String factoryPid, Dictionary<String, Object> props) {
- deployConfig.putFactoryDeployConfig(factoryPid, props);
- deployConfig.save();
- try {
- deployConfig.loadConfigs();
- } catch (IOException e) {
- throw new IllegalStateException(e);
- }
- }
-
- public Dictionary<String, Object> getProps(String factoryPid, String cn) {
- return deployConfig.getProps(factoryPid, cn);
- }
-
- private String httpPortsMsg(Object httpPort, Object httpsPort) {
- return (httpPort != null ? "HTTP " + httpPort + " " : " ") + (httpsPort != null ? "HTTPS " + httpsPort : "");
- }
-
- private void addStandardSystemRoles(UserAdmin userAdmin) {
- // we assume UserTransaction is already available (TODO make it more robust)
- WorkTransaction userTransaction = bc.getService(bc.getServiceReference(WorkTransaction.class));
- try {
- userTransaction.begin();
- Role adminRole = userAdmin.getRole(CmsConstants.ROLE_ADMIN);
- if (adminRole == null) {
- adminRole = userAdmin.createRole(CmsConstants.ROLE_ADMIN, Role.GROUP);
- }
- if (userAdmin.getRole(CmsConstants.ROLE_USER_ADMIN) == null) {
- Group userAdminRole = (Group) userAdmin.createRole(CmsConstants.ROLE_USER_ADMIN, Role.GROUP);
- userAdminRole.addMember(adminRole);
- }
- userTransaction.commit();
- } catch (Exception e) {
- try {
- userTransaction.rollback();
- } catch (Exception e1) {
- // silent
- }
- throw new IllegalStateException("Cannot add standard system roles", e);
- }
- }
-
- private void loadIpaJaasConfiguration() {
- if (System.getProperty(KernelConstants.JAAS_CONFIG_PROP) == null) {
- String jaasConfig = KernelConstants.JAAS_CONFIG_IPA;
- URL url = getClass().getClassLoader().getResource(jaasConfig);
- KernelUtils.setJaasConfiguration(url);
- log.debug("Set IPA JAAS configuration.");
- }
- }
-
- public void shutdown() {
-// if (nodeHttp != null)
-// nodeHttp.destroy();
-
- try {
- JettyConfigurator.stopServer(KernelConstants.DEFAULT_JETTY_SERVER);
- } catch (Exception e) {
- log.error("Cannot stop default Jetty server.", e);
- }
-
- if (deployConfig != null) {
- new Thread(() -> deployConfig.save(), "Save Argeo Deploy Config").start();
- }
- }
-
- /**
- * Checks whether the deployment is available according to expectations, and
- * mark it as available.
- */
- private synchronized void checkReadiness() {
- if (isAvailable())
- return;
- if (nodeAvailable && userAdminAvailable && (httpExpected ? httpAvailable : true)) {
- String data = KernelUtils.getFrameworkProp(KernelUtils.OSGI_INSTANCE_AREA);
- String state = KernelUtils.getFrameworkProp(KernelUtils.OSGI_CONFIGURATION_AREA);
- availableSince = System.currentTimeMillis();
- long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime();
- String jvmUptimeStr = " in " + (jvmUptime / 1000) + "." + (jvmUptime % 1000) + "s";
- log.info("## ARGEO NODE AVAILABLE" + (log.isDebugEnabled() ? jvmUptimeStr : "") + " ##");
- if (log.isDebugEnabled()) {
- log.debug("## state: " + state);
- if (data != null)
- log.debug("## data: " + data);
- }
- long begin = bc.getService(bc.getServiceReference(CmsState.class)).getAvailableSince();
- long initDuration = System.currentTimeMillis() - begin;
- if (log.isTraceEnabled())
- log.trace("Kernel initialization took " + initDuration + "ms");
- tributeToFreeSoftware(initDuration);
- }
- }
-
- final private void tributeToFreeSoftware(long initDuration) {
- if (log.isTraceEnabled()) {
- long ms = initDuration / 100;
- log.trace("Spend " + ms + "ms" + " reflecting on the progress brought to mankind" + " by Free Software...");
- long beginNano = System.nanoTime();
- try {
- Thread.sleep(ms, 0);
- } catch (InterruptedException e) {
- // silent
- }
- long durationNano = System.nanoTime() - beginNano;
- final double M = 1000d * 1000d;
- double sleepAccuracy = ((double) durationNano) / (ms * M);
- log.trace("Sleep accuracy: " + String.format("%.2f", 100 - (sleepAccuracy * 100 - 100)) + " %");
- }
- }
-
- @Override
- public synchronized Long getAvailableSince() {
- return availableSince;
- }
-
- public synchronized boolean isAvailable() {
- return availableSince != null;
- }
-
-}
+++ /dev/null
-package org.argeo.cms.internal.kernel;
-
-import org.argeo.api.cms.CmsLog;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.FrameworkEvent;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.launch.Framework;
-
-/** Shutdowns the OSGi framework */
-class CmsShutdown extends Thread {
- public final int EXIT_OK = 0;
- public final int EXIT_ERROR = 1;
- public final int EXIT_TIMEOUT = 2;
- public final int EXIT_UNKNOWN = 3;
-
- private final CmsLog log = CmsLog.getLog(CmsShutdown.class);
- // private final BundleContext bc =
- // FrameworkUtil.getBundle(CmsShutdown.class).getBundleContext();
- private final Framework framework;
-
- /** Shutdown timeout in ms */
- private long timeout = 10 * 60 * 1000;
-
- public CmsShutdown() {
- super("CMS Shutdown Hook");
- framework = FrameworkUtil.getBundle(CmsShutdown.class) != null
- ? (Framework) FrameworkUtil.getBundle(CmsShutdown.class).getBundleContext().getBundle(0)
- : null;
- }
-
- @Override
- public void run() {
- if (framework != null && framework.getState() != Bundle.ACTIVE) {
- return;
- }
-
- if (log.isDebugEnabled())
- log.debug("Shutting down OSGi framework...");
- try {
- if (framework != null) {
- // shutdown framework
- framework.stop();
- // wait for shutdown
- FrameworkEvent shutdownEvent = framework.waitForStop(timeout);
- int stoppedType = shutdownEvent.getType();
- Runtime runtime = Runtime.getRuntime();
- if (stoppedType == FrameworkEvent.STOPPED) {
- // close VM
- // System.exit(EXIT_OK);
- } else if (stoppedType == FrameworkEvent.ERROR) {
- log.error("The OSGi framework stopped with an error");
- runtime.halt(EXIT_ERROR);
- } else if (stoppedType == FrameworkEvent.WAIT_TIMEDOUT) {
- log.error("The OSGi framework hasn't stopped after " + timeout + "ms."
- + " Forcibly terminating the JVM...");
- runtime.halt(EXIT_TIMEOUT);
- } else {
- log.error("Unknown state of OSGi framework after " + timeout + "ms."
- + " Forcibly terminating the JVM... (" + shutdownEvent + ")");
- runtime.halt(EXIT_UNKNOWN);
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- log.error("Unexpected exception " + e + " in shutdown hook. " + " Forcibly terminating the JVM...");
- Runtime.getRuntime().halt(EXIT_UNKNOWN);
- }
- }
-
-}
+++ /dev/null
-package org.argeo.cms.internal.kernel;
-
-import static java.util.Locale.ENGLISH;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-
-import org.argeo.api.cms.CmsState;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.cms.LocaleUtils;
-import org.argeo.osgi.transaction.SimpleTransactionManager;
-import org.argeo.osgi.transaction.WorkControl;
-import org.argeo.osgi.transaction.WorkTransaction;
-import org.argeo.util.LangUtils;
-import org.osgi.framework.Constants;
-import org.osgi.service.cm.ManagedServiceFactory;
-
-/**
- * Implementation of a {@link CmsState}, initialising the required services.
- */
-public class CmsStateImpl implements CmsState {
- private final static CmsLog log = CmsLog.getLog(CmsStateImpl.class);
-// private final BundleContext bc = FrameworkUtil.getBundle(CmsState.class).getBundleContext();
-
- // REFERENCES
- private Long availableSince;
-
- // i18n
- private Locale defaultLocale;
- private List<Locale> locales = null;
-
- private ThreadGroup threadGroup = new ThreadGroup("CMS");
- private List<Runnable> stopHooks = new ArrayList<>();
-
- private final String stateUuid;
-// private final boolean cleanState;
- private String hostname;
-
- public CmsStateImpl() {
-// this.stateUuid = stateUuid;
- this.stateUuid = KernelUtils.getFrameworkProp(Constants.FRAMEWORK_UUID);
-// this.cleanState = stateUuid.equals(frameworkUuid);
- try {
- this.hostname = InetAddress.getLocalHost().getHostName();
- } catch (UnknownHostException e) {
- log.error("Cannot set hostname: " + e);
- }
-
- availableSince = System.currentTimeMillis();
- if (log.isDebugEnabled())
- // log.debug("## CMS starting... stateUuid=" + this.stateUuid + (cleanState ? "
- // (clean state) " : " "));
- log.debug("## CMS starting... (" + stateUuid + ")");
-
- initI18n();
- initServices();
-
- }
-
- private void initI18n() {
- Object defaultLocaleValue = KernelUtils.getFrameworkProp(CmsConstants.I18N_DEFAULT_LOCALE);
- defaultLocale = defaultLocaleValue != null ? new Locale(defaultLocaleValue.toString())
- : new Locale(ENGLISH.getLanguage());
- locales = LocaleUtils.asLocaleList(KernelUtils.getFrameworkProp(CmsConstants.I18N_LOCALES));
- }
-
- private void initServices() {
- // JTA
- String tmType = KernelUtils.getFrameworkProp(CmsConstants.TRANSACTION_MANAGER,
- CmsConstants.TRANSACTION_MANAGER_SIMPLE);
- if (CmsConstants.TRANSACTION_MANAGER_SIMPLE.equals(tmType)) {
- initSimpleTransactionManager();
- } else if (CmsConstants.TRANSACTION_MANAGER_BITRONIX.equals(tmType)) {
-// initBitronixTransactionManager();
- throw new UnsupportedOperationException(
- "Bitronix is not supported anymore, but could be again if there is enough interest.");
- } else {
- throw new IllegalArgumentException("Usupported transaction manager type " + tmType);
- }
-
- // POI
-// POIXMLTypeLoader.setClassLoader(CTConnection.class.getClassLoader());
-
- // Tika
-// OpenDocumentParser odfParser = new OpenDocumentParser();
-// bc.registerService(Parser.class, odfParser, new Hashtable());
-// PDFParser pdfParser = new PDFParser();
-// bc.registerService(Parser.class, pdfParser, new Hashtable());
-// OOXMLParser ooxmlParser = new OOXMLParser();
-// bc.registerService(Parser.class, ooxmlParser, new Hashtable());
-// TesseractOCRParser ocrParser = new TesseractOCRParser();
-// ocrParser.setLanguage("ara");
-// bc.registerService(Parser.class, ocrParser, new Hashtable());
-
-// // JCR
-// RepositoryServiceFactory repositoryServiceFactory = new RepositoryServiceFactory();
-// stopHooks.add(() -> repositoryServiceFactory.shutdown());
-// Activator.registerService(ManagedServiceFactory.class, repositoryServiceFactory,
-// LangUtils.dict(Constants.SERVICE_PID, NodeConstants.NODE_REPOS_FACTORY_PID));
-//
-// NodeRepositoryFactory repositoryFactory = new NodeRepositoryFactory();
-// Activator.registerService(RepositoryFactory.class, repositoryFactory, null);
-
- // Security
- NodeUserAdmin userAdmin = new NodeUserAdmin(CmsConstants.ROLES_BASEDN, CmsConstants.TOKENS_BASEDN);
- stopHooks.add(() -> userAdmin.destroy());
- Activator.registerService(ManagedServiceFactory.class, userAdmin,
- LangUtils.dict(Constants.SERVICE_PID, CmsConstants.NODE_USER_ADMIN_PID));
-
- }
-
- private void initSimpleTransactionManager() {
- SimpleTransactionManager transactionManager = new SimpleTransactionManager();
- Activator.registerService(WorkControl.class, transactionManager, null);
- Activator.registerService(WorkTransaction.class, transactionManager, null);
-// Activator.registerService(TransactionManager.class, transactionManager, null);
-// Activator.registerService(UserTransaction.class, transactionManager, null);
- // TODO TransactionSynchronizationRegistry
- }
-
-// private void initBitronixTransactionManager() {
-// // TODO manage it in a managed service, as startup could be long
-// ServiceReference<TransactionManager> existingTm = bc.getServiceReference(TransactionManager.class);
-// if (existingTm != null) {
-// if (log.isDebugEnabled())
-// log.debug("Using provided transaction manager " + existingTm);
-// return;
-// }
-//
-// if (!TransactionManagerServices.isTransactionManagerRunning()) {
-// bitronix.tm.Configuration tmConf = TransactionManagerServices.getConfiguration();
-// tmConf.setServerId(UUID.randomUUID().toString());
-//
-// Bundle bitronixBundle = FrameworkUtil.getBundle(bitronix.tm.Configuration.class);
-// File tmBaseDir = bitronixBundle.getDataFile(KernelConstants.DIR_TRANSACTIONS);
-// File tmDir1 = new File(tmBaseDir, "btm1");
-// tmDir1.mkdirs();
-// tmConf.setLogPart1Filename(new File(tmDir1, tmDir1.getName() + ".tlog").getAbsolutePath());
-// File tmDir2 = new File(tmBaseDir, "btm2");
-// tmDir2.mkdirs();
-// tmConf.setLogPart2Filename(new File(tmDir2, tmDir2.getName() + ".tlog").getAbsolutePath());
-// }
-// BitronixTransactionManager transactionManager = getTransactionManager();
-// stopHooks.add(() -> transactionManager.shutdown());
-// BitronixTransactionSynchronizationRegistry transactionSynchronizationRegistry = getTransactionSynchronizationRegistry();
-// // register
-// bc.registerService(TransactionManager.class, transactionManager, null);
-// bc.registerService(UserTransaction.class, transactionManager, null);
-// bc.registerService(TransactionSynchronizationRegistry.class, transactionSynchronizationRegistry, null);
-// if (log.isDebugEnabled())
-// log.debug("Initialised default Bitronix transaction manager");
-// }
-
- void shutdown() {
- if (log.isDebugEnabled())
- log.debug("CMS stopping... (" + this.stateUuid + ")");
-
- // In a different thread in order to avoid interruptions
- Thread stopHookThread = new Thread(() -> applyStopHooks(), "Apply Argeo Stop Hooks");
- stopHookThread.start();
- try {
- stopHookThread.join(10 * 60 * 1000);
- } catch (InterruptedException e) {
- // silent
- }
-
- long duration = ((System.currentTimeMillis() - availableSince) / 1000) / 60;
- log.info("## ARGEO CMS STOPPED after " + (duration / 60) + "h " + (duration % 60) + "min uptime ##");
- }
-
- /** Apply shutdown hoos in reverse order. */
- private void applyStopHooks() {
- for (int i = stopHooks.size() - 1; i >= 0; i--) {
- try {
- stopHooks.get(i).run();
- } catch (Exception e) {
- log.error("Could not run shutdown hook #" + i);
- }
- }
- // Clean hanging Gogo shell thread
- new GogoShellKiller().start();
- }
-
-// @Override
-// public boolean isClean() {
-// return cleanState;
-// }
-
- @Override
- public Long getAvailableSince() {
- return availableSince;
- }
-
- /*
- * ACCESSORS
- */
- public Locale getDefaultLocale() {
- return defaultLocale;
- }
-
- public List<Locale> getLocales() {
- return locales;
- }
-
- public String getHostname() {
- return hostname;
- }
-}
+++ /dev/null
-package org.argeo.cms.internal.kernel;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Writer;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Dictionary;
-import java.util.List;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-import javax.naming.InvalidNameException;
-import javax.naming.directory.Attribute;
-import javax.naming.directory.Attributes;
-import javax.naming.directory.BasicAttributes;
-import javax.naming.ldap.LdapName;
-import javax.naming.ldap.Rdn;
-
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.osgi.useradmin.UserAdminConf;
-import org.argeo.util.naming.AttributesDictionary;
-import org.argeo.util.naming.LdifParser;
-import org.argeo.util.naming.LdifWriter;
-import org.eclipse.equinox.http.jetty.JettyConfigurator;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.service.cm.Configuration;
-import org.osgi.service.cm.ConfigurationAdmin;
-import org.osgi.service.cm.ConfigurationEvent;
-import org.osgi.service.cm.ConfigurationListener;
-
-/** Manages the LDIF-based deployment configuration. */
-class DeployConfig implements ConfigurationListener {
- private final CmsLog log = CmsLog.getLog(getClass());
- private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
-
- private static Path deployConfigPath = KernelUtils.getOsgiInstancePath(KernelConstants.DEPLOY_CONFIG_PATH);
- private SortedMap<LdapName, Attributes> deployConfigs = new TreeMap<>();
-// private final DataModels dataModels;
-
- private boolean isFirstInit = false;
-
- private final static String ROLES = "roles";
-
- private ConfigurationAdmin configurationAdmin;
-
- public DeployConfig(ConfigurationAdmin configurationAdmin, boolean isClean) {
-// this.dataModels = dataModels;
- // ConfigurationAdmin configurationAdmin =
- // bc.getService(bc.getServiceReference(ConfigurationAdmin.class));
- try {
- if (!isInitialized()) { // first init
- isFirstInit = true;
- firstInit();
- }
- this.configurationAdmin = configurationAdmin;
- init(configurationAdmin, isClean, isFirstInit);
- } catch (IOException e) {
- throw new RuntimeException("Could not init deploy configs", e);
- }
- // FIXME check race conditions during initialization
- // bc.registerService(ConfigurationListener.class, this, null);
- }
-
- private void firstInit() throws IOException {
- log.info("## FIRST INIT ##");
- Files.createDirectories(deployConfigPath.getParent());
-
- // FirstInit firstInit = new FirstInit();
- InitUtils.prepareFirstInitInstanceArea();
-
- if (!Files.exists(deployConfigPath))
- deployConfigs = new TreeMap<>();
- else// config file could have juste been copied by preparation
- try (InputStream in = Files.newInputStream(deployConfigPath)) {
- deployConfigs = new LdifParser().read(in);
- }
- save();
- }
-
- private void setFromFrameworkProperties(boolean isFirstInit) {
-
- // user admin
- List<Dictionary<String, Object>> userDirectoryConfigs = InitUtils.getUserDirectoryConfigs();
- if (userDirectoryConfigs.size() != 0) {
- List<String> activeCns = new ArrayList<>();
- for (int i = 0; i < userDirectoryConfigs.size(); i++) {
- Dictionary<String, Object> userDirectoryConfig = userDirectoryConfigs.get(i);
- String baseDn = (String) userDirectoryConfig.get(UserAdminConf.baseDn.name());
- String cn;
- if (CmsConstants.ROLES_BASEDN.equals(baseDn))
- cn = ROLES;
- else
- cn = UserAdminConf.baseDnHash(userDirectoryConfig);
- activeCns.add(cn);
- userDirectoryConfig.put(CmsConstants.CN, cn);
- putFactoryDeployConfig(CmsConstants.NODE_USER_ADMIN_PID, userDirectoryConfig);
- }
- // disable others
- LdapName userAdminFactoryName = serviceFactoryDn(CmsConstants.NODE_USER_ADMIN_PID);
- for (LdapName name : deployConfigs.keySet()) {
- if (name.startsWith(userAdminFactoryName) && !name.equals(userAdminFactoryName)) {
-// try {
- Attributes attrs = deployConfigs.get(name);
- String cn = name.getRdn(name.size() - 1).getValue().toString();
- if (!activeCns.contains(cn)) {
- attrs.put(UserAdminConf.disabled.name(), "true");
- }
-// } catch (Exception e) {
-// throw new CmsException("Cannot disable user directory " + name, e);
-// }
- }
- }
- }
-
- // http server
-// Dictionary<String, Object> webServerConfig = InitUtils
-// .getHttpServerConfig(getProps(KernelConstants.JETTY_FACTORY_PID, NodeConstants.DEFAULT));
-// if (!webServerConfig.isEmpty()) {
-// // TODO check for other customizers
-// webServerConfig.put("customizer.class", "org.argeo.equinox.jetty.CmsJettyCustomizer");
-// putFactoryDeployConfig(KernelConstants.JETTY_FACTORY_PID, webServerConfig);
-// }
- LdapName defaultHttpServiceDn = serviceDn(KernelConstants.JETTY_FACTORY_PID, CmsConstants.DEFAULT);
- if (deployConfigs.containsKey(defaultHttpServiceDn)) {
- // remove old default configs since we have now to start Jetty servlet bridge
- // indirectly
- deployConfigs.remove(defaultHttpServiceDn);
- }
-
- // SAVE
- save();
- //
-
- // Explicitly configures Jetty so that the default server is not started by the
- // activator of the Equinox Jetty bundle.
- Dictionary<String, Object> webServerConfig = InitUtils
- .getHttpServerConfig(getProps(KernelConstants.JETTY_FACTORY_PID, CmsConstants.DEFAULT));
-// if (!webServerConfig.isEmpty()) {
-// webServerConfig.put("customizer.class", KernelConstants.CMS_JETTY_CUSTOMIZER_CLASS);
-//
-// // TODO centralise with Jetty extender
-// Object webSocketEnabled = webServerConfig.get(InternalHttpConstants.WEBSOCKET_ENABLED);
-// if (webSocketEnabled != null && webSocketEnabled.toString().equals("true")) {
-// bc.registerService(ServerEndpointConfig.Configurator.class, new CmsWebSocketConfigurator(), null);
-// webServerConfig.put(InternalHttpConstants.WEBSOCKET_ENABLED, "true");
-// }
-// }
-
- int tryCount = 60;
- try {
- tryGettyJetty: while (tryCount > 0) {
- try {
- JettyConfigurator.startServer(KernelConstants.DEFAULT_JETTY_SERVER, webServerConfig);
- // Explicitly starts Jetty OSGi HTTP bundle, so that it gets triggered if OSGi
- // configuration is not cleaned
- FrameworkUtil.getBundle(JettyConfigurator.class).start();
- break tryGettyJetty;
- } catch (IllegalStateException e) {
- // Jetty may not be ready
- try {
- Thread.sleep(1000);
- } catch (Exception e1) {
- // silent
- }
- tryCount--;
- }
- }
- } catch (Exception e) {
- log.error("Cannot start default Jetty server with config " + webServerConfig, e);
- }
-
- }
-
- private void init(ConfigurationAdmin configurationAdmin, boolean isClean, boolean isFirstInit) throws IOException {
-
- try (InputStream in = Files.newInputStream(deployConfigPath)) {
- deployConfigs = new LdifParser().read(in);
- }
- if (isClean) {
- if (log.isDebugEnabled())
- log.debug("Clean state, loading from framework properties...");
- setFromFrameworkProperties(isFirstInit);
- loadConfigs();
- }
- // TODO check consistency if not clean
- }
-
- public void loadConfigs() throws IOException {
- // FIXME make it more robust
- Configuration systemRolesConf = null;
- LdapName systemRolesDn;
- try {
- // FIXME make it more robust
- systemRolesDn = new LdapName("cn=roles,ou=org.argeo.api.userAdmin,ou=deploy,ou=node");
- } catch (InvalidNameException e) {
- throw new IllegalArgumentException(e);
- }
- deployConfigs: for (LdapName dn : deployConfigs.keySet()) {
- Rdn lastRdn = dn.getRdn(dn.size() - 1);
- LdapName prefix = (LdapName) dn.getPrefix(dn.size() - 1);
- if (prefix.toString().equals(CmsConstants.DEPLOY_BASEDN)) {
- if (lastRdn.getType().equals(CmsConstants.CN)) {
- // service
- String pid = lastRdn.getValue().toString();
- Configuration conf = configurationAdmin.getConfiguration(pid);
- AttributesDictionary dico = new AttributesDictionary(deployConfigs.get(dn));
- conf.update(dico);
- } else {
- // service factory definition
- }
- } else {
- Attributes config = deployConfigs.get(dn);
- Attribute disabled = config.get(UserAdminConf.disabled.name());
- if (disabled != null)
- continue deployConfigs;
- // service factory service
- Rdn beforeLastRdn = dn.getRdn(dn.size() - 2);
- assert beforeLastRdn.getType().equals(CmsConstants.OU);
- String factoryPid = beforeLastRdn.getValue().toString();
- Configuration conf = configurationAdmin.createFactoryConfiguration(factoryPid.toString(), null);
- if (systemRolesDn.equals(dn)) {
- systemRolesConf = configurationAdmin.createFactoryConfiguration(factoryPid.toString(), null);
- } else {
- AttributesDictionary dico = new AttributesDictionary(config);
- conf.update(dico);
- }
- }
- }
-
- // system roles must be last since it triggers node user admin publication
- if (systemRolesConf == null)
- throw new IllegalStateException("System roles are not configured.");
- systemRolesConf.update(new AttributesDictionary(deployConfigs.get(systemRolesDn)));
-
- }
-
- @Override
- public void configurationEvent(ConfigurationEvent event) {
- try {
- if (ConfigurationEvent.CM_UPDATED == event.getType()) {
- ConfigurationAdmin configurationAdmin = bc.getService(event.getReference());
- Configuration conf = configurationAdmin.getConfiguration(event.getPid(), null);
- LdapName serviceDn = null;
- String factoryPid = conf.getFactoryPid();
- if (factoryPid != null) {
- LdapName serviceFactoryDn = serviceFactoryDn(factoryPid);
- if (deployConfigs.containsKey(serviceFactoryDn)) {
- for (LdapName dn : deployConfigs.keySet()) {
- if (dn.startsWith(serviceFactoryDn)) {
- Rdn lastRdn = dn.getRdn(dn.size() - 1);
- assert lastRdn.getType().equals(CmsConstants.CN);
- Object value = conf.getProperties().get(lastRdn.getType());
- assert value != null;
- if (value.equals(lastRdn.getValue())) {
- serviceDn = dn;
- break;
- }
- }
- }
-
- Object cn = conf.getProperties().get(CmsConstants.CN);
- if (cn == null)
- throw new IllegalArgumentException("Properties must contain cn");
- if (serviceDn == null) {
- putFactoryDeployConfig(factoryPid, conf.getProperties());
- } else {
- Attributes attrs = deployConfigs.get(serviceDn);
- assert attrs != null;
- AttributesDictionary.copy(conf.getProperties(), attrs);
- }
- save();
- if (log.isDebugEnabled())
- log.debug("Updated deploy config " + serviceDn(factoryPid, cn.toString()));
- } else {
- // ignore non config-registered service factories
- }
- } else {
- serviceDn = serviceDn(event.getPid());
- if (deployConfigs.containsKey(serviceDn)) {
- Attributes attrs = deployConfigs.get(serviceDn);
- assert attrs != null;
- AttributesDictionary.copy(conf.getProperties(), attrs);
- save();
- if (log.isDebugEnabled())
- log.debug("Updated deploy config " + serviceDn);
- } else {
- // ignore non config-registered services
- }
- }
- }
- } catch (Exception e) {
- log.error("Could not handle configuration event", e);
- }
- }
-
- void putFactoryDeployConfig(String factoryPid, Dictionary<String, Object> props) {
- Object cn = props.get(CmsConstants.CN);
- if (cn == null)
- throw new IllegalArgumentException("cn must be set in properties");
- LdapName serviceFactoryDn = serviceFactoryDn(factoryPid);
- if (!deployConfigs.containsKey(serviceFactoryDn))
- deployConfigs.put(serviceFactoryDn, new BasicAttributes(CmsConstants.OU, factoryPid));
- LdapName serviceDn = serviceDn(factoryPid, cn.toString());
- Attributes attrs = new BasicAttributes();
- AttributesDictionary.copy(props, attrs);
- deployConfigs.put(serviceDn, attrs);
- }
-
- void putDeployConfig(String servicePid, Dictionary<String, Object> props) {
- LdapName serviceDn = serviceDn(servicePid);
- Attributes attrs = new BasicAttributes(CmsConstants.CN, servicePid);
- AttributesDictionary.copy(props, attrs);
- deployConfigs.put(serviceDn, attrs);
- }
-
- void save() {
- try (Writer writer = Files.newBufferedWriter(deployConfigPath)) {
- new LdifWriter(writer).write(deployConfigs);
- } catch (IOException e) {
- // throw new CmsException("Cannot save deploy configs", e);
- log.error("Cannot save deploy configs", e);
- }
- }
-
- /*
- * UTILITIES
- */
- private LdapName serviceFactoryDn(String factoryPid) {
- try {
- return new LdapName(CmsConstants.OU + "=" + factoryPid + "," + CmsConstants.DEPLOY_BASEDN);
- } catch (InvalidNameException e) {
- throw new IllegalArgumentException("Cannot generate DN from " + factoryPid, e);
- }
- }
-
- private LdapName serviceDn(String servicePid) {
- try {
- return new LdapName(CmsConstants.CN + "=" + servicePid + "," + CmsConstants.DEPLOY_BASEDN);
- } catch (InvalidNameException e) {
- throw new IllegalArgumentException("Cannot generate DN from " + servicePid, e);
- }
- }
-
- private LdapName serviceDn(String factoryPid, String cn) {
- try {
- return (LdapName) serviceFactoryDn(factoryPid).add(new Rdn(CmsConstants.CN, cn));
- } catch (InvalidNameException e) {
- throw new IllegalArgumentException("Cannot generate DN from " + factoryPid + " and " + cn, e);
- }
- }
-
-public Dictionary<String, Object> getProps(String factoryPid, String cn) {
- Attributes attrs = deployConfigs.get(serviceDn(factoryPid, cn));
- if (attrs != null)
- return new AttributesDictionary(attrs);
- else
- return null;
- }
-
- private static boolean isInitialized() {
- return Files.exists(deployConfigPath);
- }
-
- public boolean isFirstInit() {
- return isFirstInit;
- }
-
-}
+++ /dev/null
-package org.argeo.cms.internal.kernel;
-
-/**
- * Workaround for killing Gogo shell by system shutdown.
- *
- * @see https://issues.apache.org/jira/browse/FELIX-4208
- */
-class GogoShellKiller extends Thread {
-
- public GogoShellKiller() {
- super("Gogo Shell Killer");
- setDaemon(true);
- }
-
- @Override
- public void run() {
- ThreadGroup rootTg = getRootThreadGroup(null);
- Thread gogoShellThread = findGogoShellThread(rootTg);
- if (gogoShellThread == null)
- return;
- while (getNonDaemonCount(rootTg) > 2) {
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- // silent
- }
- }
- gogoShellThread = findGogoShellThread(rootTg);
- if (gogoShellThread == null)
- return;
- // No non-deamon threads left, forcibly halting the VM
- Runtime.getRuntime().halt(0);
- }
-
- private ThreadGroup getRootThreadGroup(ThreadGroup tg) {
- if (tg == null)
- tg = Thread.currentThread().getThreadGroup();
- if (tg.getParent() == null)
- return tg;
- else
- return getRootThreadGroup(tg.getParent());
- }
-
- private int getNonDaemonCount(ThreadGroup rootThreadGroup) {
- Thread[] threads = new Thread[rootThreadGroup.activeCount()];
- rootThreadGroup.enumerate(threads);
- int nonDameonCount = 0;
- for (Thread t : threads)
- if (t != null && !t.isDaemon())
- nonDameonCount++;
- return nonDameonCount;
- }
-
- private Thread findGogoShellThread(ThreadGroup rootThreadGroup) {
- Thread[] threads = new Thread[rootThreadGroup.activeCount()];
- rootThreadGroup.enumerate(threads, true);
- for (Thread thread : threads) {
- if (thread.getName().equals("pipe-gosh --login --noshutdown"))
- return thread;
- }
- return null;
- }
-
-}
\ No newline at end of file
+++ /dev/null
-package org.argeo.cms.internal.kernel;
-
-import static org.argeo.cms.internal.kernel.KernelUtils.getFrameworkProp;
-
-import java.io.File;
-import java.io.FileFilter;
-import java.io.IOException;
-import java.io.Reader;
-import java.net.InetAddress;
-import java.net.URI;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.security.KeyStore;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Dictionary;
-import java.util.Hashtable;
-import java.util.List;
-
-import javax.security.auth.x500.X500Principal;
-
-import org.apache.commons.io.FileUtils;
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.cms.internal.http.InternalHttpConstants;
-import org.argeo.osgi.useradmin.UserAdminConf;
-
-/**
- * Interprets framework properties in order to generate the initial deploy
- * configuration.
- */
-class InitUtils {
- private final static CmsLog log = CmsLog.getLog(InitUtils.class);
-
-
- /** Override the provided config with the framework properties */
- static Dictionary<String, Object> getHttpServerConfig(Dictionary<String, Object> provided) {
- String httpPort = getFrameworkProp("org.osgi.service.http.port");
- String httpsPort = getFrameworkProp("org.osgi.service.http.port.secure");
- /// TODO make it more generic
- String httpHost = getFrameworkProp(
- InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.HTTP_HOST);
- String httpsHost = getFrameworkProp(
- InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.HTTPS_HOST);
- String webSocketEnabled = getFrameworkProp(
- InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.WEBSOCKET_ENABLED);
-
- final Hashtable<String, Object> props = new Hashtable<String, Object>();
- // try {
- if (httpPort != null || httpsPort != null) {
- boolean httpEnabled = httpPort != null;
- props.put(InternalHttpConstants.HTTP_ENABLED, httpEnabled);
- boolean httpsEnabled = httpsPort != null;
- props.put(InternalHttpConstants.HTTPS_ENABLED, httpsEnabled);
-
- if (httpEnabled) {
- props.put(InternalHttpConstants.HTTP_PORT, httpPort);
- if (httpHost != null)
- props.put(InternalHttpConstants.HTTP_HOST, httpHost);
- }
-
- if (httpsEnabled) {
- props.put(InternalHttpConstants.HTTPS_PORT, httpsPort);
- if (httpsHost != null)
- props.put(InternalHttpConstants.HTTPS_HOST, httpsHost);
-
- // server certificate
- Path keyStorePath = KernelUtils.getOsgiInstancePath(KernelConstants.DEFAULT_KEYSTORE_PATH);
- Path pemKeyPath = KernelUtils.getOsgiInstancePath(KernelConstants.DEFAULT_PEM_KEY_PATH);
- Path pemCertPath = KernelUtils.getOsgiInstancePath(KernelConstants.DEFAULT_PEM_CERT_PATH);
- String keyStorePasswordStr = getFrameworkProp(
- InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.SSL_PASSWORD);
- char[] keyStorePassword;
- if (keyStorePasswordStr == null)
- keyStorePassword = "changeit".toCharArray();
- else
- keyStorePassword = keyStorePasswordStr.toCharArray();
-
- // if PEM files both exists, update the PKCS12 file
- if (Files.exists(pemCertPath) && Files.exists(pemKeyPath)) {
- // TODO check certificate update time? monitor changes?
- KeyStore keyStore = PkiUtils.getKeyStore(keyStorePath, keyStorePassword, PkiUtils.PKCS12);
- try (Reader key = Files.newBufferedReader(pemKeyPath, StandardCharsets.US_ASCII);
- Reader cert = Files.newBufferedReader(pemCertPath, StandardCharsets.US_ASCII);) {
- PkiUtils.loadPem(keyStore, key, keyStorePassword, cert);
- PkiUtils.saveKeyStore(keyStorePath, keyStorePassword, keyStore);
- if (log.isDebugEnabled())
- log.debug("PEM certificate stored in " + keyStorePath);
- } catch (IOException e) {
- log.error("Cannot read PEM files " + pemKeyPath + " and " + pemCertPath, e);
- }
- }
-
- if (!Files.exists(keyStorePath))
- createSelfSignedKeyStore(keyStorePath, keyStorePassword, PkiUtils.PKCS12);
- props.put(InternalHttpConstants.SSL_KEYSTORETYPE, PkiUtils.PKCS12);
- props.put(InternalHttpConstants.SSL_KEYSTORE, keyStorePath.toString());
- props.put(InternalHttpConstants.SSL_PASSWORD, new String(keyStorePassword));
-
-// props.put(InternalHttpConstants.SSL_KEYSTORETYPE, "PKCS11");
-// props.put(InternalHttpConstants.SSL_KEYSTORE, "../../nssdb");
-// props.put(InternalHttpConstants.SSL_PASSWORD, keyStorePassword);
-
- // client certificate authentication
- String wantClientAuth = getFrameworkProp(
- InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.SSL_WANTCLIENTAUTH);
- if (wantClientAuth != null)
- props.put(InternalHttpConstants.SSL_WANTCLIENTAUTH, Boolean.parseBoolean(wantClientAuth));
- String needClientAuth = getFrameworkProp(
- InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.SSL_NEEDCLIENTAUTH);
- if (needClientAuth != null)
- props.put(InternalHttpConstants.SSL_NEEDCLIENTAUTH, Boolean.parseBoolean(needClientAuth));
- }
-
- // web socket
- if (webSocketEnabled != null && webSocketEnabled.equals("true"))
- props.put(InternalHttpConstants.WEBSOCKET_ENABLED, true);
-
- props.put(CmsConstants.CN, CmsConstants.DEFAULT);
- }
- return props;
- }
-
- static List<Dictionary<String, Object>> getUserDirectoryConfigs() {
- List<Dictionary<String, Object>> res = new ArrayList<>();
- File nodeBaseDir = KernelUtils.getOsgiInstancePath(KernelConstants.DIR_NODE).toFile();
- List<String> uris = new ArrayList<>();
-
- // node roles
- String nodeRolesUri = getFrameworkProp(CmsConstants.ROLES_URI);
- String baseNodeRoleDn = CmsConstants.ROLES_BASEDN;
- if (nodeRolesUri == null) {
- nodeRolesUri = baseNodeRoleDn + ".ldif";
- File nodeRolesFile = new File(nodeBaseDir, nodeRolesUri);
- if (!nodeRolesFile.exists())
- try {
- FileUtils.copyInputStreamToFile(InitUtils.class.getResourceAsStream(baseNodeRoleDn + ".ldif"),
- nodeRolesFile);
- } catch (IOException e) {
- throw new RuntimeException("Cannot copy demo resource", e);
- }
- // nodeRolesUri = nodeRolesFile.toURI().toString();
- }
- uris.add(nodeRolesUri);
-
- // node tokens
- String nodeTokensUri = getFrameworkProp(CmsConstants.TOKENS_URI);
- String baseNodeTokensDn = CmsConstants.TOKENS_BASEDN;
- if (nodeTokensUri == null) {
- nodeTokensUri = baseNodeTokensDn + ".ldif";
- File nodeTokensFile = new File(nodeBaseDir, nodeTokensUri);
- if (!nodeTokensFile.exists())
- try {
- FileUtils.copyInputStreamToFile(InitUtils.class.getResourceAsStream(baseNodeTokensDn + ".ldif"),
- nodeTokensFile);
- } catch (IOException e) {
- throw new RuntimeException("Cannot copy demo resource", e);
- }
- // nodeRolesUri = nodeRolesFile.toURI().toString();
- }
- uris.add(nodeTokensUri);
-
- // Business roles
- String userAdminUris = getFrameworkProp(CmsConstants.USERADMIN_URIS);
- if (userAdminUris == null) {
- String demoBaseDn = "dc=example,dc=com";
- userAdminUris = demoBaseDn + ".ldif";
- File businessRolesFile = new File(nodeBaseDir, userAdminUris);
- File systemRolesFile = new File(nodeBaseDir, "ou=roles,ou=node.ldif");
- if (!businessRolesFile.exists())
- try {
- FileUtils.copyInputStreamToFile(InitUtils.class.getResourceAsStream(demoBaseDn + ".ldif"),
- businessRolesFile);
- if (!systemRolesFile.exists())
- FileUtils.copyInputStreamToFile(
- InitUtils.class.getResourceAsStream("example-ou=roles,ou=node.ldif"), systemRolesFile);
- } catch (IOException e) {
- throw new RuntimeException("Cannot copy demo resources", e);
- }
- // userAdminUris = businessRolesFile.toURI().toString();
- log.warn("## DEV Using dummy base DN " + demoBaseDn);
- // TODO downgrade security level
- }
- for (String userAdminUri : userAdminUris.split(" "))
- uris.add(userAdminUri);
-
- // Interprets URIs
- for (String uri : uris) {
- URI u;
- try {
- u = new URI(uri);
- if (u.getPath() == null)
- throw new IllegalArgumentException(
- "URI " + uri + " must have a path in order to determine base DN");
- if (u.getScheme() == null) {
- if (uri.startsWith("/") || uri.startsWith("./") || uri.startsWith("../"))
- u = new File(uri).getCanonicalFile().toURI();
- else if (!uri.contains("/")) {
- // u = KernelUtils.getOsgiInstanceUri(KernelConstants.DIR_NODE + '/' + uri);
- u = new URI(uri);
- } else
- throw new IllegalArgumentException("Cannot interpret " + uri + " as an uri");
- } else if (u.getScheme().equals(UserAdminConf.SCHEME_FILE)) {
- u = new File(u).getCanonicalFile().toURI();
- }
- } catch (Exception e) {
- throw new RuntimeException("Cannot interpret " + uri + " as an uri", e);
- }
- Dictionary<String, Object> properties = UserAdminConf.uriAsProperties(u.toString());
- res.add(properties);
- }
-
- return res;
- }
-
- /**
- * Called before node initialisation, in order populate OSGi instance are with
- * some files (typically LDIF, etc).
- */
- static void prepareFirstInitInstanceArea() {
- String nodeInits = getFrameworkProp(CmsConstants.NODE_INIT);
- if (nodeInits == null)
- nodeInits = "../../init";
-
- for (String nodeInit : nodeInits.split(",")) {
-
- if (nodeInit.startsWith("http")) {
- // TODO reconnect it
- //registerRemoteInit(nodeInit);
- } else {
-
- // TODO use java.nio.file
- File initDir;
- if (nodeInit.startsWith("."))
- initDir = KernelUtils.getExecutionDir(nodeInit);
- else
- initDir = new File(nodeInit);
- // TODO also uncompress archives
- if (initDir.exists())
- try {
- FileUtils.copyDirectory(initDir, KernelUtils.getOsgiInstanceDir(), new FileFilter() {
-
- @Override
- public boolean accept(File pathname) {
- if (pathname.getName().equals(".svn") || pathname.getName().equals(".git"))
- return false;
- return true;
- }
- });
- log.info("CMS initialized from " + initDir.getCanonicalPath());
- } catch (IOException e) {
- throw new RuntimeException("Cannot initialize from " + initDir, e);
- }
- }
- }
- }
-
- private static void createSelfSignedKeyStore(Path keyStorePath, char[] keyStorePassword, String keyStoreType) {
- // for (Provider provider : Security.getProviders())
- // System.out.println(provider.getName());
-// File keyStoreFile = keyStorePath.toFile();
- char[] keyPwd = Arrays.copyOf(keyStorePassword, keyStorePassword.length);
- if (!Files.exists(keyStorePath)) {
- try {
- Files.createDirectories(keyStorePath.getParent());
- KeyStore keyStore = PkiUtils.getKeyStore(keyStorePath, keyStorePassword, keyStoreType);
- PkiUtils.generateSelfSignedCertificate(keyStore,
- new X500Principal("CN=" + InetAddress.getLocalHost().getHostName() + ",OU=UNSECURE,O=UNSECURE"),
- 1024, keyPwd);
- PkiUtils.saveKeyStore(keyStorePath, keyStorePassword, keyStore);
- if (log.isDebugEnabled())
- log.debug("Created self-signed unsecure keystore " + keyStorePath);
- } catch (Exception e) {
- try {
- if (Files.size(keyStorePath) == 0)
- Files.delete(keyStorePath);
- } catch (IOException e1) {
- // silent
- }
- log.error("Cannot create keystore " + keyStorePath, e);
- }
- } else {
- throw new IllegalStateException("Keystore " + keyStorePath + " already exists");
- }
- }
-
-}
+++ /dev/null
-package org.argeo.cms.internal.kernel;
-
-import org.argeo.api.cms.CmsConstants;
-
-/** Internal CMS constants. */
-public interface KernelConstants {
- // Directories
- String DIR_NODE = "node";
- String DIR_REPOS = "repos";
- String DIR_INDEXES = "indexes";
- String DIR_TRANSACTIONS = "transactions";
-
- // Files
- String DEPLOY_CONFIG_PATH = DIR_NODE + '/' + CmsConstants.DEPLOY_BASEDN + ".ldif";
- String DEFAULT_KEYSTORE_PATH = DIR_NODE + '/' + CmsConstants.NODE + ".p12";
- String DEFAULT_PEM_KEY_PATH = DIR_NODE + '/' + CmsConstants.NODE + ".key";
- String DEFAULT_PEM_CERT_PATH = DIR_NODE + '/' + CmsConstants.NODE + ".crt";
- String NODE_KEY_TAB_PATH = DIR_NODE + "/krb5.keytab";
-
- // Security
- String JAAS_CONFIG = "/org/argeo/cms/internal/kernel/jaas.cfg";
- String JAAS_CONFIG_IPA = "/org/argeo/cms/internal/kernel/jaas-ipa.cfg";
-
- // Java
- String JAAS_CONFIG_PROP = "java.security.auth.login.config";
-
- // DEFAULTS JCR PATH
- String DEFAULT_HOME_BASE_PATH = "/home";
- String DEFAULT_USERS_BASE_PATH = "/users";
- String DEFAULT_GROUPS_BASE_PATH = "/groups";
-
- // KERBEROS
- String DEFAULT_KERBEROS_SERVICE = "HTTP";
-
- // HTTP client
- String COOKIE_POLICY_BROWSER_COMPATIBILITY = "compatibility";
-
- // RWT / RAP
- // String PATH_WORKBENCH = "/ui";
- // String PATH_WORKBENCH_PUBLIC = PATH_WORKBENCH + "/public";
-
- String JETTY_FACTORY_PID = "org.eclipse.equinox.http.jetty.config";
- String WHITEBOARD_PATTERN_PROP = "osgi.http.whiteboard.servlet.pattern";
- // default Jetty server configured via JettyConfigurator
- String DEFAULT_JETTY_SERVER = "default";
- String CMS_JETTY_CUSTOMIZER_CLASS = "org.argeo.equinox.jetty.CmsJettyCustomizer";
-
- // avoid dependencies
- String CONTEXT_NAME_PROP = "contextName";
- String JACKRABBIT_REPOSITORY_URI = "org.apache.jackrabbit.repository.uri";
- String JACKRABBIT_REMOTE_DEFAULT_WORKSPACE = "org.apache.jackrabbit.spi2davex.WorkspaceNameDefault";
-}
+++ /dev/null
-package org.argeo.cms.internal.kernel;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.security.URIParameter;
-import java.util.Dictionary;
-import java.util.Hashtable;
-import java.util.Properties;
-import java.util.TreeMap;
-import java.util.TreeSet;
-
-import org.argeo.api.cms.CmsLog;
-import org.argeo.cms.osgi.DataModelNamespace;
-import org.osgi.framework.BundleContext;
-import org.osgi.util.tracker.ServiceTracker;
-
-/** Package utilities */
-class KernelUtils implements KernelConstants {
- final static String OSGI_INSTANCE_AREA = "osgi.instance.area";
- final static String OSGI_CONFIGURATION_AREA = "osgi.configuration.area";
-
- static void setJaasConfiguration(URL jaasConfigurationUrl) {
- try {
- URIParameter uriParameter = new URIParameter(jaasConfigurationUrl.toURI());
- javax.security.auth.login.Configuration jaasConfiguration = javax.security.auth.login.Configuration
- .getInstance("JavaLoginConfig", uriParameter);
- javax.security.auth.login.Configuration.setConfiguration(jaasConfiguration);
- } catch (Exception e) {
- throw new IllegalArgumentException("Cannot set configuration " + jaasConfigurationUrl, e);
- }
- }
-
- static Dictionary<String, ?> asDictionary(Properties props) {
- Hashtable<String, Object> hashtable = new Hashtable<String, Object>();
- for (Object key : props.keySet()) {
- hashtable.put(key.toString(), props.get(key));
- }
- return hashtable;
- }
-
- static Dictionary<String, ?> asDictionary(ClassLoader cl, String resource) {
- Properties props = new Properties();
- try {
- props.load(cl.getResourceAsStream(resource));
- } catch (IOException e) {
- throw new IllegalArgumentException("Cannot load " + resource + " from classpath", e);
- }
- return asDictionary(props);
- }
-
- static File getExecutionDir(String relativePath) {
- File executionDir = new File(getFrameworkProp("user.dir"));
- if (relativePath == null)
- return executionDir;
- try {
- return new File(executionDir, relativePath).getCanonicalFile();
- } catch (IOException e) {
- throw new IllegalArgumentException("Cannot get canonical file", e);
- }
- }
-
- static File getOsgiInstanceDir() {
- return new File(getBundleContext().getProperty(OSGI_INSTANCE_AREA).substring("file:".length()))
- .getAbsoluteFile();
- }
-
- static Path getOsgiInstancePath(String relativePath) {
- return Paths.get(getOsgiInstanceUri(relativePath));
- }
-
- static URI getOsgiInstanceUri(String relativePath) {
- String osgiInstanceBaseUri = getFrameworkProp(OSGI_INSTANCE_AREA);
- if (osgiInstanceBaseUri != null)
- return safeUri(osgiInstanceBaseUri + (relativePath != null ? relativePath : ""));
- else
- return Paths.get(System.getProperty("user.dir")).toUri();
- }
-
- static File getOsgiConfigurationFile(String relativePath) {
- try {
- return new File(new URI(getBundleContext().getProperty(OSGI_CONFIGURATION_AREA) + relativePath))
- .getCanonicalFile();
- } catch (Exception e) {
- throw new IllegalArgumentException("Cannot get configuration file for " + relativePath, e);
- }
- }
-
- static String getFrameworkProp(String key, String def) {
- BundleContext bundleContext = Activator.getBundleContext();
- String value;
- if (bundleContext != null)
- value = bundleContext.getProperty(key);
- else
- value = System.getProperty(key);
- if (value == null)
- return def;
- return value;
- }
-
- static String getFrameworkProp(String key) {
- return getFrameworkProp(key, null);
- }
-
- // Security
- // static Subject anonymousLogin() {
- // Subject subject = new Subject();
- // LoginContext lc;
- // try {
- // lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER, subject);
- // lc.login();
- // return subject;
- // } catch (LoginException e) {
- // throw new CmsException("Cannot login as anonymous", e);
- // }
- // }
-
- static void logFrameworkProperties(CmsLog log) {
- BundleContext bc = getBundleContext();
- for (Object sysProp : new TreeSet<Object>(System.getProperties().keySet())) {
- log.debug(sysProp + "=" + bc.getProperty(sysProp.toString()));
- }
- // String[] keys = { Constants.FRAMEWORK_STORAGE,
- // Constants.FRAMEWORK_OS_NAME, Constants.FRAMEWORK_OS_VERSION,
- // Constants.FRAMEWORK_PROCESSOR, Constants.FRAMEWORK_SECURITY,
- // Constants.FRAMEWORK_TRUST_REPOSITORIES,
- // Constants.FRAMEWORK_WINDOWSYSTEM, Constants.FRAMEWORK_VENDOR,
- // Constants.FRAMEWORK_VERSION, Constants.FRAMEWORK_STORAGE_CLEAN,
- // Constants.FRAMEWORK_LANGUAGE, Constants.FRAMEWORK_UUID };
- // for (String key : keys)
- // log.debug(key + "=" + bc.getProperty(key));
- }
-
- static void printSystemProperties(PrintStream out) {
- TreeMap<String, String> display = new TreeMap<>();
- for (Object key : System.getProperties().keySet())
- display.put(key.toString(), System.getProperty(key.toString()));
- for (String key : display.keySet())
- out.println(key + "=" + display.get(key));
- }
-
-// static Session openAdminSession(Repository repository) {
-// return openAdminSession(repository, null);
-// }
-//
-// static Session openAdminSession(final Repository repository, final String workspaceName) {
-// LoginContext loginContext = loginAsDataAdmin();
-// return Subject.doAs(loginContext.getSubject(), new PrivilegedAction<Session>() {
-//
-// @Override
-// public Session run() {
-// try {
-// return repository.login(workspaceName);
-// } catch (RepositoryException e) {
-// throw new IllegalStateException("Cannot open admin session", e);
-// } finally {
-// try {
-// loginContext.logout();
-// } catch (LoginException e) {
-// throw new IllegalStateException(e);
-// }
-// }
-// }
-//
-// });
-// }
-//
-// static LoginContext loginAsDataAdmin() {
-// ClassLoader currentCl = Thread.currentThread().getContextClassLoader();
-// Thread.currentThread().setContextClassLoader(KernelUtils.class.getClassLoader());
-// LoginContext loginContext;
-// try {
-// loginContext = new LoginContext(NodeConstants.LOGIN_CONTEXT_DATA_ADMIN);
-// loginContext.login();
-// } catch (LoginException e1) {
-// throw new IllegalStateException("Could not login as data admin", e1);
-// } finally {
-// Thread.currentThread().setContextClassLoader(currentCl);
-// }
-// return loginContext;
-// }
-
-// static void doAsDataAdmin(Runnable action) {
-// LoginContext loginContext = loginAsDataAdmin();
-// Subject.doAs(loginContext.getSubject(), new PrivilegedAction<Void>() {
-//
-// @Override
-// public Void run() {
-// try {
-// action.run();
-// return null;
-// } finally {
-// try {
-// loginContext.logout();
-// } catch (LoginException e) {
-// throw new IllegalStateException(e);
-// }
-// }
-// }
-//
-// });
-// }
-
- static void asyncOpen(ServiceTracker<?, ?> st) {
- Runnable run = new Runnable() {
-
- @Override
- public void run() {
- st.open();
- }
- };
- Activator.getInternalExecutorService().execute(run);
-// new Thread(run, "Open service tracker " + st).start();
- }
-
- static BundleContext getBundleContext() {
- return Activator.getBundleContext();
- }
-
- static boolean asBoolean(String value) {
- if (value == null)
- return false;
- switch (value) {
- case "true":
- return true;
- case "false":
- return false;
- default:
- throw new IllegalArgumentException(
- "Unsupported value for attribute " + DataModelNamespace.ABSTRACT + ": " + value);
- }
- }
-
- private static URI safeUri(String uri) {
- if (uri == null)
- throw new IllegalArgumentException("URI cannot be null");
- try {
- return new URI(uri);
- } catch (URISyntaxException e) {
- throw new IllegalArgumentException("Badly formatted URI " + uri, e);
- }
- }
-
- private KernelUtils() {
-
- }
-}
+++ /dev/null
-package org.argeo.cms.internal.kernel;
-
-import java.io.IOException;
-import java.net.URI;
-import java.nio.file.FileSystems;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.StandardWatchEventKinds;
-import java.nio.file.WatchEvent;
-import java.nio.file.WatchKey;
-import java.nio.file.WatchService;
-import java.security.SignatureException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Map;
-import java.util.Properties;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.cms.ArgeoLogListener;
-import org.argeo.cms.ArgeoLogger;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.auth.CurrentUser;
-import org.argeo.osgi.useradmin.UserAdminConf;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.Constants;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.cm.ConfigurationAdmin;
-import org.osgi.service.log.LogEntry;
-import org.osgi.service.log.LogLevel;
-import org.osgi.service.log.LogListener;
-import org.osgi.service.log.LogReaderService;
-
-/** Not meant to be used directly in standard log4j config */
-class NodeLogger implements ArgeoLogger, LogListener {
- /** Internal debug for development purposes. */
- private static Boolean debug = false;
-
- private Boolean disabled = false;
-
- private String level = null;
-
-// private Level log4jLevel = null;
-
- private Properties configuration;
-
- private AppenderImpl appender;
-
- private final List<ArgeoLogListener> everythingListeners = Collections
- .synchronizedList(new ArrayList<ArgeoLogListener>());
- private final List<ArgeoLogListener> allUsersListeners = Collections
- .synchronizedList(new ArrayList<ArgeoLogListener>());
- private final Map<String, List<ArgeoLogListener>> userListeners = Collections
- .synchronizedMap(new HashMap<String, List<ArgeoLogListener>>());
-
- private BlockingQueue<LogEvent> events;
- private LogDispatcherThread logDispatcherThread = new LogDispatcherThread();
-
- private Integer maxLastEventsCount = 10 * 1000;
-
- /** Marker to prevent stack overflow */
- private ThreadLocal<Boolean> dispatching = new ThreadLocal<Boolean>() {
-
- @Override
- protected Boolean initialValue() {
- return false;
- }
- };
-
- public NodeLogger(LogReaderService lrs) {
- if (lrs != null) {
- Enumeration<LogEntry> logEntries = lrs.getLog();
- while (logEntries.hasMoreElements())
- logged(logEntries.nextElement());
- lrs.addLogListener(this);
-
- // configure log4j watcher
- String log4jConfiguration = KernelUtils.getFrameworkProp("log4j.configuration");
- if (log4jConfiguration != null && log4jConfiguration.startsWith("file:")) {
- if (log4jConfiguration.contains("..")) {
- if (log4jConfiguration.startsWith("file://"))
- log4jConfiguration = log4jConfiguration.substring("file://".length());
- else if (log4jConfiguration.startsWith("file:"))
- log4jConfiguration = log4jConfiguration.substring("file:".length());
- }
- try {
- Path log4jconfigPath;
- if (log4jConfiguration.startsWith("file:"))
- log4jconfigPath = Paths.get(new URI(log4jConfiguration));
- else
- log4jconfigPath = Paths.get(log4jConfiguration);
- Thread log4jConfWatcher = new Log4jConfWatcherThread(log4jconfigPath);
- log4jConfWatcher.start();
- } catch (Exception e) {
- stdErr("Badly formatted log4j configuration URI " + log4jConfiguration + ": " + e.getMessage());
- }
- }
- }
- }
-
- public void init() {
- try {
- events = new LinkedBlockingQueue<LogEvent>();
-
- // if (layout != null)
- // setLayout(layout);
- // else
- // setLayout(new PatternLayout(pattern));
- appender = new AppenderImpl();
- reloadConfiguration();
-// Logger.getRootLogger().addAppender(appender);
-
- logDispatcherThread = new LogDispatcherThread();
- logDispatcherThread.start();
- } catch (Exception e) {
- throw new CmsException("Cannot initialize log4j");
- }
- }
-
- public void destroy() throws Exception {
-// Logger.getRootLogger().removeAppender(appender);
- allUsersListeners.clear();
- for (List<ArgeoLogListener> lst : userListeners.values())
- lst.clear();
- userListeners.clear();
-
- events.clear();
- events = null;
- logDispatcherThread.interrupt();
- }
-
- // public void setLayout(Layout layout) {
- // this.layout = layout;
- // }
-
- public String toString() {
- return "Node Logger";
- }
-
- //
- // OSGi LOGGER
- //
- @Override
- public void logged(LogEntry status) {
- CmsLog pluginLog = CmsLog.getLog(status.getBundle().getSymbolicName());
- LogLevel severity = status.getLogLevel();
- if (severity.equals(LogLevel.ERROR) && pluginLog.isErrorEnabled()) {
- // FIXME Fix Argeo TP
- if (status.getException() instanceof SignatureException)
- return;
- pluginLog.error(msg(status), status.getException());
- } else if (severity.equals(LogLevel.WARN) && pluginLog.isWarnEnabled()) {
- if (pluginLog.isTraceEnabled())
- pluginLog.warn(msg(status), status.getException());
- else
- pluginLog.warn(msg(status));
- } else if (severity.equals(LogLevel.INFO) && pluginLog.isDebugEnabled())
- pluginLog.debug(msg(status), status.getException());
- else if (severity.equals(LogLevel.DEBUG) && pluginLog.isTraceEnabled())
- pluginLog.trace(msg(status), status.getException());
- else if (severity.equals(LogLevel.TRACE) && pluginLog.isTraceEnabled())
- pluginLog.trace(msg(status), status.getException());
- }
-
- private String msg(LogEntry status) {
- StringBuilder sb = new StringBuilder();
- sb.append(status.getMessage());
- Bundle bundle = status.getBundle();
- if (bundle != null) {
- sb.append(" '" + bundle.getSymbolicName() + "'");
- }
- ServiceReference<?> sr = status.getServiceReference();
- if (sr != null) {
- sb.append(' ');
- String[] objectClasses = (String[]) sr.getProperty(Constants.OBJECTCLASS);
- if (isSpringApplicationContext(objectClasses)) {
- sb.append("{org.springframework.context.ApplicationContext}");
- Object symbolicName = sr.getProperty(Constants.BUNDLE_SYMBOLICNAME);
- if (symbolicName != null)
- sb.append(" " + Constants.BUNDLE_SYMBOLICNAME + ": " + symbolicName);
- } else {
- sb.append(arrayToString(objectClasses));
- }
- Object cn = sr.getProperty(CmsConstants.CN);
- if (cn != null)
- sb.append(" " + CmsConstants.CN + ": " + cn);
- Object factoryPid = sr.getProperty(ConfigurationAdmin.SERVICE_FACTORYPID);
- if (factoryPid != null)
- sb.append(" " + ConfigurationAdmin.SERVICE_FACTORYPID + ": " + factoryPid);
- // else {
- // Object servicePid = sr.getProperty(Constants.SERVICE_PID);
- // if (servicePid != null)
- // sb.append(" " + Constants.SERVICE_PID + ": " + servicePid);
- // }
- // servlets
- Object whiteBoardPattern = sr.getProperty(KernelConstants.WHITEBOARD_PATTERN_PROP);
- if (whiteBoardPattern != null) {
- if (whiteBoardPattern instanceof String) {
- sb.append(" " + KernelConstants.WHITEBOARD_PATTERN_PROP + ": " + whiteBoardPattern);
- } else {
- sb.append(" " + KernelConstants.WHITEBOARD_PATTERN_PROP + ": "
- + arrayToString((String[]) whiteBoardPattern));
- }
- }
- // RWT
- Object contextName = sr.getProperty(KernelConstants.CONTEXT_NAME_PROP);
- if (contextName != null)
- sb.append(" " + KernelConstants.CONTEXT_NAME_PROP + ": " + contextName);
-
- // user directories
- Object baseDn = sr.getProperty(UserAdminConf.baseDn.name());
- if (baseDn != null)
- sb.append(" " + UserAdminConf.baseDn.name() + ": " + baseDn);
-
- }
- return sb.toString();
- }
-
- private String arrayToString(Object[] arr) {
- StringBuilder sb = new StringBuilder();
- sb.append('[');
- for (int i = 0; i < arr.length; i++) {
- if (i != 0)
- sb.append(',');
- sb.append(arr[i]);
- }
- sb.append(']');
- return sb.toString();
- }
-
- private boolean isSpringApplicationContext(String[] objectClasses) {
- for (String clss : objectClasses) {
- if (clss.equals("org.eclipse.gemini.blueprint.context.DelegatedExecutionOsgiBundleApplicationContext")) {
- return true;
- }
- }
- return false;
- }
-
- //
- // ARGEO LOGGER
- //
-
- public synchronized void register(ArgeoLogListener listener, Integer numberOfPreviousEvents) {
- String username = CurrentUser.getUsername();
- if (username == null)
- throw new CmsException("Only authenticated users can register a log listener");
-
- if (!userListeners.containsKey(username)) {
- List<ArgeoLogListener> lst = Collections.synchronizedList(new ArrayList<ArgeoLogListener>());
- userListeners.put(username, lst);
- }
- userListeners.get(username).add(listener);
- List<LogEvent> lastEvents = logDispatcherThread.getLastEvents(username, numberOfPreviousEvents);
- for (LogEvent evt : lastEvents)
- dispatchEvent(listener, evt);
- }
-
- public synchronized void registerForAll(ArgeoLogListener listener, Integer numberOfPreviousEvents,
- boolean everything) {
- if (everything)
- everythingListeners.add(listener);
- else
- allUsersListeners.add(listener);
- List<LogEvent> lastEvents = logDispatcherThread.getLastEvents(null, numberOfPreviousEvents);
- for (LogEvent evt : lastEvents)
- if (everything || evt.getUsername() != null)
- dispatchEvent(listener, evt);
- }
-
- public synchronized void unregister(ArgeoLogListener listener) {
- String username = CurrentUser.getUsername();
- if (username == null)// FIXME
- return;
- if (!userListeners.containsKey(username))
- throw new CmsException("No user listeners " + listener + " registered for user " + username);
- if (!userListeners.get(username).contains(listener))
- throw new CmsException("No user listeners " + listener + " registered for user " + username);
- userListeners.get(username).remove(listener);
- if (userListeners.get(username).isEmpty())
- userListeners.remove(username);
-
- }
-
- public synchronized void unregisterForAll(ArgeoLogListener listener) {
- everythingListeners.remove(listener);
- allUsersListeners.remove(listener);
- }
-
- /** For development purpose, since using regular logging is not easy here */
- private static void stdOut(Object obj) {
- System.out.println(obj);
- }
-
- private static void stdErr(Object obj) {
- System.err.println(obj);
- }
-
- private static void debug(Object obj) {
- if (debug)
- System.out.println(obj);
- }
-
- private static boolean isInternalDebugEnabled() {
- return debug;
- }
-
- // public void setPattern(String pattern) {
- // this.pattern = pattern;
- // }
-
- public void setDisabled(Boolean disabled) {
- this.disabled = disabled;
- }
-
- public void setLevel(String level) {
- this.level = level;
- }
-
- public void setConfiguration(Properties configuration) {
- this.configuration = configuration;
- }
-
- public void updateConfiguration(Properties configuration) {
- setConfiguration(configuration);
- reloadConfiguration();
- }
-
- public Properties getConfiguration() {
- return configuration;
- }
-
- /**
- * Reloads configuration (if the configuration {@link Properties} is set)
- */
- protected void reloadConfiguration() {
- if (configuration != null) {
-// LogManager.resetConfiguration();
-// PropertyConfigurator.configure(configuration);
- }
- }
-
- protected synchronized void processLoggingEvent(LogEvent event) {
- if (disabled)
- return;
-
- if (dispatching.get())
- return;
-
- if (level != null && !level.trim().equals("")) {
-// if (log4jLevel == null || !log4jLevel.toString().equals(level))
-// try {
-// log4jLevel = Level.toLevel(level);
-// } catch (Exception e) {
-// System.err.println("Log4j level could not be set for level '" + level + "', resetting it to null.");
-// e.printStackTrace();
-// level = null;
-// }
-//
-// if (log4jLevel != null && !event.getLoggingEvent().getLevel().isGreaterOrEqual(log4jLevel)) {
-// return;
-// }
- }
-
- try {
- // admin listeners
- Iterator<ArgeoLogListener> everythingIt = everythingListeners.iterator();
- while (everythingIt.hasNext())
- dispatchEvent(everythingIt.next(), event);
-
- if (event.getUsername() != null) {
- Iterator<ArgeoLogListener> allUsersIt = allUsersListeners.iterator();
- while (allUsersIt.hasNext())
- dispatchEvent(allUsersIt.next(), event);
-
- if (userListeners.containsKey(event.getUsername())) {
- Iterator<ArgeoLogListener> userIt = userListeners.get(event.getUsername()).iterator();
- while (userIt.hasNext())
- dispatchEvent(userIt.next(), event);
- }
- }
- } catch (Exception e) {
- stdOut("Cannot process logging event");
- e.printStackTrace();
- }
- }
-
- protected void dispatchEvent(ArgeoLogListener logListener, LogEvent evt) {
-// LoggingEvent event = evt.getLoggingEvent();
-// logListener.appendLog(evt.getUsername(), event.getTimeStamp(), event.getLevel().toString(),
-// event.getLoggerName(), event.getThreadName(), event.getMessage(), event.getThrowableStrRep());
- }
-
- private class AppenderImpl { //extends AppenderSkeleton {
- public boolean requiresLayout() {
- return false;
- }
-
- public void close() {
- }
-
-// @Override
-// protected void append(LoggingEvent event) {
-// if (events != null) {
-// try {
-// String username = CurrentUser.getUsername();
-// events.put(new LogEvent(username, event));
-// } catch (InterruptedException e) {
-// // silent
-// }
-// }
-// }
-
- }
-
- private class LogDispatcherThread extends Thread {
- /** encapsulated in order to simplify concurrency management */
- private LinkedList<LogEvent> lastEvents = new LinkedList<LogEvent>();
-
- public LogDispatcherThread() {
- super("Argeo Logging Dispatcher Thread");
- }
-
- public void run() {
- while (events != null) {
- try {
- LogEvent loggingEvent = events.take();
- processLoggingEvent(loggingEvent);
- addLastEvent(loggingEvent);
- } catch (InterruptedException e) {
- if (events == null)
- return;
- }
- }
- }
-
- protected synchronized void addLastEvent(LogEvent loggingEvent) {
- if (lastEvents.size() >= maxLastEventsCount)
- lastEvents.poll();
- lastEvents.add(loggingEvent);
- }
-
- public synchronized List<LogEvent> getLastEvents(String username, Integer maxCount) {
- LinkedList<LogEvent> evts = new LinkedList<LogEvent>();
- ListIterator<LogEvent> it = lastEvents.listIterator(lastEvents.size());
- int count = 0;
- while (it.hasPrevious() && (count < maxCount)) {
- LogEvent evt = it.previous();
- if (username == null || username.equals(evt.getUsername())) {
- evts.push(evt);
- count++;
- }
- }
- return evts;
- }
- }
-
- private class LogEvent {
- private final String username;
-// private final LoggingEvent loggingEvent;
-
- public LogEvent(String username) {
- super();
- this.username = username;
-// this.loggingEvent = loggingEvent;
- }
-
-// @Override
-// public int hashCode() {
-// return loggingEvent.hashCode();
-// }
-//
-// @Override
-// public boolean equals(Object obj) {
-// return loggingEvent.equals(obj);
-// }
-//
-// @Override
-// public String toString() {
-// return username + "@ " + loggingEvent.toString();
-// }
-
- public String getUsername() {
- return username;
- }
-
-// public LoggingEvent getLoggingEvent() {
-// return loggingEvent;
-// }
-
- }
-
- private class Log4jConfWatcherThread extends Thread {
- private Path log4jConfigurationPath;
-
- public Log4jConfWatcherThread(Path log4jConfigurationPath) {
- super("Log4j Configuration Watcher");
- try {
- this.log4jConfigurationPath = log4jConfigurationPath.toRealPath();
- } catch (IOException e) {
- this.log4jConfigurationPath = log4jConfigurationPath.toAbsolutePath();
- stdOut("Cannot determine real path for " + log4jConfigurationPath + ": " + e.getMessage());
- }
- }
-
- public void run() {
- Path parentDir = log4jConfigurationPath.getParent();
- try (final WatchService watchService = FileSystems.getDefault().newWatchService()) {
- parentDir.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);
- WatchKey wk;
- watching: while ((wk = watchService.take()) != null) {
- for (WatchEvent<?> event : wk.pollEvents()) {
- final Path changed = (Path) event.context();
- if (log4jConfigurationPath.equals(parentDir.resolve(changed))) {
- if (isInternalDebugEnabled())
- debug(log4jConfigurationPath + " has changed, reloading.");
-// PropertyConfigurator.configure(log4jConfigurationPath.toUri().toURL());
- }
- }
- // reset the key
- boolean valid = wk.reset();
- if (!valid) {
- break watching;
- }
- }
- } catch (IOException | InterruptedException e) {
- stdErr("Log4j configuration watcher failed: " + e.getMessage());
- }
- }
- }
-}
+++ /dev/null
-package org.argeo.cms.internal.kernel;
-
-import java.io.IOException;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.security.PrivilegedExceptionAction;
-import java.util.ArrayList;
-import java.util.Dictionary;
-import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-
-import javax.naming.ldap.LdapName;
-import javax.security.auth.Subject;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.NameCallback;
-import javax.security.auth.callback.UnsupportedCallbackException;
-import javax.security.auth.kerberos.KerberosPrincipal;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-
-import org.apache.commons.httpclient.auth.AuthPolicy;
-import org.apache.commons.httpclient.auth.CredentialsProvider;
-import org.apache.commons.httpclient.params.DefaultHttpParams;
-import org.apache.commons.httpclient.params.HttpMethodParams;
-import org.apache.commons.httpclient.params.HttpParams;
-import org.argeo.api.cms.CmsAuth;
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.cms.CmsUserManager;
-import org.argeo.cms.internal.auth.CmsUserManagerImpl;
-import org.argeo.cms.internal.http.client.HttpCredentialProvider;
-import org.argeo.cms.internal.http.client.SpnegoAuthScheme;
-import org.argeo.osgi.transaction.WorkControl;
-import org.argeo.osgi.transaction.WorkTransaction;
-import org.argeo.osgi.useradmin.AbstractUserDirectory;
-import org.argeo.osgi.useradmin.AggregatingUserAdmin;
-import org.argeo.osgi.useradmin.LdapUserAdmin;
-import org.argeo.osgi.useradmin.LdifUserAdmin;
-import org.argeo.osgi.useradmin.OsUserDirectory;
-import org.argeo.osgi.useradmin.UserAdminConf;
-import org.argeo.osgi.useradmin.UserDirectory;
-import org.argeo.util.naming.DnsBrowser;
-import org.ietf.jgss.GSSCredential;
-import org.ietf.jgss.GSSException;
-import org.ietf.jgss.GSSManager;
-import org.ietf.jgss.GSSName;
-import org.ietf.jgss.Oid;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.cm.ConfigurationException;
-import org.osgi.service.cm.ManagedServiceFactory;
-import org.osgi.service.useradmin.Authorization;
-import org.osgi.service.useradmin.UserAdmin;
-import org.osgi.util.tracker.ServiceTracker;
-
-/**
- * Aggregates multiple {@link UserDirectory} and integrates them with system
- * roles.
- */
-class NodeUserAdmin extends AggregatingUserAdmin implements ManagedServiceFactory, KernelConstants {
- private final static CmsLog log = CmsLog.getLog(NodeUserAdmin.class);
-// private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
-
- // OSGi
- private Map<String, LdapName> pidToBaseDn = new HashMap<>();
-// private Map<String, ServiceRegistration<UserDirectory>> pidToServiceRegs = new HashMap<>();
-// private ServiceRegistration<UserAdmin> userAdminReg;
-
- // JTA
- private final ServiceTracker<WorkControl, WorkControl> tmTracker;
- // private final String cacheName = UserDirectory.class.getName();
-
- // GSS API
- private Path nodeKeyTab = KernelUtils.getOsgiInstancePath(KernelConstants.NODE_KEY_TAB_PATH);
- private GSSCredential acceptorCredentials;
-
- private boolean singleUser = false;
-// private boolean systemRolesAvailable = false;
-
- CmsUserManagerImpl userManager;
-
- public NodeUserAdmin(String systemRolesBaseDn, String tokensBaseDn) {
- super(systemRolesBaseDn, tokensBaseDn);
- BundleContext bc = Activator.getBundleContext();
- if (bc != null) {
- tmTracker = new ServiceTracker<>(bc, WorkControl.class, null) {
-
- @Override
- public WorkControl addingService(ServiceReference<WorkControl> reference) {
- WorkControl workControl = super.addingService(reference);
- userManager = new CmsUserManagerImpl();
- userManager.setUserAdmin(NodeUserAdmin.this);
- // FIXME make it more robust
- userManager.setUserTransaction((WorkTransaction) workControl);
- bc.registerService(CmsUserManager.class, userManager, null);
- return workControl;
- }
- };
- tmTracker.open();
- } else {
- tmTracker = null;
- }
- }
-
- @Override
- public void updated(String pid, Dictionary<String, ?> properties) throws ConfigurationException {
- String uri = (String) properties.get(UserAdminConf.uri.name());
- Object realm = properties.get(UserAdminConf.realm.name());
- URI u;
- try {
- if (uri == null) {
- String baseDn = (String) properties.get(UserAdminConf.baseDn.name());
- u = KernelUtils.getOsgiInstanceUri(KernelConstants.DIR_NODE + '/' + baseDn + ".ldif");
- } else if (realm != null) {
- u = null;
- } else {
- u = new URI(uri);
- }
- } catch (URISyntaxException e) {
- throw new IllegalArgumentException("Badly formatted URI " + uri, e);
- }
-
- // Create
- AbstractUserDirectory userDirectory;
- if (realm != null || UserAdminConf.SCHEME_LDAP.equals(u.getScheme())
- || UserAdminConf.SCHEME_LDAPS.equals(u.getScheme())) {
- userDirectory = new LdapUserAdmin(properties);
- } else if (UserAdminConf.SCHEME_FILE.equals(u.getScheme())) {
- userDirectory = new LdifUserAdmin(u, properties);
- } else if (UserAdminConf.SCHEME_OS.equals(u.getScheme())) {
- userDirectory = new OsUserDirectory(u, properties);
- singleUser = true;
- } else {
- throw new IllegalArgumentException("Unsupported scheme " + u.getScheme());
- }
- addUserDirectory(userDirectory);
-
- // OSGi
- LdapName baseDn = userDirectory.getBaseDn();
- Hashtable<String, Object> regProps = new Hashtable<>();
- regProps.put(Constants.SERVICE_PID, pid);
- if (isSystemRolesBaseDn(baseDn))
- regProps.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE);
- regProps.put(UserAdminConf.baseDn.name(), baseDn);
- // ServiceRegistration<UserDirectory> reg =
- // bc.registerService(UserDirectory.class, userDirectory, regProps);
- Activator.registerService(UserDirectory.class, userDirectory, regProps);
- userManager.addUserDirectory(userDirectory, regProps);
- pidToBaseDn.put(pid, baseDn);
- // pidToServiceRegs.put(pid, reg);
-
- if (log.isDebugEnabled()) {
- log.debug("User directory " + userDirectory.getBaseDn() + (u != null ? " [" + u.getScheme() + "]" : "")
- + " enabled." + (realm != null ? " " + realm + " realm." : ""));
- }
-
- if (isSystemRolesBaseDn(baseDn)) {
- // publishes only when system roles are available
- Dictionary<String, Object> userAdminregProps = new Hashtable<>();
- userAdminregProps.put(CmsConstants.CN, CmsConstants.DEFAULT);
- userAdminregProps.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE);
- Activator.registerService(UserAdmin.class, this, userAdminregProps);
- }
-
-// if (isSystemRolesBaseDn(baseDn))
-// systemRolesAvailable = true;
-//
-// // start publishing only when system roles are available
-// if (systemRolesAvailable) {
-// // The list of baseDns is published as properties
-// // TODO clients should rather reference USerDirectory services
-// if (userAdminReg != null)
-// userAdminReg.unregister();
-// // register self as main user admin
-// Dictionary<String, Object> userAdminregProps = currentState();
-// userAdminregProps.put(NodeConstants.CN, NodeConstants.DEFAULT);
-// userAdminregProps.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE);
-// userAdminReg = bc.registerService(UserAdmin.class, this, userAdminregProps);
-// }
- }
-
- @Override
- public void deleted(String pid) {
- // assert pidToServiceRegs.get(pid) != null;
- assert pidToBaseDn.get(pid) != null;
- // pidToServiceRegs.remove(pid).unregister();
- LdapName baseDn = pidToBaseDn.remove(pid);
- removeUserDirectory(baseDn);
- }
-
- @Override
- public String getName() {
- return "Node User Admin";
- }
-
- @Override
- protected void addAbstractSystemRoles(Authorization rawAuthorization, Set<String> sysRoles) {
- if (rawAuthorization.getName() == null) {
- sysRoles.add(CmsConstants.ROLE_ANONYMOUS);
- } else {
- sysRoles.add(CmsConstants.ROLE_USER);
- }
- }
-
- protected void postAdd(AbstractUserDirectory userDirectory) {
- // JTA
- WorkControl tm = tmTracker != null ? tmTracker.getService() : null;
- if (tm == null)
- throw new IllegalStateException("A JTA transaction manager must be available.");
- userDirectory.setTransactionControl(tm);
-// if (tmTracker.getService() instanceof BitronixTransactionManager)
-// EhCacheXAResourceProducer.registerXAResource(cacheName, userDirectory.getXaResource());
-
- Object realm = userDirectory.getProperties().get(UserAdminConf.realm.name());
- if (realm != null) {
- if (Files.exists(nodeKeyTab)) {
- String servicePrincipal = getKerberosServicePrincipal(realm.toString());
- if (servicePrincipal != null) {
- CallbackHandler callbackHandler = new CallbackHandler() {
- @Override
- public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
- for (Callback callback : callbacks)
- if (callback instanceof NameCallback)
- ((NameCallback) callback).setName(servicePrincipal);
-
- }
- };
- try {
- LoginContext nodeLc = new LoginContext(CmsAuth.LOGIN_CONTEXT_NODE, callbackHandler);
- nodeLc.login();
- acceptorCredentials = logInAsAcceptor(nodeLc.getSubject(), servicePrincipal);
- } catch (LoginException e) {
- throw new IllegalStateException("Cannot log in kernel", e);
- }
- }
- }
-
- // Register client-side SPNEGO auth scheme
- AuthPolicy.registerAuthScheme(SpnegoAuthScheme.NAME, SpnegoAuthScheme.class);
- HttpParams params = DefaultHttpParams.getDefaultParams();
- ArrayList<String> schemes = new ArrayList<>();
- schemes.add(SpnegoAuthScheme.NAME);// SPNEGO preferred
- // schemes.add(AuthPolicy.BASIC);// incompatible with Basic
- params.setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, schemes);
- params.setParameter(CredentialsProvider.PROVIDER, new HttpCredentialProvider());
- params.setParameter(HttpMethodParams.COOKIE_POLICY, KernelConstants.COOKIE_POLICY_BROWSER_COMPATIBILITY);
- // params.setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
- }
- }
-
- protected void preDestroy(AbstractUserDirectory userDirectory) {
-// if (tmTracker.getService() instanceof BitronixTransactionManager)
-// EhCacheXAResourceProducer.unregisterXAResource(cacheName, userDirectory.getXaResource());
-
- Object realm = userDirectory.getProperties().get(UserAdminConf.realm.name());
- if (realm != null) {
- if (acceptorCredentials != null) {
- try {
- acceptorCredentials.dispose();
- } catch (GSSException e) {
- // silent
- }
- acceptorCredentials = null;
- }
- }
- }
-
- private String getKerberosServicePrincipal(String realm) {
- String hostname;
- try (DnsBrowser dnsBrowser = new DnsBrowser()) {
- InetAddress localhost = InetAddress.getLocalHost();
- hostname = localhost.getHostName();
- String dnsZone = hostname.substring(hostname.indexOf('.') + 1);
- String ipfromDns = dnsBrowser.getRecord(hostname, localhost instanceof Inet6Address ? "AAAA" : "A");
- boolean consistentIp = localhost.getHostAddress().equals(ipfromDns);
- String kerberosDomain = dnsBrowser.getRecord("_kerberos." + dnsZone, "TXT");
- if (consistentIp && kerberosDomain != null && kerberosDomain.equals(realm) && Files.exists(nodeKeyTab)) {
- return KernelConstants.DEFAULT_KERBEROS_SERVICE + "/" + hostname + "@" + kerberosDomain;
- } else
- return null;
- } catch (Exception e) {
- log.warn("Exception when determining kerberos principal", e);
- return null;
- }
- }
-
- private GSSCredential logInAsAcceptor(Subject subject, String servicePrincipal) {
- // GSS
- Iterator<KerberosPrincipal> krb5It = subject.getPrincipals(KerberosPrincipal.class).iterator();
- if (!krb5It.hasNext())
- return null;
- KerberosPrincipal krb5Principal = null;
- while (krb5It.hasNext()) {
- KerberosPrincipal principal = krb5It.next();
- if (principal.getName().equals(servicePrincipal))
- krb5Principal = principal;
- }
-
- if (krb5Principal == null)
- return null;
-
- GSSManager manager = GSSManager.getInstance();
- try {
- GSSName gssName = manager.createName(krb5Principal.getName(), null);
- GSSCredential serverCredentials = Subject.doAs(subject, new PrivilegedExceptionAction<GSSCredential>() {
-
- @Override
- public GSSCredential run() throws GSSException {
- return manager.createCredential(gssName, GSSCredential.INDEFINITE_LIFETIME, KERBEROS_OID,
- GSSCredential.ACCEPT_ONLY);
-
- }
-
- });
- if (log.isDebugEnabled())
- log.debug("GSS acceptor configured for " + krb5Principal);
- return serverCredentials;
- } catch (Exception gsse) {
- throw new IllegalStateException("Cannot create acceptor credentials for " + krb5Principal, gsse);
- }
- }
-
- public GSSCredential getAcceptorCredentials() {
- return acceptorCredentials;
- }
-
- public boolean isSingleUser() {
- return singleUser;
- }
-
- public final static Oid KERBEROS_OID;
- static {
- try {
- KERBEROS_OID = new Oid("1.3.6.1.5.5.2");
- } catch (GSSException e) {
- throw new IllegalStateException("Cannot create Kerberos OID", e);
- }
- }
-
-}
+++ /dev/null
-package org.argeo.cms.internal.kernel;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.Reader;
-import java.math.BigInteger;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.PrivateKey;
-import java.security.SecureRandom;
-import java.security.Security;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-import java.util.Date;
-
-import javax.security.auth.x500.X500Principal;
-
-import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
-import org.bouncycastle.cert.X509CertificateHolder;
-import org.bouncycastle.cert.X509v3CertificateBuilder;
-import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
-import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.openssl.PEMParser;
-import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
-import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
-import org.bouncycastle.operator.ContentSigner;
-import org.bouncycastle.operator.InputDecryptorProvider;
-import org.bouncycastle.operator.OperatorCreationException;
-import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
-import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
-import org.bouncycastle.pkcs.PKCSException;
-
-/**
- * Utilities around private keys and certificate, mostly wrapping BouncyCastle
- * implementations.
- */
-class PkiUtils {
- final static String PKCS12 = "PKCS12";
-
- private final static String SECURITY_PROVIDER;
- static {
- Security.addProvider(new BouncyCastleProvider());
- SECURITY_PROVIDER = "BC";
- }
-
- public static X509Certificate generateSelfSignedCertificate(KeyStore keyStore, X500Principal x500Principal,
- int keySize, char[] keyPassword) {
- try {
- KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", SECURITY_PROVIDER);
- kpGen.initialize(keySize, new SecureRandom());
- KeyPair pair = kpGen.generateKeyPair();
- Date notBefore = new Date(System.currentTimeMillis() - 10000);
- Date notAfter = new Date(System.currentTimeMillis() + 365 * 24L * 3600 * 1000);
- BigInteger serial = BigInteger.valueOf(System.currentTimeMillis());
- X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(x500Principal, serial, notBefore,
- notAfter, x500Principal, pair.getPublic());
- ContentSigner sigGen = new JcaContentSignerBuilder("SHA256WithRSAEncryption").setProvider(SECURITY_PROVIDER)
- .build(pair.getPrivate());
- X509Certificate cert = new JcaX509CertificateConverter().setProvider(SECURITY_PROVIDER)
- .getCertificate(certGen.build(sigGen));
- cert.checkValidity(new Date());
- cert.verify(cert.getPublicKey());
-
- keyStore.setKeyEntry(x500Principal.getName(), pair.getPrivate(), keyPassword, new Certificate[] { cert });
- return cert;
- } catch (GeneralSecurityException | OperatorCreationException e) {
- throw new RuntimeException("Cannot generate self-signed certificate", e);
- }
- }
-
- public static KeyStore getKeyStore(Path keyStoreFile, char[] keyStorePassword, String keyStoreType) {
- try {
- KeyStore store = KeyStore.getInstance(keyStoreType, SECURITY_PROVIDER);
- if (Files.exists(keyStoreFile)) {
- try (InputStream fis = Files.newInputStream(keyStoreFile)) {
- store.load(fis, keyStorePassword);
- }
- } else {
- store.load(null);
- }
- return store;
- } catch (GeneralSecurityException | IOException e) {
- throw new RuntimeException("Cannot load keystore " + keyStoreFile, e);
- }
- }
-
- public static void saveKeyStore(Path keyStoreFile, char[] keyStorePassword, KeyStore keyStore) {
- try {
- try (OutputStream fis = Files.newOutputStream(keyStoreFile)) {
- keyStore.store(fis, keyStorePassword);
- }
- } catch (GeneralSecurityException | IOException e) {
- throw new RuntimeException("Cannot save keystore " + keyStoreFile, e);
- }
- }
-
-// public static byte[] pemToPKCS12(final String keyFile, final String cerFile, final String password)
-// throws Exception {
-// // Get the private key
-// FileReader reader = new FileReader(keyFile);
-//
-// PEMReader pem = new PemReader(reader, new PasswordFinder() {
-// @Override
-// public char[] getPassword() {
-// return password.toCharArray();
-// }
-// });
-//
-// PrivateKey key = ((KeyPair) pem.readObject()).getPrivate();
-//
-// pem.close();
-// reader.close();
-//
-// // Get the certificate
-// reader = new FileReader(cerFile);
-// pem = new PEMReader(reader);
-//
-// X509Certificate cert = (X509Certificate) pem.readObject();
-//
-// pem.close();
-// reader.close();
-//
-// // Put them into a PKCS12 keystore and write it to a byte[]
-// ByteArrayOutputStream bos = new ByteArrayOutputStream();
-// KeyStore ks = KeyStore.getInstance("PKCS12");
-// ks.load(null);
-// ks.setKeyEntry("alias", (Key) key, password.toCharArray(), new java.security.cert.Certificate[] { cert });
-// ks.store(bos, password.toCharArray());
-// bos.close();
-// return bos.toByteArray();
-// }
-
- public static void loadPem(KeyStore keyStore, Reader key, char[] keyPassword, Reader cert) {
- PrivateKey privateKey = loadPemPrivateKey(key, keyPassword);
- X509Certificate certificate = loadPemCertificate(cert);
- try {
- keyStore.setKeyEntry(certificate.getSubjectX500Principal().getName(), privateKey, keyPassword,
- new java.security.cert.Certificate[] { certificate });
- } catch (KeyStoreException e) {
- throw new RuntimeException("Cannot store PEM certificate", e);
- }
- }
-
- public static PrivateKey loadPemPrivateKey(Reader reader, char[] keyPassword) {
- try (PEMParser pemParser = new PEMParser(reader)) {
- JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
- Object object = pemParser.readObject();
- PrivateKeyInfo privateKeyInfo;
- if (object instanceof PKCS8EncryptedPrivateKeyInfo) {
- if (keyPassword == null)
- throw new IllegalArgumentException("A key password is required");
- InputDecryptorProvider decProv = new JceOpenSSLPKCS8DecryptorProviderBuilder().build(keyPassword);
- privateKeyInfo = ((PKCS8EncryptedPrivateKeyInfo) object).decryptPrivateKeyInfo(decProv);
- } else if (object instanceof PrivateKeyInfo) {
- privateKeyInfo = (PrivateKeyInfo) object;
- } else {
- throw new IllegalArgumentException("Unsupported format for private key");
- }
- return converter.getPrivateKey(privateKeyInfo);
- } catch (IOException | OperatorCreationException | PKCSException e) {
- throw new RuntimeException("Cannot read private key", e);
- }
- }
-
- public static X509Certificate loadPemCertificate(Reader reader) {
- try (PEMParser pemParser = new PEMParser(reader)) {
- X509CertificateHolder certHolder = (X509CertificateHolder) pemParser.readObject();
- X509Certificate cert = new JcaX509CertificateConverter().setProvider(SECURITY_PROVIDER)
- .getCertificate(certHolder);
- return cert;
- } catch (IOException | CertificateException e) {
- throw new RuntimeException("Cannot read private key", e);
- }
- }
-
- public static void main(String[] args) throws Exception {
- final String ALGORITHM = "RSA";
- final String provider = "BC";
- SecureRandom secureRandom = new SecureRandom();
- long begin = System.currentTimeMillis();
- for (int i = 512; i < 1024; i = i + 2) {
- try {
- KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM, provider);
- keyGen.initialize(i, secureRandom);
- keyGen.generateKeyPair();
- } catch (Exception e) {
- System.err.println(i + " : " + e.getMessage());
- }
- }
- System.out.println((System.currentTimeMillis() - begin) + " ms");
-
- // // String text = "a";
- // String text =
- // "testtesttesttesttesttesttesttesttesttesttesttesttesttesttest";
- // try {
- // System.out.println(text);
- // PrivateKey privateKey;
- // PublicKey publicKey;
- // char[] password = "changeit".toCharArray();
- // String alias = "CN=test";
- // KeyStore keyStore = KeyStore.getInstance("pkcs12");
- // File p12file = new File("test.p12");
- // p12file.delete();
- // if (!p12file.exists()) {
- // keyStore.load(null);
- // generateSelfSignedCertificate(keyStore, new X500Principal(alias),
- // 513, password);
- // try (OutputStream out = new FileOutputStream(p12file)) {
- // keyStore.store(out, password);
- // }
- // }
- // try (InputStream in = new FileInputStream(p12file)) {
- // keyStore.load(in, password);
- // privateKey = (PrivateKey) keyStore.getKey(alias, password);
- // publicKey = keyStore.getCertificateChain(alias)[0].getPublicKey();
- // }
- // // KeyPair key;
- // // final KeyPairGenerator keyGen =
- // // KeyPairGenerator.getInstance(ALGORITHM);
- // // keyGen.initialize(4096, new SecureRandom());
- // // long begin = System.currentTimeMillis();
- // // key = keyGen.generateKeyPair();
- // // System.out.println((System.currentTimeMillis() - begin) + " ms");
- // // keyStore.load(null);
- // // keyStore.setKeyEntry("test", key.getPrivate(), password, null);
- // // try(OutputStream out=new FileOutputStream(p12file)) {
- // // keyStore.store(out, password);
- // // }
- // // privateKey = key.getPrivate();
- // // publicKey = key.getPublic();
- //
- // Cipher encrypt = Cipher.getInstance(ALGORITHM);
- // encrypt.init(Cipher.ENCRYPT_MODE, publicKey);
- // byte[] encrypted = encrypt.doFinal(text.getBytes());
- // String encryptedBase64 =
- // Base64.getEncoder().encodeToString(encrypted);
- // System.out.println(encryptedBase64);
- // byte[] encryptedFromBase64 =
- // Base64.getDecoder().decode(encryptedBase64);
- //
- // Cipher decrypt = Cipher.getInstance(ALGORITHM);
- // decrypt.init(Cipher.DECRYPT_MODE, privateKey);
- // byte[] decrypted = decrypt.doFinal(encryptedFromBase64);
- // System.out.println(new String(decrypted));
- // } catch (Exception e) {
- // e.printStackTrace();
- // }
-
- }
-
-}
+++ /dev/null
-package org.argeo.cms.internal.kernel;
-
-import java.io.FilePermission;
-import java.lang.reflect.ReflectPermission;
-import java.net.SocketPermission;
-import java.security.AllPermission;
-import java.util.PropertyPermission;
-
-import javax.security.auth.AuthPermission;
-
-import org.osgi.framework.AdminPermission;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.ServicePermission;
-import org.osgi.service.cm.ConfigurationPermission;
-import org.osgi.service.condpermadmin.BundleLocationCondition;
-import org.osgi.service.condpermadmin.ConditionInfo;
-import org.osgi.service.condpermadmin.ConditionalPermissionAdmin;
-import org.osgi.service.condpermadmin.ConditionalPermissionInfo;
-import org.osgi.service.condpermadmin.ConditionalPermissionUpdate;
-import org.osgi.service.permissionadmin.PermissionAdmin;
-import org.osgi.service.permissionadmin.PermissionInfo;
-
-/** Security profile based on OSGi {@link PermissionAdmin}. */
-public interface SecurityProfile {
- BundleContext bc = FrameworkUtil.getBundle(SecurityProfile.class).getBundleContext();
-
- default void applySystemPermissions(ConditionalPermissionAdmin permissionAdmin) {
- ConditionalPermissionUpdate update = permissionAdmin.newConditionalPermissionUpdate();
- // Self
-// String nodeAPiBundleLocation = locate(NodeUtils.class);
-// update.getConditionalPermissionInfos()
-// .add(permissionAdmin.newConditionalPermissionInfo(null,
-// new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
-// new String[] { nodeAPiBundleLocation }) },
-// new PermissionInfo[] { new PermissionInfo(AllPermission.class.getName(), null, null) },
-// ConditionalPermissionInfo.ALLOW));
- String cmsBundleLocation = locate(SecurityProfile.class);
- update.getConditionalPermissionInfos()
- .add(permissionAdmin.newConditionalPermissionInfo(null,
- new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
- new String[] { cmsBundleLocation }) },
- new PermissionInfo[] { new PermissionInfo(AllPermission.class.getName(), null, null) },
- ConditionalPermissionInfo.ALLOW));
- String frameworkBundleLocation = bc.getBundle(0).getLocation();
- update.getConditionalPermissionInfos()
- .add(permissionAdmin.newConditionalPermissionInfo(null,
- new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
- new String[] { frameworkBundleLocation }) },
- new PermissionInfo[] { new PermissionInfo(AllPermission.class.getName(), null, null) },
- ConditionalPermissionInfo.ALLOW));
- // All
- // FIXME understand why Jetty and Jackrabbit require that
- update.getConditionalPermissionInfos()
- .add(permissionAdmin.newConditionalPermissionInfo(null, null, new PermissionInfo[] {
- new PermissionInfo(SocketPermission.class.getName(), "localhost:7070", "listen,resolve"),
- new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>", "read,write,delete"),
- new PermissionInfo(PropertyPermission.class.getName(), "DEBUG", "read"),
- new PermissionInfo(PropertyPermission.class.getName(), "STOP.*", "read"),
- new PermissionInfo(PropertyPermission.class.getName(), "org.apache.jackrabbit.*", "read"),
- new PermissionInfo(RuntimePermission.class.getName(), "*", "*"), },
- ConditionalPermissionInfo.ALLOW));
-
- // Eclipse
- // update.getConditionalPermissionInfos()
- // .add(permissionAdmin.newConditionalPermissionInfo(null,
- // new ConditionInfo[] { new
- // ConditionInfo(BundleLocationCondition.class.getName(),
- // new String[] { "*/org.eclipse.*" }) },
- // new PermissionInfo[] { new
- // PermissionInfo(RuntimePermission.class.getName(), "*", "*"),
- // new PermissionInfo(AdminPermission.class.getName(), "*", "*"),
- // new PermissionInfo(ServicePermission.class.getName(), "*", "get"),
- // new PermissionInfo(ServicePermission.class.getName(), "*",
- // "register"),
- // new PermissionInfo(TopicPermission.class.getName(), "*", "publish"),
- // new PermissionInfo(TopicPermission.class.getName(), "*",
- // "subscribe"),
- // new PermissionInfo(PropertyPermission.class.getName(), "osgi.*",
- // "read"),
- // new PermissionInfo(PropertyPermission.class.getName(), "eclipse.*",
- // "read"),
- // new PermissionInfo(PropertyPermission.class.getName(),
- // "org.eclipse.*", "read"),
- // new PermissionInfo(PropertyPermission.class.getName(), "equinox.*",
- // "read"),
- // new PermissionInfo(PropertyPermission.class.getName(), "xml.*",
- // "read"),
- // new PermissionInfo("org.eclipse.equinox.log.LogPermission", "*",
- // "log"), },
- // ConditionalPermissionInfo.ALLOW));
- update.getConditionalPermissionInfos()
- .add(permissionAdmin.newConditionalPermissionInfo(null,
- new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
- new String[] { "*/org.eclipse.*" }) },
- new PermissionInfo[] { new PermissionInfo(AllPermission.class.getName(), null, null), },
- ConditionalPermissionInfo.ALLOW));
- update.getConditionalPermissionInfos()
- .add(permissionAdmin.newConditionalPermissionInfo(null,
- new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
- new String[] { "*/org.apache.felix.*" }) },
- new PermissionInfo[] { new PermissionInfo(AllPermission.class.getName(), null, null), },
- ConditionalPermissionInfo.ALLOW));
-
- // Configuration admin
-// update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
-// new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
-// new String[] { locate(configurationAdmin.getService().getClass()) }) },
-// new PermissionInfo[] { new PermissionInfo(ConfigurationPermission.class.getName(), "*", "configure"),
-// new PermissionInfo(AdminPermission.class.getName(), "*", "*"),
-// new PermissionInfo(PropertyPermission.class.getName(), "osgi.*", "read"), },
-// ConditionalPermissionInfo.ALLOW));
-
- // Bitronix
-// update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
-// new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
-// new String[] { locate(BitronixTransactionManager.class) }) },
-// new PermissionInfo[] { new PermissionInfo(PropertyPermission.class.getName(), "bitronix.tm.*", "read"),
-// new PermissionInfo(RuntimePermission.class.getName(), "getClassLoader", null),
-// new PermissionInfo(MBeanServerPermission.class.getName(), "createMBeanServer", null),
-// new PermissionInfo(MBeanPermission.class.getName(), "bitronix.tm.*", "registerMBean"),
-// new PermissionInfo(MBeanTrustPermission.class.getName(), "register", null) },
-// ConditionalPermissionInfo.ALLOW));
-
- // DS
- Bundle dsBundle = findBundle("org.eclipse.equinox.ds");
- update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
- new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
- new String[] { dsBundle.getLocation() }) },
- new PermissionInfo[] { new PermissionInfo(ConfigurationPermission.class.getName(), "*", "configure"),
- new PermissionInfo(AdminPermission.class.getName(), "*", "*"),
- new PermissionInfo(ServicePermission.class.getName(), "*", "get"),
- new PermissionInfo(ServicePermission.class.getName(), "*", "register"),
- new PermissionInfo(PropertyPermission.class.getName(), "osgi.*", "read"),
- new PermissionInfo(PropertyPermission.class.getName(), "xml.*", "read"),
- new PermissionInfo(PropertyPermission.class.getName(), "equinox.*", "read"),
- new PermissionInfo(RuntimePermission.class.getName(), "accessDeclaredMembers", null),
- new PermissionInfo(RuntimePermission.class.getName(), "getClassLoader", null),
- new PermissionInfo(ReflectPermission.class.getName(), "suppressAccessChecks", null), },
- ConditionalPermissionInfo.ALLOW));
-
- // Jetty
- // Bundle jettyUtilBundle = findBundle("org.eclipse.equinox.http.jetty");
- update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
- new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
- new String[] { "*/org.eclipse.jetty.*" }) },
- new PermissionInfo[] {
- new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>", "read,write,delete"), },
- ConditionalPermissionInfo.ALLOW));
- Bundle servletBundle = findBundle("javax.servlet");
- update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
- new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
- new String[] { servletBundle.getLocation() }) },
- new PermissionInfo[] { new PermissionInfo(PropertyPermission.class.getName(),
- "org.glassfish.web.rfc2109_cookie_names_enforced", "read") },
- ConditionalPermissionInfo.ALLOW));
-
- // required to be able to get the BundleContext in the customizer
- Bundle jettyCustomizerBundle = findBundle("org.argeo.ext.equinox.jetty");
- update.getConditionalPermissionInfos()
- .add(permissionAdmin.newConditionalPermissionInfo(null,
- new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
- new String[] { jettyCustomizerBundle.getLocation() }) },
- new PermissionInfo[] { new PermissionInfo(AdminPermission.class.getName(), "*", "*"), },
- ConditionalPermissionInfo.ALLOW));
-
- // Blueprint
-// Bundle blueprintBundle = findBundle("org.eclipse.gemini.blueprint.core");
-// update.getConditionalPermissionInfos()
-// .add(permissionAdmin.newConditionalPermissionInfo(null,
-// new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
-// new String[] { blueprintBundle.getLocation() }) },
-// new PermissionInfo[] { new PermissionInfo(RuntimePermission.class.getName(), "*", null),
-// new PermissionInfo(AdminPermission.class.getName(), "*", "*"), },
-// ConditionalPermissionInfo.ALLOW));
-// Bundle blueprintExtenderBundle = findBundle("org.eclipse.gemini.blueprint.extender");
-// update.getConditionalPermissionInfos()
-// .add(permissionAdmin
-// .newConditionalPermissionInfo(null,
-// new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
-// new String[] { blueprintExtenderBundle.getLocation() }) },
-// new PermissionInfo[] { new PermissionInfo(RuntimePermission.class.getName(), "*", null),
-// new PermissionInfo(PropertyPermission.class.getName(), "org.eclipse.gemini.*",
-// "read"),
-// new PermissionInfo(AdminPermission.class.getName(), "*", "*"),
-// new PermissionInfo(ServicePermission.class.getName(), "*", "register"), },
-// ConditionalPermissionInfo.ALLOW));
-// Bundle springCoreBundle = findBundle("org.springframework.core");
-// update.getConditionalPermissionInfos()
-// .add(permissionAdmin.newConditionalPermissionInfo(null,
-// new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
-// new String[] { springCoreBundle.getLocation() }) },
-// new PermissionInfo[] { new PermissionInfo(RuntimePermission.class.getName(), "*", null),
-// new PermissionInfo(AdminPermission.class.getName(), "*", "*"), },
-// ConditionalPermissionInfo.ALLOW));
-// Bundle blueprintIoBundle = findBundle("org.eclipse.gemini.blueprint.io");
-// update.getConditionalPermissionInfos()
-// .add(permissionAdmin.newConditionalPermissionInfo(null,
-// new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
-// new String[] { blueprintIoBundle.getLocation() }) },
-// new PermissionInfo[] { new PermissionInfo(RuntimePermission.class.getName(), "*", null),
-// new PermissionInfo(AdminPermission.class.getName(), "*", "*"), },
-// ConditionalPermissionInfo.ALLOW));
-
- // Equinox
- Bundle registryBundle = findBundle("org.eclipse.equinox.registry");
- update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
- new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
- new String[] { registryBundle.getLocation() }) },
- new PermissionInfo[] { new PermissionInfo(PropertyPermission.class.getName(), "eclipse.*", "read"),
- new PermissionInfo(PropertyPermission.class.getName(), "osgi.*", "read"),
- new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>", "read,write,delete"), },
- ConditionalPermissionInfo.ALLOW));
-
- Bundle equinoxUtilBundle = findBundle("org.eclipse.equinox.util");
- update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
- new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
- new String[] { equinoxUtilBundle.getLocation() }) },
- new PermissionInfo[] { new PermissionInfo(PropertyPermission.class.getName(), "equinox.*", "read"),
- new PermissionInfo(ServicePermission.class.getName(), "*", "get"),
- new PermissionInfo(ServicePermission.class.getName(), "*", "register"), },
- ConditionalPermissionInfo.ALLOW));
- Bundle equinoxCommonBundle = findBundle("org.eclipse.equinox.common");
- update.getConditionalPermissionInfos()
- .add(permissionAdmin.newConditionalPermissionInfo(null,
- new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
- new String[] { equinoxCommonBundle.getLocation() }) },
- new PermissionInfo[] { new PermissionInfo(AdminPermission.class.getName(), "*", "*"), },
- ConditionalPermissionInfo.ALLOW));
-
- Bundle consoleBundle = findBundle("org.eclipse.equinox.console");
- update.getConditionalPermissionInfos()
- .add(permissionAdmin.newConditionalPermissionInfo(null,
- new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
- new String[] { consoleBundle.getLocation() }) },
- new PermissionInfo[] { new PermissionInfo(ServicePermission.class.getName(), "*", "register"),
- new PermissionInfo(AdminPermission.class.getName(), "*", "listener") },
- ConditionalPermissionInfo.ALLOW));
- Bundle preferencesBundle = findBundle("org.eclipse.equinox.preferences");
- update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
- new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
- new String[] { preferencesBundle.getLocation() }) },
- new PermissionInfo[] {
- new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>", "read,write,delete"), },
- ConditionalPermissionInfo.ALLOW));
- Bundle appBundle = findBundle("org.eclipse.equinox.app");
- update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
- new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
- new String[] { appBundle.getLocation() }) },
- new PermissionInfo[] {
- new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>", "read,write,delete"), },
- ConditionalPermissionInfo.ALLOW));
-
- // Jackrabbit
- Bundle jackrabbitCoreBundle = findBundle("org.apache.jackrabbit.core");
- update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
- new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
- new String[] { jackrabbitCoreBundle.getLocation() }) },
- new PermissionInfo[] {
- new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>", "read,write,delete"),
- new PermissionInfo(PropertyPermission.class.getName(), "*", "read,write"),
- new PermissionInfo(AuthPermission.class.getName(), "getSubject", null),
- new PermissionInfo(AuthPermission.class.getName(), "getLoginConfiguration", null),
- new PermissionInfo(AuthPermission.class.getName(), "createLoginContext.Jackrabbit", null), },
- ConditionalPermissionInfo.ALLOW));
- Bundle jackrabbitDataBundle = findBundle("org.apache.jackrabbit.data");
- update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
- new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
- new String[] { jackrabbitDataBundle.getLocation() }) },
- new PermissionInfo[] { new PermissionInfo(PropertyPermission.class.getName(), "*", "read,write") },
- ConditionalPermissionInfo.ALLOW));
- Bundle jackrabbitCommonBundle = findBundle("org.apache.jackrabbit.jcr.commons");
- update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
- new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
- new String[] { jackrabbitCommonBundle.getLocation() }) },
- new PermissionInfo[] { new PermissionInfo(AuthPermission.class.getName(), "getSubject", null),
- new PermissionInfo(AuthPermission.class.getName(), "createLoginContext.Jackrabbit", null), },
- ConditionalPermissionInfo.ALLOW));
-
- Bundle jackrabbitExtBundle = findBundle("org.argeo.ext.jackrabbit");
- update.getConditionalPermissionInfos()
- .add(permissionAdmin.newConditionalPermissionInfo(null,
- new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
- new String[] { jackrabbitExtBundle.getLocation() }) },
- new PermissionInfo[] { new PermissionInfo(AuthPermission.class.getName(), "*", "*"), },
- ConditionalPermissionInfo.ALLOW));
-
- // Tika
- Bundle tikaCoreBundle = findBundle("org.apache.tika.core");
- update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
- new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
- new String[] { tikaCoreBundle.getLocation() }) },
- new PermissionInfo[] { new PermissionInfo(PropertyPermission.class.getName(), "*", "read,write"),
- new PermissionInfo(AdminPermission.class.getName(), "*", "*") },
- ConditionalPermissionInfo.ALLOW));
- Bundle luceneBundle = findBundle("org.apache.lucene");
- update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
- new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
- new String[] { luceneBundle.getLocation() }) },
- new PermissionInfo[] {
- new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>", "read,write,delete"),
- new PermissionInfo(PropertyPermission.class.getName(), "*", "read"),
- new PermissionInfo(AdminPermission.class.getName(), "*", "*") },
- ConditionalPermissionInfo.ALLOW));
-
- // COMMIT
- update.commit();
- }
-
- /** @return bundle location */
- default String locate(Class<?> clzz) {
- return FrameworkUtil.getBundle(clzz).getLocation();
- }
-
- /** Can be null */
- default Bundle findBundle(String symbolicName) {
- for (Bundle b : bc.getBundles())
- if (b.getSymbolicName().equals(symbolicName))
- return b;
- return null;
- }
-
-}
+++ /dev/null
-dn: dc=example,dc=com
-objectClass: domain
-objectClass: extensibleObject
-objectClass: top
-dc: example
-
-dn: ou=Groups,dc=example,dc=com
-objectClass: organizationalUnit
-objectClass: top
-ou: Groups
-
-dn: ou=People,dc=example,dc=com
-objectClass: organizationalUnit
-objectClass: top
-ou: People
-
-dn: uid=demo,ou=People,dc=example,dc=com
-objectClass: inetOrgPerson
-objectClass: organizationalPerson
-objectClass: person
-objectClass: top
-cn: Demo User
-description: Demo user
-givenName: Demo
-mail: demo@localhost
-sn: User
-uid: demo
-userPassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9
-
-dn: uid=root,ou=People,dc=example,dc=com
-objectClass: inetOrgPerson
-objectClass: person
-objectClass: organizationalPerson
-objectClass: top
-cn: Super User
-description: Superuser
-givenName: Super
-mail: root@localhost
-sn: User
-uid: root
-userPassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9
+++ /dev/null
-dn: cn=admin,ou=roles,ou=node
-objectClass: groupOfNames
-objectClass: top
-cn: admin
-member: uid=root,ou=People,dc=example,dc=com
-
-dn: cn=userAdmin,ou=roles,ou=node
-objectClass: groupOfNames
-objectClass: top
-member: cn=admin,ou=roles,ou=node
-cn: userAdmin
-
+++ /dev/null
-USER {
- org.argeo.cms.auth.RemoteSessionLoginModule sufficient;
- org.argeo.cms.auth.SpnegoLoginModule optional;
- com.sun.security.auth.module.Krb5LoginModule optional tryFirstPass=true;
- org.argeo.cms.auth.UserAdminLoginModule sufficient;
-};
-
-ANONYMOUS {
- org.argeo.cms.auth.RemoteSessionLoginModule sufficient;
- org.argeo.cms.auth.AnonymousLoginModule sufficient;
-};
-
-DATA_ADMIN {
- org.argeo.cms.auth.DataAdminLoginModule requisite;
-};
-
-NODE {
- com.sun.security.auth.module.Krb5LoginModule optional
- keyTab="${osgi.instance.area}node/krb5.keytab"
- useKeyTab=true
- storeKey=true;
- org.argeo.cms.auth.DataAdminLoginModule requisite;
-};
-
-KEYRING {
- org.argeo.cms.auth.KeyringLoginModule required;
-};
-
-SINGLE_USER {
- com.sun.security.auth.module.Krb5LoginModule optional
- principal="${user.name}"
- storeKey=true
- useTicketCache=true
- debug=true;
- org.argeo.cms.auth.SingleUserLoginModule requisite;
-};
-
-Jackrabbit {
- org.argeo.security.jackrabbit.SystemJackrabbitLoginModule requisite;
-};
+++ /dev/null
-USER {
- org.argeo.cms.auth.RemoteSessionLoginModule sufficient;
- org.argeo.cms.auth.IdentLoginModule optional;
- org.argeo.cms.auth.UserAdminLoginModule requisite;
-};
-
-ANONYMOUS {
- org.argeo.cms.auth.RemoteSessionLoginModule sufficient;
- org.argeo.cms.auth.AnonymousLoginModule requisite;
-};
-
-DATA_ADMIN {
- org.argeo.cms.auth.DataAdminLoginModule requisite;
-};
-
-NODE {
- org.argeo.cms.auth.DataAdminLoginModule requisite;
-};
-
-KEYRING {
- org.argeo.cms.auth.KeyringLoginModule required;
-};
-
-SINGLE_USER {
- org.argeo.cms.auth.SingleUserLoginModule requisite;
-};
-
-Jackrabbit {
- org.argeo.security.jackrabbit.SystemJackrabbitLoginModule requisite;
-};
+++ /dev/null
-dn: ou=node
-objectClass: organizationalUnit
-objectClass: top
-ou: node
-
-dn: ou=roles,ou=node
-objectClass: organizationalUnit
-objectClass: top
-ou: roles
+++ /dev/null
-dn: ou=tokens,ou=node
-objectClass: organizationalUnit
-objectClass: top
-ou: tokens
--- /dev/null
+package org.argeo.cms.internal.osgi;
+
+import java.security.AllPermission;
+import java.util.Dictionary;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.cms.ArgeoLogger;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.condpermadmin.BundleLocationCondition;
+import org.osgi.service.condpermadmin.ConditionInfo;
+import org.osgi.service.condpermadmin.ConditionalPermissionAdmin;
+import org.osgi.service.condpermadmin.ConditionalPermissionInfo;
+import org.osgi.service.condpermadmin.ConditionalPermissionUpdate;
+import org.osgi.service.http.HttpService;
+import org.osgi.service.log.LogReaderService;
+import org.osgi.service.permissionadmin.PermissionInfo;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * Activates the kernel. Gives access to kernel information for the rest of the
+ * bundle (and only it)
+ */
+public class CmsActivator implements BundleActivator {
+ private final static CmsLog log = CmsLog.getLog(CmsActivator.class);
+
+// private static Activator instance;
+
+ // TODO make it configurable
+ private boolean hardened = false;
+
+ private static BundleContext bundleContext;
+
+ private LogReaderService logReaderService;
+
+ private NodeLogger logger;
+// private CmsStateImpl nodeState;
+// private CmsDeploymentImpl nodeDeployment;
+// private CmsContextImpl nodeInstance;
+
+// private ServiceTracker<UserAdmin, NodeUserAdmin> userAdminSt;
+
+// static {
+// Bundle bundle = FrameworkUtil.getBundle(Activator.class);
+// if (bundle != null) {
+// bundleContext = bundle.getBundleContext();
+// }
+// }
+
+ void init() {
+// Runtime.getRuntime().addShutdownHook(new CmsShutdown());
+// instance = this;
+// this.bc = bundleContext;
+ if (bundleContext != null)
+ this.logReaderService = getService(LogReaderService.class);
+// this.internalExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
+//
+// try {
+// initSecurity();
+//// initArgeoLogger();
+// initNode();
+//
+// if (log.isTraceEnabled())
+// log.trace("Kernel bundle started");
+// } catch (Throwable e) {
+// log.error("## FATAL: CMS activator failed", e);
+// }
+ }
+
+ void destroy() {
+ try {
+// if (nodeInstance != null)
+// nodeInstance.shutdown();
+// if (nodeDeployment != null)
+// nodeDeployment.shutdown();
+// if (nodeState != null)
+// nodeState.shutdown();
+//
+// if (userAdminSt != null)
+// userAdminSt.close();
+
+// internalExecutorService.shutdown();
+// instance = null;
+ bundleContext = null;
+ this.logReaderService = null;
+ // this.configurationAdmin = null;
+ } catch (Exception e) {
+ log.error("CMS activator shutdown failed", e);
+ }
+ }
+
+ private void initSecurity() {
+ // code-level permissions
+ String osgiSecurity = bundleContext.getProperty(Constants.FRAMEWORK_SECURITY);
+ if (osgiSecurity != null && Constants.FRAMEWORK_SECURITY_OSGI.equals(osgiSecurity)) {
+ // TODO rather use a tracker?
+ ConditionalPermissionAdmin permissionAdmin = bundleContext
+ .getService(bundleContext.getServiceReference(ConditionalPermissionAdmin.class));
+ if (!hardened) {
+ // All permissions to all bundles
+ ConditionalPermissionUpdate update = permissionAdmin.newConditionalPermissionUpdate();
+ update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
+ new ConditionInfo[] {
+ new ConditionInfo(BundleLocationCondition.class.getName(), new String[] { "*" }) },
+ new PermissionInfo[] { new PermissionInfo(AllPermission.class.getName(), null, null) },
+ ConditionalPermissionInfo.ALLOW));
+ // TODO data admin permission
+// PermissionInfo dataAdminPerm = new PermissionInfo(AuthPermission.class.getName(),
+// "createLoginContext." + NodeConstants.LOGIN_CONTEXT_DATA_ADMIN, null);
+// update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
+// new ConditionInfo[] {
+// new ConditionInfo(BundleLocationCondition.class.getName(), new String[] { "*" }) },
+// new PermissionInfo[] { dataAdminPerm }, ConditionalPermissionInfo.DENY));
+// update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
+// new ConditionInfo[] {
+// new ConditionInfo(BundleSignerCondition.class.getName(), new String[] { "CN=\"Eclipse.org Foundation, Inc.\", OU=IT, O=\"Eclipse.org Foundation, Inc.\", L=Nepean, ST=Ontario, C=CA" }) },
+// new PermissionInfo[] { dataAdminPerm }, ConditionalPermissionInfo.ALLOW));
+ update.commit();
+ } else {
+ SecurityProfile securityProfile = new SecurityProfile() {
+ };
+ securityProfile.applySystemPermissions(permissionAdmin);
+ }
+ }
+
+ }
+
+ private void initArgeoLogger() {
+ logger = new NodeLogger(logReaderService);
+ if (bundleContext != null)
+ bundleContext.registerService(ArgeoLogger.class, logger, null);
+ }
+
+// private void initNode() throws IOException {
+// // Node state
+// nodeState = new CmsStateImpl();
+// registerService(CmsState.class, nodeState, null);
+//
+// // Node deployment
+// nodeDeployment = new CmsDeploymentImpl();
+//// registerService(NodeDeployment.class, nodeDeployment, null);
+//
+// // Node instance
+// nodeInstance = new CmsContextImpl();
+// registerService(CmsContext.class, nodeInstance, null);
+// }
+
+ public static <T> void registerService(Class<T> clss, T service, Dictionary<String, ?> properties) {
+ if (bundleContext != null) {
+ bundleContext.registerService(clss, service, properties);
+ }
+
+ }
+
+ public static <T> T getService(Class<T> clss) {
+ if (bundleContext != null) {
+ return bundleContext.getService(bundleContext.getServiceReference(clss));
+ } else {
+ return null;
+ }
+ }
+
+ /*
+ * OSGi
+ */
+
+ @Override
+ public void start(BundleContext bc) throws Exception {
+ bundleContext = bc;
+// if (!bc.getBundle().equals(bundleContext.getBundle()))
+// throw new IllegalStateException(
+// "Bundle " + bc.getBundle() + " is not consistent with " + bundleContext.getBundle());
+ init();
+// userAdminSt = new ServiceTracker<>(bundleContext, UserAdmin.class, null);
+// userAdminSt.open();
+
+ ServiceTracker<?, ?> httpSt = new ServiceTracker<HttpService, HttpService>(bc, HttpService.class, null) {
+
+ @Override
+ public HttpService addingService(ServiceReference<HttpService> sr) {
+ Object httpPort = sr.getProperty("http.port");
+ Object httpsPort = sr.getProperty("https.port");
+ log.info(httpPortsMsg(httpPort, httpsPort));
+ close();
+ return super.addingService(sr);
+ }
+ };
+ httpSt.open();
+ }
+
+ private String httpPortsMsg(Object httpPort, Object httpsPort) {
+ return (httpPort != null ? "HTTP " + httpPort + " " : " ") + (httpsPort != null ? "HTTPS " + httpsPort : "");
+ }
+
+ @Override
+ public void stop(BundleContext bc) throws Exception {
+// if (!bc.getBundle().equals(bundleContext.getBundle()))
+// throw new IllegalStateException(
+// "Bundle " + bc.getBundle() + " is not consistent with " + bundleContext.getBundle());
+ destroy();
+ bundleContext = null;
+ }
+
+// private <T> T getService(Class<T> clazz) {
+// ServiceReference<T> sr = bundleContext.getServiceReference(clazz);
+// if (sr == null)
+// throw new IllegalStateException("No service available for " + clazz);
+// return bundleContext.getService(sr);
+// }
+
+// public static GSSCredential getAcceptorCredentials() {
+// return getNodeUserAdmin().getAcceptorCredentials();
+// }
+//
+// @Deprecated
+// public static boolean isSingleUser() {
+// return getNodeUserAdmin().isSingleUser();
+// }
+//
+// public static UserAdmin getUserAdmin() {
+// return (UserAdmin) getNodeUserAdmin();
+// }
+//
+// public static String getHttpProxySslHeader() {
+// return KernelUtils.getFrameworkProp(CmsConstants.HTTP_PROXY_SSL_DN);
+// }
+//
+// private static NodeUserAdmin getNodeUserAdmin() {
+// NodeUserAdmin res;
+// try {
+// res = instance.userAdminSt.waitForService(60000);
+// } catch (InterruptedException e) {
+// throw new IllegalStateException("Cannot retrieve Node user admin", e);
+// }
+// if (res == null)
+// throw new IllegalStateException("No Node user admin found");
+//
+// return res;
+// // ServiceReference<UserAdmin> sr =
+// // instance.bc.getServiceReference(UserAdmin.class);
+// // NodeUserAdmin userAdmin = (NodeUserAdmin) instance.bc.getService(sr);
+// // return userAdmin;
+//
+// }
+
+// public static ExecutorService getInternalExecutorService() {
+// return instance.internalExecutorService;
+// }
+
+ // static CmsSecurity getCmsSecurity() {
+ // return instance.nodeSecurity;
+ // }
+
+// public String[] getLocales() {
+// // TODO optimize?
+// List<Locale> locales = CmsStateImpl.getNodeState().getLocales();
+// String[] res = new String[locales.size()];
+// for (int i = 0; i < locales.size(); i++)
+// res[i] = locales.get(i).toString();
+// return res;
+// }
+
+ public static BundleContext getBundleContext() {
+ return bundleContext;
+ }
+
+}
--- /dev/null
+package org.argeo.cms.internal.osgi;
+
+import org.argeo.api.cms.CmsLog;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.launch.Framework;
+
+/** Shutdowns the OSGi framework */
+public class CmsShutdown extends Thread {
+ public final int EXIT_OK = 0;
+ public final int EXIT_ERROR = 1;
+ public final int EXIT_TIMEOUT = 2;
+ public final int EXIT_UNKNOWN = 3;
+
+ private final CmsLog log = CmsLog.getLog(CmsShutdown.class);
+ // private final BundleContext bc =
+ // FrameworkUtil.getBundle(CmsShutdown.class).getBundleContext();
+ private final Framework framework;
+
+ /** Shutdown timeout in ms */
+ private long timeout = 10 * 60 * 1000;
+
+ public CmsShutdown() {
+ super("CMS Shutdown Hook");
+ framework = FrameworkUtil.getBundle(CmsShutdown.class) != null
+ ? (Framework) FrameworkUtil.getBundle(CmsShutdown.class).getBundleContext().getBundle(0)
+ : null;
+ }
+
+ @Override
+ public void run() {
+ if (framework != null && framework.getState() != Bundle.ACTIVE) {
+ return;
+ }
+
+ if (log.isDebugEnabled())
+ log.debug("Shutting down OSGi framework...");
+ try {
+ if (framework != null) {
+ // shutdown framework
+ framework.stop();
+ // wait for shutdown
+ FrameworkEvent shutdownEvent = framework.waitForStop(timeout);
+ int stoppedType = shutdownEvent.getType();
+ Runtime runtime = Runtime.getRuntime();
+ if (stoppedType == FrameworkEvent.STOPPED) {
+ // close VM
+ // System.exit(EXIT_OK);
+ } else if (stoppedType == FrameworkEvent.ERROR) {
+ log.error("The OSGi framework stopped with an error");
+ runtime.halt(EXIT_ERROR);
+ } else if (stoppedType == FrameworkEvent.WAIT_TIMEDOUT) {
+ log.error("The OSGi framework hasn't stopped after " + timeout + "ms."
+ + " Forcibly terminating the JVM...");
+ runtime.halt(EXIT_TIMEOUT);
+ } else {
+ log.error("Unknown state of OSGi framework after " + timeout + "ms."
+ + " Forcibly terminating the JVM... (" + shutdownEvent + ")");
+ runtime.halt(EXIT_UNKNOWN);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ log.error("Unexpected exception " + e + " in shutdown hook. " + " Forcibly terminating the JVM...");
+ Runtime.getRuntime().halt(EXIT_UNKNOWN);
+ }
+ }
+
+}
--- /dev/null
+package org.argeo.cms.internal.osgi;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Writer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import javax.naming.InvalidNameException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.Rdn;
+
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.cms.internal.runtime.InitUtils;
+import org.argeo.cms.internal.runtime.KernelConstants;
+import org.argeo.cms.internal.runtime.KernelUtils;
+import org.argeo.osgi.useradmin.UserAdminConf;
+import org.argeo.util.naming.AttributesDictionary;
+import org.argeo.util.naming.LdifParser;
+import org.argeo.util.naming.LdifWriter;
+import org.eclipse.equinox.http.jetty.JettyConfigurator;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.cm.ConfigurationEvent;
+import org.osgi.service.cm.ConfigurationListener;
+
+/** Manages the LDIF-based deployment configuration. */
+public class DeployConfig implements ConfigurationListener {
+ private final CmsLog log = CmsLog.getLog(getClass());
+// private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
+
+ private static Path deployConfigPath = KernelUtils.getOsgiInstancePath(KernelConstants.DEPLOY_CONFIG_PATH);
+ private SortedMap<LdapName, Attributes> deployConfigs = new TreeMap<>();
+// private final DataModels dataModels;
+
+ private boolean isFirstInit = false;
+
+ private final static String ROLES = "roles";
+
+ private ConfigurationAdmin configurationAdmin;
+
+ public DeployConfig() {
+// this.dataModels = dataModels;
+ // ConfigurationAdmin configurationAdmin =
+// // bc.getService(bc.getServiceReference(ConfigurationAdmin.class));
+// try {
+// if (!isInitialized()) { // first init
+// isFirstInit = true;
+// firstInit();
+// }
+// this.configurationAdmin = configurationAdmin;
+//// init(configurationAdmin, isClean, isFirstInit);
+// } catch (IOException e) {
+// throw new RuntimeException("Could not init deploy configs", e);
+// }
+ // FIXME check race conditions during initialization
+ // bc.registerService(ConfigurationListener.class, this, null);
+ }
+
+ private void firstInit() throws IOException {
+ log.info("## FIRST INIT ##");
+ Files.createDirectories(deployConfigPath.getParent());
+
+ // FirstInit firstInit = new FirstInit();
+ InitUtils.prepareFirstInitInstanceArea();
+
+ if (!Files.exists(deployConfigPath))
+ deployConfigs = new TreeMap<>();
+ else// config file could have juste been copied by preparation
+ try (InputStream in = Files.newInputStream(deployConfigPath)) {
+ deployConfigs = new LdifParser().read(in);
+ }
+ save();
+ }
+
+ private void setFromFrameworkProperties(boolean isFirstInit) {
+
+ // user admin
+ List<Dictionary<String, Object>> userDirectoryConfigs = InitUtils.getUserDirectoryConfigs();
+ if (userDirectoryConfigs.size() != 0) {
+ List<String> activeCns = new ArrayList<>();
+ for (int i = 0; i < userDirectoryConfigs.size(); i++) {
+ Dictionary<String, Object> userDirectoryConfig = userDirectoryConfigs.get(i);
+ String baseDn = (String) userDirectoryConfig.get(UserAdminConf.baseDn.name());
+ String cn;
+ if (CmsConstants.ROLES_BASEDN.equals(baseDn))
+ cn = ROLES;
+ else
+ cn = UserAdminConf.baseDnHash(userDirectoryConfig);
+ activeCns.add(cn);
+ userDirectoryConfig.put(CmsConstants.CN, cn);
+ putFactoryDeployConfig(CmsConstants.NODE_USER_ADMIN_PID, userDirectoryConfig);
+ }
+ // disable others
+ LdapName userAdminFactoryName = serviceFactoryDn(CmsConstants.NODE_USER_ADMIN_PID);
+ for (LdapName name : deployConfigs.keySet()) {
+ if (name.startsWith(userAdminFactoryName) && !name.equals(userAdminFactoryName)) {
+// try {
+ Attributes attrs = deployConfigs.get(name);
+ String cn = name.getRdn(name.size() - 1).getValue().toString();
+ if (!activeCns.contains(cn)) {
+ attrs.put(UserAdminConf.disabled.name(), "true");
+ }
+// } catch (Exception e) {
+// throw new CmsException("Cannot disable user directory " + name, e);
+// }
+ }
+ }
+ }
+
+ // http server
+// Dictionary<String, Object> webServerConfig = InitUtils
+// .getHttpServerConfig(getProps(KernelConstants.JETTY_FACTORY_PID, NodeConstants.DEFAULT));
+// if (!webServerConfig.isEmpty()) {
+// // TODO check for other customizers
+// webServerConfig.put("customizer.class", "org.argeo.equinox.jetty.CmsJettyCustomizer");
+// putFactoryDeployConfig(KernelConstants.JETTY_FACTORY_PID, webServerConfig);
+// }
+ LdapName defaultHttpServiceDn = serviceDn(KernelConstants.JETTY_FACTORY_PID, CmsConstants.DEFAULT);
+ if (deployConfigs.containsKey(defaultHttpServiceDn)) {
+ // remove old default configs since we have now to start Jetty servlet bridge
+ // indirectly
+ deployConfigs.remove(defaultHttpServiceDn);
+ }
+
+ // SAVE
+ save();
+ //
+
+ // Explicitly configures Jetty so that the default server is not started by the
+ // activator of the Equinox Jetty bundle.
+ Dictionary<String, Object> webServerConfig = InitUtils
+ .getHttpServerConfig(getProps(KernelConstants.JETTY_FACTORY_PID, CmsConstants.DEFAULT));
+// if (!webServerConfig.isEmpty()) {
+// webServerConfig.put("customizer.class", KernelConstants.CMS_JETTY_CUSTOMIZER_CLASS);
+//
+// // TODO centralise with Jetty extender
+// Object webSocketEnabled = webServerConfig.get(InternalHttpConstants.WEBSOCKET_ENABLED);
+// if (webSocketEnabled != null && webSocketEnabled.toString().equals("true")) {
+// bc.registerService(ServerEndpointConfig.Configurator.class, new CmsWebSocketConfigurator(), null);
+// webServerConfig.put(InternalHttpConstants.WEBSOCKET_ENABLED, "true");
+// }
+// }
+
+ int tryCount = 60;
+ try {
+ tryGettyJetty: while (tryCount > 0) {
+ try {
+ JettyConfigurator.startServer(KernelConstants.DEFAULT_JETTY_SERVER, webServerConfig);
+ // Explicitly starts Jetty OSGi HTTP bundle, so that it gets triggered if OSGi
+ // configuration is not cleaned
+ FrameworkUtil.getBundle(JettyConfigurator.class).start();
+ break tryGettyJetty;
+ } catch (IllegalStateException e) {
+ // Jetty may not be ready
+ try {
+ Thread.sleep(1000);
+ } catch (Exception e1) {
+ // silent
+ }
+ tryCount--;
+ }
+ }
+ } catch (Exception e) {
+ log.error("Cannot start default Jetty server with config " + webServerConfig, e);
+ }
+
+ }
+
+ public void init() throws IOException {
+ if (!isInitialized()) { // first init
+ isFirstInit = true;
+ firstInit();
+ }
+
+ boolean isClean;
+ try {
+ Configuration[] confs = configurationAdmin
+ .listConfigurations("(service.factoryPid=" + CmsConstants.NODE_USER_ADMIN_PID + ")");
+ isClean = confs == null || confs.length == 0;
+ } catch (Exception e) {
+ throw new IllegalStateException("Cannot analyse clean state", e);
+ }
+
+ try (InputStream in = Files.newInputStream(deployConfigPath)) {
+ deployConfigs = new LdifParser().read(in);
+ }
+ if (isClean) {
+ if (log.isDebugEnabled())
+ log.debug("Clean state, loading from framework properties...");
+ setFromFrameworkProperties(isFirstInit);
+ loadConfigs();
+ }
+ // TODO check consistency if not clean
+ }
+
+ public void destroy() {
+
+ }
+
+ public void loadConfigs() throws IOException {
+ // FIXME make it more robust
+ Configuration systemRolesConf = null;
+ LdapName systemRolesDn;
+ try {
+ // FIXME make it more robust
+ systemRolesDn = new LdapName("cn=roles,ou=org.argeo.api.userAdmin,ou=deploy,ou=node");
+ } catch (InvalidNameException e) {
+ throw new IllegalArgumentException(e);
+ }
+ deployConfigs: for (LdapName dn : deployConfigs.keySet()) {
+ Rdn lastRdn = dn.getRdn(dn.size() - 1);
+ LdapName prefix = (LdapName) dn.getPrefix(dn.size() - 1);
+ if (prefix.toString().equals(CmsConstants.DEPLOY_BASEDN)) {
+ if (lastRdn.getType().equals(CmsConstants.CN)) {
+ // service
+ String pid = lastRdn.getValue().toString();
+ Configuration conf = configurationAdmin.getConfiguration(pid);
+ AttributesDictionary dico = new AttributesDictionary(deployConfigs.get(dn));
+ conf.update(dico);
+ } else {
+ // service factory definition
+ }
+ } else {
+ Attributes config = deployConfigs.get(dn);
+ Attribute disabled = config.get(UserAdminConf.disabled.name());
+ if (disabled != null)
+ continue deployConfigs;
+ // service factory service
+ Rdn beforeLastRdn = dn.getRdn(dn.size() - 2);
+ assert beforeLastRdn.getType().equals(CmsConstants.OU);
+ String factoryPid = beforeLastRdn.getValue().toString();
+ Configuration conf = configurationAdmin.createFactoryConfiguration(factoryPid.toString(), null);
+ if (systemRolesDn.equals(dn)) {
+ systemRolesConf = configurationAdmin.createFactoryConfiguration(factoryPid.toString(), null);
+ } else {
+ AttributesDictionary dico = new AttributesDictionary(config);
+ conf.update(dico);
+ }
+ }
+ }
+
+ // system roles must be last since it triggers node user admin publication
+ if (systemRolesConf == null)
+ throw new IllegalStateException("System roles are not configured.");
+ systemRolesConf.update(new AttributesDictionary(deployConfigs.get(systemRolesDn)));
+
+ }
+
+ @Override
+ public void configurationEvent(ConfigurationEvent event) {
+ try {
+ if (ConfigurationEvent.CM_UPDATED == event.getType()) {
+ Configuration conf = configurationAdmin.getConfiguration(event.getPid(), null);
+ LdapName serviceDn = null;
+ String factoryPid = conf.getFactoryPid();
+ if (factoryPid != null) {
+ LdapName serviceFactoryDn = serviceFactoryDn(factoryPid);
+ if (deployConfigs.containsKey(serviceFactoryDn)) {
+ for (LdapName dn : deployConfigs.keySet()) {
+ if (dn.startsWith(serviceFactoryDn)) {
+ Rdn lastRdn = dn.getRdn(dn.size() - 1);
+ assert lastRdn.getType().equals(CmsConstants.CN);
+ Object value = conf.getProperties().get(lastRdn.getType());
+ assert value != null;
+ if (value.equals(lastRdn.getValue())) {
+ serviceDn = dn;
+ break;
+ }
+ }
+ }
+
+ Object cn = conf.getProperties().get(CmsConstants.CN);
+ if (cn == null)
+ throw new IllegalArgumentException("Properties must contain cn");
+ if (serviceDn == null) {
+ putFactoryDeployConfig(factoryPid, conf.getProperties());
+ } else {
+ Attributes attrs = deployConfigs.get(serviceDn);
+ assert attrs != null;
+ AttributesDictionary.copy(conf.getProperties(), attrs);
+ }
+ save();
+ if (log.isDebugEnabled())
+ log.debug("Updated deploy config " + serviceDn(factoryPid, cn.toString()));
+ } else {
+ // ignore non config-registered service factories
+ }
+ } else {
+ serviceDn = serviceDn(event.getPid());
+ if (deployConfigs.containsKey(serviceDn)) {
+ Attributes attrs = deployConfigs.get(serviceDn);
+ assert attrs != null;
+ AttributesDictionary.copy(conf.getProperties(), attrs);
+ save();
+ if (log.isDebugEnabled())
+ log.debug("Updated deploy config " + serviceDn);
+ } else {
+ // ignore non config-registered services
+ }
+ }
+ }
+ } catch (Exception e) {
+ log.error("Could not handle configuration event", e);
+ }
+ }
+
+ public void putFactoryDeployConfig(String factoryPid, Dictionary<String, Object> props) {
+ Object cn = props.get(CmsConstants.CN);
+ if (cn == null)
+ throw new IllegalArgumentException("cn must be set in properties");
+ LdapName serviceFactoryDn = serviceFactoryDn(factoryPid);
+ if (!deployConfigs.containsKey(serviceFactoryDn))
+ deployConfigs.put(serviceFactoryDn, new BasicAttributes(CmsConstants.OU, factoryPid));
+ LdapName serviceDn = serviceDn(factoryPid, cn.toString());
+ Attributes attrs = new BasicAttributes();
+ AttributesDictionary.copy(props, attrs);
+ deployConfigs.put(serviceDn, attrs);
+ }
+
+ void putDeployConfig(String servicePid, Dictionary<String, Object> props) {
+ LdapName serviceDn = serviceDn(servicePid);
+ Attributes attrs = new BasicAttributes(CmsConstants.CN, servicePid);
+ AttributesDictionary.copy(props, attrs);
+ deployConfigs.put(serviceDn, attrs);
+ }
+
+ public void save() {
+ try (Writer writer = Files.newBufferedWriter(deployConfigPath)) {
+ new LdifWriter(writer).write(deployConfigs);
+ } catch (IOException e) {
+ // throw new CmsException("Cannot save deploy configs", e);
+ log.error("Cannot save deploy configs", e);
+ }
+ }
+
+ public void setConfigurationAdmin(ConfigurationAdmin configurationAdmin) {
+ this.configurationAdmin = configurationAdmin;
+ }
+
+ public boolean hasDomain() {
+ Configuration[] configs;
+ try {
+ configs = configurationAdmin
+ .listConfigurations("(service.factoryPid=" + CmsConstants.NODE_USER_ADMIN_PID + ")");
+ } catch (IOException | InvalidSyntaxException e) {
+ throw new IllegalStateException("Cannot list user directories", e);
+ }
+
+ boolean hasDomain = false;
+ for (Configuration config : configs) {
+ Object realm = config.getProperties().get(UserAdminConf.realm.name());
+ if (realm != null) {
+ log.debug("Found realm: " + realm);
+ hasDomain = true;
+ }
+ }
+ return hasDomain;
+ }
+
+ /*
+ * UTILITIES
+ */
+ private LdapName serviceFactoryDn(String factoryPid) {
+ try {
+ return new LdapName(CmsConstants.OU + "=" + factoryPid + "," + CmsConstants.DEPLOY_BASEDN);
+ } catch (InvalidNameException e) {
+ throw new IllegalArgumentException("Cannot generate DN from " + factoryPid, e);
+ }
+ }
+
+ private LdapName serviceDn(String servicePid) {
+ try {
+ return new LdapName(CmsConstants.CN + "=" + servicePid + "," + CmsConstants.DEPLOY_BASEDN);
+ } catch (InvalidNameException e) {
+ throw new IllegalArgumentException("Cannot generate DN from " + servicePid, e);
+ }
+ }
+
+ private LdapName serviceDn(String factoryPid, String cn) {
+ try {
+ return (LdapName) serviceFactoryDn(factoryPid).add(new Rdn(CmsConstants.CN, cn));
+ } catch (InvalidNameException e) {
+ throw new IllegalArgumentException("Cannot generate DN from " + factoryPid + " and " + cn, e);
+ }
+ }
+
+ public Dictionary<String, Object> getProps(String factoryPid, String cn) {
+ Attributes attrs = deployConfigs.get(serviceDn(factoryPid, cn));
+ if (attrs != null)
+ return new AttributesDictionary(attrs);
+ else
+ return null;
+ }
+
+ private static boolean isInitialized() {
+ return Files.exists(deployConfigPath);
+ }
+
+ public boolean isFirstInit() {
+ return isFirstInit;
+ }
+
+}
--- /dev/null
+package org.argeo.cms.internal.osgi;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.FileSystems;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardWatchEventKinds;
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchKey;
+import java.nio.file.WatchService;
+import java.security.SignatureException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.cms.ArgeoLogListener;
+import org.argeo.cms.ArgeoLogger;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.internal.runtime.KernelConstants;
+import org.argeo.cms.internal.runtime.KernelUtils;
+import org.argeo.osgi.useradmin.UserAdminConf;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.log.LogEntry;
+import org.osgi.service.log.LogLevel;
+import org.osgi.service.log.LogListener;
+import org.osgi.service.log.LogReaderService;
+
+/** Not meant to be used directly in standard log4j config */
+public class NodeLogger implements ArgeoLogger, LogListener {
+ /** Internal debug for development purposes. */
+ private static Boolean debug = false;
+
+ private Boolean disabled = false;
+
+ private String level = null;
+
+// private Level log4jLevel = null;
+
+ private Properties configuration;
+
+ private AppenderImpl appender;
+
+ private final List<ArgeoLogListener> everythingListeners = Collections
+ .synchronizedList(new ArrayList<ArgeoLogListener>());
+ private final List<ArgeoLogListener> allUsersListeners = Collections
+ .synchronizedList(new ArrayList<ArgeoLogListener>());
+ private final Map<String, List<ArgeoLogListener>> userListeners = Collections
+ .synchronizedMap(new HashMap<String, List<ArgeoLogListener>>());
+
+ private BlockingQueue<LogEvent> events;
+ private LogDispatcherThread logDispatcherThread = new LogDispatcherThread();
+
+ private Integer maxLastEventsCount = 10 * 1000;
+
+ /** Marker to prevent stack overflow */
+ private ThreadLocal<Boolean> dispatching = new ThreadLocal<Boolean>() {
+
+ @Override
+ protected Boolean initialValue() {
+ return false;
+ }
+ };
+
+ public NodeLogger(LogReaderService lrs) {
+ if (lrs != null) {
+ Enumeration<LogEntry> logEntries = lrs.getLog();
+ while (logEntries.hasMoreElements())
+ logged(logEntries.nextElement());
+ lrs.addLogListener(this);
+
+ // configure log4j watcher
+ String log4jConfiguration = KernelUtils.getFrameworkProp("log4j.configuration");
+ if (log4jConfiguration != null && log4jConfiguration.startsWith("file:")) {
+ if (log4jConfiguration.contains("..")) {
+ if (log4jConfiguration.startsWith("file://"))
+ log4jConfiguration = log4jConfiguration.substring("file://".length());
+ else if (log4jConfiguration.startsWith("file:"))
+ log4jConfiguration = log4jConfiguration.substring("file:".length());
+ }
+ try {
+ Path log4jconfigPath;
+ if (log4jConfiguration.startsWith("file:"))
+ log4jconfigPath = Paths.get(new URI(log4jConfiguration));
+ else
+ log4jconfigPath = Paths.get(log4jConfiguration);
+ Thread log4jConfWatcher = new Log4jConfWatcherThread(log4jconfigPath);
+ log4jConfWatcher.start();
+ } catch (Exception e) {
+ stdErr("Badly formatted log4j configuration URI " + log4jConfiguration + ": " + e.getMessage());
+ }
+ }
+ }
+ }
+
+ public void init() {
+ try {
+ events = new LinkedBlockingQueue<LogEvent>();
+
+ // if (layout != null)
+ // setLayout(layout);
+ // else
+ // setLayout(new PatternLayout(pattern));
+ appender = new AppenderImpl();
+ reloadConfiguration();
+// Logger.getRootLogger().addAppender(appender);
+
+ logDispatcherThread = new LogDispatcherThread();
+ logDispatcherThread.start();
+ } catch (Exception e) {
+ throw new CmsException("Cannot initialize log4j");
+ }
+ }
+
+ public void destroy() throws Exception {
+// Logger.getRootLogger().removeAppender(appender);
+ allUsersListeners.clear();
+ for (List<ArgeoLogListener> lst : userListeners.values())
+ lst.clear();
+ userListeners.clear();
+
+ events.clear();
+ events = null;
+ logDispatcherThread.interrupt();
+ }
+
+ // public void setLayout(Layout layout) {
+ // this.layout = layout;
+ // }
+
+ public String toString() {
+ return "Node Logger";
+ }
+
+ //
+ // OSGi LOGGER
+ //
+ @Override
+ public void logged(LogEntry status) {
+ CmsLog pluginLog = CmsLog.getLog(status.getBundle().getSymbolicName());
+ LogLevel severity = status.getLogLevel();
+ if (severity.equals(LogLevel.ERROR) && pluginLog.isErrorEnabled()) {
+ // FIXME Fix Argeo TP
+ if (status.getException() instanceof SignatureException)
+ return;
+ pluginLog.error(msg(status), status.getException());
+ } else if (severity.equals(LogLevel.WARN) && pluginLog.isWarnEnabled()) {
+ if (pluginLog.isTraceEnabled())
+ pluginLog.warn(msg(status), status.getException());
+ else
+ pluginLog.warn(msg(status));
+ } else if (severity.equals(LogLevel.INFO) && pluginLog.isDebugEnabled())
+ pluginLog.debug(msg(status), status.getException());
+ else if (severity.equals(LogLevel.DEBUG) && pluginLog.isTraceEnabled())
+ pluginLog.trace(msg(status), status.getException());
+ else if (severity.equals(LogLevel.TRACE) && pluginLog.isTraceEnabled())
+ pluginLog.trace(msg(status), status.getException());
+ }
+
+ private String msg(LogEntry status) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(status.getMessage());
+ Bundle bundle = status.getBundle();
+ if (bundle != null) {
+ sb.append(" '" + bundle.getSymbolicName() + "'");
+ }
+ ServiceReference<?> sr = status.getServiceReference();
+ if (sr != null) {
+ sb.append(' ');
+ String[] objectClasses = (String[]) sr.getProperty(Constants.OBJECTCLASS);
+ if (isSpringApplicationContext(objectClasses)) {
+ sb.append("{org.springframework.context.ApplicationContext}");
+ Object symbolicName = sr.getProperty(Constants.BUNDLE_SYMBOLICNAME);
+ if (symbolicName != null)
+ sb.append(" " + Constants.BUNDLE_SYMBOLICNAME + ": " + symbolicName);
+ } else {
+ sb.append(arrayToString(objectClasses));
+ }
+ Object cn = sr.getProperty(CmsConstants.CN);
+ if (cn != null)
+ sb.append(" " + CmsConstants.CN + ": " + cn);
+ Object factoryPid = sr.getProperty(ConfigurationAdmin.SERVICE_FACTORYPID);
+ if (factoryPid != null)
+ sb.append(" " + ConfigurationAdmin.SERVICE_FACTORYPID + ": " + factoryPid);
+ // else {
+ // Object servicePid = sr.getProperty(Constants.SERVICE_PID);
+ // if (servicePid != null)
+ // sb.append(" " + Constants.SERVICE_PID + ": " + servicePid);
+ // }
+ // servlets
+ Object whiteBoardPattern = sr.getProperty(KernelConstants.WHITEBOARD_PATTERN_PROP);
+ if (whiteBoardPattern != null) {
+ if (whiteBoardPattern instanceof String) {
+ sb.append(" " + KernelConstants.WHITEBOARD_PATTERN_PROP + ": " + whiteBoardPattern);
+ } else {
+ sb.append(" " + KernelConstants.WHITEBOARD_PATTERN_PROP + ": "
+ + arrayToString((String[]) whiteBoardPattern));
+ }
+ }
+ // RWT
+ Object contextName = sr.getProperty(KernelConstants.CONTEXT_NAME_PROP);
+ if (contextName != null)
+ sb.append(" " + KernelConstants.CONTEXT_NAME_PROP + ": " + contextName);
+
+ // user directories
+ Object baseDn = sr.getProperty(UserAdminConf.baseDn.name());
+ if (baseDn != null)
+ sb.append(" " + UserAdminConf.baseDn.name() + ": " + baseDn);
+
+ }
+ return sb.toString();
+ }
+
+ private String arrayToString(Object[] arr) {
+ StringBuilder sb = new StringBuilder();
+ sb.append('[');
+ for (int i = 0; i < arr.length; i++) {
+ if (i != 0)
+ sb.append(',');
+ sb.append(arr[i]);
+ }
+ sb.append(']');
+ return sb.toString();
+ }
+
+ private boolean isSpringApplicationContext(String[] objectClasses) {
+ for (String clss : objectClasses) {
+ if (clss.equals("org.eclipse.gemini.blueprint.context.DelegatedExecutionOsgiBundleApplicationContext")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ //
+ // ARGEO LOGGER
+ //
+
+ public synchronized void register(ArgeoLogListener listener, Integer numberOfPreviousEvents) {
+ String username = CurrentUser.getUsername();
+ if (username == null)
+ throw new CmsException("Only authenticated users can register a log listener");
+
+ if (!userListeners.containsKey(username)) {
+ List<ArgeoLogListener> lst = Collections.synchronizedList(new ArrayList<ArgeoLogListener>());
+ userListeners.put(username, lst);
+ }
+ userListeners.get(username).add(listener);
+ List<LogEvent> lastEvents = logDispatcherThread.getLastEvents(username, numberOfPreviousEvents);
+ for (LogEvent evt : lastEvents)
+ dispatchEvent(listener, evt);
+ }
+
+ public synchronized void registerForAll(ArgeoLogListener listener, Integer numberOfPreviousEvents,
+ boolean everything) {
+ if (everything)
+ everythingListeners.add(listener);
+ else
+ allUsersListeners.add(listener);
+ List<LogEvent> lastEvents = logDispatcherThread.getLastEvents(null, numberOfPreviousEvents);
+ for (LogEvent evt : lastEvents)
+ if (everything || evt.getUsername() != null)
+ dispatchEvent(listener, evt);
+ }
+
+ public synchronized void unregister(ArgeoLogListener listener) {
+ String username = CurrentUser.getUsername();
+ if (username == null)// FIXME
+ return;
+ if (!userListeners.containsKey(username))
+ throw new CmsException("No user listeners " + listener + " registered for user " + username);
+ if (!userListeners.get(username).contains(listener))
+ throw new CmsException("No user listeners " + listener + " registered for user " + username);
+ userListeners.get(username).remove(listener);
+ if (userListeners.get(username).isEmpty())
+ userListeners.remove(username);
+
+ }
+
+ public synchronized void unregisterForAll(ArgeoLogListener listener) {
+ everythingListeners.remove(listener);
+ allUsersListeners.remove(listener);
+ }
+
+ /** For development purpose, since using regular logging is not easy here */
+ private static void stdOut(Object obj) {
+ System.out.println(obj);
+ }
+
+ private static void stdErr(Object obj) {
+ System.err.println(obj);
+ }
+
+ private static void debug(Object obj) {
+ if (debug)
+ System.out.println(obj);
+ }
+
+ private static boolean isInternalDebugEnabled() {
+ return debug;
+ }
+
+ // public void setPattern(String pattern) {
+ // this.pattern = pattern;
+ // }
+
+ public void setDisabled(Boolean disabled) {
+ this.disabled = disabled;
+ }
+
+ public void setLevel(String level) {
+ this.level = level;
+ }
+
+ public void setConfiguration(Properties configuration) {
+ this.configuration = configuration;
+ }
+
+ public void updateConfiguration(Properties configuration) {
+ setConfiguration(configuration);
+ reloadConfiguration();
+ }
+
+ public Properties getConfiguration() {
+ return configuration;
+ }
+
+ /**
+ * Reloads configuration (if the configuration {@link Properties} is set)
+ */
+ protected void reloadConfiguration() {
+ if (configuration != null) {
+// LogManager.resetConfiguration();
+// PropertyConfigurator.configure(configuration);
+ }
+ }
+
+ protected synchronized void processLoggingEvent(LogEvent event) {
+ if (disabled)
+ return;
+
+ if (dispatching.get())
+ return;
+
+ if (level != null && !level.trim().equals("")) {
+// if (log4jLevel == null || !log4jLevel.toString().equals(level))
+// try {
+// log4jLevel = Level.toLevel(level);
+// } catch (Exception e) {
+// System.err.println("Log4j level could not be set for level '" + level + "', resetting it to null.");
+// e.printStackTrace();
+// level = null;
+// }
+//
+// if (log4jLevel != null && !event.getLoggingEvent().getLevel().isGreaterOrEqual(log4jLevel)) {
+// return;
+// }
+ }
+
+ try {
+ // admin listeners
+ Iterator<ArgeoLogListener> everythingIt = everythingListeners.iterator();
+ while (everythingIt.hasNext())
+ dispatchEvent(everythingIt.next(), event);
+
+ if (event.getUsername() != null) {
+ Iterator<ArgeoLogListener> allUsersIt = allUsersListeners.iterator();
+ while (allUsersIt.hasNext())
+ dispatchEvent(allUsersIt.next(), event);
+
+ if (userListeners.containsKey(event.getUsername())) {
+ Iterator<ArgeoLogListener> userIt = userListeners.get(event.getUsername()).iterator();
+ while (userIt.hasNext())
+ dispatchEvent(userIt.next(), event);
+ }
+ }
+ } catch (Exception e) {
+ stdOut("Cannot process logging event");
+ e.printStackTrace();
+ }
+ }
+
+ protected void dispatchEvent(ArgeoLogListener logListener, LogEvent evt) {
+// LoggingEvent event = evt.getLoggingEvent();
+// logListener.appendLog(evt.getUsername(), event.getTimeStamp(), event.getLevel().toString(),
+// event.getLoggerName(), event.getThreadName(), event.getMessage(), event.getThrowableStrRep());
+ }
+
+ private class AppenderImpl { //extends AppenderSkeleton {
+ public boolean requiresLayout() {
+ return false;
+ }
+
+ public void close() {
+ }
+
+// @Override
+// protected void append(LoggingEvent event) {
+// if (events != null) {
+// try {
+// String username = CurrentUser.getUsername();
+// events.put(new LogEvent(username, event));
+// } catch (InterruptedException e) {
+// // silent
+// }
+// }
+// }
+
+ }
+
+ private class LogDispatcherThread extends Thread {
+ /** encapsulated in order to simplify concurrency management */
+ private LinkedList<LogEvent> lastEvents = new LinkedList<LogEvent>();
+
+ public LogDispatcherThread() {
+ super("Argeo Logging Dispatcher Thread");
+ }
+
+ public void run() {
+ while (events != null) {
+ try {
+ LogEvent loggingEvent = events.take();
+ processLoggingEvent(loggingEvent);
+ addLastEvent(loggingEvent);
+ } catch (InterruptedException e) {
+ if (events == null)
+ return;
+ }
+ }
+ }
+
+ protected synchronized void addLastEvent(LogEvent loggingEvent) {
+ if (lastEvents.size() >= maxLastEventsCount)
+ lastEvents.poll();
+ lastEvents.add(loggingEvent);
+ }
+
+ public synchronized List<LogEvent> getLastEvents(String username, Integer maxCount) {
+ LinkedList<LogEvent> evts = new LinkedList<LogEvent>();
+ ListIterator<LogEvent> it = lastEvents.listIterator(lastEvents.size());
+ int count = 0;
+ while (it.hasPrevious() && (count < maxCount)) {
+ LogEvent evt = it.previous();
+ if (username == null || username.equals(evt.getUsername())) {
+ evts.push(evt);
+ count++;
+ }
+ }
+ return evts;
+ }
+ }
+
+ private class LogEvent {
+ private final String username;
+// private final LoggingEvent loggingEvent;
+
+ public LogEvent(String username) {
+ super();
+ this.username = username;
+// this.loggingEvent = loggingEvent;
+ }
+
+// @Override
+// public int hashCode() {
+// return loggingEvent.hashCode();
+// }
+//
+// @Override
+// public boolean equals(Object obj) {
+// return loggingEvent.equals(obj);
+// }
+//
+// @Override
+// public String toString() {
+// return username + "@ " + loggingEvent.toString();
+// }
+
+ public String getUsername() {
+ return username;
+ }
+
+// public LoggingEvent getLoggingEvent() {
+// return loggingEvent;
+// }
+
+ }
+
+ private class Log4jConfWatcherThread extends Thread {
+ private Path log4jConfigurationPath;
+
+ public Log4jConfWatcherThread(Path log4jConfigurationPath) {
+ super("Log4j Configuration Watcher");
+ try {
+ this.log4jConfigurationPath = log4jConfigurationPath.toRealPath();
+ } catch (IOException e) {
+ this.log4jConfigurationPath = log4jConfigurationPath.toAbsolutePath();
+ stdOut("Cannot determine real path for " + log4jConfigurationPath + ": " + e.getMessage());
+ }
+ }
+
+ public void run() {
+ Path parentDir = log4jConfigurationPath.getParent();
+ try (final WatchService watchService = FileSystems.getDefault().newWatchService()) {
+ parentDir.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);
+ WatchKey wk;
+ watching: while ((wk = watchService.take()) != null) {
+ for (WatchEvent<?> event : wk.pollEvents()) {
+ final Path changed = (Path) event.context();
+ if (log4jConfigurationPath.equals(parentDir.resolve(changed))) {
+ if (isInternalDebugEnabled())
+ debug(log4jConfigurationPath + " has changed, reloading.");
+// PropertyConfigurator.configure(log4jConfigurationPath.toUri().toURL());
+ }
+ }
+ // reset the key
+ boolean valid = wk.reset();
+ if (!valid) {
+ break watching;
+ }
+ }
+ } catch (IOException | InterruptedException e) {
+ stdErr("Log4j configuration watcher failed: " + e.getMessage());
+ }
+ }
+ }
+}
--- /dev/null
+package org.argeo.cms.internal.osgi;
+
+import java.io.IOException;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.naming.ldap.LdapName;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import org.apache.commons.httpclient.auth.AuthPolicy;
+import org.apache.commons.httpclient.auth.CredentialsProvider;
+import org.apache.commons.httpclient.params.DefaultHttpParams;
+import org.apache.commons.httpclient.params.HttpMethodParams;
+import org.apache.commons.httpclient.params.HttpParams;
+import org.argeo.api.cms.CmsAuth;
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.cms.internal.http.client.HttpCredentialProvider;
+import org.argeo.cms.internal.http.client.SpnegoAuthScheme;
+import org.argeo.cms.internal.runtime.KernelConstants;
+import org.argeo.cms.internal.runtime.KernelUtils;
+import org.argeo.osgi.transaction.WorkControl;
+import org.argeo.osgi.transaction.WorkTransaction;
+import org.argeo.osgi.useradmin.AbstractUserDirectory;
+import org.argeo.osgi.useradmin.AggregatingUserAdmin;
+import org.argeo.osgi.useradmin.LdapUserAdmin;
+import org.argeo.osgi.useradmin.LdifUserAdmin;
+import org.argeo.osgi.useradmin.OsUserDirectory;
+import org.argeo.osgi.useradmin.UserAdminConf;
+import org.argeo.osgi.useradmin.UserDirectory;
+import org.argeo.util.naming.DnsBrowser;
+import org.ietf.jgss.GSSCredential;
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.GSSManager;
+import org.ietf.jgss.GSSName;
+import org.ietf.jgss.Oid;
+import org.osgi.framework.Constants;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedServiceFactory;
+import org.osgi.service.useradmin.Authorization;
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.UserAdmin;
+
+/**
+ * Aggregates multiple {@link UserDirectory} and integrates them with system
+ * roles.
+ */
+public class NodeUserAdmin extends AggregatingUserAdmin implements ManagedServiceFactory, KernelConstants {
+ private final static CmsLog log = CmsLog.getLog(NodeUserAdmin.class);
+
+ // OSGi
+ private Map<String, LdapName> pidToBaseDn = new HashMap<>();
+// private Map<String, ServiceRegistration<UserDirectory>> pidToServiceRegs = new HashMap<>();
+// private ServiceRegistration<UserAdmin> userAdminReg;
+
+ // JTA
+// private final ServiceTracker<WorkControl, WorkControl> tmTracker;
+ // private final String cacheName = UserDirectory.class.getName();
+
+ // GSS API
+ private Path nodeKeyTab = KernelUtils.getOsgiInstancePath(KernelConstants.NODE_KEY_TAB_PATH);
+ private GSSCredential acceptorCredentials;
+
+ private boolean singleUser = false;
+// private boolean systemRolesAvailable = false;
+
+// CmsUserManagerImpl userManager;
+ private WorkControl transactionManager;
+ private WorkTransaction userTransaction;
+
+ public NodeUserAdmin() {
+ super(CmsConstants.ROLES_BASEDN, CmsConstants.TOKENS_BASEDN);
+// BundleContext bc = Activator.getBundleContext();
+// if (bc != null) {
+// tmTracker = new ServiceTracker<>(bc, WorkControl.class, null) {
+//
+// @Override
+// public WorkControl addingService(ServiceReference<WorkControl> reference) {
+// WorkControl workControl = super.addingService(reference);
+// userManager = new CmsUserManagerImpl();
+// userManager.setUserAdmin(NodeUserAdmin.this);
+// // FIXME make it more robust
+// userManager.setUserTransaction((WorkTransaction) workControl);
+// bc.registerService(CmsUserManager.class, userManager, null);
+// return workControl;
+// }
+// };
+// tmTracker.open();
+// } else {
+// tmTracker = null;
+// }
+ }
+
+ public void init() {
+ }
+
+ public void destroy() {
+ }
+
+ @Override
+ public void updated(String pid, Dictionary<String, ?> properties) throws ConfigurationException {
+ String uri = (String) properties.get(UserAdminConf.uri.name());
+ Object realm = properties.get(UserAdminConf.realm.name());
+ URI u;
+ try {
+ if (uri == null) {
+ String baseDn = (String) properties.get(UserAdminConf.baseDn.name());
+ u = KernelUtils.getOsgiInstanceUri(KernelConstants.DIR_NODE + '/' + baseDn + ".ldif");
+ } else if (realm != null) {
+ u = null;
+ } else {
+ u = new URI(uri);
+ }
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException("Badly formatted URI " + uri, e);
+ }
+
+ // Create
+ AbstractUserDirectory userDirectory;
+ if (realm != null || UserAdminConf.SCHEME_LDAP.equals(u.getScheme())
+ || UserAdminConf.SCHEME_LDAPS.equals(u.getScheme())) {
+ userDirectory = new LdapUserAdmin(properties);
+ } else if (UserAdminConf.SCHEME_FILE.equals(u.getScheme())) {
+ userDirectory = new LdifUserAdmin(u, properties);
+ } else if (UserAdminConf.SCHEME_OS.equals(u.getScheme())) {
+ userDirectory = new OsUserDirectory(u, properties);
+ singleUser = true;
+ } else {
+ throw new IllegalArgumentException("Unsupported scheme " + u.getScheme());
+ }
+ addUserDirectory(userDirectory);
+
+ // OSGi
+ LdapName baseDn = userDirectory.getBaseDn();
+ Hashtable<String, Object> regProps = new Hashtable<>();
+ regProps.put(Constants.SERVICE_PID, pid);
+ if (isSystemRolesBaseDn(baseDn))
+ regProps.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE);
+ regProps.put(UserAdminConf.baseDn.name(), baseDn);
+ // ServiceRegistration<UserDirectory> reg =
+ // bc.registerService(UserDirectory.class, userDirectory, regProps);
+ CmsActivator.getBundleContext().registerService(UserDirectory.class, userDirectory, regProps);
+// userManager.addUserDirectory(userDirectory, regProps);
+ pidToBaseDn.put(pid, baseDn);
+ // pidToServiceRegs.put(pid, reg);
+
+ if (log.isDebugEnabled()) {
+ log.debug("User directory " + userDirectory.getBaseDn() + (u != null ? " [" + u.getScheme() + "]" : "")
+ + " enabled." + (realm != null ? " " + realm + " realm." : ""));
+ }
+
+ if (isSystemRolesBaseDn(baseDn)) {
+ addStandardSystemRoles();
+
+ // publishes itself as user admin only when system roles are available
+ Dictionary<String, Object> userAdminregProps = new Hashtable<>();
+ userAdminregProps.put(CmsConstants.CN, CmsConstants.DEFAULT);
+ userAdminregProps.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE);
+ CmsActivator.getBundleContext().registerService(UserAdmin.class, this, userAdminregProps);
+ }
+
+// if (isSystemRolesBaseDn(baseDn))
+// systemRolesAvailable = true;
+//
+// // start publishing only when system roles are available
+// if (systemRolesAvailable) {
+// // The list of baseDns is published as properties
+// // TODO clients should rather reference USerDirectory services
+// if (userAdminReg != null)
+// userAdminReg.unregister();
+// // register self as main user admin
+// Dictionary<String, Object> userAdminregProps = currentState();
+// userAdminregProps.put(NodeConstants.CN, NodeConstants.DEFAULT);
+// userAdminregProps.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE);
+// userAdminReg = bc.registerService(UserAdmin.class, this, userAdminregProps);
+// }
+ }
+
+ private void addStandardSystemRoles() {
+ // we assume UserTransaction is already available (TODO make it more robust)
+ try {
+ userTransaction.begin();
+ Role adminRole = getRole(CmsConstants.ROLE_ADMIN);
+ if (adminRole == null) {
+ adminRole = createRole(CmsConstants.ROLE_ADMIN, Role.GROUP);
+ }
+ if (getRole(CmsConstants.ROLE_USER_ADMIN) == null) {
+ Group userAdminRole = (Group) createRole(CmsConstants.ROLE_USER_ADMIN, Role.GROUP);
+ userAdminRole.addMember(adminRole);
+ }
+ userTransaction.commit();
+ } catch (Exception e) {
+ try {
+ userTransaction.rollback();
+ } catch (Exception e1) {
+ // silent
+ }
+ throw new IllegalStateException("Cannot add standard system roles", e);
+ }
+ }
+
+ @Override
+ public void deleted(String pid) {
+ // assert pidToServiceRegs.get(pid) != null;
+ assert pidToBaseDn.get(pid) != null;
+ // pidToServiceRegs.remove(pid).unregister();
+ LdapName baseDn = pidToBaseDn.remove(pid);
+ removeUserDirectory(baseDn);
+ }
+
+ @Override
+ public String getName() {
+ return "Node User Admin";
+ }
+
+ @Override
+ protected void addAbstractSystemRoles(Authorization rawAuthorization, Set<String> sysRoles) {
+ if (rawAuthorization.getName() == null) {
+ sysRoles.add(CmsConstants.ROLE_ANONYMOUS);
+ } else {
+ sysRoles.add(CmsConstants.ROLE_USER);
+ }
+ }
+
+ protected void postAdd(AbstractUserDirectory userDirectory) {
+ // JTA
+// WorkControl tm = tmTracker != null ? tmTracker.getService() : null;
+// if (tm == null)
+// throw new IllegalStateException("A JTA transaction manager must be available.");
+ userDirectory.setTransactionControl(transactionManager);
+// if (tmTracker.getService() instanceof BitronixTransactionManager)
+// EhCacheXAResourceProducer.registerXAResource(cacheName, userDirectory.getXaResource());
+
+ Object realm = userDirectory.getProperties().get(UserAdminConf.realm.name());
+ if (realm != null) {
+ if (Files.exists(nodeKeyTab)) {
+ String servicePrincipal = getKerberosServicePrincipal(realm.toString());
+ if (servicePrincipal != null) {
+ CallbackHandler callbackHandler = new CallbackHandler() {
+ @Override
+ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+ for (Callback callback : callbacks)
+ if (callback instanceof NameCallback)
+ ((NameCallback) callback).setName(servicePrincipal);
+
+ }
+ };
+ try {
+ LoginContext nodeLc = new LoginContext(CmsAuth.LOGIN_CONTEXT_NODE, callbackHandler);
+ nodeLc.login();
+ acceptorCredentials = logInAsAcceptor(nodeLc.getSubject(), servicePrincipal);
+ } catch (LoginException e) {
+ throw new IllegalStateException("Cannot log in kernel", e);
+ }
+ }
+ }
+
+ // Register client-side SPNEGO auth scheme
+ AuthPolicy.registerAuthScheme(SpnegoAuthScheme.NAME, SpnegoAuthScheme.class);
+ HttpParams params = DefaultHttpParams.getDefaultParams();
+ ArrayList<String> schemes = new ArrayList<>();
+ schemes.add(SpnegoAuthScheme.NAME);// SPNEGO preferred
+ // schemes.add(AuthPolicy.BASIC);// incompatible with Basic
+ params.setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, schemes);
+ params.setParameter(CredentialsProvider.PROVIDER, new HttpCredentialProvider());
+ params.setParameter(HttpMethodParams.COOKIE_POLICY, KernelConstants.COOKIE_POLICY_BROWSER_COMPATIBILITY);
+ // params.setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
+ }
+ }
+
+ protected void preDestroy(AbstractUserDirectory userDirectory) {
+// if (tmTracker.getService() instanceof BitronixTransactionManager)
+// EhCacheXAResourceProducer.unregisterXAResource(cacheName, userDirectory.getXaResource());
+
+ Object realm = userDirectory.getProperties().get(UserAdminConf.realm.name());
+ if (realm != null) {
+ if (acceptorCredentials != null) {
+ try {
+ acceptorCredentials.dispose();
+ } catch (GSSException e) {
+ // silent
+ }
+ acceptorCredentials = null;
+ }
+ }
+ }
+
+ private String getKerberosServicePrincipal(String realm) {
+ String hostname;
+ try (DnsBrowser dnsBrowser = new DnsBrowser()) {
+ InetAddress localhost = InetAddress.getLocalHost();
+ hostname = localhost.getHostName();
+ String dnsZone = hostname.substring(hostname.indexOf('.') + 1);
+ String ipfromDns = dnsBrowser.getRecord(hostname, localhost instanceof Inet6Address ? "AAAA" : "A");
+ boolean consistentIp = localhost.getHostAddress().equals(ipfromDns);
+ String kerberosDomain = dnsBrowser.getRecord("_kerberos." + dnsZone, "TXT");
+ if (consistentIp && kerberosDomain != null && kerberosDomain.equals(realm) && Files.exists(nodeKeyTab)) {
+ return KernelConstants.DEFAULT_KERBEROS_SERVICE + "/" + hostname + "@" + kerberosDomain;
+ } else
+ return null;
+ } catch (Exception e) {
+ log.warn("Exception when determining kerberos principal", e);
+ return null;
+ }
+ }
+
+ private GSSCredential logInAsAcceptor(Subject subject, String servicePrincipal) {
+ // GSS
+ Iterator<KerberosPrincipal> krb5It = subject.getPrincipals(KerberosPrincipal.class).iterator();
+ if (!krb5It.hasNext())
+ return null;
+ KerberosPrincipal krb5Principal = null;
+ while (krb5It.hasNext()) {
+ KerberosPrincipal principal = krb5It.next();
+ if (principal.getName().equals(servicePrincipal))
+ krb5Principal = principal;
+ }
+
+ if (krb5Principal == null)
+ return null;
+
+ GSSManager manager = GSSManager.getInstance();
+ try {
+ GSSName gssName = manager.createName(krb5Principal.getName(), null);
+ GSSCredential serverCredentials = Subject.doAs(subject, new PrivilegedExceptionAction<GSSCredential>() {
+
+ @Override
+ public GSSCredential run() throws GSSException {
+ return manager.createCredential(gssName, GSSCredential.INDEFINITE_LIFETIME, KERBEROS_OID,
+ GSSCredential.ACCEPT_ONLY);
+
+ }
+
+ });
+ if (log.isDebugEnabled())
+ log.debug("GSS acceptor configured for " + krb5Principal);
+ return serverCredentials;
+ } catch (Exception gsse) {
+ throw new IllegalStateException("Cannot create acceptor credentials for " + krb5Principal, gsse);
+ }
+ }
+
+ public GSSCredential getAcceptorCredentials() {
+ return acceptorCredentials;
+ }
+
+ public boolean hasAcceptorCredentials() {
+ return acceptorCredentials != null;
+ }
+
+ public boolean isSingleUser() {
+ return singleUser;
+ }
+
+ public void setTransactionManager(WorkControl transactionManager) {
+ this.transactionManager = transactionManager;
+ }
+
+ public void setUserTransaction(WorkTransaction userTransaction) {
+ this.userTransaction = userTransaction;
+ }
+
+ /*
+ * STATIC
+ */
+
+ public final static Oid KERBEROS_OID;
+ static {
+ try {
+ KERBEROS_OID = new Oid("1.3.6.1.5.5.2");
+ } catch (GSSException e) {
+ throw new IllegalStateException("Cannot create Kerberos OID", e);
+ }
+ }
+}
--- /dev/null
+package org.argeo.cms.internal.osgi;
+
+import java.io.FilePermission;
+import java.lang.reflect.ReflectPermission;
+import java.net.SocketPermission;
+import java.security.AllPermission;
+import java.util.PropertyPermission;
+
+import javax.security.auth.AuthPermission;
+
+import org.osgi.framework.AdminPermission;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServicePermission;
+import org.osgi.service.cm.ConfigurationPermission;
+import org.osgi.service.condpermadmin.BundleLocationCondition;
+import org.osgi.service.condpermadmin.ConditionInfo;
+import org.osgi.service.condpermadmin.ConditionalPermissionAdmin;
+import org.osgi.service.condpermadmin.ConditionalPermissionInfo;
+import org.osgi.service.condpermadmin.ConditionalPermissionUpdate;
+import org.osgi.service.permissionadmin.PermissionAdmin;
+import org.osgi.service.permissionadmin.PermissionInfo;
+
+/** Security profile based on OSGi {@link PermissionAdmin}. */
+public interface SecurityProfile {
+ BundleContext bc = FrameworkUtil.getBundle(SecurityProfile.class).getBundleContext();
+
+ default void applySystemPermissions(ConditionalPermissionAdmin permissionAdmin) {
+ ConditionalPermissionUpdate update = permissionAdmin.newConditionalPermissionUpdate();
+ // Self
+// String nodeAPiBundleLocation = locate(NodeUtils.class);
+// update.getConditionalPermissionInfos()
+// .add(permissionAdmin.newConditionalPermissionInfo(null,
+// new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+// new String[] { nodeAPiBundleLocation }) },
+// new PermissionInfo[] { new PermissionInfo(AllPermission.class.getName(), null, null) },
+// ConditionalPermissionInfo.ALLOW));
+ String cmsBundleLocation = locate(SecurityProfile.class);
+ update.getConditionalPermissionInfos()
+ .add(permissionAdmin.newConditionalPermissionInfo(null,
+ new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+ new String[] { cmsBundleLocation }) },
+ new PermissionInfo[] { new PermissionInfo(AllPermission.class.getName(), null, null) },
+ ConditionalPermissionInfo.ALLOW));
+ String frameworkBundleLocation = bc.getBundle(0).getLocation();
+ update.getConditionalPermissionInfos()
+ .add(permissionAdmin.newConditionalPermissionInfo(null,
+ new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+ new String[] { frameworkBundleLocation }) },
+ new PermissionInfo[] { new PermissionInfo(AllPermission.class.getName(), null, null) },
+ ConditionalPermissionInfo.ALLOW));
+ // All
+ // FIXME understand why Jetty and Jackrabbit require that
+ update.getConditionalPermissionInfos()
+ .add(permissionAdmin.newConditionalPermissionInfo(null, null, new PermissionInfo[] {
+ new PermissionInfo(SocketPermission.class.getName(), "localhost:7070", "listen,resolve"),
+ new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>", "read,write,delete"),
+ new PermissionInfo(PropertyPermission.class.getName(), "DEBUG", "read"),
+ new PermissionInfo(PropertyPermission.class.getName(), "STOP.*", "read"),
+ new PermissionInfo(PropertyPermission.class.getName(), "org.apache.jackrabbit.*", "read"),
+ new PermissionInfo(RuntimePermission.class.getName(), "*", "*"), },
+ ConditionalPermissionInfo.ALLOW));
+
+ // Eclipse
+ // update.getConditionalPermissionInfos()
+ // .add(permissionAdmin.newConditionalPermissionInfo(null,
+ // new ConditionInfo[] { new
+ // ConditionInfo(BundleLocationCondition.class.getName(),
+ // new String[] { "*/org.eclipse.*" }) },
+ // new PermissionInfo[] { new
+ // PermissionInfo(RuntimePermission.class.getName(), "*", "*"),
+ // new PermissionInfo(AdminPermission.class.getName(), "*", "*"),
+ // new PermissionInfo(ServicePermission.class.getName(), "*", "get"),
+ // new PermissionInfo(ServicePermission.class.getName(), "*",
+ // "register"),
+ // new PermissionInfo(TopicPermission.class.getName(), "*", "publish"),
+ // new PermissionInfo(TopicPermission.class.getName(), "*",
+ // "subscribe"),
+ // new PermissionInfo(PropertyPermission.class.getName(), "osgi.*",
+ // "read"),
+ // new PermissionInfo(PropertyPermission.class.getName(), "eclipse.*",
+ // "read"),
+ // new PermissionInfo(PropertyPermission.class.getName(),
+ // "org.eclipse.*", "read"),
+ // new PermissionInfo(PropertyPermission.class.getName(), "equinox.*",
+ // "read"),
+ // new PermissionInfo(PropertyPermission.class.getName(), "xml.*",
+ // "read"),
+ // new PermissionInfo("org.eclipse.equinox.log.LogPermission", "*",
+ // "log"), },
+ // ConditionalPermissionInfo.ALLOW));
+ update.getConditionalPermissionInfos()
+ .add(permissionAdmin.newConditionalPermissionInfo(null,
+ new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+ new String[] { "*/org.eclipse.*" }) },
+ new PermissionInfo[] { new PermissionInfo(AllPermission.class.getName(), null, null), },
+ ConditionalPermissionInfo.ALLOW));
+ update.getConditionalPermissionInfos()
+ .add(permissionAdmin.newConditionalPermissionInfo(null,
+ new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+ new String[] { "*/org.apache.felix.*" }) },
+ new PermissionInfo[] { new PermissionInfo(AllPermission.class.getName(), null, null), },
+ ConditionalPermissionInfo.ALLOW));
+
+ // Configuration admin
+// update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
+// new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+// new String[] { locate(configurationAdmin.getService().getClass()) }) },
+// new PermissionInfo[] { new PermissionInfo(ConfigurationPermission.class.getName(), "*", "configure"),
+// new PermissionInfo(AdminPermission.class.getName(), "*", "*"),
+// new PermissionInfo(PropertyPermission.class.getName(), "osgi.*", "read"), },
+// ConditionalPermissionInfo.ALLOW));
+
+ // Bitronix
+// update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
+// new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+// new String[] { locate(BitronixTransactionManager.class) }) },
+// new PermissionInfo[] { new PermissionInfo(PropertyPermission.class.getName(), "bitronix.tm.*", "read"),
+// new PermissionInfo(RuntimePermission.class.getName(), "getClassLoader", null),
+// new PermissionInfo(MBeanServerPermission.class.getName(), "createMBeanServer", null),
+// new PermissionInfo(MBeanPermission.class.getName(), "bitronix.tm.*", "registerMBean"),
+// new PermissionInfo(MBeanTrustPermission.class.getName(), "register", null) },
+// ConditionalPermissionInfo.ALLOW));
+
+ // DS
+ Bundle dsBundle = findBundle("org.eclipse.equinox.ds");
+ update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
+ new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+ new String[] { dsBundle.getLocation() }) },
+ new PermissionInfo[] { new PermissionInfo(ConfigurationPermission.class.getName(), "*", "configure"),
+ new PermissionInfo(AdminPermission.class.getName(), "*", "*"),
+ new PermissionInfo(ServicePermission.class.getName(), "*", "get"),
+ new PermissionInfo(ServicePermission.class.getName(), "*", "register"),
+ new PermissionInfo(PropertyPermission.class.getName(), "osgi.*", "read"),
+ new PermissionInfo(PropertyPermission.class.getName(), "xml.*", "read"),
+ new PermissionInfo(PropertyPermission.class.getName(), "equinox.*", "read"),
+ new PermissionInfo(RuntimePermission.class.getName(), "accessDeclaredMembers", null),
+ new PermissionInfo(RuntimePermission.class.getName(), "getClassLoader", null),
+ new PermissionInfo(ReflectPermission.class.getName(), "suppressAccessChecks", null), },
+ ConditionalPermissionInfo.ALLOW));
+
+ // Jetty
+ // Bundle jettyUtilBundle = findBundle("org.eclipse.equinox.http.jetty");
+ update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
+ new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+ new String[] { "*/org.eclipse.jetty.*" }) },
+ new PermissionInfo[] {
+ new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>", "read,write,delete"), },
+ ConditionalPermissionInfo.ALLOW));
+ Bundle servletBundle = findBundle("javax.servlet");
+ update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
+ new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+ new String[] { servletBundle.getLocation() }) },
+ new PermissionInfo[] { new PermissionInfo(PropertyPermission.class.getName(),
+ "org.glassfish.web.rfc2109_cookie_names_enforced", "read") },
+ ConditionalPermissionInfo.ALLOW));
+
+ // required to be able to get the BundleContext in the customizer
+ Bundle jettyCustomizerBundle = findBundle("org.argeo.ext.equinox.jetty");
+ update.getConditionalPermissionInfos()
+ .add(permissionAdmin.newConditionalPermissionInfo(null,
+ new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+ new String[] { jettyCustomizerBundle.getLocation() }) },
+ new PermissionInfo[] { new PermissionInfo(AdminPermission.class.getName(), "*", "*"), },
+ ConditionalPermissionInfo.ALLOW));
+
+ // Blueprint
+// Bundle blueprintBundle = findBundle("org.eclipse.gemini.blueprint.core");
+// update.getConditionalPermissionInfos()
+// .add(permissionAdmin.newConditionalPermissionInfo(null,
+// new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+// new String[] { blueprintBundle.getLocation() }) },
+// new PermissionInfo[] { new PermissionInfo(RuntimePermission.class.getName(), "*", null),
+// new PermissionInfo(AdminPermission.class.getName(), "*", "*"), },
+// ConditionalPermissionInfo.ALLOW));
+// Bundle blueprintExtenderBundle = findBundle("org.eclipse.gemini.blueprint.extender");
+// update.getConditionalPermissionInfos()
+// .add(permissionAdmin
+// .newConditionalPermissionInfo(null,
+// new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+// new String[] { blueprintExtenderBundle.getLocation() }) },
+// new PermissionInfo[] { new PermissionInfo(RuntimePermission.class.getName(), "*", null),
+// new PermissionInfo(PropertyPermission.class.getName(), "org.eclipse.gemini.*",
+// "read"),
+// new PermissionInfo(AdminPermission.class.getName(), "*", "*"),
+// new PermissionInfo(ServicePermission.class.getName(), "*", "register"), },
+// ConditionalPermissionInfo.ALLOW));
+// Bundle springCoreBundle = findBundle("org.springframework.core");
+// update.getConditionalPermissionInfos()
+// .add(permissionAdmin.newConditionalPermissionInfo(null,
+// new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+// new String[] { springCoreBundle.getLocation() }) },
+// new PermissionInfo[] { new PermissionInfo(RuntimePermission.class.getName(), "*", null),
+// new PermissionInfo(AdminPermission.class.getName(), "*", "*"), },
+// ConditionalPermissionInfo.ALLOW));
+// Bundle blueprintIoBundle = findBundle("org.eclipse.gemini.blueprint.io");
+// update.getConditionalPermissionInfos()
+// .add(permissionAdmin.newConditionalPermissionInfo(null,
+// new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+// new String[] { blueprintIoBundle.getLocation() }) },
+// new PermissionInfo[] { new PermissionInfo(RuntimePermission.class.getName(), "*", null),
+// new PermissionInfo(AdminPermission.class.getName(), "*", "*"), },
+// ConditionalPermissionInfo.ALLOW));
+
+ // Equinox
+ Bundle registryBundle = findBundle("org.eclipse.equinox.registry");
+ update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
+ new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+ new String[] { registryBundle.getLocation() }) },
+ new PermissionInfo[] { new PermissionInfo(PropertyPermission.class.getName(), "eclipse.*", "read"),
+ new PermissionInfo(PropertyPermission.class.getName(), "osgi.*", "read"),
+ new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>", "read,write,delete"), },
+ ConditionalPermissionInfo.ALLOW));
+
+ Bundle equinoxUtilBundle = findBundle("org.eclipse.equinox.util");
+ update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
+ new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+ new String[] { equinoxUtilBundle.getLocation() }) },
+ new PermissionInfo[] { new PermissionInfo(PropertyPermission.class.getName(), "equinox.*", "read"),
+ new PermissionInfo(ServicePermission.class.getName(), "*", "get"),
+ new PermissionInfo(ServicePermission.class.getName(), "*", "register"), },
+ ConditionalPermissionInfo.ALLOW));
+ Bundle equinoxCommonBundle = findBundle("org.eclipse.equinox.common");
+ update.getConditionalPermissionInfos()
+ .add(permissionAdmin.newConditionalPermissionInfo(null,
+ new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+ new String[] { equinoxCommonBundle.getLocation() }) },
+ new PermissionInfo[] { new PermissionInfo(AdminPermission.class.getName(), "*", "*"), },
+ ConditionalPermissionInfo.ALLOW));
+
+ Bundle consoleBundle = findBundle("org.eclipse.equinox.console");
+ update.getConditionalPermissionInfos()
+ .add(permissionAdmin.newConditionalPermissionInfo(null,
+ new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+ new String[] { consoleBundle.getLocation() }) },
+ new PermissionInfo[] { new PermissionInfo(ServicePermission.class.getName(), "*", "register"),
+ new PermissionInfo(AdminPermission.class.getName(), "*", "listener") },
+ ConditionalPermissionInfo.ALLOW));
+ Bundle preferencesBundle = findBundle("org.eclipse.equinox.preferences");
+ update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
+ new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+ new String[] { preferencesBundle.getLocation() }) },
+ new PermissionInfo[] {
+ new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>", "read,write,delete"), },
+ ConditionalPermissionInfo.ALLOW));
+ Bundle appBundle = findBundle("org.eclipse.equinox.app");
+ update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
+ new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+ new String[] { appBundle.getLocation() }) },
+ new PermissionInfo[] {
+ new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>", "read,write,delete"), },
+ ConditionalPermissionInfo.ALLOW));
+
+ // Jackrabbit
+ Bundle jackrabbitCoreBundle = findBundle("org.apache.jackrabbit.core");
+ update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
+ new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+ new String[] { jackrabbitCoreBundle.getLocation() }) },
+ new PermissionInfo[] {
+ new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>", "read,write,delete"),
+ new PermissionInfo(PropertyPermission.class.getName(), "*", "read,write"),
+ new PermissionInfo(AuthPermission.class.getName(), "getSubject", null),
+ new PermissionInfo(AuthPermission.class.getName(), "getLoginConfiguration", null),
+ new PermissionInfo(AuthPermission.class.getName(), "createLoginContext.Jackrabbit", null), },
+ ConditionalPermissionInfo.ALLOW));
+ Bundle jackrabbitDataBundle = findBundle("org.apache.jackrabbit.data");
+ update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
+ new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+ new String[] { jackrabbitDataBundle.getLocation() }) },
+ new PermissionInfo[] { new PermissionInfo(PropertyPermission.class.getName(), "*", "read,write") },
+ ConditionalPermissionInfo.ALLOW));
+ Bundle jackrabbitCommonBundle = findBundle("org.apache.jackrabbit.jcr.commons");
+ update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
+ new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+ new String[] { jackrabbitCommonBundle.getLocation() }) },
+ new PermissionInfo[] { new PermissionInfo(AuthPermission.class.getName(), "getSubject", null),
+ new PermissionInfo(AuthPermission.class.getName(), "createLoginContext.Jackrabbit", null), },
+ ConditionalPermissionInfo.ALLOW));
+
+ Bundle jackrabbitExtBundle = findBundle("org.argeo.ext.jackrabbit");
+ update.getConditionalPermissionInfos()
+ .add(permissionAdmin.newConditionalPermissionInfo(null,
+ new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+ new String[] { jackrabbitExtBundle.getLocation() }) },
+ new PermissionInfo[] { new PermissionInfo(AuthPermission.class.getName(), "*", "*"), },
+ ConditionalPermissionInfo.ALLOW));
+
+ // Tika
+ Bundle tikaCoreBundle = findBundle("org.apache.tika.core");
+ update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
+ new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+ new String[] { tikaCoreBundle.getLocation() }) },
+ new PermissionInfo[] { new PermissionInfo(PropertyPermission.class.getName(), "*", "read,write"),
+ new PermissionInfo(AdminPermission.class.getName(), "*", "*") },
+ ConditionalPermissionInfo.ALLOW));
+ Bundle luceneBundle = findBundle("org.apache.lucene");
+ update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
+ new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+ new String[] { luceneBundle.getLocation() }) },
+ new PermissionInfo[] {
+ new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>", "read,write,delete"),
+ new PermissionInfo(PropertyPermission.class.getName(), "*", "read"),
+ new PermissionInfo(AdminPermission.class.getName(), "*", "*") },
+ ConditionalPermissionInfo.ALLOW));
+
+ // COMMIT
+ update.commit();
+ }
+
+ /** @return bundle location */
+ default String locate(Class<?> clzz) {
+ return FrameworkUtil.getBundle(clzz).getLocation();
+ }
+
+ /** Can be null */
+ default Bundle findBundle(String symbolicName) {
+ for (Bundle b : bc.getBundles())
+ if (b.getSymbolicName().equals(symbolicName))
+ return b;
+ return null;
+ }
+
+}
--- /dev/null
+package org.argeo.cms.internal.runtime;
+
+import static java.util.Locale.ENGLISH;
+
+import java.lang.management.ManagementFactory;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.api.cms.CmsContext;
+import org.argeo.api.cms.CmsDeployment;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.CmsState;
+import org.argeo.cms.LocaleUtils;
+import org.argeo.cms.internal.osgi.NodeUserAdmin;
+import org.ietf.jgss.GSSCredential;
+import org.osgi.service.useradmin.UserAdmin;
+
+public class CmsContextImpl implements CmsContext {
+ private final CmsLog log = CmsLog.getLog(getClass());
+// private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
+
+// private EgoRepository egoRepository;
+ private static CompletableFuture<CmsContextImpl> instance = new CompletableFuture<CmsContextImpl>();
+
+ private CmsState cmsState;
+ private CmsDeployment cmsDeployment;
+ private UserAdmin userAdmin;
+
+ // i18n
+ private Locale defaultLocale;
+ private List<Locale> locales = null;
+
+ private Long availableSince;
+
+// public CmsContextImpl() {
+// initTrackers();
+// }
+
+ public void init() {
+ Object defaultLocaleValue = KernelUtils.getFrameworkProp(CmsConstants.I18N_DEFAULT_LOCALE);
+ defaultLocale = defaultLocaleValue != null ? new Locale(defaultLocaleValue.toString())
+ : new Locale(ENGLISH.getLanguage());
+ locales = LocaleUtils.asLocaleList(KernelUtils.getFrameworkProp(CmsConstants.I18N_LOCALES));
+ // node repository
+// new ServiceTracker<Repository, Repository>(bc, Repository.class, null) {
+// @Override
+// public Repository addingService(ServiceReference<Repository> reference) {
+// Object cn = reference.getProperty(NodeConstants.CN);
+// if (cn != null && cn.equals(NodeConstants.EGO_REPOSITORY)) {
+//// egoRepository = (EgoRepository) bc.getService(reference);
+// if (log.isTraceEnabled())
+// log.trace("Home repository is available");
+// }
+// return super.addingService(reference);
+// }
+//
+// @Override
+// public void removedService(ServiceReference<Repository> reference, Repository service) {
+// super.removedService(reference, service);
+//// egoRepository = null;
+// }
+//
+// }.open();
+
+ checkReadiness();
+
+ setInstance(this);
+ }
+
+ public void destroy() {
+ setInstance(null);
+ }
+
+ /**
+ * Checks whether the deployment is available according to expectations, and
+ * mark it as available.
+ */
+ private void checkReadiness() {
+ if (isAvailable())
+ return;
+ if (cmsDeployment != null && userAdmin != null) {
+ String data = KernelUtils.getFrameworkProp(KernelUtils.OSGI_INSTANCE_AREA);
+ String state = KernelUtils.getFrameworkProp(KernelUtils.OSGI_CONFIGURATION_AREA);
+ availableSince = System.currentTimeMillis();
+ long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime();
+ String jvmUptimeStr = " in " + (jvmUptime / 1000) + "." + (jvmUptime % 1000) + "s";
+ log.info("## ARGEO CMS AVAILABLE" + (log.isDebugEnabled() ? jvmUptimeStr : "") + " ##");
+ if (log.isDebugEnabled()) {
+ log.debug("## state: " + state);
+ if (data != null)
+ log.debug("## data: " + data);
+ }
+ long begin = cmsState.getAvailableSince();
+ long initDuration = System.currentTimeMillis() - begin;
+ if (log.isTraceEnabled())
+ log.trace("Kernel initialization took " + initDuration + "ms");
+ tributeToFreeSoftware(initDuration);
+ } else {
+ throw new IllegalStateException("Deployment is not available");
+ }
+ }
+
+ final private void tributeToFreeSoftware(long initDuration) {
+ if (log.isTraceEnabled()) {
+ long ms = initDuration / 100;
+ log.trace("Spend " + ms + "ms" + " reflecting on the progress brought to mankind" + " by Free Software...");
+ long beginNano = System.nanoTime();
+ try {
+ Thread.sleep(ms, 0);
+ } catch (InterruptedException e) {
+ // silent
+ }
+ long durationNano = System.nanoTime() - beginNano;
+ final double M = 1000d * 1000d;
+ double sleepAccuracy = ((double) durationNano) / (ms * M);
+ log.trace("Sleep accuracy: " + String.format("%.2f", 100 - (sleepAccuracy * 100 - 100)) + " %");
+ }
+ }
+
+ @Override
+ public void createWorkgroup(String dn) {
+// if (egoRepository == null)
+// throw new CmsException("Ego repository is not available");
+// // TODO add check that the group exists
+// egoRepository.createWorkgroup(dn);
+ throw new UnsupportedOperationException();
+ }
+
+ public void setCmsDeployment(CmsDeployment cmsDeployment) {
+ this.cmsDeployment = cmsDeployment;
+ }
+
+ public void setCmsState(CmsState cmsState) {
+ this.cmsState = cmsState;
+ }
+
+ public void setUserAdmin(UserAdmin userAdmin) {
+ this.userAdmin = userAdmin;
+ }
+
+ @Override
+ public Locale getDefaultLocale() {
+ return defaultLocale;
+ }
+
+ @Override
+ public List<Locale> getLocales() {
+ return locales;
+ }
+
+ @Override
+ public synchronized Long getAvailableSince() {
+ return availableSince;
+ }
+
+ public synchronized boolean isAvailable() {
+ return availableSince != null;
+ }
+
+ /*
+ * STATIC
+ */
+
+ public synchronized static CmsContext getCmsContext() {
+ return getInstance();
+ }
+
+ /** Required by USER login module. */
+ public synchronized static UserAdmin getUserAdmin() {
+ return getInstance().userAdmin;
+ }
+
+ /** Required by SPNEGO login module. */
+ @Deprecated
+ public synchronized static GSSCredential getAcceptorCredentials() {
+ // FIXME find a cleaner way
+ return ((NodeUserAdmin) getInstance().userAdmin).getAcceptorCredentials();
+ }
+
+ private synchronized static void setInstance(CmsContextImpl cmsContextImpl) {
+ if (cmsContextImpl != null) {
+ if (instance.isDone())
+ throw new IllegalStateException("CMS Context is already set");
+ instance.complete(cmsContextImpl);
+ } else {
+ instance = new CompletableFuture<CmsContextImpl>();
+ }
+ }
+
+ private synchronized static CmsContextImpl getInstance() {
+ try {
+ return instance.get();
+ } catch (InterruptedException | ExecutionException e) {
+ throw new IllegalStateException("Cannot retrieve CMS Context", e);
+ }
+ }
+
+}
--- /dev/null
+package org.argeo.cms.internal.runtime;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Dictionary;
+
+import org.argeo.api.cms.CmsDeployment;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.CmsState;
+import org.argeo.cms.internal.osgi.DeployConfig;
+import org.eclipse.equinox.http.jetty.JettyConfigurator;
+import org.osgi.service.http.HttpService;
+
+/** Implementation of a CMS deployment. */
+public class CmsDeploymentImpl implements CmsDeployment {
+ private final CmsLog log = CmsLog.getLog(getClass());
+// private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
+
+// private Long availableSince;
+
+ // Readiness
+// private boolean nodeAvailable = false;
+// private boolean userAdminAvailable = false;
+ private boolean httpExpected = false;
+// private boolean httpAvailable = false;
+ private HttpService httpService;
+
+ private CmsState cmsState;
+ private DeployConfig deployConfig;
+
+ public CmsDeploymentImpl() {
+// ServiceReference<NodeState> nodeStateSr = bc.getServiceReference(NodeState.class);
+// if (nodeStateSr == null)
+// throw new CmsException("No node state available");
+
+// NodeState nodeState = bc.getService(nodeStateSr);
+// cleanState = nodeState.isClean();
+
+// nodeHttp = new NodeHttp();
+ initTrackers();
+ }
+
+ private void initTrackers() {
+// ServiceTracker<?, ?> httpSt = new ServiceTracker<HttpService, HttpService>(bc, HttpService.class, null) {
+//
+// @Override
+// public HttpService addingService(ServiceReference<HttpService> sr) {
+// httpAvailable = true;
+// Object httpPort = sr.getProperty("http.port");
+// Object httpsPort = sr.getProperty("https.port");
+// log.info(httpPortsMsg(httpPort, httpsPort));
+// checkReadiness();
+// return super.addingService(sr);
+// }
+// };
+// // httpSt.open();
+// KernelUtils.asyncOpen(httpSt);
+
+// ServiceTracker<?, ?> userAdminSt = new ServiceTracker<UserAdmin, UserAdmin>(bc, UserAdmin.class, null) {
+// @Override
+// public UserAdmin addingService(ServiceReference<UserAdmin> reference) {
+// UserAdmin userAdmin = super.addingService(reference);
+// addStandardSystemRoles(userAdmin);
+// userAdminAvailable = true;
+// checkReadiness();
+// return userAdmin;
+// }
+// };
+// // userAdminSt.open();
+// KernelUtils.asyncOpen(userAdminSt);
+
+// ServiceTracker<?, ?> confAdminSt = new ServiceTracker<ConfigurationAdmin, ConfigurationAdmin>(bc,
+// ConfigurationAdmin.class, null) {
+// @Override
+// public ConfigurationAdmin addingService(ServiceReference<ConfigurationAdmin> reference) {
+// ConfigurationAdmin configurationAdmin = bc.getService(reference);
+//// boolean isClean;
+//// try {
+//// Configuration[] confs = configurationAdmin
+//// .listConfigurations("(service.factoryPid=" + CmsConstants.NODE_USER_ADMIN_PID + ")");
+//// isClean = confs == null || confs.length == 0;
+//// } catch (Exception e) {
+//// throw new IllegalStateException("Cannot analyse clean state", e);
+//// }
+// deployConfig = new DeployConfig(configurationAdmin, isClean);
+// Activator.registerService(CmsDeployment.class, CmsDeploymentImpl.this, null);
+//// JcrInitUtils.addToDeployment(CmsDeployment.this);
+// httpExpected = deployConfig.getProps(KernelConstants.JETTY_FACTORY_PID, "default") != null;
+// try {
+// Configuration[] configs = configurationAdmin
+// .listConfigurations("(service.factoryPid=" + CmsConstants.NODE_USER_ADMIN_PID + ")");
+//
+// boolean hasDomain = false;
+// for (Configuration config : configs) {
+// Object realm = config.getProperties().get(UserAdminConf.realm.name());
+// if (realm != null) {
+// log.debug("Found realm: " + realm);
+// hasDomain = true;
+// }
+// }
+// if (hasDomain) {
+// loadIpaJaasConfiguration();
+// }
+// } catch (Exception e) {
+// throw new IllegalStateException("Cannot initialize config", e);
+// }
+// return super.addingService(reference);
+// }
+// };
+// // confAdminSt.open();
+// KernelUtils.asyncOpen(confAdminSt);
+ }
+
+ public void init() {
+ httpExpected = deployConfig.getProps(KernelConstants.JETTY_FACTORY_PID, "default") != null;
+ if (deployConfig.hasDomain()) {
+ loadIpaJaasConfiguration();
+ }
+
+// while (!isHttpAvailableOrNotExpected()) {
+// try {
+// Thread.sleep(100);
+// } catch (InterruptedException e) {
+// log.error("Interrupted while waiting for http");
+// }
+// }
+ }
+
+ public void addFactoryDeployConfig(String factoryPid, Dictionary<String, Object> props) {
+ deployConfig.putFactoryDeployConfig(factoryPid, props);
+ deployConfig.save();
+ try {
+ deployConfig.loadConfigs();
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ public Dictionary<String, Object> getProps(String factoryPid, String cn) {
+ return deployConfig.getProps(factoryPid, cn);
+ }
+
+// private void addStandardSystemRoles(UserAdmin userAdmin) {
+// // we assume UserTransaction is already available (TODO make it more robust)
+// WorkTransaction userTransaction = bc.getService(bc.getServiceReference(WorkTransaction.class));
+// try {
+// userTransaction.begin();
+// Role adminRole = userAdmin.getRole(CmsConstants.ROLE_ADMIN);
+// if (adminRole == null) {
+// adminRole = userAdmin.createRole(CmsConstants.ROLE_ADMIN, Role.GROUP);
+// }
+// if (userAdmin.getRole(CmsConstants.ROLE_USER_ADMIN) == null) {
+// Group userAdminRole = (Group) userAdmin.createRole(CmsConstants.ROLE_USER_ADMIN, Role.GROUP);
+// userAdminRole.addMember(adminRole);
+// }
+// userTransaction.commit();
+// } catch (Exception e) {
+// try {
+// userTransaction.rollback();
+// } catch (Exception e1) {
+// // silent
+// }
+// throw new IllegalStateException("Cannot add standard system roles", e);
+// }
+// }
+
+ public boolean isHttpAvailableOrNotExpected() {
+ return (httpExpected ? httpService != null : true);
+ }
+
+ private void loadIpaJaasConfiguration() {
+ if (System.getProperty(KernelConstants.JAAS_CONFIG_PROP) == null) {
+ String jaasConfig = KernelConstants.JAAS_CONFIG_IPA;
+ URL url = getClass().getClassLoader().getResource(jaasConfig);
+ KernelUtils.setJaasConfiguration(url);
+ log.debug("Set IPA JAAS configuration.");
+ }
+ }
+
+ public void destroy() {
+// if (nodeHttp != null)
+// nodeHttp.destroy();
+
+ try {
+ JettyConfigurator.stopServer(KernelConstants.DEFAULT_JETTY_SERVER);
+ } catch (Exception e) {
+ log.error("Cannot stop default Jetty server.", e);
+ }
+
+ if (deployConfig != null) {
+ new Thread(() -> deployConfig.save(), "Save Argeo Deploy Config").start();
+ }
+ }
+
+ public void setDeployConfig(DeployConfig deployConfig) {
+ this.deployConfig = deployConfig;
+ }
+
+ public void setCmsState(CmsState cmsState) {
+ this.cmsState = cmsState;
+ }
+
+ public void setHttpService(HttpService httpService) {
+ this.httpService = httpService;
+ }
+
+}
--- /dev/null
+package org.argeo.cms.internal.runtime;
+
+import java.net.InetAddress;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.security.auth.login.Configuration;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.CmsState;
+import org.argeo.cms.auth.ident.IdentClient;
+import org.argeo.cms.internal.osgi.CmsShutdown;
+import org.osgi.framework.Constants;
+
+/**
+ * Implementation of a {@link CmsState}, initialising the required services.
+ */
+public class CmsStateImpl implements CmsState {
+ private final static CmsLog log = CmsLog.getLog(CmsStateImpl.class);
+// private final BundleContext bc = FrameworkUtil.getBundle(CmsState.class).getBundleContext();
+
+// private static CmsStateImpl instance;
+
+// private ExecutorService internalExecutorService;
+
+ // REFERENCES
+ private Long availableSince;
+
+
+// private ThreadGroup threadGroup = new ThreadGroup("CMS");
+ private List<Runnable> stopHooks = new ArrayList<>();
+
+ private String stateUuid;
+// private final boolean cleanState;
+ private String hostname;
+
+ public void init() {
+// instance = this;
+
+ Runtime.getRuntime().addShutdownHook(new CmsShutdown());
+// this.internalExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
+
+ try {
+ initSecurity();
+// initArgeoLogger();
+// initNode();
+
+ if (log.isTraceEnabled())
+ log.trace("CMS State started");
+ } catch (Throwable e) {
+ log.error("## FATAL: CMS activator failed", e);
+ }
+
+ this.stateUuid = KernelUtils.getFrameworkProp(Constants.FRAMEWORK_UUID);
+// this.cleanState = stateUuid.equals(frameworkUuid);
+ try {
+ this.hostname = InetAddress.getLocalHost().getHostName();
+ } catch (UnknownHostException e) {
+ log.error("Cannot set hostname: " + e);
+ }
+
+ availableSince = System.currentTimeMillis();
+ if (log.isDebugEnabled())
+ // log.debug("## CMS starting... stateUuid=" + this.stateUuid + (cleanState ? "
+ // (clean state) " : " "));
+ log.debug("## CMS starting... (" + stateUuid + ")");
+
+// initI18n();
+// initServices();
+
+ }
+
+ private void initSecurity() {
+ if (System.getProperty(KernelConstants.JAAS_CONFIG_PROP) == null) {
+ String jaasConfig = KernelConstants.JAAS_CONFIG;
+ URL url = getClass().getResource(jaasConfig);
+ // System.setProperty(KernelConstants.JAAS_CONFIG_PROP,
+ // url.toExternalForm());
+ KernelUtils.setJaasConfiguration(url);
+ }
+ // explicitly load JAAS configuration
+ Configuration.getConfiguration();
+ }
+
+// private void initI18n() {
+// Object defaultLocaleValue = KernelUtils.getFrameworkProp(CmsConstants.I18N_DEFAULT_LOCALE);
+// defaultLocale = defaultLocaleValue != null ? new Locale(defaultLocaleValue.toString())
+// : new Locale(ENGLISH.getLanguage());
+// locales = LocaleUtils.asLocaleList(KernelUtils.getFrameworkProp(CmsConstants.I18N_LOCALES));
+// }
+
+ private void initServices() {
+ // JTA
+// String tmType = KernelUtils.getFrameworkProp(CmsConstants.TRANSACTION_MANAGER,
+// CmsConstants.TRANSACTION_MANAGER_SIMPLE);
+// if (CmsConstants.TRANSACTION_MANAGER_SIMPLE.equals(tmType)) {
+// initSimpleTransactionManager();
+// } else if (CmsConstants.TRANSACTION_MANAGER_BITRONIX.equals(tmType)) {
+//// initBitronixTransactionManager();
+// throw new UnsupportedOperationException(
+// "Bitronix is not supported anymore, but could be again if there is enough interest.");
+// } else {
+// throw new IllegalArgumentException("Usupported transaction manager type " + tmType);
+// }
+
+ // POI
+// POIXMLTypeLoader.setClassLoader(CTConnection.class.getClassLoader());
+
+ // Tika
+// OpenDocumentParser odfParser = new OpenDocumentParser();
+// bc.registerService(Parser.class, odfParser, new Hashtable());
+// PDFParser pdfParser = new PDFParser();
+// bc.registerService(Parser.class, pdfParser, new Hashtable());
+// OOXMLParser ooxmlParser = new OOXMLParser();
+// bc.registerService(Parser.class, ooxmlParser, new Hashtable());
+// TesseractOCRParser ocrParser = new TesseractOCRParser();
+// ocrParser.setLanguage("ara");
+// bc.registerService(Parser.class, ocrParser, new Hashtable());
+
+// // JCR
+// RepositoryServiceFactory repositoryServiceFactory = new RepositoryServiceFactory();
+// stopHooks.add(() -> repositoryServiceFactory.shutdown());
+// Activator.registerService(ManagedServiceFactory.class, repositoryServiceFactory,
+// LangUtils.dict(Constants.SERVICE_PID, NodeConstants.NODE_REPOS_FACTORY_PID));
+//
+// NodeRepositoryFactory repositoryFactory = new NodeRepositoryFactory();
+// Activator.registerService(RepositoryFactory.class, repositoryFactory, null);
+
+ // Security
+// NodeUserAdmin userAdmin = new NodeUserAdmin(CmsConstants.ROLES_BASEDN, CmsConstants.TOKENS_BASEDN);
+// stopHooks.add(() -> userAdmin.destroy());
+// Activator.registerService(ManagedServiceFactory.class, userAdmin,
+// LangUtils.dict(Constants.SERVICE_PID, CmsConstants.NODE_USER_ADMIN_PID));
+
+ }
+
+// private void initSimpleTransactionManager() {
+// SimpleTransactionManager transactionManager = new SimpleTransactionManager();
+// Activator.registerService(WorkControl.class, transactionManager, null);
+// Activator.registerService(WorkTransaction.class, transactionManager, null);
+//// Activator.registerService(TransactionManager.class, transactionManager, null);
+//// Activator.registerService(UserTransaction.class, transactionManager, null);
+// // TODO TransactionSynchronizationRegistry
+// }
+
+// private void initBitronixTransactionManager() {
+// // TODO manage it in a managed service, as startup could be long
+// ServiceReference<TransactionManager> existingTm = bc.getServiceReference(TransactionManager.class);
+// if (existingTm != null) {
+// if (log.isDebugEnabled())
+// log.debug("Using provided transaction manager " + existingTm);
+// return;
+// }
+//
+// if (!TransactionManagerServices.isTransactionManagerRunning()) {
+// bitronix.tm.Configuration tmConf = TransactionManagerServices.getConfiguration();
+// tmConf.setServerId(UUID.randomUUID().toString());
+//
+// Bundle bitronixBundle = FrameworkUtil.getBundle(bitronix.tm.Configuration.class);
+// File tmBaseDir = bitronixBundle.getDataFile(KernelConstants.DIR_TRANSACTIONS);
+// File tmDir1 = new File(tmBaseDir, "btm1");
+// tmDir1.mkdirs();
+// tmConf.setLogPart1Filename(new File(tmDir1, tmDir1.getName() + ".tlog").getAbsolutePath());
+// File tmDir2 = new File(tmBaseDir, "btm2");
+// tmDir2.mkdirs();
+// tmConf.setLogPart2Filename(new File(tmDir2, tmDir2.getName() + ".tlog").getAbsolutePath());
+// }
+// BitronixTransactionManager transactionManager = getTransactionManager();
+// stopHooks.add(() -> transactionManager.shutdown());
+// BitronixTransactionSynchronizationRegistry transactionSynchronizationRegistry = getTransactionSynchronizationRegistry();
+// // register
+// bc.registerService(TransactionManager.class, transactionManager, null);
+// bc.registerService(UserTransaction.class, transactionManager, null);
+// bc.registerService(TransactionSynchronizationRegistry.class, transactionSynchronizationRegistry, null);
+// if (log.isDebugEnabled())
+// log.debug("Initialised default Bitronix transaction manager");
+// }
+
+ public void destroy() {
+ if (log.isDebugEnabled())
+ log.debug("CMS stopping... (" + this.stateUuid + ")");
+
+ // In a different thread in order to avoid interruptions
+ Thread stopHookThread = new Thread(() -> applyStopHooks(), "Apply Argeo Stop Hooks");
+ stopHookThread.start();
+ try {
+ stopHookThread.join(10 * 60 * 1000);
+ } catch (InterruptedException e) {
+ // silent
+ }
+
+// internalExecutorService.shutdown();
+
+ long duration = ((System.currentTimeMillis() - availableSince) / 1000) / 60;
+ log.info("## ARGEO CMS STOPPED after " + (duration / 60) + "h " + (duration % 60) + "min uptime ##");
+ }
+
+ /** Apply shutdown hoos in reverse order. */
+ private void applyStopHooks() {
+ for (int i = stopHooks.size() - 1; i >= 0; i--) {
+ try {
+ stopHooks.get(i).run();
+ } catch (Exception e) {
+ log.error("Could not run shutdown hook #" + i);
+ }
+ }
+ // Clean hanging Gogo shell thread
+ new GogoShellKiller().start();
+
+// instance = null;
+ }
+
+// @Override
+// public boolean isClean() {
+// return cleanState;
+// }
+
+ @Override
+ public Long getAvailableSince() {
+ return availableSince;
+ }
+
+ /*
+ * ACCESSORS
+ */
+ public String getHostname() {
+ return hostname;
+ }
+
+ /*
+ * STATIC
+ */
+ public static IdentClient getIdentClient(String remoteAddr) {
+ if (!IdentClient.isDefaultAuthdPassphraseFileAvailable())
+ return null;
+ // TODO make passphrase more configurable
+ return new IdentClient(remoteAddr);
+ }
+}
--- /dev/null
+package org.argeo.cms.internal.runtime;
+
+/**
+ * Workaround for killing Gogo shell by system shutdown.
+ *
+ * @see https://issues.apache.org/jira/browse/FELIX-4208
+ */
+class GogoShellKiller extends Thread {
+
+ public GogoShellKiller() {
+ super("Gogo Shell Killer");
+ setDaemon(true);
+ }
+
+ @Override
+ public void run() {
+ ThreadGroup rootTg = getRootThreadGroup(null);
+ Thread gogoShellThread = findGogoShellThread(rootTg);
+ if (gogoShellThread == null)
+ return;
+ while (getNonDaemonCount(rootTg) > 2) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ // silent
+ }
+ }
+ gogoShellThread = findGogoShellThread(rootTg);
+ if (gogoShellThread == null)
+ return;
+ // No non-deamon threads left, forcibly halting the VM
+ Runtime.getRuntime().halt(0);
+ }
+
+ private ThreadGroup getRootThreadGroup(ThreadGroup tg) {
+ if (tg == null)
+ tg = Thread.currentThread().getThreadGroup();
+ if (tg.getParent() == null)
+ return tg;
+ else
+ return getRootThreadGroup(tg.getParent());
+ }
+
+ private int getNonDaemonCount(ThreadGroup rootThreadGroup) {
+ Thread[] threads = new Thread[rootThreadGroup.activeCount()];
+ rootThreadGroup.enumerate(threads);
+ int nonDameonCount = 0;
+ for (Thread t : threads)
+ if (t != null && !t.isDaemon())
+ nonDameonCount++;
+ return nonDameonCount;
+ }
+
+ private Thread findGogoShellThread(ThreadGroup rootThreadGroup) {
+ Thread[] threads = new Thread[rootThreadGroup.activeCount()];
+ rootThreadGroup.enumerate(threads, true);
+ for (Thread thread : threads) {
+ if (thread.getName().equals("pipe-gosh --login --noshutdown"))
+ return thread;
+ }
+ return null;
+ }
+
+}
\ No newline at end of file
--- /dev/null
+package org.argeo.cms.internal.runtime;
+
+import static org.argeo.cms.internal.runtime.KernelUtils.getFrameworkProp;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.io.Reader;
+import java.net.InetAddress;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.KeyStore;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.apache.commons.io.FileUtils;
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.cms.internal.http.InternalHttpConstants;
+import org.argeo.osgi.useradmin.UserAdminConf;
+
+/**
+ * Interprets framework properties in order to generate the initial deploy
+ * configuration.
+ */
+public class InitUtils {
+ private final static CmsLog log = CmsLog.getLog(InitUtils.class);
+
+ /** Override the provided config with the framework properties */
+ public static Dictionary<String, Object> getHttpServerConfig(Dictionary<String, Object> provided) {
+ String httpPort = getFrameworkProp("org.osgi.service.http.port");
+ String httpsPort = getFrameworkProp("org.osgi.service.http.port.secure");
+ /// TODO make it more generic
+ String httpHost = getFrameworkProp(
+ InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.HTTP_HOST);
+ String httpsHost = getFrameworkProp(
+ InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.HTTPS_HOST);
+ String webSocketEnabled = getFrameworkProp(
+ InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.WEBSOCKET_ENABLED);
+
+ final Hashtable<String, Object> props = new Hashtable<String, Object>();
+ // try {
+ if (httpPort != null || httpsPort != null) {
+ boolean httpEnabled = httpPort != null;
+ props.put(InternalHttpConstants.HTTP_ENABLED, httpEnabled);
+ boolean httpsEnabled = httpsPort != null;
+ props.put(InternalHttpConstants.HTTPS_ENABLED, httpsEnabled);
+
+ if (httpEnabled) {
+ props.put(InternalHttpConstants.HTTP_PORT, httpPort);
+ if (httpHost != null)
+ props.put(InternalHttpConstants.HTTP_HOST, httpHost);
+ }
+
+ if (httpsEnabled) {
+ props.put(InternalHttpConstants.HTTPS_PORT, httpsPort);
+ if (httpsHost != null)
+ props.put(InternalHttpConstants.HTTPS_HOST, httpsHost);
+
+ // server certificate
+ Path keyStorePath = KernelUtils.getOsgiInstancePath(KernelConstants.DEFAULT_KEYSTORE_PATH);
+ Path pemKeyPath = KernelUtils.getOsgiInstancePath(KernelConstants.DEFAULT_PEM_KEY_PATH);
+ Path pemCertPath = KernelUtils.getOsgiInstancePath(KernelConstants.DEFAULT_PEM_CERT_PATH);
+ String keyStorePasswordStr = getFrameworkProp(
+ InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.SSL_PASSWORD);
+ char[] keyStorePassword;
+ if (keyStorePasswordStr == null)
+ keyStorePassword = "changeit".toCharArray();
+ else
+ keyStorePassword = keyStorePasswordStr.toCharArray();
+
+ // if PEM files both exists, update the PKCS12 file
+ if (Files.exists(pemCertPath) && Files.exists(pemKeyPath)) {
+ // TODO check certificate update time? monitor changes?
+ KeyStore keyStore = PkiUtils.getKeyStore(keyStorePath, keyStorePassword, PkiUtils.PKCS12);
+ try (Reader key = Files.newBufferedReader(pemKeyPath, StandardCharsets.US_ASCII);
+ Reader cert = Files.newBufferedReader(pemCertPath, StandardCharsets.US_ASCII);) {
+ PkiUtils.loadPem(keyStore, key, keyStorePassword, cert);
+ PkiUtils.saveKeyStore(keyStorePath, keyStorePassword, keyStore);
+ if (log.isDebugEnabled())
+ log.debug("PEM certificate stored in " + keyStorePath);
+ } catch (IOException e) {
+ log.error("Cannot read PEM files " + pemKeyPath + " and " + pemCertPath, e);
+ }
+ }
+
+ if (!Files.exists(keyStorePath))
+ createSelfSignedKeyStore(keyStorePath, keyStorePassword, PkiUtils.PKCS12);
+ props.put(InternalHttpConstants.SSL_KEYSTORETYPE, PkiUtils.PKCS12);
+ props.put(InternalHttpConstants.SSL_KEYSTORE, keyStorePath.toString());
+ props.put(InternalHttpConstants.SSL_PASSWORD, new String(keyStorePassword));
+
+// props.put(InternalHttpConstants.SSL_KEYSTORETYPE, "PKCS11");
+// props.put(InternalHttpConstants.SSL_KEYSTORE, "../../nssdb");
+// props.put(InternalHttpConstants.SSL_PASSWORD, keyStorePassword);
+
+ // client certificate authentication
+ String wantClientAuth = getFrameworkProp(
+ InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.SSL_WANTCLIENTAUTH);
+ if (wantClientAuth != null)
+ props.put(InternalHttpConstants.SSL_WANTCLIENTAUTH, Boolean.parseBoolean(wantClientAuth));
+ String needClientAuth = getFrameworkProp(
+ InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.SSL_NEEDCLIENTAUTH);
+ if (needClientAuth != null)
+ props.put(InternalHttpConstants.SSL_NEEDCLIENTAUTH, Boolean.parseBoolean(needClientAuth));
+ }
+
+ // web socket
+ if (webSocketEnabled != null && webSocketEnabled.equals("true"))
+ props.put(InternalHttpConstants.WEBSOCKET_ENABLED, true);
+
+ props.put(CmsConstants.CN, CmsConstants.DEFAULT);
+ }
+ return props;
+ }
+
+ public static List<Dictionary<String, Object>> getUserDirectoryConfigs() {
+ List<Dictionary<String, Object>> res = new ArrayList<>();
+ File nodeBaseDir = KernelUtils.getOsgiInstancePath(KernelConstants.DIR_NODE).toFile();
+ List<String> uris = new ArrayList<>();
+
+ // node roles
+ String nodeRolesUri = getFrameworkProp(CmsConstants.ROLES_URI);
+ String baseNodeRoleDn = CmsConstants.ROLES_BASEDN;
+ if (nodeRolesUri == null) {
+ nodeRolesUri = baseNodeRoleDn + ".ldif";
+ File nodeRolesFile = new File(nodeBaseDir, nodeRolesUri);
+ if (!nodeRolesFile.exists())
+ try {
+ FileUtils.copyInputStreamToFile(InitUtils.class.getResourceAsStream(baseNodeRoleDn + ".ldif"),
+ nodeRolesFile);
+ } catch (IOException e) {
+ throw new RuntimeException("Cannot copy demo resource", e);
+ }
+ // nodeRolesUri = nodeRolesFile.toURI().toString();
+ }
+ uris.add(nodeRolesUri);
+
+ // node tokens
+ String nodeTokensUri = getFrameworkProp(CmsConstants.TOKENS_URI);
+ String baseNodeTokensDn = CmsConstants.TOKENS_BASEDN;
+ if (nodeTokensUri == null) {
+ nodeTokensUri = baseNodeTokensDn + ".ldif";
+ File nodeTokensFile = new File(nodeBaseDir, nodeTokensUri);
+ if (!nodeTokensFile.exists())
+ try {
+ FileUtils.copyInputStreamToFile(InitUtils.class.getResourceAsStream(baseNodeTokensDn + ".ldif"),
+ nodeTokensFile);
+ } catch (IOException e) {
+ throw new RuntimeException("Cannot copy demo resource", e);
+ }
+ // nodeRolesUri = nodeRolesFile.toURI().toString();
+ }
+ uris.add(nodeTokensUri);
+
+ // Business roles
+ String userAdminUris = getFrameworkProp(CmsConstants.USERADMIN_URIS);
+ if (userAdminUris == null) {
+ String demoBaseDn = "dc=example,dc=com";
+ userAdminUris = demoBaseDn + ".ldif";
+ File businessRolesFile = new File(nodeBaseDir, userAdminUris);
+ File systemRolesFile = new File(nodeBaseDir, "ou=roles,ou=node.ldif");
+ if (!businessRolesFile.exists())
+ try {
+ FileUtils.copyInputStreamToFile(InitUtils.class.getResourceAsStream(demoBaseDn + ".ldif"),
+ businessRolesFile);
+ if (!systemRolesFile.exists())
+ FileUtils.copyInputStreamToFile(
+ InitUtils.class.getResourceAsStream("example-ou=roles,ou=node.ldif"), systemRolesFile);
+ } catch (IOException e) {
+ throw new RuntimeException("Cannot copy demo resources", e);
+ }
+ // userAdminUris = businessRolesFile.toURI().toString();
+ log.warn("## DEV Using dummy base DN " + demoBaseDn);
+ // TODO downgrade security level
+ }
+ for (String userAdminUri : userAdminUris.split(" "))
+ uris.add(userAdminUri);
+
+ // Interprets URIs
+ for (String uri : uris) {
+ URI u;
+ try {
+ u = new URI(uri);
+ if (u.getPath() == null)
+ throw new IllegalArgumentException(
+ "URI " + uri + " must have a path in order to determine base DN");
+ if (u.getScheme() == null) {
+ if (uri.startsWith("/") || uri.startsWith("./") || uri.startsWith("../"))
+ u = new File(uri).getCanonicalFile().toURI();
+ else if (!uri.contains("/")) {
+ // u = KernelUtils.getOsgiInstanceUri(KernelConstants.DIR_NODE + '/' + uri);
+ u = new URI(uri);
+ } else
+ throw new IllegalArgumentException("Cannot interpret " + uri + " as an uri");
+ } else if (u.getScheme().equals(UserAdminConf.SCHEME_FILE)) {
+ u = new File(u).getCanonicalFile().toURI();
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Cannot interpret " + uri + " as an uri", e);
+ }
+ Dictionary<String, Object> properties = UserAdminConf.uriAsProperties(u.toString());
+ res.add(properties);
+ }
+
+ return res;
+ }
+
+ /**
+ * Called before node initialisation, in order populate OSGi instance are with
+ * some files (typically LDIF, etc).
+ */
+ public static void prepareFirstInitInstanceArea() {
+ String nodeInits = getFrameworkProp(CmsConstants.NODE_INIT);
+ if (nodeInits == null)
+ nodeInits = "../../init";
+
+ for (String nodeInit : nodeInits.split(",")) {
+
+ if (nodeInit.startsWith("http")) {
+ // TODO reconnect it
+ // registerRemoteInit(nodeInit);
+ } else {
+
+ // TODO use java.nio.file
+ File initDir;
+ if (nodeInit.startsWith("."))
+ initDir = KernelUtils.getExecutionDir(nodeInit);
+ else
+ initDir = new File(nodeInit);
+ // TODO also uncompress archives
+ if (initDir.exists())
+ try {
+ FileUtils.copyDirectory(initDir, KernelUtils.getOsgiInstanceDir(), new FileFilter() {
+
+ @Override
+ public boolean accept(File pathname) {
+ if (pathname.getName().equals(".svn") || pathname.getName().equals(".git"))
+ return false;
+ return true;
+ }
+ });
+ log.info("CMS initialized from " + initDir.getCanonicalPath());
+ } catch (IOException e) {
+ throw new RuntimeException("Cannot initialize from " + initDir, e);
+ }
+ }
+ }
+ }
+
+ private static void createSelfSignedKeyStore(Path keyStorePath, char[] keyStorePassword, String keyStoreType) {
+ // for (Provider provider : Security.getProviders())
+ // System.out.println(provider.getName());
+// File keyStoreFile = keyStorePath.toFile();
+ char[] keyPwd = Arrays.copyOf(keyStorePassword, keyStorePassword.length);
+ if (!Files.exists(keyStorePath)) {
+ try {
+ Files.createDirectories(keyStorePath.getParent());
+ KeyStore keyStore = PkiUtils.getKeyStore(keyStorePath, keyStorePassword, keyStoreType);
+ PkiUtils.generateSelfSignedCertificate(keyStore,
+ new X500Principal("CN=" + InetAddress.getLocalHost().getHostName() + ",OU=UNSECURE,O=UNSECURE"),
+ 1024, keyPwd);
+ PkiUtils.saveKeyStore(keyStorePath, keyStorePassword, keyStore);
+ if (log.isDebugEnabled())
+ log.debug("Created self-signed unsecure keystore " + keyStorePath);
+ } catch (Exception e) {
+ try {
+ if (Files.size(keyStorePath) == 0)
+ Files.delete(keyStorePath);
+ } catch (IOException e1) {
+ // silent
+ }
+ log.error("Cannot create keystore " + keyStorePath, e);
+ }
+ } else {
+ throw new IllegalStateException("Keystore " + keyStorePath + " already exists");
+ }
+ }
+
+}
--- /dev/null
+package org.argeo.cms.internal.runtime;
+
+import org.argeo.api.cms.CmsConstants;
+
+/** Internal CMS constants. */
+public interface KernelConstants {
+ // Directories
+ String DIR_NODE = "node";
+ String DIR_REPOS = "repos";
+ String DIR_INDEXES = "indexes";
+ String DIR_TRANSACTIONS = "transactions";
+
+ // Files
+ String DEPLOY_CONFIG_PATH = DIR_NODE + '/' + CmsConstants.DEPLOY_BASEDN + ".ldif";
+ String DEFAULT_KEYSTORE_PATH = DIR_NODE + '/' + CmsConstants.NODE + ".p12";
+ String DEFAULT_PEM_KEY_PATH = DIR_NODE + '/' + CmsConstants.NODE + ".key";
+ String DEFAULT_PEM_CERT_PATH = DIR_NODE + '/' + CmsConstants.NODE + ".crt";
+ String NODE_KEY_TAB_PATH = DIR_NODE + "/krb5.keytab";
+
+ // Security
+ String JAAS_CONFIG = "/org/argeo/cms/internal/kernel/jaas.cfg";
+ String JAAS_CONFIG_IPA = "/org/argeo/cms/internal/kernel/jaas-ipa.cfg";
+
+ // Java
+ String JAAS_CONFIG_PROP = "java.security.auth.login.config";
+
+ // DEFAULTS JCR PATH
+ String DEFAULT_HOME_BASE_PATH = "/home";
+ String DEFAULT_USERS_BASE_PATH = "/users";
+ String DEFAULT_GROUPS_BASE_PATH = "/groups";
+
+ // KERBEROS
+ String DEFAULT_KERBEROS_SERVICE = "HTTP";
+
+ // HTTP client
+ String COOKIE_POLICY_BROWSER_COMPATIBILITY = "compatibility";
+
+ // RWT / RAP
+ // String PATH_WORKBENCH = "/ui";
+ // String PATH_WORKBENCH_PUBLIC = PATH_WORKBENCH + "/public";
+
+ String JETTY_FACTORY_PID = "org.eclipse.equinox.http.jetty.config";
+ String WHITEBOARD_PATTERN_PROP = "osgi.http.whiteboard.servlet.pattern";
+ // default Jetty server configured via JettyConfigurator
+ String DEFAULT_JETTY_SERVER = "default";
+ String CMS_JETTY_CUSTOMIZER_CLASS = "org.argeo.equinox.jetty.CmsJettyCustomizer";
+
+ // avoid dependencies
+ String CONTEXT_NAME_PROP = "contextName";
+ String JACKRABBIT_REPOSITORY_URI = "org.apache.jackrabbit.repository.uri";
+ String JACKRABBIT_REMOTE_DEFAULT_WORKSPACE = "org.apache.jackrabbit.spi2davex.WorkspaceNameDefault";
+}
--- /dev/null
+package org.argeo.cms.internal.runtime;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.URIParameter;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.cms.internal.osgi.CmsActivator;
+
+/** Package utilities */
+public class KernelUtils implements KernelConstants {
+ final static String OSGI_INSTANCE_AREA = "osgi.instance.area";
+ final static String OSGI_CONFIGURATION_AREA = "osgi.configuration.area";
+
+ static void setJaasConfiguration(URL jaasConfigurationUrl) {
+ try {
+ URIParameter uriParameter = new URIParameter(jaasConfigurationUrl.toURI());
+ javax.security.auth.login.Configuration jaasConfiguration = javax.security.auth.login.Configuration
+ .getInstance("JavaLoginConfig", uriParameter);
+ javax.security.auth.login.Configuration.setConfiguration(jaasConfiguration);
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Cannot set configuration " + jaasConfigurationUrl, e);
+ }
+ }
+
+ static Dictionary<String, ?> asDictionary(Properties props) {
+ Hashtable<String, Object> hashtable = new Hashtable<String, Object>();
+ for (Object key : props.keySet()) {
+ hashtable.put(key.toString(), props.get(key));
+ }
+ return hashtable;
+ }
+
+ static Dictionary<String, ?> asDictionary(ClassLoader cl, String resource) {
+ Properties props = new Properties();
+ try {
+ props.load(cl.getResourceAsStream(resource));
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Cannot load " + resource + " from classpath", e);
+ }
+ return asDictionary(props);
+ }
+
+ static File getExecutionDir(String relativePath) {
+ File executionDir = new File(getFrameworkProp("user.dir"));
+ if (relativePath == null)
+ return executionDir;
+ try {
+ return new File(executionDir, relativePath).getCanonicalFile();
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Cannot get canonical file", e);
+ }
+ }
+
+ static File getOsgiInstanceDir() {
+ return new File(CmsActivator.getBundleContext().getProperty(OSGI_INSTANCE_AREA).substring("file:".length()))
+ .getAbsoluteFile();
+ }
+
+ public static Path getOsgiInstancePath(String relativePath) {
+ return Paths.get(getOsgiInstanceUri(relativePath));
+ }
+
+ public static URI getOsgiInstanceUri(String relativePath) {
+ String osgiInstanceBaseUri = getFrameworkProp(OSGI_INSTANCE_AREA);
+ if (osgiInstanceBaseUri != null)
+ return safeUri(osgiInstanceBaseUri + (relativePath != null ? relativePath : ""));
+ else
+ return Paths.get(System.getProperty("user.dir")).toUri();
+ }
+
+ static File getOsgiConfigurationFile(String relativePath) {
+ try {
+ return new File(new URI(CmsActivator.getBundleContext().getProperty(OSGI_CONFIGURATION_AREA) + relativePath))
+ .getCanonicalFile();
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Cannot get configuration file for " + relativePath, e);
+ }
+ }
+
+ static String getFrameworkProp(String key, String def) {
+ String value;
+ if (CmsActivator.getBundleContext() != null)
+ value = CmsActivator.getBundleContext().getProperty(key);
+ else
+ value = System.getProperty(key);
+ if (value == null)
+ return def;
+ return value;
+ }
+
+ public static String getFrameworkProp(String key) {
+ return getFrameworkProp(key, null);
+ }
+
+ // Security
+ // static Subject anonymousLogin() {
+ // Subject subject = new Subject();
+ // LoginContext lc;
+ // try {
+ // lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER, subject);
+ // lc.login();
+ // return subject;
+ // } catch (LoginException e) {
+ // throw new CmsException("Cannot login as anonymous", e);
+ // }
+ // }
+
+ static void logFrameworkProperties(CmsLog log) {
+ for (Object sysProp : new TreeSet<Object>(System.getProperties().keySet())) {
+ log.debug(sysProp + "=" + getFrameworkProp(sysProp.toString()));
+ }
+ // String[] keys = { Constants.FRAMEWORK_STORAGE,
+ // Constants.FRAMEWORK_OS_NAME, Constants.FRAMEWORK_OS_VERSION,
+ // Constants.FRAMEWORK_PROCESSOR, Constants.FRAMEWORK_SECURITY,
+ // Constants.FRAMEWORK_TRUST_REPOSITORIES,
+ // Constants.FRAMEWORK_WINDOWSYSTEM, Constants.FRAMEWORK_VENDOR,
+ // Constants.FRAMEWORK_VERSION, Constants.FRAMEWORK_STORAGE_CLEAN,
+ // Constants.FRAMEWORK_LANGUAGE, Constants.FRAMEWORK_UUID };
+ // for (String key : keys)
+ // log.debug(key + "=" + bc.getProperty(key));
+ }
+
+ static void printSystemProperties(PrintStream out) {
+ TreeMap<String, String> display = new TreeMap<>();
+ for (Object key : System.getProperties().keySet())
+ display.put(key.toString(), System.getProperty(key.toString()));
+ for (String key : display.keySet())
+ out.println(key + "=" + display.get(key));
+ }
+
+// static Session openAdminSession(Repository repository) {
+// return openAdminSession(repository, null);
+// }
+//
+// static Session openAdminSession(final Repository repository, final String workspaceName) {
+// LoginContext loginContext = loginAsDataAdmin();
+// return Subject.doAs(loginContext.getSubject(), new PrivilegedAction<Session>() {
+//
+// @Override
+// public Session run() {
+// try {
+// return repository.login(workspaceName);
+// } catch (RepositoryException e) {
+// throw new IllegalStateException("Cannot open admin session", e);
+// } finally {
+// try {
+// loginContext.logout();
+// } catch (LoginException e) {
+// throw new IllegalStateException(e);
+// }
+// }
+// }
+//
+// });
+// }
+//
+// static LoginContext loginAsDataAdmin() {
+// ClassLoader currentCl = Thread.currentThread().getContextClassLoader();
+// Thread.currentThread().setContextClassLoader(KernelUtils.class.getClassLoader());
+// LoginContext loginContext;
+// try {
+// loginContext = new LoginContext(NodeConstants.LOGIN_CONTEXT_DATA_ADMIN);
+// loginContext.login();
+// } catch (LoginException e1) {
+// throw new IllegalStateException("Could not login as data admin", e1);
+// } finally {
+// Thread.currentThread().setContextClassLoader(currentCl);
+// }
+// return loginContext;
+// }
+
+// static void doAsDataAdmin(Runnable action) {
+// LoginContext loginContext = loginAsDataAdmin();
+// Subject.doAs(loginContext.getSubject(), new PrivilegedAction<Void>() {
+//
+// @Override
+// public Void run() {
+// try {
+// action.run();
+// return null;
+// } finally {
+// try {
+// loginContext.logout();
+// } catch (LoginException e) {
+// throw new IllegalStateException(e);
+// }
+// }
+// }
+//
+// });
+// }
+
+// public static void asyncOpen(ServiceTracker<?, ?> st) {
+// Runnable run = new Runnable() {
+//
+// @Override
+// public void run() {
+// st.open();
+// }
+// };
+// Activator.getInternalExecutorService().execute(run);
+//// new Thread(run, "Open service tracker " + st).start();
+// }
+
+// static BundleContext getBundleContext() {
+// return Activator.getBundleContext();
+// }
+
+ static boolean asBoolean(String value) {
+ if (value == null)
+ return false;
+ switch (value) {
+ case "true":
+ return true;
+ case "false":
+ return false;
+ default:
+ throw new IllegalArgumentException("Unsupported value for boolean attribute : " + value);
+ }
+ }
+
+ private static URI safeUri(String uri) {
+ if (uri == null)
+ throw new IllegalArgumentException("URI cannot be null");
+ try {
+ return new URI(uri);
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException("Badly formatted URI " + uri, e);
+ }
+ }
+
+ private KernelUtils() {
+
+ }
+}
--- /dev/null
+package org.argeo.cms.internal.runtime;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.math.BigInteger;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.PrivateKey;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
+import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.InputDecryptorProvider;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
+import org.bouncycastle.pkcs.PKCSException;
+
+/**
+ * Utilities around private keys and certificate, mostly wrapping BouncyCastle
+ * implementations.
+ */
+class PkiUtils {
+ final static String PKCS12 = "PKCS12";
+
+ private final static String SECURITY_PROVIDER;
+ static {
+ Security.addProvider(new BouncyCastleProvider());
+ SECURITY_PROVIDER = "BC";
+ }
+
+ public static X509Certificate generateSelfSignedCertificate(KeyStore keyStore, X500Principal x500Principal,
+ int keySize, char[] keyPassword) {
+ try {
+ KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", SECURITY_PROVIDER);
+ kpGen.initialize(keySize, new SecureRandom());
+ KeyPair pair = kpGen.generateKeyPair();
+ Date notBefore = new Date(System.currentTimeMillis() - 10000);
+ Date notAfter = new Date(System.currentTimeMillis() + 365 * 24L * 3600 * 1000);
+ BigInteger serial = BigInteger.valueOf(System.currentTimeMillis());
+ X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(x500Principal, serial, notBefore,
+ notAfter, x500Principal, pair.getPublic());
+ ContentSigner sigGen = new JcaContentSignerBuilder("SHA256WithRSAEncryption").setProvider(SECURITY_PROVIDER)
+ .build(pair.getPrivate());
+ X509Certificate cert = new JcaX509CertificateConverter().setProvider(SECURITY_PROVIDER)
+ .getCertificate(certGen.build(sigGen));
+ cert.checkValidity(new Date());
+ cert.verify(cert.getPublicKey());
+
+ keyStore.setKeyEntry(x500Principal.getName(), pair.getPrivate(), keyPassword, new Certificate[] { cert });
+ return cert;
+ } catch (GeneralSecurityException | OperatorCreationException e) {
+ throw new RuntimeException("Cannot generate self-signed certificate", e);
+ }
+ }
+
+ public static KeyStore getKeyStore(Path keyStoreFile, char[] keyStorePassword, String keyStoreType) {
+ try {
+ KeyStore store = KeyStore.getInstance(keyStoreType, SECURITY_PROVIDER);
+ if (Files.exists(keyStoreFile)) {
+ try (InputStream fis = Files.newInputStream(keyStoreFile)) {
+ store.load(fis, keyStorePassword);
+ }
+ } else {
+ store.load(null);
+ }
+ return store;
+ } catch (GeneralSecurityException | IOException e) {
+ throw new RuntimeException("Cannot load keystore " + keyStoreFile, e);
+ }
+ }
+
+ public static void saveKeyStore(Path keyStoreFile, char[] keyStorePassword, KeyStore keyStore) {
+ try {
+ try (OutputStream fis = Files.newOutputStream(keyStoreFile)) {
+ keyStore.store(fis, keyStorePassword);
+ }
+ } catch (GeneralSecurityException | IOException e) {
+ throw new RuntimeException("Cannot save keystore " + keyStoreFile, e);
+ }
+ }
+
+// public static byte[] pemToPKCS12(final String keyFile, final String cerFile, final String password)
+// throws Exception {
+// // Get the private key
+// FileReader reader = new FileReader(keyFile);
+//
+// PEMReader pem = new PemReader(reader, new PasswordFinder() {
+// @Override
+// public char[] getPassword() {
+// return password.toCharArray();
+// }
+// });
+//
+// PrivateKey key = ((KeyPair) pem.readObject()).getPrivate();
+//
+// pem.close();
+// reader.close();
+//
+// // Get the certificate
+// reader = new FileReader(cerFile);
+// pem = new PEMReader(reader);
+//
+// X509Certificate cert = (X509Certificate) pem.readObject();
+//
+// pem.close();
+// reader.close();
+//
+// // Put them into a PKCS12 keystore and write it to a byte[]
+// ByteArrayOutputStream bos = new ByteArrayOutputStream();
+// KeyStore ks = KeyStore.getInstance("PKCS12");
+// ks.load(null);
+// ks.setKeyEntry("alias", (Key) key, password.toCharArray(), new java.security.cert.Certificate[] { cert });
+// ks.store(bos, password.toCharArray());
+// bos.close();
+// return bos.toByteArray();
+// }
+
+ public static void loadPem(KeyStore keyStore, Reader key, char[] keyPassword, Reader cert) {
+ PrivateKey privateKey = loadPemPrivateKey(key, keyPassword);
+ X509Certificate certificate = loadPemCertificate(cert);
+ try {
+ keyStore.setKeyEntry(certificate.getSubjectX500Principal().getName(), privateKey, keyPassword,
+ new java.security.cert.Certificate[] { certificate });
+ } catch (KeyStoreException e) {
+ throw new RuntimeException("Cannot store PEM certificate", e);
+ }
+ }
+
+ public static PrivateKey loadPemPrivateKey(Reader reader, char[] keyPassword) {
+ try (PEMParser pemParser = new PEMParser(reader)) {
+ JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
+ Object object = pemParser.readObject();
+ PrivateKeyInfo privateKeyInfo;
+ if (object instanceof PKCS8EncryptedPrivateKeyInfo) {
+ if (keyPassword == null)
+ throw new IllegalArgumentException("A key password is required");
+ InputDecryptorProvider decProv = new JceOpenSSLPKCS8DecryptorProviderBuilder().build(keyPassword);
+ privateKeyInfo = ((PKCS8EncryptedPrivateKeyInfo) object).decryptPrivateKeyInfo(decProv);
+ } else if (object instanceof PrivateKeyInfo) {
+ privateKeyInfo = (PrivateKeyInfo) object;
+ } else {
+ throw new IllegalArgumentException("Unsupported format for private key");
+ }
+ return converter.getPrivateKey(privateKeyInfo);
+ } catch (IOException | OperatorCreationException | PKCSException e) {
+ throw new RuntimeException("Cannot read private key", e);
+ }
+ }
+
+ public static X509Certificate loadPemCertificate(Reader reader) {
+ try (PEMParser pemParser = new PEMParser(reader)) {
+ X509CertificateHolder certHolder = (X509CertificateHolder) pemParser.readObject();
+ X509Certificate cert = new JcaX509CertificateConverter().setProvider(SECURITY_PROVIDER)
+ .getCertificate(certHolder);
+ return cert;
+ } catch (IOException | CertificateException e) {
+ throw new RuntimeException("Cannot read private key", e);
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ final String ALGORITHM = "RSA";
+ final String provider = "BC";
+ SecureRandom secureRandom = new SecureRandom();
+ long begin = System.currentTimeMillis();
+ for (int i = 512; i < 1024; i = i + 2) {
+ try {
+ KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM, provider);
+ keyGen.initialize(i, secureRandom);
+ keyGen.generateKeyPair();
+ } catch (Exception e) {
+ System.err.println(i + " : " + e.getMessage());
+ }
+ }
+ System.out.println((System.currentTimeMillis() - begin) + " ms");
+
+ // // String text = "a";
+ // String text =
+ // "testtesttesttesttesttesttesttesttesttesttesttesttesttesttest";
+ // try {
+ // System.out.println(text);
+ // PrivateKey privateKey;
+ // PublicKey publicKey;
+ // char[] password = "changeit".toCharArray();
+ // String alias = "CN=test";
+ // KeyStore keyStore = KeyStore.getInstance("pkcs12");
+ // File p12file = new File("test.p12");
+ // p12file.delete();
+ // if (!p12file.exists()) {
+ // keyStore.load(null);
+ // generateSelfSignedCertificate(keyStore, new X500Principal(alias),
+ // 513, password);
+ // try (OutputStream out = new FileOutputStream(p12file)) {
+ // keyStore.store(out, password);
+ // }
+ // }
+ // try (InputStream in = new FileInputStream(p12file)) {
+ // keyStore.load(in, password);
+ // privateKey = (PrivateKey) keyStore.getKey(alias, password);
+ // publicKey = keyStore.getCertificateChain(alias)[0].getPublicKey();
+ // }
+ // // KeyPair key;
+ // // final KeyPairGenerator keyGen =
+ // // KeyPairGenerator.getInstance(ALGORITHM);
+ // // keyGen.initialize(4096, new SecureRandom());
+ // // long begin = System.currentTimeMillis();
+ // // key = keyGen.generateKeyPair();
+ // // System.out.println((System.currentTimeMillis() - begin) + " ms");
+ // // keyStore.load(null);
+ // // keyStore.setKeyEntry("test", key.getPrivate(), password, null);
+ // // try(OutputStream out=new FileOutputStream(p12file)) {
+ // // keyStore.store(out, password);
+ // // }
+ // // privateKey = key.getPrivate();
+ // // publicKey = key.getPublic();
+ //
+ // Cipher encrypt = Cipher.getInstance(ALGORITHM);
+ // encrypt.init(Cipher.ENCRYPT_MODE, publicKey);
+ // byte[] encrypted = encrypt.doFinal(text.getBytes());
+ // String encryptedBase64 =
+ // Base64.getEncoder().encodeToString(encrypted);
+ // System.out.println(encryptedBase64);
+ // byte[] encryptedFromBase64 =
+ // Base64.getDecoder().decode(encryptedBase64);
+ //
+ // Cipher decrypt = Cipher.getInstance(ALGORITHM);
+ // decrypt.init(Cipher.DECRYPT_MODE, privateKey);
+ // byte[] decrypted = decrypt.doFinal(encryptedFromBase64);
+ // System.out.println(new String(decrypted));
+ // } catch (Exception e) {
+ // e.printStackTrace();
+ // }
+
+ }
+
+}
--- /dev/null
+dn: dc=example,dc=com
+objectClass: domain
+objectClass: extensibleObject
+objectClass: top
+dc: example
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: top
+ou: Groups
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: top
+ou: People
+
+dn: uid=demo,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+objectClass: person
+objectClass: top
+cn: Demo User
+description: Demo user
+givenName: Demo
+mail: demo@localhost
+sn: User
+uid: demo
+userPassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9
+
+dn: uid=root,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: person
+objectClass: organizationalPerson
+objectClass: top
+cn: Super User
+description: Superuser
+givenName: Super
+mail: root@localhost
+sn: User
+uid: root
+userPassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9
--- /dev/null
+dn: cn=admin,ou=roles,ou=node
+objectClass: groupOfNames
+objectClass: top
+cn: admin
+member: uid=root,ou=People,dc=example,dc=com
+
+dn: cn=userAdmin,ou=roles,ou=node
+objectClass: groupOfNames
+objectClass: top
+member: cn=admin,ou=roles,ou=node
+cn: userAdmin
+
--- /dev/null
+USER {
+ org.argeo.cms.auth.RemoteSessionLoginModule sufficient;
+ org.argeo.cms.auth.SpnegoLoginModule optional;
+ com.sun.security.auth.module.Krb5LoginModule optional tryFirstPass=true;
+ org.argeo.cms.auth.UserAdminLoginModule sufficient;
+};
+
+ANONYMOUS {
+ org.argeo.cms.auth.RemoteSessionLoginModule sufficient;
+ org.argeo.cms.auth.AnonymousLoginModule sufficient;
+};
+
+DATA_ADMIN {
+ org.argeo.cms.auth.DataAdminLoginModule requisite;
+};
+
+NODE {
+ com.sun.security.auth.module.Krb5LoginModule optional
+ keyTab="${osgi.instance.area}node/krb5.keytab"
+ useKeyTab=true
+ storeKey=true;
+ org.argeo.cms.auth.DataAdminLoginModule requisite;
+};
+
+KEYRING {
+ org.argeo.cms.auth.KeyringLoginModule required;
+};
+
+SINGLE_USER {
+ com.sun.security.auth.module.Krb5LoginModule optional
+ principal="${user.name}"
+ storeKey=true
+ useTicketCache=true
+ debug=true;
+ org.argeo.cms.auth.SingleUserLoginModule requisite;
+};
+
+Jackrabbit {
+ org.argeo.security.jackrabbit.SystemJackrabbitLoginModule requisite;
+};
--- /dev/null
+USER {
+ org.argeo.cms.auth.RemoteSessionLoginModule sufficient;
+ org.argeo.cms.auth.IdentLoginModule optional;
+ org.argeo.cms.auth.UserAdminLoginModule requisite;
+};
+
+ANONYMOUS {
+ org.argeo.cms.auth.RemoteSessionLoginModule sufficient;
+ org.argeo.cms.auth.AnonymousLoginModule requisite;
+};
+
+DATA_ADMIN {
+ org.argeo.cms.auth.DataAdminLoginModule requisite;
+};
+
+NODE {
+ org.argeo.cms.auth.DataAdminLoginModule requisite;
+};
+
+KEYRING {
+ org.argeo.cms.auth.KeyringLoginModule required;
+};
+
+SINGLE_USER {
+ org.argeo.cms.auth.SingleUserLoginModule requisite;
+};
+
+Jackrabbit {
+ org.argeo.security.jackrabbit.SystemJackrabbitLoginModule requisite;
+};
--- /dev/null
+dn: ou=node
+objectClass: organizationalUnit
+objectClass: top
+ou: node
+
+dn: ou=roles,ou=node
+objectClass: organizationalUnit
+objectClass: top
+ou: roles
--- /dev/null
+dn: ou=tokens,ou=node
+objectClass: organizationalUnit
+objectClass: top
+ou: tokens
private AbstractUserDirectory tokens = null;
private Map<LdapName, AbstractUserDirectory> businessRoles = new HashMap<LdapName, AbstractUserDirectory>();
+ // TODO rather use an empty constructor and an init method
public AggregatingUserAdmin(String systemRolesBaseDn, String tokensBaseDn) {
try {
this.systemRolesBaseDn = new LdapName(systemRolesBaseDn);