package org.gcube.portlets.admin.createusers.server; import static org.gcube.common.authorization.client.Constants.authorizationService; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Locale; import org.gcube.common.authorization.library.provider.AccessTokenProvider; import org.gcube.common.authorization.library.provider.SecurityTokenProvider; import org.gcube.common.portal.PortalContext; import org.gcube.common.storagehub.client.dsl.StorageHubClient; import org.gcube.portal.mailing.message.Recipient; import org.gcube.portal.mailing.service.EmailTemplateService; import org.gcube.portal.mailing.templates.TemplateWelcomeCreatedAccount; import org.gcube.portal.oidc.lr62.OIDCUmaUtil; import org.gcube.portlets.admin.createusers.client.HandleUsersService; import org.gcube.portlets.admin.createusers.orchestrator.CreateUserAddToVre; import org.gcube.portlets.admin.createusers.orchestrator.CreateUserAddToVrePublisher; import org.gcube.portlets.admin.createusers.shared.VreUserBean; import org.gcube.vomanagement.usermanagement.exception.UserManagementPortalException; import org.gcube.vomanagement.usermanagement.exception.UserManagementSystemException; 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.GCubeGroup; import org.gcube.vomanagement.usermanagement.model.GCubeRole; import org.gcube.vomanagement.usermanagement.model.GCubeUser; import org.gcube.vomanagement.usermanagement.model.GatewayRolesNames; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.gwt.user.server.rpc.RemoteServiceServlet; import com.liferay.portal.kernel.dao.jdbc.DataAccess; import com.liferay.portal.kernel.exception.PortalException; import com.liferay.portal.kernel.exception.SystemException; import com.liferay.portal.kernel.util.GetterUtil; import com.liferay.portal.kernel.util.PropsUtil; import com.liferay.portal.model.Company; import com.liferay.portal.model.Group; import com.liferay.portal.model.User; import com.liferay.portal.service.CompanyLocalServiceUtil; import com.liferay.portal.service.GroupLocalServiceUtil; import com.liferay.portal.service.ServiceContext; import com.liferay.portal.service.UserLocalServiceUtil; import com.liferay.portal.util.PortalUtil; /** * The server side implementation of the RPC service. * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) */ public class CreateUsersImpl extends RemoteServiceServlet implements HandleUsersService{ private final static Logger logger = LoggerFactory.getLogger(CreateUsersImpl.class); private static final long serialVersionUID = -3124676000683430170L; private static final String REGISTERED_USERS_TABLE = "registered_users"; private static final String INITIAL_PWD = "training1"; // SQL TABLE FIELDS private static final String FIELD_EMAIL = "email"; private static final String FIELD_NAME = "name"; private static final String FIELD_SURNAME = "surname"; private static final String FIELD_INSTITUTION = "institution_organization"; private static final String FIELD_REGISTRATION_DATE = "registration_date"; private static final String FIELD_VRE = "vre"; public static final String DEFAULT_COMPANY_WEB_ID = "liferay.com"; @Override public void init() { logger.debug("Trying to get connect to liferay's DB from API"); try { Connection con = DataAccess.getConnection(); boolean exists = tableExists(con); if(exists) logger.debug("Table " + REGISTERED_USERS_TABLE + " already exists."); else initializeTable(con); } catch (Exception e) { logger.error("Failed to connect to liferay's DB"); return; } } /** * check if tables exist in the database * @param conn . * @throws SQLException */ private boolean tableExists(Connection con) throws SQLException { logger.debug("Looking for " + REGISTERED_USERS_TABLE + " table"); Statement s = con.createStatement(); ResultSet rs = s.executeQuery("SELECT * FROM pg_tables where schemaname='public' and tablename = '" + REGISTERED_USERS_TABLE +"' "); boolean toReturn = rs.next(); if (toReturn) logger.debug("Auxiliary Table Found! Returning ... "); return toReturn; } /** * create the table REGISTERED_USERS_TABLE * @throws SQLException */ private void initializeTable(Connection con) { try { logger.debug("Creating table " + REGISTERED_USERS_TABLE); Statement s = con.createStatement(); s.execute("CREATE TABLE " + REGISTERED_USERS_TABLE + " (id serial primary key, " // like autoincrement + "email varchar(255) NOT NULL, " + "name" + " varchar(255) NOT NULL, " + "surname" + " varchar(255) NOT NULL, " + "institution_organization" + " varchar(255) DEFAULT NULL, " + "vre" + " varchar(255) NOT NULL, " + "registration_date" + " TIMESTAMP NOT NULL)"); logger.info(REGISTERED_USERS_TABLE + " created"); } catch (SQLException e) { logger.error("Error while creating table", e); } try { con.close(); } catch (SQLException e1) { logger.error("Error while closing connection", e1); } } @Override public boolean deleteInvitedUser(String email) { try{ Connection con = DataAccess.getConnection(); boolean deletedLiferay = deleteUserFromLiferay(email); boolean deletedTable = deleteUserFromTable(email, con); return deletedLiferay && deletedTable; }catch(SQLException e){ logger.debug("Error while trying to delete user with email = " + email, e); } return false; } @Override public void sendEmailToUser(String email, String nameCreatedUser) { try{ logger.debug("Sending welcome message to user with email " + email); PortalContext context = PortalContext.getConfiguration(); String gatewayName = context.getGatewayName(getThreadLocalRequest()); String gatewayURL = context.getGatewayURL(getThreadLocalRequest()); long groupId = context.getCurrentGroupId(getThreadLocalRequest()); GCubeGroup currVRE = new LiferayGroupManager().getGroup(groupId); GCubeUser theManagerUser =context.getCurrentUser(getThreadLocalRequest()); String subject = "Your user account for " + currVRE.getGroupName() + " VRE has been created"; EmailTemplateService.send( subject, new TemplateWelcomeCreatedAccount( gatewayName, gatewayURL, email, nameCreatedUser, theManagerUser, currVRE), getThreadLocalRequest(), new Recipient(email)); }catch(Exception e){ logger.error("Error while sending email to user " + email, e); } } @Override public VreUserBean register(String name, String surname, String institution, String email, boolean sendEmail, boolean isMale) { PortalContext pContext = PortalContext.getConfiguration(); String vre = pContext.getCurrentScope(getThreadLocalRequest()); long timestamp = System.currentTimeMillis(); //checking if the user has been already registered or is already in the portal LiferayUserManager userManager = new LiferayUserManager(); boolean exists = userManager.userExistsByEmail(email); if(exists){ logger.debug("User with this email already present in the portal."); return null; } else{ logger.debug("Trying to add user: " + name + ", " + surname + ", " + email + ", " + institution); Connection con = null; try{ con = DataAccess.getConnection(); String insert = "INSERT into " + REGISTERED_USERS_TABLE + "("+ FIELD_EMAIL + "," + FIELD_NAME +"," + FIELD_SURNAME + "," + FIELD_INSTITUTION + "," + FIELD_REGISTRATION_DATE + "," + FIELD_VRE + ") values(?, ?, ?, ?, ?, ?)"; PreparedStatement statement = con.prepareStatement(insert); statement.setString(1, email); statement.setString(2, name); statement.setString(3, surname); statement.setString(4, institution); statement.setTimestamp(5, new Timestamp(timestamp)); statement.setString(6, vre); int res = statement.executeUpdate(); if(res != 0){ logger.debug("User added in " + REGISTERED_USERS_TABLE); // add in liferay and send event to Keycloak too try{ User user = register(name, surname, email, institution, sendEmail); logger.debug("User registered to the Site, username assigned=" + user.getScreenName()); //here we wait that the user-registration-hook performs all the tasks int numOfTries = 0; do { logger.debug("Waiting for user-registration-hook to finish create workspace via shub for "+ user.getScreenName() + ",\n attempt: " + (numOfTries+1)); Thread.sleep(2000); numOfTries++; } while ( ( !workspaceExists( user.getScreenName()) ) && numOfTries < 10); if (numOfTries >= 10) { logger.error("Something wrong for this user, the workspace of " + user.getScreenName() + " could not be found") ; return null; } logger.info("Create workspace via shub has been done, proceed with user add to VRE"); // adding to the current VRE long userId = userManager.getUserId(user.getScreenName()); logger.info("trying to send the event to create the account for this user to the orchestrator"); long currGroupId = pContext.getCurrentGroupId(getThreadLocalRequest()); Group theVRE = GroupLocalServiceUtil.getGroup(currGroupId); new CreateUserAddToVrePublisher().publish( CreateUserAddToVre.newEvent(user.getScreenName(), name, surname, email, INITIAL_PWD, theVRE)); logger.debug("... sleeping 5 seconds"); Thread.sleep(5000); logger.debug("... now adding his user to the VRE"); userManager.assignUserToGroup(getCurrentGroupID(), userId); if (sendEmail) sendEmailToUser(email, name + " " + surname); }catch(Exception e){ // unable to create.. we need to delete it from the list of users logger.error("Unable to create the user " + email + " in liferay. Removing he/she from the table " + REGISTERED_USERS_TABLE, e); deleteUserFromTable(email, con); return null; } } else{ logger.debug("User NOT added in " + REGISTERED_USERS_TABLE); return null; } }catch(Exception e){ logger.error("Unable to add user, sorry..", e); return null; }finally{ try { if(con != null) con.close(); } catch (SQLException e) { logger.error("Unable to close connection to the DB"); } } return new VreUserBean(name, surname, institution, email, false, timestamp, isMale); } } private boolean workspaceExists(String usernameToCheck) { String previousToken = SecurityTokenProvider.instance.get(); String previousUMAToken = AccessTokenProvider.instance.get(); boolean toReturn = false; try { //get the super user logger.info("Getting super user with role " + GatewayRolesNames.INFRASTRUCTURE_MANAGER.getRoleName()); //get the super user String infraContext = "/"+PortalContext.getConfiguration().getInfrastructureName(); long rootgroupId = new LiferayGroupManager().getGroupIdFromInfrastructureScope(infraContext); User theAdmin = LiferayUserManager.getRandomUserWithRole(rootgroupId, GatewayRolesNames.INFRASTRUCTURE_MANAGER); if (theAdmin == null) { logger.error("Cannot check if workspace exists, could not find any " + GatewayRolesNames.INFRASTRUCTURE_MANAGER + " on context: " + infraContext); return false; } String adminUsername = theAdmin.getScreenName(); String theAdminToken = PortalContext.getConfiguration().getCurrentUserToken(infraContext, adminUsername); List rolesString = new ArrayList(); List theAdminRoles = new LiferayRoleManager().listRolesByUserAndGroup(theAdmin.getUserId(), rootgroupId); for (GCubeRole gCubeRole : theAdminRoles) { rolesString.add(gCubeRole.getRoleName()); } rolesString.add(GatewayRolesNames.INFRASTRUCTURE_MANAGER.getRoleName()); logger.debug("legacy authorizationService().setTokenRoles done"); authorizationService().setTokenRoles(theAdminToken, rolesString); SecurityTokenProvider.instance.set(theAdminToken); OIDCUmaUtil.provideConfiguredPortalClientUMATokenInThreadLocal("/" + PortalContext.getConfiguration().getInfrastructureName()); logger.info("\nCreateUser.workspaceExists() new authorizationService PortalClient UMA-Token In ThreadLocal done\n"); //here we actually make the authorised call to check if the workspace exists through shub StorageHubClient shc = new StorageHubClient(); toReturn = shc.userExists(usernameToCheck); logger.info("\nshub userExists(" + usernameToCheck + ") returns "+toReturn); AccessTokenProvider.instance.set(previousUMAToken); SecurityTokenProvider.instance.set(previousToken); return toReturn; } catch (Exception e) { AccessTokenProvider.instance.set(previousUMAToken); SecurityTokenProvider.instance.set(previousToken); logger.error("Cannot check if worspace exists for " + usernameToCheck, e); return false; } } private User register(String firstName, String lastName, String email, String institution, boolean sendEmail) { User toReturn = null; try{ logger.debug("Trying createuser and instatiate serviceContext" + email); Long defaultCompanyId = PortalUtil.getDefaultCompanyId(); Long defaultUserId = UserLocalServiceUtil.getDefaultUserId(defaultCompanyId); PortalContext pContext = PortalContext.getConfiguration(); //ServiceContext instanciated as follows is needed when the Email verification is sent ServiceContext serviceContext = new ServiceContext(); serviceContext.setScopeGroupId(pContext.getCurrentGroupId(getThreadLocalRequest())); serviceContext.setPortalURL(pContext.getGatewayURL(getThreadLocalRequest())); serviceContext.setPathMain("/c"); boolean autoPassword = false; Locale locale = new Locale("en_US"); int prefixId = 0; int suffixId = 0; int birthdayMonth = 1; int birthdayDay = 1; int birthdayYear = 1970; String password1 = "training1"; String password2 = password1; toReturn = UserLocalServiceUtil.addUser( defaultUserId, defaultCompanyId, autoPassword, password1, password2, true, "", email, 0L, "", locale, firstName, "", lastName, prefixId, suffixId, true, birthdayMonth, birthdayDay, birthdayYear, institution, null, null, null, null, false, serviceContext); logger.debug("Created User on LR " + lastName + " SUCCESS"); UserLocalServiceUtil.updateEmailAddressVerified(toReturn.getUserId(), true); //UserLocalServiceUtil.updateAgreedToTermsOfUse(toReturn.getUserId(), true); //UserLocalServiceUtil.updatePasswordReset(toReturn.getUserId(), true); UserLocalServiceUtil.updateReminderQuery(toReturn.getUserId(), "What was your initial password?", "training1"); logger.debug("User " + lastName + " has agreed to ToU"); logger.debug("User " + lastName + " has verified the Email"); logger.debug("User " + lastName + " updatePasswordReset & updateReminderQuery"); } catch(Exception e){ // unable to create.. we need to delete it from the list of users logger.error("Unable to create the user " + email + " in liferay.", e); } return toReturn; } @Override public List getAlreadyRegisterdUsers() { List toReturn = new ArrayList(); LiferayUserManager userManager = new LiferayUserManager(); // evaluate current vre PortalContext pContext = PortalContext.getConfiguration(); String vre = pContext.getCurrentScope(getThreadLocalRequest()); Connection con = null; try{ con = DataAccess.getConnection(); Statement stmt = con.createStatement(); String sql = "SELECT * FROM " + REGISTERED_USERS_TABLE + " WHERE " + FIELD_VRE + "='" + vre + "';"; ResultSet rs = stmt.executeQuery(sql); while(rs.next()){ String name = rs.getString(FIELD_NAME); String surname = rs.getString(FIELD_SURNAME); String institution = rs.getString(FIELD_INSTITUTION); String email = rs.getString(FIELD_EMAIL); long registrationDate = rs.getTimestamp(FIELD_REGISTRATION_DATE).getTime(); // check if the password has been changed or not wrt the default one boolean passwordChanged = userManager.isPasswordChanged(email); toReturn.add(new VreUserBean(name, surname, institution, email, passwordChanged, registrationDate, false)); } // now, make sure these users are still on portal Iterator iterator = toReturn.iterator(); while (iterator.hasNext()) { VreUserBean user = (VreUserBean) iterator.next(); if(!userManager.userExistsByEmail(user.getEmail())){ // remove from the table deleteUserFromTable(user.getEmail(), con); // remove from this collection iterator.remove(); } } }catch(Exception e){ logger.error("Unable to retrieve users list, sorry...", e); return null; }finally{ try { if(con != null) con.close(); } catch (SQLException e) { logger.error("Unable to close connection to the DB"); } } return toReturn; } /** * Remove a row from the table of the registered users * @param email * @param con */ private boolean deleteUserFromTable(String email, Connection con) { try{ logger.debug("Going to delete user with email " + email + " from the table of registered users"); String remove = "DELETE FROM " + REGISTERED_USERS_TABLE + " WHERE " + FIELD_EMAIL + "= ?"; PreparedStatement statementDelete = con.prepareStatement(remove); statementDelete.setString(1, email); int res = statementDelete.executeUpdate(); if(res == 1) return true; }catch(Exception e){ logger.error("Error while deleting user=" + email + "from the table"); } return false; } /** * Delete user from liferay * @param email */ private boolean deleteUserFromLiferay(String email) { LiferayUserManager userManager = new LiferayUserManager(); try { userManager.deleteUserByEMail(email); return true; } catch (PortalException | SystemException | UserManagementSystemException | UserManagementPortalException e) { logger.error("Unable to delete user from liferay", e); } return false; } /** * Get the current group ID */ private Long getCurrentGroupID() { PortalContext pContext = PortalContext.getConfiguration(); return pContext.getCurrentGroupId(getThreadLocalRequest()); } public static Company getCompany() throws PortalException, SystemException { return CompanyLocalServiceUtil.getCompanyByWebId(getDefaultCompanyWebId()); } /** * * @return the default company web-id (e.g. iMarine.eu) */ public static String getDefaultCompanyWebId() { String defaultWebId = ""; try { defaultWebId = GetterUtil.getString(PropsUtil.get("company.default.web.id")); } catch (NullPointerException e) { logger.error("Cound not find property company.default.web.id in portal.ext file returning default web id: " + DEFAULT_COMPANY_WEB_ID); return DEFAULT_COMPANY_WEB_ID; } return defaultWebId; } }