diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..fdca409 --- /dev/null +++ b/.classpath @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b83d222 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/.project b/.project new file mode 100644 index 0000000..272c6d6 --- /dev/null +++ b/.project @@ -0,0 +1,36 @@ + + + remove-account-library + + + + + + org.eclipse.wst.common.project.facet.core.builder + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.wst.validation.validationbuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jem.workbench.JavaEMFNature + org.eclipse.wst.common.modulecore.ModuleCoreNature + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + org.eclipse.wst.common.project.facet.core.nature + + diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..f9fe345 --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +encoding//src/main/java=UTF-8 +encoding//src/test/java=UTF-8 +encoding/=UTF-8 diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..58f03bf --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,6 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=1.7 diff --git a/pom.xml b/pom.xml index d080798..18b84b6 100644 --- a/pom.xml +++ b/pom.xml @@ -12,6 +12,7 @@ org.gcube.portal remove-account-library 0.0.1-SNAPSHOT + Remove Account Library jar @@ -33,7 +34,7 @@ org.gcube.distribution maven-portal-bom - 3.6.0 + 3.6.0-SNAPSHOT pom import @@ -41,23 +42,16 @@ - org.gcube.portal - social-networking-library - provided + org.gcube.resources.discovery + ic-client - org.gcube.portlets.widgets - pickitem-widget - [2.0.0-SNAPSHOT, 3.0.0-SNAPSHOT) + org.gcube.common + storagehub-client-library - org.gcube.portlets.user - gcube-widgets - compile - - - org.gcube.common.portal - portal-manager + org.gcube.core + common-encryption provided @@ -66,16 +60,15 @@ provided - org.gcube.core - common-scope + org.gcube.common.portal + portal-manager provided - commons-codec - commons-codec + com.sun.mail + javax.mail + provided - org.slf4j slf4j-api @@ -91,7 +84,11 @@ htmlparser 2.1 - + + com.liferay.portal + portal-service + provided + junit junit diff --git a/src/main/java/org/gcube/portal/removeaccount/App.java b/src/main/java/org/gcube/portal/removeaccount/App.java deleted file mode 100644 index bace085..0000000 --- a/src/main/java/org/gcube/portal/removeaccount/App.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.gcube.portal.removeaccount; - -/** - * Hello world! - * - */ -public class App -{ - public static void main( String[] args ) - { - System.out.println( "Hello World!" ); - } -} diff --git a/src/main/java/org/gcube/portal/removeaccount/Constants.java b/src/main/java/org/gcube/portal/removeaccount/Constants.java new file mode 100644 index 0000000..0ec6d92 --- /dev/null +++ b/src/main/java/org/gcube/portal/removeaccount/Constants.java @@ -0,0 +1,6 @@ +package org.gcube.portal.removeaccount; + +public class Constants { + public static final String AUTORISED_INFRA_ROLE = "Infrastructure-Manager"; + +} diff --git a/src/main/java/org/gcube/portal/removeaccount/D4ScienceRemoveAccountManager.java b/src/main/java/org/gcube/portal/removeaccount/D4ScienceRemoveAccountManager.java new file mode 100644 index 0000000..da6bc15 --- /dev/null +++ b/src/main/java/org/gcube/portal/removeaccount/D4ScienceRemoveAccountManager.java @@ -0,0 +1,47 @@ +package org.gcube.portal.removeaccount; + +import org.gcube.portal.removeaccount.thread.RemovedUserAccountThread; +import org.gcube.portal.removeaccount.thread.RemovedUserFromLDAPThread; +import org.gcube.vomanagement.usermanagement.impl.LiferayUserManager; +import org.gcube.vomanagement.usermanagement.model.GCubeUser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author Massimiliano Assante ISTI-CNR + * This class takes care of implementing all the required actions to remove a user account from D4Science. + */ +public class D4ScienceRemoveAccountManager { + private static final Logger _log = LoggerFactory.getLogger(D4ScienceRemoveAccountManager.class); + public static final String AUTORISED_INFRA_ROLE = "Infrastructure-Manager"; + private String username2Delete; + public D4ScienceRemoveAccountManager(String username2Delete) { + super(); + this.username2Delete = username2Delete; + } + /** + * async remove user account, this method launches a series of async threads to remove all the user data from the infrastructure. + * @return true is the users exists and threads are launched correctly + */ + public boolean doAsyncRemoveAccount() { + GCubeUser user = null; + try { + user = new LiferayUserManager().getUserByUsername(username2Delete); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + _log.info("Trying to remove user from LDAP ..."); + Thread removeFromLDAPThread = new Thread(new RemovedUserFromLDAPThread(username2Delete)); + removeFromLDAPThread.start(); + _log.info("Trying to remove user from Liferay DB and JCR and notify infra-managers ..."); + Thread emailManagersThread = new Thread(new RemovedUserAccountThread( + user.getUserId(), + user.getUsername(), + user.getFullname(), + user.getEmail())); + emailManagersThread.start(); + return true; + } +} diff --git a/src/main/java/org/gcube/portal/removeaccount/thread/RemoveUserFromJCR.java b/src/main/java/org/gcube/portal/removeaccount/thread/RemoveUserFromJCR.java new file mode 100644 index 0000000..29e6a70 --- /dev/null +++ b/src/main/java/org/gcube/portal/removeaccount/thread/RemoveUserFromJCR.java @@ -0,0 +1,75 @@ +package org.gcube.portal.removeaccount.thread; + +import static org.gcube.common.authorization.client.Constants.authorizationService; + +import java.util.ArrayList; +import java.util.List; + +import org.gcube.common.authorization.library.provider.SecurityTokenProvider; +import org.gcube.common.portal.PortalContext; +import org.gcube.common.storagehub.client.plugins.AbstractPlugin; +import org.gcube.common.storagehub.client.proxies.UserManagerClient; +import org.gcube.portal.removeaccount.Constants; +import org.gcube.vomanagement.usermanagement.GroupManager; +import org.gcube.vomanagement.usermanagement.RoleManager; +import org.gcube.vomanagement.usermanagement.UserManager; +import org.gcube.vomanagement.usermanagement.impl.LiferayRoleManager; +import org.gcube.vomanagement.usermanagement.model.GCubeRole; +import org.gcube.vomanagement.usermanagement.model.GCubeUser; + +import com.liferay.portal.kernel.log.Log; +import com.liferay.portal.kernel.log.LogFactoryUtil; + +/** + * + * @author Massimiliano Assante ISTI-CNR + * + */ +public class RemoveUserFromJCR { + private static Log _log = LogFactoryUtil.getLog(RemoveUserFromJCR.class); + + private String username2Delete; + private GroupManager gm; + private UserManager uMan; + + public RemoveUserFromJCR(String username2Delete, GroupManager gm, UserManager uMan) { + this.username2Delete = username2Delete; + this.gm = gm; + this.uMan = uMan; + } + + public boolean remove() { + try { + //get the super user + String infraContext = "/"+PortalContext.getConfiguration().getInfrastructureName(); + long groupId = gm.getGroupIdFromInfrastructureScope(infraContext); + RoleManager rm = new LiferayRoleManager(); + long roleId = rm.getRoleId(Constants.AUTORISED_INFRA_ROLE, groupId); + List users = uMan.listUsersByGroupAndRole(groupId, roleId); + if (users.isEmpty()) { + _log.error("Cannot delete the user: there is no user having role " + Constants.AUTORISED_INFRA_ROLE + " on context: " + infraContext); + return false; + } + else { + GCubeUser theAdmin = users.get(0); + String adminUsername = theAdmin.getUsername(); + String theAdminToken = PortalContext.getConfiguration().getCurrentUserToken(infraContext, adminUsername); + List theAdminRoles = rm.listRolesByUserAndGroup(theAdmin.getUserId(), groupId); + List rolesString = new ArrayList(); + for (GCubeRole gCubeRole : theAdminRoles) { + rolesString.add(gCubeRole.getRoleName()); + } + authorizationService().setTokenRoles(theAdminToken, rolesString); + SecurityTokenProvider.instance.set(theAdminToken); + UserManagerClient userClient = AbstractPlugin.users().build(); + userClient.removeUser(username2Delete); + return true; + } + } catch (Exception e) { + _log.error("Could not delete " + username2Delete + " from JCR ", e); + return false; + } + } + + +} diff --git a/src/main/java/org/gcube/portal/removeaccount/thread/RemovedUserAccountThread.java b/src/main/java/org/gcube/portal/removeaccount/thread/RemovedUserAccountThread.java new file mode 100644 index 0000000..929be7e --- /dev/null +++ b/src/main/java/org/gcube/portal/removeaccount/thread/RemovedUserAccountThread.java @@ -0,0 +1,134 @@ +package org.gcube.portal.removeaccount.thread; + +import java.util.List; + +import org.gcube.common.portal.PortalContext; +import org.gcube.common.portal.mailing.EmailNotification; +import org.gcube.portal.removeaccount.Constants; +import org.gcube.vomanagement.usermanagement.GroupManager; +import org.gcube.vomanagement.usermanagement.RoleManager; +import org.gcube.vomanagement.usermanagement.UserManager; +import org.gcube.vomanagement.usermanagement.exception.RoleRetrievalFault; +import org.gcube.vomanagement.usermanagement.impl.LiferayGroupManager; +import org.gcube.vomanagement.usermanagement.impl.LiferayRoleManager; +import org.gcube.vomanagement.usermanagement.impl.LiferayUserManager; +import org.gcube.vomanagement.usermanagement.model.GCubeUser; +import org.gcube.vomanagement.usermanagement.model.GatewayRolesNames; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +import com.liferay.portal.service.UserLocalServiceUtil; + + +/** + * + * @author Massimiliano Assante ISTI-CNR + * + */ +public class RemovedUserAccountThread implements Runnable { + + private static final Logger _log = LoggerFactory.getLogger(RemovedUserAccountThread.class); + + final String SUBJECT = "User account REMOVAL notification"; + + private String userName; + private String fullName; + private String emailAddress; + private long userId; + private GroupManager gm; + private UserManager uMan; + + public RemovedUserAccountThread(long userId,String userName, String fullName, String emailAddress) { + super(); + this.userId = userId; + this.userName = userName; + this.fullName = fullName; + this.emailAddress = emailAddress; + this.uMan = new LiferayUserManager(); + this.gm = new LiferayGroupManager(); + } + + @Override + public void run() { + try { + _log.info("Trying to remove user " + userName + " from JCR first, using storageHub with role: "+Constants.AUTORISED_INFRA_ROLE); + RemoveUserFromJCR rmJCR = new RemoveUserFromJCR(userName, gm, uMan); + boolean result = rmJCR.remove(); + _log.info("The user " + userName + " has been removed from JCR with success? " + result); + + } catch (Exception e) { + _log.error("An error occurred during user workspace removal: ", e); + } + handleUserRemoval(userId, userName, fullName, emailAddress); + } + + private void handleUserRemoval(long userId, String userName, String fullName, String emailAddress) { + _log.info("trying removeUser account for " + userName); + //first remove the account + try { + UserLocalServiceUtil.deleteUser(userId); + } catch (Exception e) { + e.printStackTrace(); + } + _log.info("removeUser account for " + userName + " done with success, now notify the managers ... "); + //the notify the managers + + RoleManager rm = new LiferayRoleManager(); + try { + String rootVoName = PortalContext.getConfiguration().getInfrastructureName(); + long groupId = gm.getGroupIdFromInfrastructureScope("/"+rootVoName); + long infraManagerRoleId = -1; + try { + infraManagerRoleId = rm.getRoleIdByName(GatewayRolesNames.INFRASTRUCTURE_MANAGER.getRoleName()); + } + catch (RoleRetrievalFault e) { + _log.warn("There is no (Site) Role " + infraManagerRoleId + " in this portal. Will not notify about removed user accounts."); + return; + } + _log.trace("Root is: " + rootVoName + " Scanning roles ...."); + + List managers = uMan.listUsersByGroupAndRole(groupId, infraManagerRoleId); + if (managers == null || managers.isEmpty()) { + _log.warn("There are no users with (Site) Role " + infraManagerRoleId + " on " + rootVoName + " in this portal. Will not notify about removed user accounts."); + } + else { + for (GCubeUser manager : managers) { + sendNotification(manager, userName, fullName, emailAddress); + _log.info("sent email to manager: " + manager.getEmail()); + } + } + + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void sendNotification(GCubeUser manager, String newUserUserName, String newUserFullName, String newUserEmailAddress) { + EmailNotification toSend = new EmailNotification(manager.getEmail(), SUBJECT, + getHTMLEmail(manager.getFirstName(), newUserUserName, newUserFullName, newUserEmailAddress), null); + toSend.sendEmail(); + } + + private static String getHTMLEmail(String userFirstName, String newUserUserName, String newUserFullName, String newUserEmailAddress) { + String sender = newUserFullName + " ("+newUserUserName+") "; + + StringBuilder body = new StringBuilder(); + + body.append("
") + .append("
") + .append("Dear ").append(userFirstName).append(",") //dear + .append("

").append(sender).append(" ").append("removed his/her account from the portal with the following email: ") // has done something + .append(newUserEmailAddress) + .append("


") + .append("

You received this email because you are an Infrastructure Manager in this portal

") + .append("

") + .append(""); + + return body.toString(); + + } + + + +} diff --git a/src/main/java/org/gcube/portal/removeaccount/thread/RemovedUserFromLDAPThread.java b/src/main/java/org/gcube/portal/removeaccount/thread/RemovedUserFromLDAPThread.java new file mode 100644 index 0000000..e89fe17 --- /dev/null +++ b/src/main/java/org/gcube/portal/removeaccount/thread/RemovedUserFromLDAPThread.java @@ -0,0 +1,153 @@ +package org.gcube.portal.removeaccount.thread; + +import static org.gcube.resources.discovery.icclient.ICFactory.clientFor; +import static org.gcube.resources.discovery.icclient.ICFactory.queryFor; + +import java.util.List; +import java.util.Properties; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NameNotFoundException; +import javax.naming.NamingException; + +import org.gcube.common.encryption.encrypter.StringEncrypter; +import org.gcube.common.portal.PortalContext; +import org.gcube.common.resources.gcore.ServiceEndpoint; +import org.gcube.common.resources.gcore.ServiceEndpoint.AccessPoint; +import org.gcube.common.resources.gcore.ServiceEndpoint.Property; +import org.gcube.common.resources.gcore.utils.Group; +import org.gcube.common.scope.api.ScopeProvider; +import org.gcube.resources.discovery.client.api.DiscoveryClient; +import org.gcube.resources.discovery.client.queries.api.SimpleQuery; + +import com.liferay.portal.kernel.log.Log; +import com.liferay.portal.kernel.log.LogFactoryUtil; + +/** + * + * @author Massimiliano Assante ISTI-CNR + * + */ +public class RemovedUserFromLDAPThread implements Runnable { + private static Log _log = LogFactoryUtil.getLog(RemovedUserFromLDAPThread.class); + private static final String LDAP_SERVER_NAME = "LDAPServer"; + private static final String LDAP_SERVER_FILTER_NAME = "filter"; + private static final String LDAP_SERVER_PRINCPAL_NAME = "ldapPrincipal"; + private static final String USER_CONTEXT = ",ou=People,o=D4Science,ou=Organizations,dc=d4science,dc=org"; + + private String portalName; + private String ldapUrl; + private String principal; + private String ldapPassword; + + private String username2Delete; + + public RemovedUserFromLDAPThread(String username2Delete) { + this.username2Delete = username2Delete; + } + + + @SuppressWarnings("deprecation") + @Override + public void run() { + portalName = PortalContext.getPortalInstanceName(); + + PortalContext context = PortalContext.getConfiguration(); + String scope = "/" + context.getInfrastructureName(); + ScopeProvider.instance.set(scope); + + SimpleQuery query = queryFor(ServiceEndpoint.class); + query.addCondition("$resource/Profile/Category/text() eq 'Portal'"); + query.addCondition("$resource/Profile/Name/text() eq '" + portalName + "'"); + + DiscoveryClient client = clientFor(ServiceEndpoint.class); + + List list = client.submit(query); + if (list == null || list.isEmpty()) { + _log.error("Could not find any Service endpoint registred in the infrastructure for this portal: " + portalName); + } + else if (list.size() > 1) { + _log.warn("Found more than one Service endpoint registred in the infrastructure for this portal: " + portalName); + } + else { + for (ServiceEndpoint res : list) { + Group apGroup = res.profile().accessPoints(); + AccessPoint[] accessPoints = (AccessPoint[]) apGroup.toArray(new AccessPoint[apGroup.size()]); + for (int i = 0; i < accessPoints.length; i++) { + if (accessPoints[i].name().compareTo(LDAP_SERVER_NAME) == 0) { + _log.info("Found credentials for " + LDAP_SERVER_NAME); + AccessPoint found = accessPoints[i]; + ldapUrl = found.address(); + String encrPassword = found.password(); + try { + ldapPassword = StringEncrypter.getEncrypter().decrypt( encrPassword); + } catch (Exception e) { + _log.error("Something went wrong while decrypting password for " + LDAP_SERVER_NAME); + e.printStackTrace(); + } + Group propGroup = found.properties(); + Property[] props = (Property[]) propGroup.toArray(new Property[propGroup.size()]); + for (int j = 0; j < props.length; j++) { + if (props[j].name().compareTo(LDAP_SERVER_PRINCPAL_NAME) == 0) { + _log.info("\tFound properties of " + LDAP_SERVER_PRINCPAL_NAME); + String encrValue = props[j].value(); + try { + principal = StringEncrypter.getEncrypter().decrypt(encrValue); + } catch (Exception e) { + _log.error("Something went wrong while decrypting value for " + LDAP_SERVER_PRINCPAL_NAME); + e.printStackTrace(); + } + } + } + + } + } + } + _log.debug("Got LDAP connection info from IS Resource ..."); + /*************** */ + _log.debug("Initializing LDAP connection ..."); + + Properties env = new Properties(); + env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory"); + env.put(Context.PROVIDER_URL, ldapUrl); + env.put(Context.SECURITY_PRINCIPAL, principal); + env.put(Context.SECURITY_CREDENTIALS, ldapPassword); + + try { + Context ctx = new InitialContext(env); + String userCtx2Delete = getSubContext(username2Delete); + // Remove the binding + _log.debug("***** trying delete userCtx=" + userCtx2Delete); + ctx.unbind(userCtx2Delete); + // Check that it is gone + Object obj = null; + try { + obj = ctx.lookup(userCtx2Delete); + } catch (NameNotFoundException ne) { + _log.info("unbind successful for "+userCtx2Delete); + return; + } + _log.error("unbind failed; object still there: " + obj); + // Close the context when we're done + ctx.close(); + } catch (NamingException e) { + _log.error("Something went Wrong during LDAP remove user"); + e.printStackTrace(); + } catch (Exception es) { + _log.error("Something went Wrong during LDAP remove user in retrieving Liferay Organization"); + es.printStackTrace(); + } + } + } + + /** + * + * @param username + * @return the single user subContext + */ + private String getSubContext(String username) { + return "uid="+username+USER_CONTEXT; + } + +} diff --git a/target/classes/META-INF/MANIFEST.MF b/target/classes/META-INF/MANIFEST.MF index 74854e7..b2662b0 100644 --- a/target/classes/META-INF/MANIFEST.MF +++ b/target/classes/META-INF/MANIFEST.MF @@ -1,12 +1,12 @@ Manifest-Version: 1.0 Built-By: massi Build-Jdk: 1.8.0_222 -Specification-Title: remove-account-library +Specification-Title: Remove Account Library Specification-Version: 0.0.1-SNAPSHOT -Implementation-Title: remove-account-library +Implementation-Title: Remove Account Library Implementation-Version: 0.0.1-SNAPSHOT Implementation-Vendor-Id: org.gcube.portal -Build-Time: 20191211-090816 +Build-Time: 20191211-100255 Created-By: Maven Integration for Eclipse SCM-Branch: SCM-Revision: diff --git a/target/classes/META-INF/maven/org.gcube.portal/remove-account-library/pom.properties b/target/classes/META-INF/maven/org.gcube.portal/remove-account-library/pom.properties index f6fd228..66b0831 100644 --- a/target/classes/META-INF/maven/org.gcube.portal/remove-account-library/pom.properties +++ b/target/classes/META-INF/maven/org.gcube.portal/remove-account-library/pom.properties @@ -1,5 +1,5 @@ #Generated by Maven Integration for Eclipse -#Wed Dec 11 10:08:16 CET 2019 +#Wed Dec 11 11:02:56 CET 2019 version=0.0.1-SNAPSHOT groupId=org.gcube.portal m2e.projectName=remove-account-library diff --git a/target/classes/META-INF/maven/org.gcube.portal/remove-account-library/pom.xml b/target/classes/META-INF/maven/org.gcube.portal/remove-account-library/pom.xml index d080798..18b84b6 100644 --- a/target/classes/META-INF/maven/org.gcube.portal/remove-account-library/pom.xml +++ b/target/classes/META-INF/maven/org.gcube.portal/remove-account-library/pom.xml @@ -12,6 +12,7 @@ org.gcube.portal remove-account-library 0.0.1-SNAPSHOT + Remove Account Library jar @@ -33,7 +34,7 @@ org.gcube.distribution maven-portal-bom - 3.6.0 + 3.6.0-SNAPSHOT pom import @@ -41,23 +42,16 @@ - org.gcube.portal - social-networking-library - provided + org.gcube.resources.discovery + ic-client - org.gcube.portlets.widgets - pickitem-widget - [2.0.0-SNAPSHOT, 3.0.0-SNAPSHOT) + org.gcube.common + storagehub-client-library - org.gcube.portlets.user - gcube-widgets - compile - - - org.gcube.common.portal - portal-manager + org.gcube.core + common-encryption provided @@ -66,16 +60,15 @@ provided - org.gcube.core - common-scope + org.gcube.common.portal + portal-manager provided - commons-codec - commons-codec + com.sun.mail + javax.mail + provided - org.slf4j slf4j-api @@ -91,7 +84,11 @@ htmlparser 2.1 - + + com.liferay.portal + portal-service + provided + junit junit diff --git a/target/classes/org/gcube/portal/removeaccount/App.class b/target/classes/org/gcube/portal/removeaccount/App.class deleted file mode 100644 index 1f67773..0000000 Binary files a/target/classes/org/gcube/portal/removeaccount/App.class and /dev/null differ diff --git a/target/classes/org/gcube/portal/removeaccount/App.java b/target/classes/org/gcube/portal/removeaccount/App.java deleted file mode 100644 index bace085..0000000 --- a/target/classes/org/gcube/portal/removeaccount/App.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.gcube.portal.removeaccount; - -/** - * Hello world! - * - */ -public class App -{ - public static void main( String[] args ) - { - System.out.println( "Hello World!" ); - } -}