Integrate IULdapSSHAccountProvisioner with fixes
Project: http://git-wip-us.apache.org/repos/asf/airavata/repo Commit: http://git-wip-us.apache.org/repos/asf/airavata/commit/74371129 Tree: http://git-wip-us.apache.org/repos/asf/airavata/tree/74371129 Diff: http://git-wip-us.apache.org/repos/asf/airavata/diff/74371129 Branch: refs/heads/AIRAVATA-2500 Commit: 743711290ee07336005ee7a4d023cdce5325a6f6 Parents: 1c2ab27 Author: Marcus Christie <[email protected]> Authored: Mon Sep 18 15:12:16 2017 -0400 Committer: Marcus Christie <[email protected]> Committed: Tue Sep 19 15:08:05 2017 -0400 ---------------------------------------------------------------------- .../IULdapSSHAccountProvisioner.java | 242 ++++++++++--------- .../IULdapSSHAccountProvisionerProvider.java | 47 ++-- .../SSHAccountProvisionerFactoryTest.java | 4 - .../TestSSHAccountProvisionerProvider.java | 2 +- 4 files changed, 153 insertions(+), 142 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/airavata/blob/74371129/modules/compute-account-provisioning/src/main/java/org/apache/airavata/accountprovisioning/provisioner/IULdapSSHAccountProvisioner.java ---------------------------------------------------------------------- diff --git a/modules/compute-account-provisioning/src/main/java/org/apache/airavata/accountprovisioning/provisioner/IULdapSSHAccountProvisioner.java b/modules/compute-account-provisioning/src/main/java/org/apache/airavata/accountprovisioning/provisioner/IULdapSSHAccountProvisioner.java index 331c01c..d0c204f 100644 --- a/modules/compute-account-provisioning/src/main/java/org/apache/airavata/accountprovisioning/provisioner/IULdapSSHAccountProvisioner.java +++ b/modules/compute-account-provisioning/src/main/java/org/apache/airavata/accountprovisioning/provisioner/IULdapSSHAccountProvisioner.java @@ -21,152 +21,158 @@ package org.apache.airavata.accountprovisioning.provisioner; import org.apache.airavata.accountprovisioning.ConfigParam; +import org.apache.airavata.accountprovisioning.SSHAccountManager; import org.apache.airavata.accountprovisioning.SSHAccountProvisioner; -import org.apache.directory.api.ldap.model.cursor.CursorException; -import org.apache.directory.ldap.client.api.*; -import org.apache.directory.api.ldap.model.cursor.EntryCursor; -import org.apache.directory.api.ldap.model.entry.Attribute; +import org.apache.directory.api.ldap.model.entry.DefaultAttribute; import org.apache.directory.api.ldap.model.entry.Entry; -import org.apache.directory.api.ldap.model.entry.Modification; -import org.apache.directory.api.ldap.model.entry.DefaultEntry; +import org.apache.directory.api.ldap.model.entry.ModificationOperation; import org.apache.directory.api.ldap.model.exception.LdapException; -import org.apache.directory.api.ldap.model.message.SearchScope; -import org.apache.directory.api.ldap.model.message.DeleteResponse; +import org.apache.directory.api.ldap.model.message.ModifyRequest; +import org.apache.directory.api.ldap.model.message.ModifyRequestImpl; +import org.apache.directory.api.ldap.model.message.ModifyResponse; import org.apache.directory.api.ldap.model.message.ResultCodeEnum; -import org.junit.Assert; +import org.apache.directory.api.ldap.model.name.Dn; +import org.apache.directory.ldap.client.api.LdapConnection; +import org.apache.directory.ldap.client.api.LdapNetworkConnection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.HashMap; import java.util.Map; -import java.util.List; +import java.util.function.Function; public class IULdapSSHAccountProvisioner implements SSHAccountProvisioner { - String ldaphost, adminDN, ldap_username, ldap_password, adminPass, ldapBaseDN; - int ldapport, ldapPortId; - LdapConnection connection; + private final static Logger logger = LoggerFactory.getLogger(SSHAccountManager.class); + public static final String LDAP_PUBLIC_KEY_OBJECT_CLASS = "ldapPublicKey"; + public static final String SSH_PUBLIC_KEY_ATTRIBUTE_NAME = "sshPublicKey"; + + private String ldapHost, ldapUsername, ldapPassword, ldapBaseDN, canonicalScratchLocation; + private int ldapPort; @Override public void init(Map<ConfigParam, String> config) { - // TODO: implement - ldapServerName = config.get(new ConfigParam("ldaphost"));//"bazooka.hps.iu.edu" - ldapPortId = config.get(new ConfigParam("ldapport"));//"636" - ldap_username = config.get(new ConfigParam("ldap_username"));//"cn=sgrcusr" - ldap_password = config.get(new ConfigParam("ldap_password"));//"lore footwork engorge" - ldapBaseDN = config.get(new ConfigParam( "ldapBaseDN" ));//"dc=rt,dc=iu,dc=edu" - try { - connection = new LdapNetworkConnection(ldaphost, ldapport, true); - - - System.out.println( "binding connection:" ); - String AuthDN=ldap_username+","+ldapBaseDN; - connection.bind(AuthDN,ldap_password); - //check that we're auth'ed and connected - System.out.println("asserting bound:"); - Assert.assertTrue( connection.isAuthenticated() ); - Assert.assertTrue( connection.isConnected() ); - } catch (Exception e) { - System.out.println("Exception caught!"); - System.out.println(e.getClass().getCanonicalName()); - System.out.println(e.getMessage()); - System.out.println(e.getCause()); - - } - //catch (LdapException le) { - //System.out.println("Ldap Exception caught!", le); - //} - + ldapHost = config.get(IULdapSSHAccountProvisionerProvider.LDAP_HOST);//"bazooka.hps.iu.edu" + ldapPort = Integer.valueOf(config.get(IULdapSSHAccountProvisionerProvider.LDAP_PORT));//"636" + ldapUsername = config.get(IULdapSSHAccountProvisionerProvider.LDAP_USERNAME);//"cn=sgrcusr" + ldapPassword = config.get(IULdapSSHAccountProvisionerProvider.LDAP_PASSWORD); //"secret password" + ldapBaseDN = config.get(IULdapSSHAccountProvisionerProvider.LDAP_BASE_DN);//"dc=rt,dc=iu,dc=edu" + canonicalScratchLocation = config.get(IULdapSSHAccountProvisionerProvider.CANONICAL_SCRATCH_LOCATION); //"/N/dc2/scratch/username/iu-gateway" } @Override - public boolean hasAccount(String username) { - // TODO: implement - // To verify if the user has a login on a remote host - // if not advice the user to get an account (if possible) before returning. - // a search at the ldap is used to set the value - System.out.println("attempting search:"); - String uidName="uid="+username; - List<String> userClusters = new ArrayList(); - try { - EntryCursor cursor = connection.search( ldapBaseDN, uidName, SearchScope.SUBTREE, "*" ); - System.out.println( "Printing LDAP-wide results for " + username + ":" ); - while (cursor.next()) { - Entry entry = cursor.get(); - String DNName = entry.getDn().getName(); - String[] words=DNName.split(","); - String cluster = words[1].replace("ou="); - userClusters.add(cluster); - //System.out.println( entry.getDn().getName() ); - //System.out.println( entry.getAttributes() ); - return true; + public boolean hasAccount(String userId) { + String username = getUsername(userId); + boolean result = withLdapConnection(ldapConnection -> { + try { + return ldapConnection.exists("uid=" + username + "," + ldapBaseDN); + } catch (LdapException e) { + throw new RuntimeException(e); } - cursor.close(); - }catch (Exception e) { - System.out.println( "Exception caught!" ); - System.out.println( e.getClass().getCanonicalName() ); - System.out.println( e.getMessage() ); - System.out.println( e.getCause() ); - } catch (CursorException ce) { - System.out.println( "Cursor Exception caught!" ); - }catch (LdapException le) { - System.out.println( "Ldap Exception caught!" ); - } - return false; + }); + return result; } @Override - public void createAccount(String username, String sshPublicKey) { + public void createAccount(String userId, String sshPublicKey) { throw new UnsupportedOperationException("IULdapSSHAccountProvisioner does not support creating cluster accounts at this time."); } @Override - public void installSSHKey(String username, String sshPublicKey) { - // TODO: implement - // use Eric Coulter's LdapBazookaSearchAndAdd to accomplsih this - String GatewaySSHPublicKey = sshPublicKey; - String IULocalUserName = username; - - - /* - LdapConnectionConfig lcconfig = new LdapConnectionConfig(); - lcconfig.setLdapHost(ldapservername);// LdapServerName = ldapserverName;//from ConfigParam should be like bazooka.hpc.iu.edu - lcconfig.setLdapPort(ldapPortId);// LdapPortID = ldapPortId;//from ConfigParam 636 - lcconfig.setName(adminDN);// = adminName;//from ConfigParam sgrcusr - lcconfig.setCredentials(AdminPass);//from ConfigParam "lore footwork engorge" - - DefaultLdapConnectionFactory lcfactory = new DefaultLdapConnectionFactory( lcconfig ); - lcfactory.setTimeOut( connectionTimeout ); - */ - - Modification addSSHPublicKeyAdd = new DefaultModification(ModificationOperation.ADD_ATTRIBUTE,"add","sshPublicKey"); - Modification SSHPublicKey = new DefaultModification(ModificationOperation.ADD_ATTRIBUTE, "sshPublicKey",GatewaySSHPublicKey); - /* - Entry modentry = New DefaultEntry( - "cn=sgrcusr,dc=rt,dc=iu,dc=edu", - "ObjectClass: person", - "ObjectClass: ldapPublicKey", - "cn", username, - "dn", "uid=",username, "ou=bigred2-sgrc,dc=rt,dc=iu,dc=edu", - "add: sshPublicKey", - "sshPublicKey", GatewaySSHPublicKey ); - */ + public void installSSHKey(String userId, String sshPublicKey) { + String username = getUsername(userId); + boolean success = withLdapConnection(ldapConnection -> { try { - connection.modify(ldapBaseDN, addSSHPublicKeyAdd );//ldapmodify - connection.modify(ldapBaseDN, SSHPublicKey ); - } catch (Exception e) { - System.out.println("Exception caught!", e); - System.out.println( e.getClass().getCanonicalName() ); - System.out.println( e.getMessage() ); - System.out.println( e.getCause() ); + String dn = "uid=" + username + "," + ldapBaseDN; + + Entry entry = ldapConnection.lookup(dn); + if (entry == null) { + throw new RuntimeException("User [" + username + "] has no entry for " + dn); + } + boolean hasLdapPublicKey = entry.hasObjectClass(LDAP_PUBLIC_KEY_OBJECT_CLASS); + + ModifyRequest modifyRequest = new ModifyRequestImpl(); + modifyRequest.setName(new Dn(dn)); + + // Add or Replace, depending on whether there is already an ldapPublicKey on the entry + if (!hasLdapPublicKey) { + + modifyRequest.addModification(new DefaultAttribute("objectclass", LDAP_PUBLIC_KEY_OBJECT_CLASS), ModificationOperation.ADD_ATTRIBUTE); + modifyRequest.addModification(new DefaultAttribute(SSH_PUBLIC_KEY_ATTRIBUTE_NAME, sshPublicKey), ModificationOperation.ADD_ATTRIBUTE); + } else { + + modifyRequest.addModification(new DefaultAttribute(SSH_PUBLIC_KEY_ATTRIBUTE_NAME, sshPublicKey), ModificationOperation.REPLACE_ATTRIBUTE); + } + ModifyResponse modifyResponse = ldapConnection.modify(modifyRequest); + if (modifyResponse.getLdapResult().getResultCode() != ResultCodeEnum.SUCCESS) { + logger.warn("installSSHKey ldap operation reported not being successful: " + modifyResponse); + } else { + logger.debug("installSSHKey ldap operation was successful: " + modifyResponse); + } + return true; + } catch (LdapException e) { + throw new RuntimeException(e); } - + }); } @Override - public String getScratchLocation(String username) { - // TODO: implement - //if scratch location is available get it or else set a new scratch location for the user - String canonicalScratch = config.get(new ConfigParam("canonicalScratch"));//"/N/cd2/_USER_/scratch" - String scratchLocation = canonicalScratch.replace("_USER_",username); + public String getScratchLocation(String userId) { + String username = getUsername(userId); + String scratchLocation = canonicalScratchLocation.replace("${username}",username); return scratchLocation; - //return null; + } + + private <R> R withLdapConnection(Function<LdapConnection,R> function) { + + try (LdapConnection connection = new LdapNetworkConnection(ldapHost, ldapPort, true)) { + + connection.bind(ldapUsername, ldapPassword); + + R result = function.apply(connection); + + connection.unBind(); + + return result; + } catch (IOException e) { + throw new RuntimeException(e); + } catch (LdapException e) { + throw new RuntimeException(e); + } + } + + /** + * Convert from Airavata userId to cluster username. The assumption here is that a userId will be + * an IU email address and the username is just the username portion of the email address. + */ + private String getUsername(String userId) { + int atSignIndex = userId.indexOf("@"); + if (atSignIndex < 0) { + throw new RuntimeException("userId is not an email address: " + userId); + } + return userId.substring(0, atSignIndex); + } + + public static void main(String[] args) { + String ldapPassword = args[0]; + IULdapSSHAccountProvisioner sshAccountProvisioner = new IULdapSSHAccountProvisioner(); + Map<ConfigParam,String> config = new HashMap<>(); + // Create SSH tunnel to server that has firewall access to bazooka: + // ssh [email protected] -L 9000:bazooka.hps.iu.edu:636 -N & + // Put entry in /etc/hosts with the following + // 127.0.0.1 bazooka.hps.iu.edu + config.put(IULdapSSHAccountProvisionerProvider.LDAP_HOST, "bazooka.hps.iu.edu"); + config.put(IULdapSSHAccountProvisionerProvider.LDAP_PORT, "9000"); // ssh tunnel port + config.put(IULdapSSHAccountProvisionerProvider.LDAP_USERNAME, "cn=sgrcusr,dc=rt,dc=iu,dc=edu"); + config.put(IULdapSSHAccountProvisionerProvider.LDAP_PASSWORD, ldapPassword); + config.put(IULdapSSHAccountProvisionerProvider.LDAP_BASE_DN, "ou=bigred2-sgrc,dc=rt,dc=iu,dc=edu"); + config.put(IULdapSSHAccountProvisionerProvider.CANONICAL_SCRATCH_LOCATION, "/N/dc2/scratch/${username}/iu-gateway"); + sshAccountProvisioner.init(config); + String userId = "[email protected]"; + System.out.println("hasAccount=" + sshAccountProvisioner.hasAccount(userId)); + System.out.println("scratchLocation=" + sshAccountProvisioner.getScratchLocation(userId)); + sshAccountProvisioner.installSSHKey(userId, "foobar1234"); } } http://git-wip-us.apache.org/repos/asf/airavata/blob/74371129/modules/compute-account-provisioning/src/main/java/org/apache/airavata/accountprovisioning/provisioner/IULdapSSHAccountProvisionerProvider.java ---------------------------------------------------------------------- diff --git a/modules/compute-account-provisioning/src/main/java/org/apache/airavata/accountprovisioning/provisioner/IULdapSSHAccountProvisionerProvider.java b/modules/compute-account-provisioning/src/main/java/org/apache/airavata/accountprovisioning/provisioner/IULdapSSHAccountProvisionerProvider.java index 45dc0a6..9089ac3 100644 --- a/modules/compute-account-provisioning/src/main/java/org/apache/airavata/accountprovisioning/provisioner/IULdapSSHAccountProvisionerProvider.java +++ b/modules/compute-account-provisioning/src/main/java/org/apache/airavata/accountprovisioning/provisioner/IULdapSSHAccountProvisionerProvider.java @@ -24,32 +24,41 @@ import org.apache.airavata.accountprovisioning.ConfigParam; import org.apache.airavata.accountprovisioning.SSHAccountProvisioner; import org.apache.airavata.accountprovisioning.SSHAccountProvisionerProvider; -import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; public class IULdapSSHAccountProvisionerProvider implements SSHAccountProvisionerProvider { + public static final ConfigParam LDAP_HOST = new ConfigParam("ldap-host") + .setDescription("Hostname of LDAP server") + .setOptional(false) + .setType(ConfigParam.ConfigParamType.STRING); + public static final ConfigParam LDAP_PORT = new ConfigParam("ldap-port") + .setDescription("Port of LDAP server") + .setOptional(false) + .setType(ConfigParam.ConfigParamType.STRING); + public static final ConfigParam LDAP_USERNAME = new ConfigParam("ldap-username") + .setDescription("Username for LDAP server") + .setOptional(false) + .setType(ConfigParam.ConfigParamType.STRING); + public static final ConfigParam LDAP_PASSWORD = new ConfigParam("ldap-password") + .setDescription("Password for LDAP server") + .setOptional(false) + .setType(ConfigParam.ConfigParamType.CRED_STORE_PASSWORD_TOKEN); + public static final ConfigParam LDAP_BASE_DN = new ConfigParam("ldap-base-dn") + .setDescription("Base DN for searching, modifying cluster LDAP") + .setOptional(false) + .setType(ConfigParam.ConfigParamType.STRING); + public static final ConfigParam CANONICAL_SCRATCH_LOCATION = new ConfigParam("canonical-scratch-location") + .setDescription("Pattern for scratch location. Use ${username} as replacement for username. For example, '/N/dc2/scratch/${username}/iu-gateway'.") + .setOptional(false) + .setType(ConfigParam.ConfigParamType.STRING); + public static final List<ConfigParam> CONFIG_PARAMS = Arrays.asList(LDAP_HOST, LDAP_PORT, LDAP_USERNAME, LDAP_PASSWORD, LDAP_BASE_DN, CANONICAL_SCRATCH_LOCATION); + @Override public List<ConfigParam> getConfigParams() { - List<ConfigParam> configParams = new ArrayList<>(); - configParams.add(new ConfigParam("ldap-host") - .setDescription("Hostname of LDAP server") - .setOptional(false) - .setType(ConfigParam.ConfigParamType.STRING)); - configParams.add(new ConfigParam("ldap-port") - .setDescription("Port of LDAP server") - .setOptional(false) - .setType(ConfigParam.ConfigParamType.STRING)); - configParams.add(new ConfigParam("ldap-username") - .setDescription("Username for LDAP server") - .setOptional(false) - .setType(ConfigParam.ConfigParamType.STRING)); - configParams.add(new ConfigParam("ldap-password") - .setDescription("Password for LDAP server") - .setOptional(false) - .setType(ConfigParam.ConfigParamType.CRED_STORE_PASSWORD_TOKEN)); - return configParams; + return CONFIG_PARAMS; } @Override http://git-wip-us.apache.org/repos/asf/airavata/blob/74371129/modules/compute-account-provisioning/src/test/java/org/apache/airavata/accountprovisioning/SSHAccountProvisionerFactoryTest.java ---------------------------------------------------------------------- diff --git a/modules/compute-account-provisioning/src/test/java/org/apache/airavata/accountprovisioning/SSHAccountProvisionerFactoryTest.java b/modules/compute-account-provisioning/src/test/java/org/apache/airavata/accountprovisioning/SSHAccountProvisionerFactoryTest.java index 54d5f1c..34171af 100644 --- a/modules/compute-account-provisioning/src/test/java/org/apache/airavata/accountprovisioning/SSHAccountProvisionerFactoryTest.java +++ b/modules/compute-account-provisioning/src/test/java/org/apache/airavata/accountprovisioning/SSHAccountProvisionerFactoryTest.java @@ -56,10 +56,6 @@ public class SSHAccountProvisionerFactoryTest { ConfigParam ldapPassword = configParams.get(3); Assert.assertEquals("ldap_password", ldapPassword.getName()); Assert.assertEquals(ConfigParam.ConfigParamType.CRED_STORE_PASSWORD_TOKEN, ldapPassword.getType()); - ConfigParam ldapBaseDN = configParams.get(4); - Assert.assertArrayEquals("ldapBaseDN", ldapBaseDN.getName() ); - Assert.assertArrayEquals( ConfigParam.ConfigParamType.STRING,ldapBaseDN.getType() ); - } @Test http://git-wip-us.apache.org/repos/asf/airavata/blob/74371129/modules/compute-account-provisioning/src/test/java/org/apache/airavata/accountprovisioning/provisioner/TestSSHAccountProvisionerProvider.java ---------------------------------------------------------------------- diff --git a/modules/compute-account-provisioning/src/test/java/org/apache/airavata/accountprovisioning/provisioner/TestSSHAccountProvisionerProvider.java b/modules/compute-account-provisioning/src/test/java/org/apache/airavata/accountprovisioning/provisioner/TestSSHAccountProvisionerProvider.java index 7eb4250..3b11a91 100644 --- a/modules/compute-account-provisioning/src/test/java/org/apache/airavata/accountprovisioning/provisioner/TestSSHAccountProvisionerProvider.java +++ b/modules/compute-account-provisioning/src/test/java/org/apache/airavata/accountprovisioning/provisioner/TestSSHAccountProvisionerProvider.java @@ -57,7 +57,7 @@ public class TestSSHAccountProvisionerProvider implements SSHAccountProvisionerP configParams.add(new ConfigParam("ldapBaseDN") .setDescription( "Base DN for the ldap entry" ) .setOptional( false ) - .setType( ConfigParam.ConfigParamType.STRING ); + .setType( ConfigParam.ConfigParamType.STRING )); return configParams; }
