From 6decc943ce5bca1b57ef407b7e9c6bb6ad6f3c97 Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Thu, 23 Feb 2017 20:16:05 +0100 Subject: [PATCH] - Merge JAAS configs - Refactor login modules - Move argeo: namespace from Node to CMS --- .../jcr/commands/AddRemoteRepository.java | 4 +- .../jcr/model/RemoteRepositoryElem.java | 2 +- .../internal/jcr/model/RepositoriesElem.java | 2 +- .../internal/useradmin/commands/NewGroup.java | 2 +- .../internal/useradmin/commands/NewUser.java | 2 +- .../useradmin/parts/GroupMainPage.java | 2 +- .../internal/useradmin/parts/GroupsView.java | 2 +- .../useradmin/parts/UserMainPage.java | 2 +- .../internal/useradmin/parts/UsersView.java | 2 +- .../org/argeo/cms/tabular/JcrTabularTest.java | 8 +- .../src/org/argeo/cms}/ArgeoNames.java | 37 ++-- .../src/org/argeo/cms}/ArgeoTypes.java | 2 +- .../cms/auth/HttpSessionLoginModule.java | 30 +-- .../argeo/cms/auth/UserAdminLoginModule.java | 174 ++++++++++++------ org.argeo.cms/src/org/argeo/cms/cms.cnd | 35 ++++ ...vider.java => HttpCredentialProvider.java} | 8 +- .../http/client/SpnegoAuthScheme.java | 27 +-- .../http/client/SpnegoCredentials.java | 7 + .../argeo/cms/internal/kernel/Activator.java | 30 ++- .../cms/internal/kernel/CmsSecurity.java | 46 +++-- .../argeo/cms/internal/kernel/FirstInit.java | 7 +- .../cms/internal/kernel/NodeUserAdmin.java | 159 +++++++++++++++- .../org/argeo/cms/internal/kernel/jaas.cfg | 9 +- .../org/argeo/cms/security/JcrKeyring.java | 4 +- .../cms/tabular/JcrTabularRowIterator.java | 2 +- .../argeo/cms/tabular/JcrTabularWriter.java | 2 +- .../osgi/useradmin/AbstractUserDirectory.java | 60 +++--- .../osgi/useradmin/AggregatingUserAdmin.java | 2 +- .../org/argeo/osgi/useradmin/IpaUtils.java | 4 +- .../argeo/osgi/useradmin/UserAdminConf.java | 37 +++- .../src/org/argeo/node/NodeOID.java | 7 - .../src/org/argeo/node/node.cnd | 34 ---- .../argeo/node/security/UserPrincipal.java | 1 + 33 files changed, 516 insertions(+), 236 deletions(-) rename {org.argeo.node.api/src/org/argeo/node => org.argeo.cms/src/org/argeo/cms}/ArgeoNames.java (68%) rename {org.argeo.node.api/src/org/argeo/node => org.argeo.cms/src/org/argeo/cms}/ArgeoTypes.java (98%) rename org.argeo.cms/src/org/argeo/cms/internal/http/client/{SpnegoCredentialProvider.java => HttpCredentialProvider.java} (65%) create mode 100644 org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoCredentials.java diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/jcr/commands/AddRemoteRepository.java b/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/jcr/commands/AddRemoteRepository.java index 6fdb274d0..3539cacc1 100644 --- a/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/jcr/commands/AddRemoteRepository.java +++ b/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/jcr/commands/AddRemoteRepository.java @@ -24,13 +24,13 @@ import javax.jcr.RepositoryFactory; import javax.jcr.Session; import javax.jcr.SimpleCredentials; +import org.argeo.cms.ArgeoNames; +import org.argeo.cms.ArgeoTypes; import org.argeo.cms.ui.workbench.internal.WorkbenchConstants; import org.argeo.cms.ui.workbench.util.CommandUtils; import org.argeo.eclipse.ui.EclipseUiException; import org.argeo.eclipse.ui.dialogs.ErrorFeedback; import org.argeo.jcr.JcrUtils; -import org.argeo.node.ArgeoNames; -import org.argeo.node.ArgeoTypes; import org.argeo.node.NodeConstants; import org.argeo.node.NodeUtils; import org.argeo.node.security.Keyring; diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/jcr/model/RemoteRepositoryElem.java b/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/jcr/model/RemoteRepositoryElem.java index bd3cd32d6..4712f9273 100644 --- a/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/jcr/model/RemoteRepositoryElem.java +++ b/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/jcr/model/RemoteRepositoryElem.java @@ -24,9 +24,9 @@ import javax.jcr.RepositoryFactory; import javax.jcr.Session; import javax.jcr.SimpleCredentials; +import org.argeo.cms.ArgeoNames; import org.argeo.eclipse.ui.EclipseUiException; import org.argeo.eclipse.ui.TreeParent; -import org.argeo.node.ArgeoNames; import org.argeo.node.NodeUtils; import org.argeo.node.security.Keyring; diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/jcr/model/RepositoriesElem.java b/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/jcr/model/RepositoriesElem.java index 7a162c18f..70922fe1b 100644 --- a/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/jcr/model/RepositoriesElem.java +++ b/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/jcr/model/RepositoriesElem.java @@ -24,11 +24,11 @@ import javax.jcr.RepositoryException; import javax.jcr.RepositoryFactory; import javax.jcr.Session; +import org.argeo.cms.ArgeoNames; import org.argeo.eclipse.ui.EclipseUiException; import org.argeo.eclipse.ui.TreeParent; import org.argeo.eclipse.ui.dialogs.ErrorFeedback; import org.argeo.jcr.RepositoryRegister; -import org.argeo.node.ArgeoNames; import org.argeo.node.NodeUtils; import org.argeo.node.security.Keyring; diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/useradmin/commands/NewGroup.java b/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/useradmin/commands/NewGroup.java index 4858effa2..51a14fcbd 100644 --- a/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/useradmin/commands/NewGroup.java +++ b/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/useradmin/commands/NewGroup.java @@ -18,13 +18,13 @@ package org.argeo.cms.ui.workbench.internal.useradmin.commands; import java.util.Dictionary; import java.util.Map; +import org.argeo.cms.ArgeoNames; import org.argeo.cms.CmsException; import org.argeo.cms.ui.workbench.WorkbenchUiPlugin; import org.argeo.cms.ui.workbench.internal.useradmin.UserAdminWrapper; import org.argeo.eclipse.ui.EclipseUiUtils; import org.argeo.eclipse.ui.dialogs.ErrorFeedback; import org.argeo.naming.LdapAttrs; -import org.argeo.node.ArgeoNames; import org.argeo.osgi.useradmin.UserAdminConf; import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/useradmin/commands/NewUser.java b/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/useradmin/commands/NewUser.java index c342342dc..94aa6352c 100644 --- a/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/useradmin/commands/NewUser.java +++ b/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/useradmin/commands/NewUser.java @@ -23,6 +23,7 @@ import javax.naming.InvalidNameException; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; +import org.argeo.cms.ArgeoNames; import org.argeo.cms.CmsException; import org.argeo.cms.ui.workbench.WorkbenchUiPlugin; import org.argeo.cms.ui.workbench.internal.useradmin.UiAdminUtils; @@ -31,7 +32,6 @@ import org.argeo.cms.util.UserAdminUtils; import org.argeo.eclipse.ui.EclipseUiUtils; import org.argeo.eclipse.ui.dialogs.ErrorFeedback; import org.argeo.naming.LdapAttrs; -import org.argeo.node.ArgeoNames; import org.argeo.osgi.useradmin.UserAdminConf; import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/useradmin/parts/GroupMainPage.java b/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/useradmin/parts/GroupMainPage.java index 41cb0217c..0b16466ad 100644 --- a/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/useradmin/parts/GroupMainPage.java +++ b/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/useradmin/parts/GroupMainPage.java @@ -27,6 +27,7 @@ import javax.naming.InvalidNameException; import javax.naming.ldap.LdapName; import javax.transaction.UserTransaction; +import org.argeo.cms.ArgeoNames; import org.argeo.cms.CmsException; import org.argeo.cms.ui.workbench.CmsWorkbenchStyles; import org.argeo.cms.ui.workbench.internal.useradmin.SecurityAdminImages; @@ -46,7 +47,6 @@ import org.argeo.eclipse.ui.EclipseUiUtils; import org.argeo.eclipse.ui.parts.LdifUsersTable; import org.argeo.jcr.JcrUtils; import org.argeo.naming.LdapAttrs; -import org.argeo.node.ArgeoNames; import org.argeo.node.NodeInstance; import org.argeo.node.NodeUtils; import org.eclipse.jface.action.Action; diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/useradmin/parts/GroupsView.java b/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/useradmin/parts/GroupsView.java index 6ae12d9df..580e05150 100644 --- a/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/useradmin/parts/GroupsView.java +++ b/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/useradmin/parts/GroupsView.java @@ -20,6 +20,7 @@ import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.argeo.cms.ArgeoNames; import org.argeo.cms.CmsException; import org.argeo.cms.auth.CurrentUser; import org.argeo.cms.ui.workbench.WorkbenchUiPlugin; @@ -36,7 +37,6 @@ import org.argeo.eclipse.ui.EclipseUiUtils; import org.argeo.eclipse.ui.parts.LdifUsersTable; import org.argeo.naming.LdapAttrs; import org.argeo.naming.LdapObjs; -import org.argeo.node.ArgeoNames; import org.argeo.node.NodeConstants; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.swt.SWT; diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/useradmin/parts/UserMainPage.java b/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/useradmin/parts/UserMainPage.java index d2ef9e27a..a4bb3a4c0 100644 --- a/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/useradmin/parts/UserMainPage.java +++ b/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/useradmin/parts/UserMainPage.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import org.argeo.cms.ArgeoNames; import org.argeo.cms.auth.CurrentUser; import org.argeo.cms.ui.workbench.CmsWorkbenchStyles; import org.argeo.cms.ui.workbench.internal.useradmin.SecurityAdminImages; @@ -37,7 +38,6 @@ import org.argeo.eclipse.ui.ColumnDefinition; import org.argeo.eclipse.ui.EclipseUiUtils; import org.argeo.eclipse.ui.parts.LdifUsersTable; import org.argeo.naming.LdapAttrs; -import org.argeo.node.ArgeoNames; import org.argeo.node.NodeConstants; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.ToolBarManager; diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/useradmin/parts/UsersView.java b/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/useradmin/parts/UsersView.java index 3660cebca..4a3b1572e 100644 --- a/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/useradmin/parts/UsersView.java +++ b/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/useradmin/parts/UsersView.java @@ -18,6 +18,7 @@ package org.argeo.cms.ui.workbench.internal.useradmin.parts; import java.util.ArrayList; import java.util.List; +import org.argeo.cms.ArgeoNames; import org.argeo.cms.CmsException; import org.argeo.cms.auth.CurrentUser; import org.argeo.cms.ui.workbench.WorkbenchUiPlugin; @@ -34,7 +35,6 @@ import org.argeo.eclipse.ui.EclipseUiUtils; import org.argeo.eclipse.ui.parts.LdifUsersTable; import org.argeo.naming.LdapAttrs; import org.argeo.naming.LdapObjs; -import org.argeo.node.ArgeoNames; import org.argeo.node.NodeConstants; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.swt.SWT; diff --git a/org.argeo.cms/ext/test/org/argeo/cms/tabular/JcrTabularTest.java b/org.argeo.cms/ext/test/org/argeo/cms/tabular/JcrTabularTest.java index b52ffe3c1..112562c3f 100644 --- a/org.argeo.cms/ext/test/org/argeo/cms/tabular/JcrTabularTest.java +++ b/org.argeo.cms/ext/test/org/argeo/cms/tabular/JcrTabularTest.java @@ -25,9 +25,9 @@ import javax.jcr.PropertyType; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.jackrabbit.commons.cnd.CndImporter; +import org.argeo.cms.ArgeoNames; +import org.argeo.cms.ArgeoTypes; import org.argeo.jackrabbit.unit.AbstractJackrabbitTestCase; -import org.argeo.node.ArgeoNames; -import org.argeo.node.ArgeoTypes; import org.argeo.node.tabular.TabularColumn; import org.argeo.node.tabular.TabularRow; import org.argeo.node.tabular.TabularRowIterator; @@ -42,6 +42,10 @@ public class JcrTabularTest extends AbstractJackrabbitTestCase { .getResourceAsStream("/org/argeo/node/node.cnd")); CndImporter.registerNodeTypes(reader, session()); reader.close(); + reader = new InputStreamReader(getClass() + .getResourceAsStream("/org/argeo/cms/cms.cnd")); + CndImporter.registerNodeTypes(reader, session()); + reader.close(); // write Integer columnCount = 15; diff --git a/org.argeo.node.api/src/org/argeo/node/ArgeoNames.java b/org.argeo.cms/src/org/argeo/cms/ArgeoNames.java similarity index 68% rename from org.argeo.node.api/src/org/argeo/node/ArgeoNames.java rename to org.argeo.cms/src/org/argeo/cms/ArgeoNames.java index 3ef32e1e9..c2c671384 100644 --- a/org.argeo.node.api/src/org/argeo/node/ArgeoNames.java +++ b/org.argeo.cms/src/org/argeo/cms/ArgeoNames.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.argeo.node; +package org.argeo.cms; /** JCR names in the http://www.argeo.org/argeo namespace */ public interface ArgeoNames { @@ -33,25 +33,22 @@ public interface ArgeoNames { // public final static String ARGEO_PROFILE = "argeo:profile"; // spring security - @Deprecated - public final static String ARGEO_ENABLED = "argeo:enabled"; -// public final static String ARGEO_ACCOUNT_NON_EXPIRED = "argeo:accountNonExpired"; -// public final static String ARGEO_ACCOUNT_NON_LOCKED = "argeo:accountNonLocked"; -// public final static String ARGEO_CREDENTIALS_NON_EXPIRED = "argeo:credentialsNonExpired"; - - // personal details - /** @deprecated Use org.argeo.naming.LdapAttrs */ - @Deprecated - public final static String ARGEO_FIRST_NAME = "argeo:firstName"; - /** @deprecated Use org.argeo.naming.LdapAttrs */ - @Deprecated - public final static String ARGEO_LAST_NAME = "argeo:lastName"; - /** @deprecated Use org.argeo.naming.LdapAttrs */ - @Deprecated - public final static String ARGEO_PRIMARY_EMAIL = "argeo:primaryEmail"; - /** @deprecated Use org.argeo.naming.LdapAttrs */ - @Deprecated - public final static String ARGEO_PRIMARY_ORGANIZATION = "argeo:primaryOrganization"; +// @Deprecated +// public final static String ARGEO_ENABLED = "argeo:enabled"; +// +// // personal details +// /** @deprecated Use org.argeo.naming.LdapAttrs */ +// @Deprecated +// public final static String ARGEO_FIRST_NAME = "argeo:firstName"; +// /** @deprecated Use org.argeo.naming.LdapAttrs */ +// @Deprecated +// public final static String ARGEO_LAST_NAME = "argeo:lastName"; +// /** @deprecated Use org.argeo.naming.LdapAttrs */ +// @Deprecated +// public final static String ARGEO_PRIMARY_EMAIL = "argeo:primaryEmail"; +// /** @deprecated Use org.argeo.naming.LdapAttrs */ +// @Deprecated +// public final static String ARGEO_PRIMARY_ORGANIZATION = "argeo:primaryOrganization"; // tabular public final static String ARGEO_IS_KEY = "argeo:isKey"; diff --git a/org.argeo.node.api/src/org/argeo/node/ArgeoTypes.java b/org.argeo.cms/src/org/argeo/cms/ArgeoTypes.java similarity index 98% rename from org.argeo.node.api/src/org/argeo/node/ArgeoTypes.java rename to org.argeo.cms/src/org/argeo/cms/ArgeoTypes.java index bdf9c3fc0..9f4cd0e9d 100644 --- a/org.argeo.node.api/src/org/argeo/node/ArgeoTypes.java +++ b/org.argeo.cms/src/org/argeo/cms/ArgeoTypes.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.argeo.node; +package org.argeo.cms; /** JCR types in the http://www.argeo.org/argeo namespace */ public interface ArgeoTypes { diff --git a/org.argeo.cms/src/org/argeo/cms/auth/HttpSessionLoginModule.java b/org.argeo.cms/src/org/argeo/cms/auth/HttpSessionLoginModule.java index be8a693ba..19f6ee0c6 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/HttpSessionLoginModule.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/HttpSessionLoginModule.java @@ -101,20 +101,26 @@ public class HttpSessionLoginModule implements LoginModule { @Override public boolean commit() throws LoginException { - // TODO create CmsSession in another module - Authorization authorizationToRegister; - if (authorization == null) { - authorizationToRegister = (Authorization) sharedState.get(CmsAuthUtils.SHARED_STATE_AUTHORIZATION); - } else { // this login module did the authorization + if(authorization!=null){ CmsAuthUtils.addAuthentication(subject, authorization); - authorizationToRegister = authorization; - } - if (authorizationToRegister == null) { - return false; + CmsAuthUtils.registerSessionAuthorization(bc, request, subject, authorization); } - if (request == null) - return false; - CmsAuthUtils.registerSessionAuthorization(bc, request, subject, authorizationToRegister); + + // TODO create CmsSession in another module +// Authorization authorizationToRegister; +// if (authorization == null) { +// authorizationToRegister = (Authorization) sharedState.get(CmsAuthUtils.SHARED_STATE_AUTHORIZATION); +// } +// else { // this login module did the authorization +// CmsAuthUtils.addAuthentication(subject, authorization); +// authorizationToRegister = authorization; +// } +// if (authorizationToRegister == null) { +// return false; +// } +// if (request == null) +// return false; +// CmsAuthUtils.registerSessionAuthorization(bc, request, subject, authorizationToRegister); byte[] outToken = (byte[]) sharedState.get(CmsAuthUtils.SHARED_STATE_SPNEGO_OUT_TOKEN); if (outToken != null) { diff --git a/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java b/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java index b6ab04b21..c77a5929f 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java @@ -1,9 +1,15 @@ package org.argeo.cms.auth; import java.io.IOException; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.Locale; 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; @@ -11,13 +17,19 @@ import javax.security.auth.callback.LanguageCallback; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.kerberos.KerberosPrincipal; import javax.security.auth.login.CredentialNotFoundException; import javax.security.auth.login.FailedLoginException; import javax.security.auth.login.LoginException; import javax.security.auth.spi.LoginModule; +import javax.servlet.http.HttpServletRequest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.argeo.cms.CmsException; import org.argeo.eclipse.ui.specific.UiContext; +import org.argeo.naming.LdapAttrs; +import org.argeo.osgi.useradmin.IpaUtils; import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; import org.osgi.service.useradmin.Authorization; @@ -25,15 +37,20 @@ import org.osgi.service.useradmin.User; import org.osgi.service.useradmin.UserAdmin; public class UserAdminLoginModule implements LoginModule { + private final static Log log = LogFactory.getLog(UserAdminLoginModule.class); + private Subject subject; private CallbackHandler callbackHandler; private Map sharedState = null; // private boolean isAnonymous = false; + private List indexedUserProperties = Arrays + .asList(new String[] { LdapAttrs.uid.name(), LdapAttrs.mail.name(), LdapAttrs.cn.name() }); // private state private BundleContext bc; - private Authorization authorization; + // private Authorization authorization; + private User authenticatedUser = null; @SuppressWarnings("unchecked") @Override @@ -64,23 +81,23 @@ public class UserAdminLoginModule implements LoginModule { } UserAdmin userAdmin = bc.getService(bc.getServiceReference(UserAdmin.class)); if (callbackHandler == null) {// anonymous - authorization = userAdmin.getAuthorization(null); - sharedState.put(CmsAuthUtils.SHARED_STATE_AUTHORIZATION, authorization); +// authorization = userAdmin.getAuthorization(null); +// sharedState.put(CmsAuthUtils.SHARED_STATE_AUTHORIZATION, authorization); return true; } final String username; final char[] password; - if (callbackHandler == null && sharedState.containsKey(CmsAuthUtils.SHARED_STATE_NAME) + if (sharedState.containsKey(CmsAuthUtils.SHARED_STATE_NAME) && sharedState.containsKey(CmsAuthUtils.SHARED_STATE_PWD)) { username = (String) sharedState.get(CmsAuthUtils.SHARED_STATE_NAME); password = (char[]) sharedState.get(CmsAuthUtils.SHARED_STATE_PWD); - // TODO locale? - // NB: raw user name is used - AuthenticatingUser authenticatingUser = new AuthenticatingUser(username, password); - authorization = userAdmin.getAuthorization(authenticatingUser); + // // TODO locale? + // // NB: raw user name is used + // AuthenticatingUser authenticatingUser = new + // AuthenticatingUser(username, password); + // authorization = userAdmin.getAuthorization(authenticatingUser); } else { - // ask for username and password NameCallback nameCallback = new NameCallback("User"); PasswordCallback passwordCallback = new PasswordCallback("Password", false); @@ -89,9 +106,6 @@ public class UserAdminLoginModule implements LoginModule { callbackHandler.handle(new Callback[] { nameCallback, passwordCallback, langCallback }); } catch (IOException e) { throw new LoginException("Cannot handle callback: " + e.getMessage()); - // } catch (ThreadDeath e) { - // throw new ThreadDeathLoginException("Callbackhandler thread - // died", e); } catch (UnsupportedCallbackException e) { return false; } @@ -102,71 +116,91 @@ public class UserAdminLoginModule implements LoginModule { locale = Locale.getDefault(); UiContext.setLocale(locale); - // authorization = (Authorization) - // sharedState.get(CmsAuthUtils.SHARED_STATE_AUTHORIZATION); - // - // if (authorization == null) { - // create credentials username = nameCallback.getName(); if (username == null || username.trim().equals("")) { // authorization = userAdmin.getAuthorization(null); throw new CredentialNotFoundException("No credentials provided"); } - // char[] password = {}; if (passwordCallback.getPassword() != null) password = passwordCallback.getPassword(); else throw new CredentialNotFoundException("No credentials provided"); // FIXME move Argeo specific convention from user admin to here - User user = userAdmin.getUser(null, username); - if (user == null) - throw new FailedLoginException("Invalid credentials"); - if (!user.hasCredential(null, password)) - throw new FailedLoginException("Invalid credentials"); - // return false; - - // Log and monitor new login - // if (log.isDebugEnabled()) - // log.debug("Logged in to CMS with username [" + username + - // "]"); - - authorization = userAdmin.getAuthorization(user); - assert authorization != null; } - // } - // if - // (!sharedState.containsKey(CmsAuthUtils.SHARED_STATE_AUTHORIZATION)) - sharedState.put(CmsAuthUtils.SHARED_STATE_AUTHORIZATION, authorization); - return authorization != null; + // User user = userAdmin.getUser(null, username); + User user = searchForUser(userAdmin, username); + if (user == null) + return true;// expect Kerberos + // throw new FailedLoginException("Invalid credentials"); + if (!user.hasCredential(null, password)) + throw new FailedLoginException("Invalid credentials"); + authenticatedUser = user; + // return false; + + // Log and monitor new login + // if (log.isDebugEnabled()) + // log.debug("Logged in to CMS with username [" + username + + // "]"); + + // authorization = userAdmin.getAuthorization(user); + // assert authorization != null; + // + // sharedState.put(CmsAuthUtils.SHARED_STATE_AUTHORIZATION, + // authorization); + return true; } @Override public boolean commit() throws LoginException { - // Set kerberosPrincipals = - // subject.getPrincipals(KerberosPrincipal.class); - // if (kerberosPrincipals.size() != 0) { - // KerberosPrincipal kerberosPrincipal = - // kerberosPrincipals.iterator().next(); - // System.out.println(kerberosPrincipal); - // UserAdmin userAdmin = - // bc.getService(bc.getServiceReference(UserAdmin.class)); - // User user = userAdmin.getUser(null, kerberosPrincipal.getName()); - // Authorization authorization = userAdmin.getAuthorization(user); - // sharedState.put(SHARED_STATE_AUTHORIZATION, authorization); + // if (authorization == null) { + // return false; + // // throw new LoginException("Authorization should not be null"); + // } else { + // CmsAuthUtils.addAuthentication(subject, authorization); + // return true; // } - if (authorization == null) { - return false; - // throw new LoginException("Authorization should not be null"); + UserAdmin userAdmin = bc.getService(bc.getServiceReference(UserAdmin.class)); + Authorization authorization = null; + User authenticatingUser; + Set kerberosPrincipals = subject.getPrincipals(KerberosPrincipal.class); + if (kerberosPrincipals.isEmpty()) { + if (callbackHandler == null) { + authorization = userAdmin.getAuthorization(null); + } + if (authenticatedUser == null) { + return false; + } else { + authenticatingUser = authenticatedUser; + } } else { - CmsAuthUtils.addAuthentication(subject, authorization); - return true; + KerberosPrincipal kerberosPrincipal = kerberosPrincipals.iterator().next(); + LdapName dn = IpaUtils.kerberosToDn(kerberosPrincipal.getName()); + authenticatingUser = new AuthenticatingUser(dn); + } + if (authorization == null) + authorization = Subject.doAs(subject, new PrivilegedAction() { + + @Override + public Authorization run() { + Authorization authorization = userAdmin.getAuthorization(authenticatingUser); + return authorization; + } + + }); + if (authorization == null) + return false; + CmsAuthUtils.addAuthentication(subject, authorization); + HttpServletRequest request = (HttpServletRequest) sharedState.get(CmsAuthUtils.SHARED_STATE_HTTP_REQUEST); + if (request != null) { + CmsAuthUtils.registerSessionAuthorization(bc, request, subject, authorization); } + return true; } @Override public boolean abort() throws LoginException { - authorization = null; +// authorization = null; return true; } @@ -175,4 +209,36 @@ public class UserAdminLoginModule implements LoginModule { CmsAuthUtils.cleanUp(subject); return true; } + + protected User searchForUser(UserAdmin userAdmin, String providedUsername) { + try { + // TODO check value null or empty + List collectedUsers = new ArrayList(); + // try dn + User user = null; + try { + user = (User) userAdmin.getRole(providedUsername); + if (user != null) + collectedUsers.add(user); + } catch (Exception e) { + // silent + } + // try all indexes + for (String attr : indexedUserProperties) { + user = userAdmin.getUser(attr, providedUsername); + if (user != null) + collectedUsers.add(user); + } + if (collectedUsers.size() == 1) + return collectedUsers.get(0); + else if (collectedUsers.size() > 1) + log.warn(collectedUsers.size() + " users for provided username" + providedUsername); + return null; + } catch (Exception e) { + if (log.isTraceEnabled()) + log.warn("Cannot search for user " + providedUsername, e); + return null; + } + + } } diff --git a/org.argeo.cms/src/org/argeo/cms/cms.cnd b/org.argeo.cms/src/org/argeo/cms/cms.cnd index 67dcfb518..955a4a3ac 100644 --- a/org.argeo.cms/src/org/argeo/cms/cms.cnd +++ b/org.argeo.cms/src/org/argeo/cms/cms.cnd @@ -1,5 +1,40 @@ + +// GENERIC TYPES +[argeo:remoteRepository] > nt:unstructured +- argeo:uri (STRING) +- argeo:userID (STRING) ++ argeo:password (argeo:encrypted) + +// TABULAR CONTENT +[argeo:table] > nt:file ++ * (argeo:column) * + +[argeo:column] > mix:title +- jcr:requiredType (STRING) = 'STRING' + +[argeo:csv] > nt:resource + +// CRYPTO +[argeo:encrypted] > nt:base +mixin +// initialization vector used by some algorithms +- argeo:iv (BINARY) + +[argeo:pbeKeySpec] > nt:base +mixin +- argeo:secretKeyFactory (STRING) +- argeo:salt (BINARY) +- argeo:iterationCount (LONG) +- argeo:keyLength (LONG) +- argeo:secretKeyEncryption (STRING) + +[argeo:pbeSpec] > argeo:pbeKeySpec +mixin +- argeo:cipher (STRING) + +// TEXT [cms:styled] mixin - cms:style (STRING) diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoCredentialProvider.java b/org.argeo.cms/src/org/argeo/cms/internal/http/client/HttpCredentialProvider.java similarity index 65% rename from org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoCredentialProvider.java rename to org.argeo.cms/src/org/argeo/cms/internal/http/client/HttpCredentialProvider.java index fff27c0d4..4a9392c83 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoCredentialProvider.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/http/client/HttpCredentialProvider.java @@ -6,13 +6,15 @@ import org.apache.commons.httpclient.auth.CredentialsNotAvailableException; import org.apache.commons.httpclient.auth.CredentialsProvider; /** SPNEGO credential provider */ -public class SpnegoCredentialProvider implements CredentialsProvider { +public class HttpCredentialProvider implements CredentialsProvider { @Override public Credentials getCredentials(AuthScheme scheme, String host, int port, boolean proxy) throws CredentialsNotAvailableException { - return new Credentials() { - }; + if (scheme instanceof SpnegoAuthScheme) + return new SpnegoCredentials(); + else + throw new UnsupportedOperationException("Auth scheme " + scheme.getSchemeName() + " not supported"); } } diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoAuthScheme.java b/org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoAuthScheme.java index c18e7ac24..64dbaab74 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoAuthScheme.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoAuthScheme.java @@ -1,6 +1,5 @@ package org.argeo.cms.internal.http.client; -import java.net.URI; import java.net.URL; import java.security.PrivilegedExceptionAction; import java.util.ArrayList; @@ -21,8 +20,7 @@ import org.apache.commons.httpclient.auth.MalformedChallengeException; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.params.DefaultHttpParams; import org.apache.commons.httpclient.params.HttpParams; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.argeo.cms.internal.http.NodeHttp; import org.ietf.jgss.GSSContext; import org.ietf.jgss.GSSException; import org.ietf.jgss.GSSManager; @@ -31,7 +29,7 @@ import org.ietf.jgss.Oid; /** Implementation of the SPNEGO auth scheme. */ public class SpnegoAuthScheme implements AuthScheme { - private final static Log log = LogFactory.getLog(SpnegoAuthScheme.class); +// private final static Log log = LogFactory.getLog(SpnegoAuthScheme.class); public static final String NAME = "Negotiate"; private final static Oid KERBEROS_OID; @@ -45,14 +43,13 @@ public class SpnegoAuthScheme implements AuthScheme { private boolean complete = false; private String realm; - private String tokenStr; @Override public void processChallenge(String challenge) throws MalformedChallengeException { -// if(tokenStr!=null){ -// log.error("Received challenge while there is a token. Failing."); -// complete = false; -// } + // if(tokenStr!=null){ + // log.error("Received challenge while there is a token. Failing."); + // complete = false; + // } } @@ -88,8 +85,8 @@ public class SpnegoAuthScheme implements AuthScheme { @Override public String authenticate(Credentials credentials, String method, String uri) throws AuthenticationException { -// log.debug("authenticate " + method + " " + uri); -// return null; + // log.debug("authenticate " + method + " " + uri); + // return null; throw new UnsupportedOperationException(); } @@ -103,7 +100,7 @@ public class SpnegoAuthScheme implements AuthScheme { } catch (URIException e1) { throw new IllegalStateException("Cannot authenticate", e1); } - String serverPrinc = "HTTP@" + hostname; + String serverPrinc = NodeHttp.DEFAULT_SERVICE + "@" + hostname; try { // Get service's principal name @@ -133,10 +130,6 @@ public class SpnegoAuthScheme implements AuthScheme { throw new AuthenticationException("Cannot authenticate to " + serverPrinc, e); } } - - private void doAuthenticate(URI uri){ - - } public static void main(String[] args) { if (args.length == 0) { @@ -157,7 +150,7 @@ public class SpnegoAuthScheme implements AuthScheme { ArrayList schemes = new ArrayList<>(); schemes.add(SpnegoAuthScheme.NAME); params.setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, schemes); - params.setParameter(CredentialsProvider.PROVIDER, new SpnegoCredentialProvider()); + params.setParameter(CredentialsProvider.PROVIDER, new HttpCredentialProvider()); int responseCode = Subject.doAs(lc.getSubject(), new PrivilegedExceptionAction() { public Integer run() throws Exception { diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoCredentials.java b/org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoCredentials.java new file mode 100644 index 000000000..f59b72a0b --- /dev/null +++ b/org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoCredentials.java @@ -0,0 +1,7 @@ +package org.argeo.cms.internal.http.client; + +import org.apache.commons.httpclient.Credentials; + +public class SpnegoCredentials implements Credentials { + +} diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/Activator.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/Activator.java index 73c530c82..e5930303a 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/Activator.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/Activator.java @@ -1,12 +1,16 @@ package org.argeo.cms.internal.kernel; +import java.awt.image.Kernel; import java.io.IOException; +import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.util.Dictionary; import java.util.List; import java.util.Locale; +import javax.security.auth.login.Configuration; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.argeo.cms.CmsException; @@ -22,6 +26,7 @@ import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.framework.ServiceReference; import org.osgi.service.log.LogReaderService; +import org.osgi.service.useradmin.UserAdmin; /** * Activates the {@link Kernel} from the provided {@link BundleContext}. Gives @@ -33,7 +38,7 @@ public class Activator implements BundleActivator { private static Activator instance; private BundleContext bc; - private CmsSecurity nodeSecurity; + // private CmsSecurity nodeSecurity; private LogReaderService logReaderService; // private ConfigurationAdmin configurationAdmin; @@ -50,7 +55,8 @@ public class Activator implements BundleActivator { // this.configurationAdmin = getService(ConfigurationAdmin.class); try { - nodeSecurity = new CmsSecurity(); + // nodeSecurity = new CmsSecurity(); + initSecurity(); initArgeoLogger(); initNode(); } catch (Exception e) { @@ -59,6 +65,16 @@ public class Activator implements BundleActivator { } } + private void initSecurity() { + if (System.getProperty(KernelConstants.JAAS_CONFIG_PROP) == null) { + String jaasConfig = KernelConstants.JAAS_CONFIG; + URL url = getClass().getClassLoader().getResource(jaasConfig); + System.setProperty(KernelConstants.JAAS_CONFIG_PROP, url.toExternalForm()); + } + // explicitly load JAAS configuration + Configuration.getConfiguration(); + } + private void initArgeoLogger() { logger = new NodeLogger(logReaderService); bc.registerService(ArgeoLogger.class, logger, null); @@ -112,12 +128,14 @@ public class Activator implements BundleActivator { } public static GSSCredential getAcceptorCredentials() { - return getCmsSecurity().getServerCredentials(); + ServiceReference sr = instance.bc.getServiceReference(UserAdmin.class); + NodeUserAdmin userAdmin = (NodeUserAdmin) instance.bc.getService(sr); + return userAdmin.getAcceptorCredentials(); } - static CmsSecurity getCmsSecurity() { - return instance.nodeSecurity; - } + // static CmsSecurity getCmsSecurity() { + // return instance.nodeSecurity; + // } public String[] getLocales() { // TODO optimize? diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsSecurity.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsSecurity.java index 061ecfab2..4f25e6106 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsSecurity.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsSecurity.java @@ -32,7 +32,7 @@ import org.apache.commons.logging.LogFactory; import org.argeo.cms.CmsException; import org.argeo.cms.internal.http.NodeHttp; import org.argeo.cms.internal.http.client.SpnegoAuthScheme; -import org.argeo.cms.internal.http.client.SpnegoCredentialProvider; +import org.argeo.cms.internal.http.client.HttpCredentialProvider; import org.argeo.naming.DnsBrowser; import org.argeo.node.NodeConstants; import org.ietf.jgss.GSSCredential; @@ -42,10 +42,11 @@ import org.ietf.jgss.GSSName; import org.ietf.jgss.Oid; /** Low-level kernel security */ +@Deprecated public class CmsSecurity implements KernelConstants { private final static Log log = LogFactory.getLog(CmsSecurity.class); // http://java.sun.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html - public final static Oid KERBEROS_OID; + private final static Oid KERBEROS_OID; static { try { KERBEROS_OID = new Oid("1.3.6.1.5.5.2"); @@ -72,20 +73,25 @@ public class CmsSecurity implements KernelConstants { private Path nodeKeyTab = KernelUtils.getOsgiInstancePath(KernelConstants.NODE_KEY_TAB_PATH); CmsSecurity() { - // Register client-side SPNEGO auth scheme - AuthPolicy.registerAuthScheme(SpnegoAuthScheme.NAME, SpnegoAuthScheme.class); - HttpParams params = DefaultHttpParams.getDefaultParams(); - ArrayList schemes = new ArrayList<>(); - schemes.add(SpnegoAuthScheme.NAME); - params.setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, schemes); - params.setParameter(CredentialsProvider.PROVIDER, new SpnegoCredentialProvider()); - params.setParameter(HttpMethodParams.COOKIE_POLICY, CookiePolicy.BROWSER_COMPATIBILITY); - // params.setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY); if (!DeployConfig.isInitialized()) // first init FirstInit.prepareInstanceArea(); securityLevel = evaluateSecurityLevel(); + + if (securityLevel == DEPLOYED) { + // Register client-side SPNEGO auth scheme + AuthPolicy.registerAuthScheme(SpnegoAuthScheme.NAME, SpnegoAuthScheme.class); + HttpParams params = DefaultHttpParams.getDefaultParams(); + ArrayList 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, CookiePolicy.BROWSER_COMPATIBILITY); + // params.setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY); + } + // Configure JAAS first if (System.getProperty(JAAS_CONFIG_PROP) == null) { String jaasConfig = securityLevel < DEPLOYED ? JAAS_CONFIG : JAAS_CONFIG_IPA; @@ -269,17 +275,17 @@ public class CmsSecurity implements KernelConstants { return securityLevel; } - public String getKerberosDomain() { - return kerberosDomain; - } +// public String getKerberosDomain() { +// return kerberosDomain; +// } - public Subject getNodeSubject() { - return nodeSubject; - } +// public Subject getNodeSubject() { +// return nodeSubject; +// } - public GSSCredential getServerCredentials() { - return acceptorCredentials; - } +// public GSSCredential getServerCredentials() { +// return acceptorCredentials; +// } // public void setSecurityLevel(int newValue) { // if (newValue != STANDALONE || newValue != DEV) diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/FirstInit.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/FirstInit.java index 8bd348f8e..78eb68289 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/FirstInit.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/FirstInit.java @@ -115,10 +115,6 @@ class FirstInit { // Business roles String userAdminUris = getFrameworkProp(NodeConstants.USERADMIN_URIS); if (userAdminUris == null) { - String kerberosDomain = Activator.getCmsSecurity().getKerberosDomain(); - if (kerberosDomain != null) { - userAdminUris = "ipa:///" + kerberosDomain; - } else { String demoBaseDn = "dc=example,dc=com"; File businessRolesFile = new File(nodeBaseDir, demoBaseDn + ".ldif"); if (!businessRolesFile.exists()) @@ -131,7 +127,6 @@ class FirstInit { userAdminUris = businessRolesFile.toURI().toString(); log.warn("## DEV Using dummy base DN " + demoBaseDn); // TODO downgrade security level - } } for (String userAdminUri : userAdminUris.split(" ")) uris.add(userAdminUri); @@ -163,7 +158,7 @@ class FirstInit { return res; } - + /** * Called before node initialisation, in order populate OSGi instance are * with some files (typically LDIF, etc). diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java index d0cf93d80..caadadefa 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java @@ -1,18 +1,44 @@ 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 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 javax.transaction.TransactionManager; +import org.apache.commons.httpclient.auth.AuthPolicy; +import org.apache.commons.httpclient.auth.CredentialsProvider; +import org.apache.commons.httpclient.cookie.CookiePolicy; +import org.apache.commons.httpclient.params.DefaultHttpParams; +import org.apache.commons.httpclient.params.HttpMethodParams; +import org.apache.commons.httpclient.params.HttpParams; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.argeo.cms.CmsException; +import org.argeo.cms.internal.http.NodeHttp; +import org.argeo.cms.internal.http.client.HttpCredentialProvider; +import org.argeo.cms.internal.http.client.SpnegoAuthScheme; +import org.argeo.naming.DnsBrowser; import org.argeo.node.NodeConstants; import org.argeo.osgi.useradmin.AbstractUserDirectory; import org.argeo.osgi.useradmin.AggregatingUserAdmin; @@ -20,6 +46,11 @@ import org.argeo.osgi.useradmin.LdapUserAdmin; import org.argeo.osgi.useradmin.LdifUserAdmin; import org.argeo.osgi.useradmin.UserAdminConf; import org.argeo.osgi.useradmin.UserDirectory; +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.FrameworkUtil; @@ -49,6 +80,10 @@ class NodeUserAdmin extends AggregatingUserAdmin implements ManagedServiceFactor private final ServiceTracker tmTracker; private final String cacheName = UserDirectory.class.getName(); + // GSS API + private Path nodeKeyTab = KernelUtils.getOsgiInstancePath(KernelConstants.NODE_KEY_TAB_PATH); + private GSSCredential acceptorCredentials; + public NodeUserAdmin(String systemRolesBaseDn) { super(systemRolesBaseDn); tmTracker = new ServiceTracker<>(bc, TransactionManager.class, null); @@ -68,13 +103,14 @@ class NodeUserAdmin extends AggregatingUserAdmin implements ManagedServiceFactor // Create AbstractUserDirectory userDirectory = u.getScheme().equals("ldap") ? new LdapUserAdmin(properties) : new LdifUserAdmin(properties); + Object realm = userDirectory.getProperties().get(UserAdminConf.realm.name()); addUserDirectory(userDirectory); // OSGi LdapName baseDn = userDirectory.getBaseDn(); Dictionary regProps = new Hashtable<>(); regProps.put(Constants.SERVICE_PID, pid); - if(isSystemRolesBaseDn(baseDn)) + if (isSystemRolesBaseDn(baseDn)) regProps.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE); regProps.put(UserAdminConf.baseDn.name(), baseDn); ServiceRegistration reg = bc.registerService(UserDirectory.class, userDirectory, regProps); @@ -82,7 +118,8 @@ class NodeUserAdmin extends AggregatingUserAdmin implements ManagedServiceFactor pidToServiceRegs.put(pid, reg); if (log.isDebugEnabled()) - log.debug("User directory " + userDirectory.getBaseDn() + " [" + u.getScheme() + "] enabled."); + log.debug("User directory " + userDirectory.getBaseDn() + " [" + u.getScheme() + "] enabled." + + (realm != null ? " " + realm + " realm." : "")); if (!isSystemRolesBaseDn(baseDn)) { if (userAdminReg != null) @@ -117,11 +154,127 @@ class NodeUserAdmin extends AggregatingUserAdmin implements ManagedServiceFactor userDirectory.setTransactionManager(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(NodeConstants.LOGIN_CONTEXT_NODE, callbackHandler); + nodeLc.login(); + acceptorCredentials = logInAsAcceptor(nodeLc.getSubject(), servicePrincipal); + } catch (LoginException e) { + throw new CmsException("Cannot log in kernel", e); + } + } + } + + // Register client-side SPNEGO auth scheme + AuthPolicy.registerAuthScheme(SpnegoAuthScheme.NAME, SpnegoAuthScheme.class); + HttpParams params = DefaultHttpParams.getDefaultParams(); + ArrayList 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, CookiePolicy.BROWSER_COMPATIBILITY); + // params.setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY); + } } - protected void preDestroy(UserDirectory userDirectory) { + 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 NodeHttp.DEFAULT_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 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() { + + @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 CmsException("Cannot create acceptor credentials for " + krb5Principal, gsse); + } + } + + public GSSCredential getAcceptorCredentials() { + return acceptorCredentials; + } + + 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); + } } } diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas.cfg b/org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas.cfg index 9ce961b28..a06230ff7 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas.cfg +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas.cfg @@ -1,6 +1,8 @@ USER { org.argeo.cms.auth.HttpSessionLoginModule sufficient; - org.argeo.cms.auth.UserAdminLoginModule requisite; + org.argeo.cms.auth.SpnegoLoginModule optional; + com.sun.security.auth.module.Krb5LoginModule optional; + org.argeo.cms.auth.UserAdminLoginModule sufficient; }; DATA_ADMIN { @@ -8,6 +10,11 @@ DATA_ADMIN { }; NODE { + com.sun.security.auth.module.Krb5LoginModule optional + keyTab="${osgi.instance.area}node/krb5.keytab" + useKeyTab=true + storeKey=true + debug=true; org.argeo.cms.auth.DataAdminLoginModule requisite; }; diff --git a/org.argeo.cms/src/org/argeo/cms/security/JcrKeyring.java b/org.argeo.cms/src/org/argeo/cms/security/JcrKeyring.java index 5c35e2779..43eab4b3c 100644 --- a/org.argeo.cms/src/org/argeo/cms/security/JcrKeyring.java +++ b/org.argeo.cms/src/org/argeo/cms/security/JcrKeyring.java @@ -34,11 +34,11 @@ import javax.jcr.RepositoryException; import javax.jcr.Session; import org.apache.commons.io.IOUtils; +import org.argeo.cms.ArgeoNames; +import org.argeo.cms.ArgeoTypes; import org.argeo.cms.CmsException; import org.argeo.jcr.ArgeoJcrException; import org.argeo.jcr.JcrUtils; -import org.argeo.node.ArgeoNames; -import org.argeo.node.ArgeoTypes; import org.argeo.node.NodeUtils; import org.argeo.node.security.PBEKeySpecCallback; diff --git a/org.argeo.cms/src/org/argeo/cms/tabular/JcrTabularRowIterator.java b/org.argeo.cms/src/org/argeo/cms/tabular/JcrTabularRowIterator.java index 5d380cfa5..04dce92c8 100644 --- a/org.argeo.cms/src/org/argeo/cms/tabular/JcrTabularRowIterator.java +++ b/org.argeo.cms/src/org/argeo/cms/tabular/JcrTabularRowIterator.java @@ -28,8 +28,8 @@ import javax.jcr.PropertyType; import javax.jcr.RepositoryException; import org.apache.commons.io.IOUtils; +import org.argeo.cms.ArgeoTypes; import org.argeo.jcr.ArgeoJcrException; -import org.argeo.node.ArgeoTypes; import org.argeo.node.tabular.ArrayTabularRow; import org.argeo.node.tabular.TabularColumn; import org.argeo.node.tabular.TabularRow; diff --git a/org.argeo.cms/src/org/argeo/cms/tabular/JcrTabularWriter.java b/org.argeo.cms/src/org/argeo/cms/tabular/JcrTabularWriter.java index ac57d2ab7..a6e1e286c 100644 --- a/org.argeo.cms/src/org/argeo/cms/tabular/JcrTabularWriter.java +++ b/org.argeo.cms/src/org/argeo/cms/tabular/JcrTabularWriter.java @@ -27,9 +27,9 @@ import javax.jcr.PropertyType; import javax.jcr.RepositoryException; import org.apache.commons.io.IOUtils; +import org.argeo.cms.ArgeoTypes; import org.argeo.jcr.ArgeoJcrException; import org.argeo.jcr.JcrUtils; -import org.argeo.node.ArgeoTypes; import org.argeo.node.tabular.TabularColumn; import org.argeo.node.tabular.TabularWriter; import org.argeo.util.CsvWriter; diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java index 03eeeebdc..c20260056 100644 --- a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java +++ b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java @@ -55,8 +55,9 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory private final URI uri; private UserAdmin externalRoles; - private List indexedUserProperties = Arrays - .asList(new String[] { LdapAttrs.uid.name(), LdapAttrs.mail.name(), LdapAttrs.cn.name() }); + // private List indexedUserProperties = Arrays + // .asList(new String[] { LdapAttrs.uid.name(), LdapAttrs.mail.name(), + // LdapAttrs.cn.name() }); private String memberAttributeId = "member"; private List credentialAttributeIds = Arrays.asList(new String[] { LdapAttrs.userPassword.name() }); @@ -238,22 +239,23 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory @Override public User getUser(String key, String value) { // TODO check value null or empty - List collectedUsers = new ArrayList(getIndexedUserProperties().size()); + List collectedUsers = new ArrayList(); if (key != null) { doGetUser(key, value, collectedUsers); } else { - // try dn - DirectoryUser user = null; - try { - user = (DirectoryUser) getRole(value); - if (user != null) - collectedUsers.add(user); - } catch (Exception e) { - // silent - } - // try all indexes - for (String attr : getIndexedUserProperties()) - doGetUser(attr, value, collectedUsers); + throw new UserDirectoryException("Key cannot be null"); + // // try dn + // DirectoryUser user = null; + // try { + // user = (DirectoryUser) getRole(value); + // if (user != null) + // collectedUsers.add(user); + // } catch (Exception e) { + // // silent + // } + // // try all indexes + // for (String attr : getIndexedUserProperties()) + // doGetUser(attr, value, collectedUsers); } if (collectedUsers.size() == 1) return collectedUsers.get(0); @@ -279,11 +281,14 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory } else { // bind AbstractUserDirectory scopedUserAdmin = scope(user); - DirectoryUser directoryUser = (DirectoryUser) scopedUserAdmin.getRole(user.getName()); - LdifAuthorization authorization = new LdifAuthorization(directoryUser, - scopedUserAdmin.getAllRoles(directoryUser)); - scopedUserAdmin.destroy(); - return authorization; + try { + DirectoryUser directoryUser = (DirectoryUser) scopedUserAdmin.getRole(user.getName()); + LdifAuthorization authorization = new LdifAuthorization(directoryUser, + scopedUserAdmin.getAllRoles(directoryUser)); + return authorization; + } finally { + scopedUserAdmin.destroy(); + } } } @@ -392,13 +397,14 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory return uri; } - protected List getIndexedUserProperties() { - return indexedUserProperties; - } - - protected void setIndexedUserProperties(List indexedUserProperties) { - this.indexedUserProperties = indexedUserProperties; - } + // protected List getIndexedUserProperties() { + // return indexedUserProperties; + // } + // + // protected void setIndexedUserProperties(List + // indexedUserProperties) { + // this.indexedUserProperties = indexedUserProperties; + // } private static boolean readOnlyDefault(URI uri) { if (uri == null) diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java index 860c5efc9..93ecdca47 100644 --- a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java +++ b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java @@ -183,7 +183,7 @@ public class AggregatingUserAdmin implements UserAdmin { * Called before each user directory is destroyed, so that additional * actions can be performed. */ - protected void preDestroy(UserDirectory userDirectory) { + protected void preDestroy(AbstractUserDirectory userDirectory) { } } diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/IpaUtils.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/IpaUtils.java index 1d198c66e..9d0056c55 100644 --- a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/IpaUtils.java +++ b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/IpaUtils.java @@ -16,8 +16,8 @@ public class IpaUtils { public final static String IPA_USER_DIRECTORY_CONFIG = UserAdminConf.userBase + "=" + IPA_USER_BASE + "&" + UserAdminConf.groupBase + "=" + IPA_GROUP_BASE + "&" + UserAdminConf.readOnly + "=true"; - static String domainToUserDirectoryConfigPath(String domain) { - return domainToBaseDn(domain) + "?" + IPA_USER_DIRECTORY_CONFIG; + static String domainToUserDirectoryConfigPath(String realm) { + return domainToBaseDn(realm) + "?" + IPA_USER_DIRECTORY_CONFIG + "&" + UserAdminConf.realm.name() + "=" + realm; } public static String domainToBaseDn(String domain) { diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserAdminConf.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserAdminConf.java index 1d377dcb6..093b44370 100644 --- a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserAdminConf.java +++ b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserAdminConf.java @@ -2,6 +2,7 @@ package org.argeo.osgi.useradmin; import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.net.InetAddress; import java.net.URI; import java.net.URISyntaxException; import java.net.URLDecoder; @@ -42,7 +43,10 @@ public enum UserAdminConf { groupBase("ou=Groups"), /** Read-only source */ - readOnly(null); + readOnly(null), + + /** Authentication realm */ + realm(null); public final static String FACTORY_PID = "org.argeo.osgi.useradmin.config"; private final static Log log = LogFactory.getLog(UserAdminConf.class); @@ -173,13 +177,20 @@ public enum UserAdminConf { } private static URI convertIpaConfig(URI uri) { - assert uri.getPath() != null; - assert uri.getPath().length() > 1; - String kerberosDomain = uri.getPath().substring(1); + String path = uri.getPath(); + String kerberosRealm; + if (path == null || path.length() <= 1) { + kerberosRealm = kerberosDomainFromDns(); + } else { + kerberosRealm = path.substring(1); + } + + if (kerberosRealm == null) + throw new UserDirectoryException("No Kerberos domain available for " + uri); try (DnsBrowser dnsBrowser = new DnsBrowser()) { String ldapHostsStr = uri.getHost(); if (ldapHostsStr == null || ldapHostsStr.trim().equals("")) { - List ldapHosts = dnsBrowser.getSrvRecordsAsHosts("_ldap._tcp." + kerberosDomain.toLowerCase()); + List ldapHosts = dnsBrowser.getSrvRecordsAsHosts("_ldap._tcp." + kerberosRealm.toLowerCase()); if (ldapHosts == null || ldapHosts.size() == 0) { throw new UserDirectoryException("Cannot configure LDAP for IPA " + uri); } else { @@ -187,7 +198,7 @@ public enum UserAdminConf { } } URI convertedUri = new URI( - "ldap://" + ldapHostsStr + "/" + IpaUtils.domainToUserDirectoryConfigPath(kerberosDomain)); + "ldap://" + ldapHostsStr + "/" + IpaUtils.domainToUserDirectoryConfigPath(kerberosRealm)); if (log.isDebugEnabled()) log.debug("Converted " + uri + " to " + convertedUri); return convertedUri; @@ -196,6 +207,20 @@ public enum UserAdminConf { } } + private static String kerberosDomainFromDns() { + String kerberosDomain; + try (DnsBrowser dnsBrowser = new DnsBrowser()) { + InetAddress localhost = InetAddress.getLocalHost(); + String hostname = localhost.getHostName(); + String dnsZone = hostname.substring(hostname.indexOf('.') + 1); + kerberosDomain = dnsBrowser.getRecord("_kerberos." + dnsZone, "TXT"); + return kerberosDomain; + } catch (Exception e) { + throw new UserDirectoryException("Cannot determine Kerberos domain from DNS", e); + } + + } + private static Map> splitQuery(String query) throws UnsupportedEncodingException { final Map> query_pairs = new LinkedHashMap>(); if (query == null) diff --git a/org.argeo.node.api/src/org/argeo/node/NodeOID.java b/org.argeo.node.api/src/org/argeo/node/NodeOID.java index 9d8ff3d3a..387d511d2 100644 --- a/org.argeo.node.api/src/org/argeo/node/NodeOID.java +++ b/org.argeo.node.api/src/org/argeo/node/NodeOID.java @@ -5,14 +5,7 @@ interface NodeOID { // ATTRIBUTE TYPES String ATTRIBUTE_TYPES = BASE + ".4"; - String URI = ATTRIBUTE_TYPES + ".1"; - String HTTP_PORT = ATTRIBUTE_TYPES + ".2"; - String HTTPS_PORT = ATTRIBUTE_TYPES + ".3"; // OBJECT CLASSES String OBJECT_CLASSES = BASE + ".6"; - String JCR_REPOSITORY = OBJECT_CLASSES + ".1"; - - // EXTERNAL - String LABELED_URI = "1.3.6.1.4.1.250.1.57"; } diff --git a/org.argeo.node.api/src/org/argeo/node/node.cnd b/org.argeo.node.api/src/org/argeo/node/node.cnd index aeedf4db7..f3d0619ef 100644 --- a/org.argeo.node.api/src/org/argeo/node/node.cnd +++ b/org.argeo.node.api/src/org/argeo/node/node.cnd @@ -1,6 +1,5 @@ - // DN (see https://tools.ietf.org/html/rfc4514) @@ -21,36 +20,3 @@ mixin [node:groupHome] mixin - ldap:cn (STRING) m - -[argeo:remoteRepository] > nt:unstructured -- argeo:uri (STRING) -- argeo:userID (STRING) -+ argeo:password (argeo:encrypted) - -// TABULAR CONTENT -[argeo:table] > nt:file -+ * (argeo:column) * - -[argeo:column] > mix:title -- jcr:requiredType (STRING) = 'STRING' - -[argeo:csv] > nt:resource - -// CRYPTO -[argeo:encrypted] > nt:base -mixin -// initialization vector used by some algorithms -- argeo:iv (BINARY) - -[argeo:pbeKeySpec] > nt:base -mixin -- argeo:secretKeyFactory (STRING) -- argeo:salt (BINARY) -- argeo:iterationCount (LONG) -- argeo:keyLength (LONG) -- argeo:secretKeyEncryption (STRING) - -[argeo:pbeSpec] > argeo:pbeKeySpec -mixin -- argeo:cipher (STRING) - diff --git a/org.argeo.node.api/src/org/argeo/node/security/UserPrincipal.java b/org.argeo.node.api/src/org/argeo/node/security/UserPrincipal.java index 0c51adbd0..e65c4f845 100644 --- a/org.argeo.node.api/src/org/argeo/node/security/UserPrincipal.java +++ b/org.argeo.node.api/src/org/argeo/node/security/UserPrincipal.java @@ -7,6 +7,7 @@ import javax.naming.ldap.LdapName; import org.argeo.node.NodeConstants; /** Marker for logged in users. */ +@Deprecated public final class UserPrincipal implements Principal { private final String name = NodeConstants.ROLE_USER; -- 2.30.2