X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.cms%2Fsrc%2Forg%2Fargeo%2Fcms%2Finternal%2Fruntime%2FCmsUserAdmin.java;h=6aa490a69ae144f9ec697a0e6c2f39b0ce482762;hb=54df376a9c2dd458a82eaa09bfbb718fe699dd0d;hp=7c4d807746ff481e7bcdd56f7058ecc5d3b8c86c;hpb=b843d903237a2a4192c40d8c933e71137284050b;p=lgpl%2Fargeo-commons.git diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsUserAdmin.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsUserAdmin.java index 7c4d80774..6aa490a69 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsUserAdmin.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsUserAdmin.java @@ -1,19 +1,21 @@ package org.argeo.cms.internal.runtime; import java.io.IOException; -import java.net.Inet6Address; import java.net.InetAddress; import java.net.URI; import java.net.URISyntaxException; +import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.Dictionary; import java.util.Iterator; +import java.util.List; +import java.util.Optional; 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; @@ -23,26 +25,18 @@ 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.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.argeo.api.cms.CmsState; +import org.argeo.api.cms.transaction.WorkControl; +import org.argeo.api.cms.transaction.WorkTransaction; +import org.argeo.cms.CmsDeployProperty; +import org.argeo.cms.dns.DnsBrowser; +import org.argeo.cms.osgi.useradmin.AggregatingUserAdmin; +import org.argeo.cms.osgi.useradmin.DirectoryUserAdmin; +import org.argeo.cms.osgi.useradmin.UserDirectory; +import org.argeo.cms.runtime.DirectoryConf; import org.ietf.jgss.GSSCredential; import org.ietf.jgss.GSSException; import org.ietf.jgss.GSSManager; @@ -56,7 +50,7 @@ import org.osgi.service.useradmin.Role; * Aggregates multiple {@link UserDirectory} and integrates them with system * roles. */ -public class CmsUserAdmin extends AggregatingUserAdmin { +public class CmsUserAdmin extends AggregatingUserAdmin { private final static CmsLog log = CmsLog.getLog(CmsUserAdmin.class); // GSS API @@ -68,24 +62,141 @@ public class CmsUserAdmin extends AggregatingUserAdmin { private WorkControl transactionManager; private WorkTransaction userTransaction; + private CmsState cmsState; + public CmsUserAdmin() { - super(CmsConstants.ROLES_BASEDN, CmsConstants.TOKENS_BASEDN); + super(CmsConstants.SYSTEM_ROLES_BASEDN, CmsConstants.TOKENS_BASEDN); } public void start() { + super.start(); + List> configs = getUserDirectoryConfigs(); + for (Dictionary config : configs) { + enableUserDirectory(config); +// if (userDirectory.getRealm().isPresent()) +// loadIpaJaasConfiguration(); + } + log.debug(() -> "CMS user admin available"); } public void stop() { +// for (UserDirectory userDirectory : getUserDirectories()) { +// removeUserDirectory(userDirectory); +// } + super.stop(); + } + + protected List> getUserDirectoryConfigs() { + List> res = new ArrayList<>(); + Path nodeBase = cmsState.getDataPath(KernelConstants.DIR_PRIVATE); + List uris = new ArrayList<>(); + + // node roles + String nodeRolesUri = null;// getFrameworkProp(CmsConstants.ROLES_URI); + String baseNodeRoleDn = CmsConstants.SYSTEM_ROLES_BASEDN; + if (nodeRolesUri == null && nodeBase != null) { + nodeRolesUri = baseNodeRoleDn + ".ldif"; + Path nodeRolesFile = nodeBase.resolve(nodeRolesUri); + if (!Files.exists(nodeRolesFile)) + try { + Files.copy(CmsUserAdmin.class.getResourceAsStream(baseNodeRoleDn + ".ldif"), nodeRolesFile); + } catch (IOException e) { + throw new RuntimeException("Cannot copy demo resource", e); + } + // nodeRolesUri = nodeRolesFile.toURI().toString(); + } + if (nodeRolesUri != null) + uris.add(nodeRolesUri); + + // node tokens + String nodeTokensUri = null;// getFrameworkProp(CmsConstants.TOKENS_URI); + String baseNodeTokensDn = CmsConstants.TOKENS_BASEDN; + if (nodeTokensUri == null && nodeBase != null) { + nodeTokensUri = baseNodeTokensDn + ".ldif"; + Path nodeTokensFile = nodeBase.resolve(nodeTokensUri); + if (!Files.exists(nodeTokensFile)) + try { + Files.copy(CmsUserAdmin.class.getResourceAsStream(baseNodeTokensDn + ".ldif"), nodeTokensFile); + } catch (IOException e) { + throw new RuntimeException("Cannot copy demo resource", e); + } + // nodeRolesUri = nodeRolesFile.toURI().toString(); + } + if (nodeTokensUri != null) + uris.add(nodeTokensUri); + + // Business roles +// String userAdminUris = getFrameworkProp(CmsConstants.USERADMIN_URIS); + List userAdminUris = CmsStateImpl.getDeployProperties(cmsState, CmsDeployProperty.DIRECTORY);// getFrameworkProp(CmsConstants.USERADMIN_URIS); + for (String userAdminUri : userAdminUris) { + if (userAdminUri == null) + continue; +// if (!userAdminUri.trim().equals("")) + uris.add(userAdminUri); + } + + if (uris.size() == 0 && nodeBase != null) { + // TODO put this somewhere else + String demoBaseDn = "dc=example,dc=com"; + String userAdminUri = demoBaseDn + ".ldif"; + Path businessRolesFile = nodeBase.resolve(userAdminUri); + Path systemRolesFile = nodeBase.resolve("ou=roles,ou=node.ldif"); + if (!Files.exists(businessRolesFile)) + try { + Files.copy(CmsUserAdmin.class.getResourceAsStream(demoBaseDn + ".ldif"), businessRolesFile); + if (!Files.exists(systemRolesFile)) + Files.copy(CmsUserAdmin.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 + } + + // 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 = Paths.get(uri).toRealPath().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(DirectoryConf.SCHEME_FILE)) { + u = Paths.get(u).toRealPath().toUri(); + } + } catch (Exception e) { + throw new RuntimeException("Cannot interpret " + uri + " as an uri", e); + } + + try { + Dictionary properties = DirectoryConf.uriAsProperties(u.toString()); + res.add(properties); + } catch (Exception e) { + log.error("Cannot load user directory " + u, e); + } + } + + return res; } - + public UserDirectory enableUserDirectory(Dictionary properties) { - String uri = (String) properties.get(UserAdminConf.uri.name()); - Object realm = properties.get(UserAdminConf.realm.name()); + String uri = (String) properties.get(DirectoryConf.uri.name()); + Object realm = properties.get(DirectoryConf.realm.name()); URI u; try { if (uri == null) { - String baseDn = (String) properties.get(UserAdminConf.baseDn.name()); - u = KernelUtils.getOsgiInstanceUri(KernelConstants.DIR_NODE + '/' + baseDn + ".ldif"); + String baseDn = (String) properties.get(DirectoryConf.baseDn.name()); + u = KernelUtils.getOsgiInstanceUri(KernelConstants.DIR_PRIVATE + '/' + baseDn + ".ldif"); } else if (realm != null) { u = null; } else { @@ -96,32 +207,31 @@ public class CmsUserAdmin extends AggregatingUserAdmin { } // 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()); - } - LdapName baseDn = userDirectory.getBaseDn(); + UserDirectory userDirectory = new DirectoryUserAdmin(u, properties); +// if (realm != null || DirectoryConf.SCHEME_LDAP.equals(u.getScheme()) +// || DirectoryConf.SCHEME_LDAPS.equals(u.getScheme())) { +// userDirectory = new LdapUserAdmin(properties); +// } else if (DirectoryConf.SCHEME_FILE.equals(u.getScheme())) { +// userDirectory = new LdifUserAdmin(u, properties); +// } else if (DirectoryConf.SCHEME_OS.equals(u.getScheme())) { +// userDirectory = new OsUserDirectory(u, properties); +// singleUser = true; +// } else { +// throw new IllegalArgumentException("Unsupported scheme " + u.getScheme()); +// } + String basePath = userDirectory.getBase(); addUserDirectory(userDirectory); - if (isSystemRolesBaseDn(baseDn)) { + if (isSystemRolesBaseDn(basePath)) { addStandardSystemRoles(); - } + } if (log.isDebugEnabled()) { - log.debug("User directory " + userDirectory.getBaseDn() + (u != null ? " [" + u.getScheme() + "]" : "") + log.debug("User directory " + userDirectory.getBase() + (u != null ? " [" + u.getScheme() + "]" : "") + " enabled." + (realm != null ? " " + realm + " realm." : "")); } return userDirectory; } - protected void addStandardSystemRoles() { // we assume UserTransaction is already available (TODO make it more robust) try { @@ -145,7 +255,6 @@ public class CmsUserAdmin extends AggregatingUserAdmin { } } - @Override protected void addAbstractSystemRoles(Authorization rawAuthorization, Set sysRoles) { if (rawAuthorization.getName() == null) { @@ -155,13 +264,15 @@ public class CmsUserAdmin extends AggregatingUserAdmin { } } - protected void postAdd(AbstractUserDirectory userDirectory) { + @Override + protected void postAdd(UserDirectory userDirectory) { userDirectory.setTransactionControl(transactionManager); - Object realm = userDirectory.getProperties().get(UserAdminConf.realm.name()); - if (realm != null) { + Optional realm = userDirectory.getRealm(); + if (realm.isPresent()) { + loadIpaJaasConfiguration(); if (Files.exists(nodeKeyTab)) { - String servicePrincipal = getKerberosServicePrincipal(realm.toString()); + String servicePrincipal = getKerberosServicePrincipal(realm.get()); if (servicePrincipal != null) { CallbackHandler callbackHandler = new CallbackHandler() { @Override @@ -173,7 +284,7 @@ public class CmsUserAdmin extends AggregatingUserAdmin { } }; try { - LoginContext nodeLc = new LoginContext(CmsAuth.LOGIN_CONTEXT_NODE, callbackHandler); + LoginContext nodeLc = CmsAuth.NODE.newLoginContext(callbackHandler); nodeLc.login(); acceptorCredentials = logInAsAcceptor(nodeLc.getSubject(), servicePrincipal); } catch (LoginException e) { @@ -182,22 +293,13 @@ public class CmsUserAdmin extends AggregatingUserAdmin { } } - // 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, KernelConstants.COOKIE_POLICY_BROWSER_COMPATIBILITY); - // params.setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY); } } - protected void preDestroy(AbstractUserDirectory userDirectory) { - Object realm = userDirectory.getProperties().get(UserAdminConf.realm.name()); - if (realm != null) { + @Override + protected void preDestroy(UserDirectory userDirectory) { + Optional realm = userDirectory.getRealm(); + if (realm.isPresent()) { if (acceptorCredentials != null) { try { acceptorCredentials.dispose(); @@ -209,16 +311,30 @@ public class CmsUserAdmin extends AggregatingUserAdmin { } } - private String getKerberosServicePrincipal(String realm) { - String hostname; - try (DnsBrowser dnsBrowser = new DnsBrowser()) { - InetAddress localhost = InetAddress.getLocalHost(); - hostname = localhost.getHostName(); + private void loadIpaJaasConfiguration() { + if (CmsStateImpl.getDeployProperty(cmsState, CmsDeployProperty.JAVA_LOGIN_CONFIG) == null) { + String jaasConfig = KernelConstants.JAAS_CONFIG_IPA; + URL url = getClass().getClassLoader().getResource(jaasConfig); + KernelUtils.setJaasConfiguration(url); + log.debug("Set IPA JAAS configuration."); + } + } + + protected String getKerberosServicePrincipal(String realm) { + if (!Files.exists(nodeKeyTab)) + return null; + List dns = CmsStateImpl.getDeployProperties(cmsState, CmsDeployProperty.DNS); + String hostname = CmsStateImpl.getDeployProperty(cmsState, CmsDeployProperty.HOST); + try (DnsBrowser dnsBrowser = new DnsBrowser(dns)) { + hostname = hostname != null ? hostname : InetAddress.getLocalHost().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 ipv4fromDns = dnsBrowser.getRecord(hostname, "A"); + String ipv6fromDns = dnsBrowser.getRecord(hostname, "AAAA"); + if (ipv4fromDns == null && ipv6fromDns == null) + throw new IllegalStateException("hostname " + hostname + " is not registered in DNS"); + // boolean consistentIp = localhost.getHostAddress().equals(ipfromDns); String kerberosDomain = dnsBrowser.getRecord("_kerberos." + dnsZone, "TXT"); - if (consistentIp && kerberosDomain != null && kerberosDomain.equals(realm) && Files.exists(nodeKeyTab)) { + if (kerberosDomain != null && kerberosDomain.equals(realm)) { return KernelConstants.DEFAULT_KERBEROS_SERVICE + "/" + hostname + "@" + kerberosDomain; } else return null; @@ -229,6 +345,13 @@ public class CmsUserAdmin extends AggregatingUserAdmin { } private GSSCredential logInAsAcceptor(Subject subject, String servicePrincipal) { + // not static because class is not supported by Android + final Oid KERBEROS_OID; + try { + KERBEROS_OID = new Oid("1.3.6.1.5.5.2"); + } catch (GSSException e) { + throw new IllegalStateException("Cannot create Kerberos OID", e); + } // GSS Iterator krb5It = subject.getPrincipals(KerberosPrincipal.class).iterator(); if (!krb5It.hasNext()) @@ -284,16 +407,8 @@ public class CmsUserAdmin extends AggregatingUserAdmin { 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); - } + public void setCmsState(CmsState cmsState) { + this.cmsState = cmsState; } + }