| @@ -0,0 +1,39 @@ | ||
| <assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0" | ||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd"> | ||
|
|
||
| <id>build-amp-file</id> | ||
| <formats> | ||
| <format>amp</format> | ||
| </formats> | ||
|
|
||
|
|
||
| <includeBaseDirectory>false</includeBaseDirectory> | ||
|
|
||
| <files> | ||
| <!-- Filter module.properties and put at top level in the AMP --> | ||
| <file> | ||
| <source>src/main/resources/alfresco/module/resetPassword/module.properties</source> | ||
| <filtered>true</filtered> | ||
| </file> | ||
| </files> | ||
|
|
||
| <fileSets> | ||
| <fileSet> | ||
| <directory>src/main/config</directory> | ||
| <outputDirectory>config</outputDirectory> | ||
| <filtered>false</filtered> | ||
| </fileSet> | ||
| </fileSets> | ||
|
|
||
| <!-- Include the project artifact (JAR) in the /lib directory in the AMP, and any 3rd party libraries (JARs) | ||
| used by the customization. | ||
| --> | ||
| <dependencySets> | ||
| <dependencySet> | ||
| <outputDirectory>lib</outputDirectory> | ||
| <useProjectArtifact>true</useProjectArtifact> | ||
| </dependencySet> | ||
| </dependencySets> | ||
|
|
||
| </assembly> |
| @@ -0,0 +1,29 @@ | ||
| <?xml version='1.0' encoding='utf-8'?> | ||
|
|
||
| <Context> | ||
| <Resources className="org.apache.catalina.webresources.StandardRoot"> | ||
| <PreResources className="org.apache.catalina.webresources.DirResourceSet" | ||
| base="/usr/local/tomcat/webapps/alfresco/development/classes" | ||
| webAppMount="/WEB-INF/classes"/> | ||
| <JarResources className="org.apache.catalina.webresources.DirResourceSet" | ||
| base="/usr/local/tomcat/webapps/alfresco/development/lib" | ||
| webAppMount="/WEB-INF/lib"/> | ||
| </Resources> | ||
|
|
||
| <!-- Uncomment in $CATALINA_BASE/conf/[enginename]/[hostname]/alfresco.xml to define your own datasource. Add value attributes to each of the environment tags below to define your environment settings. --> | ||
| <!-- Resource defaultTransactionIsolation="-1" defaultAutoCommit="false" maxActive="100" initialSize="10" password="alfresco" username="alfresco" url="jdbc:mysql:///alfresco" driverClassName="org.gjt.mm.mysql.Driver" type="javax.sql.DataSource" auth="Container" name="jdbc/dataSource"/--> | ||
| <Environment override="false" type="java.lang.Boolean" name="properties/startup.enable" | ||
| description="A flag that globally enables or disables startup of the major Alfresco subsystems." | ||
| value="true"/> | ||
| <Environment override="false" type="java.lang.String" name="properties/dir.root" | ||
| description="The filesystem directory below which content and index data is stored. Should be on a shared disk if this is a clustered installation."/> | ||
| <Environment override="false" type="java.lang.String" name="properties/hibernate.dialect" | ||
| description="The fully qualified name of a org.hibernate.dialect.Dialect subclass that allows Hibernate to generate SQL optimized for a particular relational database. Choose from org.hibernate.dialect.DerbyDialect, org.hibernate.dialect.MySQLInnoDBDialect, org.alfresco.repo.domain.hibernate.dialect.AlfrescoOracle9Dialect, org.alfresco.repo.domain.hibernate.dialect.AlfrescoSybaseAnywhereDialect, org.alfresco.repo.domain.hibernate.dialect.AlfrescoSQLServerDialect, org.hibernate.dialect.PostgreSQLDialect"/> | ||
| <Environment override="false" type="java.lang.String" name="properties/hibernate.query.substitutions" | ||
| description="Mapping from tokens in Hibernate queries to SQL tokens. For PostgreSQL, set this to "true TRUE, false FALSE"."/> | ||
| <Environment override="false" type="java.lang.Boolean" name="properties/hibernate.jdbc.use_get_generated_keys" | ||
| description="Enable use of JDBC3 PreparedStatement.getGeneratedKeys() to retrieve natively generated keys after insert. Requires JDBC3+ driver. Set to false if your driver has problems with the Hibernate identifier generators. By default, tries to determine the driver capabilities using connection metadata."/> | ||
| <Environment override="false" type="java.lang.String" name="properties/hibernate.default_schema" | ||
| description="Qualify unqualified table names with the given schema/tablespace in generated SQL. It may be necessary to set this when the target database has more than one schema."/> | ||
| </Context> | ||
|
|
| @@ -0,0 +1,93 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| <modelVersion>4.0.0</modelVersion> | ||
| <parent> | ||
| <artifactId>resetpassword</artifactId> | ||
| <groupId>com.flex-solution</groupId> | ||
| <version>1.2.0.RC1</version> | ||
| </parent> | ||
|
|
||
| <artifactId>reset-password-repo</artifactId> | ||
| <version>1.2.0.RC1</version> | ||
| <name>Reset Password Repo AMP</name> | ||
| <packaging>jar</packaging> | ||
|
|
||
| <dependencies> | ||
| <dependency> | ||
| <groupId>org.alfresco</groupId> | ||
| <artifactId>content-services-community</artifactId> | ||
| <version>6.2.0-ga</version> | ||
| <scope>provided</scope> | ||
| <type>pom</type> | ||
| </dependency> | ||
| </dependencies> | ||
|
|
||
| <build> | ||
| <resources> | ||
| <resource> | ||
| <directory>src/main/resources</directory> | ||
| <filtering>true</filtering> | ||
| </resource> | ||
| <resource> | ||
| <directory>src/main/config</directory> | ||
| <filtering>true</filtering> | ||
| </resource> | ||
| </resources> | ||
| <plugins> | ||
| <plugin> | ||
| <groupId>org.apache.maven.plugins</groupId> | ||
| <artifactId>maven-compiler-plugin</artifactId> | ||
| <configuration> | ||
| <source>8</source> | ||
| <target>8</target> | ||
| </configuration> | ||
| </plugin> | ||
|
|
||
| <plugin> | ||
| <artifactId>maven-dependency-plugin</artifactId> | ||
| <executions> | ||
| <execution> | ||
| <phase>package</phase> | ||
| <goals> | ||
| <goal>copy-dependencies</goal> | ||
| </goals> | ||
| <configuration> | ||
| <outputDirectory>${project.build.directory}/lib</outputDirectory> | ||
| <excludeScope>provided</excludeScope> | ||
| </configuration> | ||
| </execution> | ||
| </executions> | ||
| </plugin> | ||
|
|
||
| <plugin> | ||
| <artifactId>maven-assembly-plugin</artifactId> | ||
| <version>2.6</version> | ||
| <executions> | ||
| <execution> | ||
| <id>build-amp-file</id> | ||
| <phase>package</phase> | ||
| <goals> | ||
| <goal>single</goal> | ||
| </goals> | ||
|
|
||
| <configuration> | ||
| <appendAssemblyId>false</appendAssemblyId> | ||
| <descriptors> | ||
| <descriptor>amp.xml</descriptor> | ||
| </descriptors> | ||
| </configuration> | ||
| </execution> | ||
| </executions> | ||
| <dependencies> | ||
| <dependency> | ||
| <groupId>org.alfresco.maven.plugin</groupId> | ||
| <artifactId>alfresco-maven-plugin</artifactId> | ||
| <version>3.1.0</version> | ||
| </dependency> | ||
| </dependencies> | ||
| </plugin> | ||
| </plugins> | ||
| </build> | ||
|
|
||
| </project> |
| @@ -9,7 +9,7 @@ | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public class EmailHelper extends BaseScopableProcessorExtension { | ||
|
|
||
| private ServiceRegistry services; | ||
| private Repository repositoryHelper; | ||
| @@ -0,0 +1,38 @@ | ||
| package com.flexsolution.resetpassword.util; | ||
|
|
||
| import org.alfresco.error.AlfrescoRuntimeException; | ||
| import org.alfresco.repo.jscript.BaseScopableProcessorExtension; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
| import org.springframework.security.crypto.bcrypt.BCrypt; | ||
|
|
||
| import java.util.Properties; | ||
| import java.util.UUID; | ||
|
|
||
|
|
||
| public class TokenGenerator extends BaseScopableProcessorExtension { | ||
| private static final String SALT_KEY = "fs.passreset.salt"; | ||
| private static final Logger logger = LoggerFactory.getLogger(TokenGenerator.class); | ||
|
|
||
| private Properties globalProps; | ||
|
|
||
| public String genToken (String userName) { | ||
| String salt = globalProps.getProperty(SALT_KEY); | ||
| if(salt == null || salt.equals("")) { | ||
| logger.error("Property fs.passreset.salt is missing"); | ||
| throw new AlfrescoRuntimeException("Reset Password addon is not configured"); | ||
| } | ||
| String token = BCrypt.hashpw(userName, salt).substring(salt.length()); | ||
| return token + UUID.randomUUID().toString().substring(0, 14); | ||
| } | ||
|
|
||
| public String getHashFromToken (String token) { | ||
| return token.substring(0, token.length() - 14); | ||
| } | ||
|
|
||
| public void setGlobalProps (Properties globalProps) { | ||
| this.globalProps = globalProps; | ||
| } | ||
| } | ||
|
|
||
|
|
| @@ -0,0 +1,89 @@ | ||
| package com.flexsolution.resetpassword.util; | ||
|
|
||
| import org.activiti.engine.HistoryService; | ||
| import org.activiti.engine.history.HistoricTaskInstance; | ||
| import org.activiti.engine.history.HistoricTaskInstanceQuery; | ||
| import org.alfresco.repo.security.authentication.AuthenticationUtil; | ||
| import org.alfresco.repo.tenant.TenantUtil; | ||
| import org.alfresco.repo.workflow.activiti.ActivitiConstants; | ||
| import org.alfresco.service.cmr.workflow.WorkflowService; | ||
| import org.alfresco.service.cmr.workflow.WorkflowTask; | ||
| import org.alfresco.service.cmr.workflow.WorkflowTaskQuery; | ||
| import org.alfresco.service.cmr.workflow.WorkflowTaskState; | ||
| import org.alfresco.util.Pair; | ||
| import org.apache.log4j.Logger; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public class WorkflowHelper { | ||
|
|
||
| private static WorkflowService workflowService; | ||
|
|
||
| private static Logger logger = Logger.getLogger(WorkflowHelper.class); | ||
|
|
||
| private static HistoryService activitiHistoryService; | ||
|
|
||
| public static void cancelPreviousWorkflows(final String userName) { | ||
|
|
||
| Pair<String, String> userTenant = AuthenticationUtil.getUserTenant(userName); | ||
|
|
||
| final String tenantDomain = userTenant.getSecond(); | ||
|
|
||
| TenantUtil.runAsUserTenant(new TenantUtil.TenantRunAsWork<Object>() { | ||
| @Override | ||
| public Object doWork() throws Exception { | ||
|
|
||
| List<WorkflowTask> workflowTasks = getTasksInProgress(userName); | ||
|
|
||
| if(logger.isDebugEnabled()) { | ||
| logger.debug("Found workflow tasks = " + workflowTasks.size()); | ||
| } | ||
|
|
||
| for (WorkflowTask task : workflowTasks) { | ||
| if (logger.isDebugEnabled()) { | ||
| logger.debug("Current task: " + task.getName()); | ||
| } | ||
|
|
||
| if ("fs-reset:review".equals(task.getName())) { | ||
| if (logger.isDebugEnabled()) { | ||
| logger.debug("Try to end task " + task.toString()); | ||
| } | ||
| workflowService.cancelWorkflow(task.getPath().getInstance().getId()); | ||
| } | ||
| } | ||
| return null; | ||
| } | ||
| }, userName, tenantDomain); | ||
| } | ||
|
|
||
| public static List<HistoricTaskInstance> getResetPassTasksByUserTokenAcrossTenants (String token){ | ||
| HistoricTaskInstanceQuery query = activitiHistoryService.createHistoricTaskInstanceQuery() | ||
| .includeProcessVariables() | ||
| .unfinished() | ||
| .processVariableValueEquals("fs-reset:token", token); | ||
|
|
||
| return query.list(); | ||
| } | ||
|
|
||
| private static List<WorkflowTask> getTasksInProgress(String userName) { | ||
|
|
||
| WorkflowTaskQuery query = new WorkflowTaskQuery(); | ||
| query.setEngineId(ActivitiConstants.ENGINE_ID); | ||
| query.setTaskState(WorkflowTaskState.IN_PROGRESS); | ||
| query.setActorId(userName); | ||
|
|
||
| if (logger.isDebugEnabled()) { | ||
| logger.debug("Try to delete workflows..."); | ||
| } | ||
|
|
||
| return workflowService.queryTasks(query, true); | ||
| } | ||
|
|
||
| public void setWorkflowService(WorkflowService workflowService) { | ||
| WorkflowHelper.workflowService = workflowService; | ||
| } | ||
|
|
||
| public void setActivitiHistoryService (HistoryService activitiHistoryService) { | ||
| WorkflowHelper.activitiHistoryService = activitiHistoryService; | ||
| } | ||
| } |
| @@ -0,0 +1,125 @@ | ||
| package com.flexsolution.resetpassword.webscripts; | ||
|
|
||
| import com.flexsolution.resetpassword.util.TokenGenerator; | ||
| import com.flexsolution.resetpassword.util.WorkflowHelper; | ||
| import org.activiti.engine.history.HistoricTaskInstance; | ||
| import org.alfresco.error.AlfrescoRuntimeException; | ||
| import org.alfresco.repo.security.authentication.AuthenticationUtil; | ||
| import org.alfresco.repo.tenant.TenantUtil; | ||
| import org.alfresco.repo.workflow.activiti.ActivitiConstants; | ||
| import org.alfresco.service.cmr.security.MutableAuthenticationService; | ||
| import org.alfresco.service.cmr.workflow.WorkflowService; | ||
| import org.json.JSONObject; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
| import org.springframework.extensions.surf.util.Content; | ||
| import org.springframework.extensions.webscripts.*; | ||
|
|
||
| import java.util.HashMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.Objects; | ||
|
|
||
| public class ApplyChangedPasswordPost extends DeclarativeWebScript { | ||
|
|
||
| public static final String NEW_PAS = "new-password"; | ||
| public static final String NEW_PAS_CONFIRM = "new-password-confirm"; | ||
| public static final String TOKEN = "userToken"; | ||
|
|
||
|
|
||
| private WorkflowService workflowService; | ||
| private MutableAuthenticationService authenticationService; | ||
| private TokenGenerator tokenGenerator; | ||
|
|
||
| private static final Logger logger = LoggerFactory.getLogger(ApplyChangedPasswordPost.class); | ||
|
|
||
| @Override | ||
| protected Map<String, Object> executeImpl (WebScriptRequest req, Status status, Cache cache) { | ||
|
|
||
| final Map<String, String> data = getDataFromRequest(req); | ||
|
|
||
| String pass = data.get(NEW_PAS); | ||
| String confirmPass = data.get(NEW_PAS_CONFIRM); | ||
|
|
||
| if(!Objects.equals(pass, confirmPass)) { | ||
| logger.error("Password and confirm password are not equal"); | ||
| throw new AlfrescoRuntimeException("Password and confirm password are not equal"); | ||
| } | ||
|
|
||
| String incomingToken = data.get(TOKEN); | ||
| List<HistoricTaskInstance> candidateTasks = WorkflowHelper.getResetPassTasksByUserTokenAcrossTenants(incomingToken); | ||
|
|
||
| if(candidateTasks.isEmpty()) { | ||
| logger.error("Invalid 'change password' request received. Process by token={} does not exist or has been finished", incomingToken); | ||
| throw new AlfrescoRuntimeException("Request to change password is not valid"); | ||
| } | ||
| if(candidateTasks.size() != 1) { | ||
| logger.error("Found more than one process by token={}", incomingToken); | ||
| throw new AlfrescoRuntimeException("Request to change password is not valid"); | ||
| } | ||
| final HistoricTaskInstance historicTaskInstance = candidateTasks.get(0); | ||
|
|
||
| String assignee = historicTaskInstance.getAssignee(); | ||
| String hashForCurrentAssignee = tokenGenerator.getHashFromToken(tokenGenerator.genToken(assignee)); | ||
|
|
||
| if(!hashForCurrentAssignee.equals(tokenGenerator.getHashFromToken(incomingToken))) { | ||
| logger.error("Invalid 'change password' request received. Token={} is not valid for user {}", incomingToken, assignee); | ||
| throw new AlfrescoRuntimeException("Request to change password is not valid"); | ||
| } | ||
|
|
||
| String tenant_domain = (String) historicTaskInstance.getProcessVariables().get(ActivitiConstants.VAR_TENANT_DOMAIN); | ||
|
|
||
| if(tenant_domain == null) { | ||
| tenant_domain = ""; | ||
| } | ||
|
|
||
| // clear context - to avoid MT concurrency issue (causing domain mismatch) | ||
| AuthenticationUtil.clearCurrentSecurityContext(); | ||
|
|
||
| TenantUtil.runAsSystemTenant(new TenantUtil.TenantRunAsWork<Object>() { | ||
|
|
||
| @Override | ||
| public Object doWork () throws Exception { | ||
| String activitiTaskId = "activiti$" + historicTaskInstance.getId(); | ||
| authenticationService.setAuthentication(assignee, pass.toCharArray()); | ||
| workflowService.endTask(activitiTaskId, null); | ||
| return null; | ||
| } | ||
| }, tenant_domain); | ||
|
|
||
| return new HashMap<String, Object>() {{ | ||
| put("message", "OK"); | ||
| }}; | ||
| } | ||
|
|
||
| private Map<String, String> getDataFromRequest (WebScriptRequest req) { | ||
|
|
||
| HashMap<String, String> result = new HashMap<>(); | ||
| Content content = req.getContent(); | ||
|
|
||
| try { | ||
| JSONObject jsonObject = new JSONObject(content.getContent()); | ||
|
|
||
| result.put(TOKEN, jsonObject.getString(TOKEN)); | ||
| result.put(NEW_PAS, jsonObject.getString(NEW_PAS)); | ||
| result.put(NEW_PAS_CONFIRM, jsonObject.getString(NEW_PAS_CONFIRM)); | ||
|
|
||
| } catch (Exception e) { | ||
| logger.error(e.getMessage()); | ||
| throw new WebScriptException("Failed to get data from request. Please, contact system administrator"); | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| public void setWorkflowService (WorkflowService workflowService) { | ||
| this.workflowService = workflowService; | ||
| } | ||
|
|
||
| public void setAuthenticationService (MutableAuthenticationService authenticationService) { | ||
| this.authenticationService = authenticationService; | ||
| } | ||
|
|
||
| public void setTokenGenerator (TokenGenerator tokenGenerator) { | ||
| this.tokenGenerator = tokenGenerator; | ||
| } | ||
| } |
| @@ -0,0 +1,214 @@ | ||
| package com.flexsolution.resetpassword.webscripts; | ||
|
|
||
| import com.flexsolution.resetpassword.util.WorkflowHelper; | ||
| import org.alfresco.model.ContentModel; | ||
| import org.alfresco.repo.model.Repository; | ||
| import org.alfresco.repo.security.authentication.AuthenticationUtil; | ||
| import org.alfresco.repo.tenant.TenantContextHolder; | ||
| import org.alfresco.repo.workflow.WorkflowModel; | ||
| import org.alfresco.service.cmr.repository.*; | ||
| import org.alfresco.service.cmr.search.SearchService; | ||
| import org.alfresco.service.cmr.security.PersonService; | ||
| import org.alfresco.service.cmr.workflow.WorkflowDefinition; | ||
| import org.alfresco.service.cmr.workflow.WorkflowService; | ||
| import org.alfresco.service.namespace.NamespaceService; | ||
| import org.alfresco.service.namespace.QName; | ||
| import org.json.JSONException; | ||
| import org.json.JSONObject; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
| import org.springframework.beans.factory.annotation.Autowired; | ||
| import org.springframework.extensions.surf.util.Content; | ||
| import org.springframework.extensions.webscripts.*; | ||
|
|
||
| import java.io.IOException; | ||
| import java.io.Serializable; | ||
| import java.util.HashMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
|
|
||
| public class ResetPasswordPost extends DeclarativeWebScript { | ||
|
|
||
| @Autowired | ||
| private ContentService contentService; | ||
|
|
||
| @Autowired | ||
| private NodeService nodeService; | ||
|
|
||
| @Autowired | ||
| private Repository repository; | ||
|
|
||
| @Autowired | ||
| private SearchService searchService; | ||
|
|
||
| @Autowired | ||
| private NamespaceService namespaceService; | ||
|
|
||
| private WorkflowService workflowService; | ||
| private PersonService personService; | ||
|
|
||
| private static final String RESET_PASS_EMAIL_TEMPLATE_XPATH = "app:dictionary/app:email_templates/cm:workflownotification/cm:reset-password.ftl"; | ||
| private static final String WORKFLOW_NOTIFICATION_XPATH = "app:dictionary/app:email_templates/cm:workflownotification"; | ||
|
|
||
| private static final String RESET_PASS_FILE_NAME = "reset-password.ftl"; | ||
|
|
||
| private static final Logger logger = LoggerFactory.getLogger(ResetPasswordPost.class); | ||
|
|
||
| @Override | ||
| public Map<String, Object> executeImpl (WebScriptRequest req, Status status, Cache cache) { | ||
|
|
||
| final String userName = getUserNameFromRequest(req); | ||
|
|
||
| // clear context - to avoid MT concurrency issue (causing domain mismatch) | ||
| AuthenticationUtil.clearCurrentSecurityContext(); | ||
|
|
||
| final String tenantDomain = AuthenticationUtil.getUserTenant(userName).getSecond(); | ||
|
|
||
| createEmailTemplateIfNotExists(tenantDomain); | ||
|
|
||
| TenantContextHolder.setTenantDomain(tenantDomain); | ||
| AuthenticationUtil.setRunAsUser(userName); | ||
|
|
||
| NodeRef user = getUserByUserName(userName); | ||
|
|
||
| WorkflowHelper.cancelPreviousWorkflows(userName); | ||
|
|
||
| logger.debug("Try to start workflow with user " + userName); | ||
|
|
||
| startWorkFlow(user); | ||
|
|
||
| logger.debug("Workflow has been started"); | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| private void createEmailTemplateIfNotExists (String tenantDomain) { | ||
| AuthenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<Object>() { | ||
| @Override | ||
| public Object doWork () throws Exception { | ||
| if(!emailTemplateExists(tenantDomain)) { | ||
| addEmailTemplate(tenantDomain); | ||
| } | ||
| return null; | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| private boolean emailTemplateExists (String tenantDomain) { | ||
|
|
||
| TenantContextHolder.setTenantDomain(tenantDomain); | ||
|
|
||
| NodeRef companyHome = repository.getCompanyHome(); | ||
|
|
||
| List<NodeRef> emailTemplateFiles = searchService.selectNodes(companyHome, RESET_PASS_EMAIL_TEMPLATE_XPATH, null, namespaceService, false); | ||
|
|
||
| return !emailTemplateFiles.isEmpty(); | ||
| } | ||
|
|
||
| private void addEmailTemplate (String tenantDomain) { | ||
|
|
||
| TenantContextHolder.setTenantDomain(tenantDomain); | ||
|
|
||
| NodeRef companyHome = repository.getCompanyHome(); | ||
|
|
||
| List<NodeRef> workflowNotificationFolder = searchService.selectNodes(companyHome, WORKFLOW_NOTIFICATION_XPATH, null, namespaceService, false); | ||
|
|
||
| Map<QName, Serializable> properties = new HashMap<>(); | ||
| properties.put(ContentModel.PROP_NAME, RESET_PASS_FILE_NAME); | ||
|
|
||
| ChildAssociationRef newEmailTemplateCreated = nodeService.createNode(workflowNotificationFolder.get(0), | ||
| ContentModel.ASSOC_CONTAINS, | ||
| QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, QName.createValidLocalName(RESET_PASS_FILE_NAME)), | ||
| ContentModel.TYPE_CONTENT, | ||
| properties); | ||
| ContentWriter invWriter = contentService.getWriter(newEmailTemplateCreated.getChildRef(), ContentModel.PROP_CONTENT, true); | ||
|
|
||
| String adminTenant = ""; | ||
|
|
||
| TenantContextHolder.setTenantDomain(adminTenant); | ||
|
|
||
| companyHome = repository.getCompanyHome(); | ||
|
|
||
| List<NodeRef> emailTemplateFiles = searchService.selectNodes(companyHome, RESET_PASS_EMAIL_TEMPLATE_XPATH, null, namespaceService, false); | ||
|
|
||
| NodeRef emailTemplate = emailTemplateFiles.get(0); | ||
|
|
||
| ContentReader reader = contentService.getReader(emailTemplate, ContentModel.PROP_CONTENT); | ||
|
|
||
| TenantContextHolder.setTenantDomain(tenantDomain); | ||
|
|
||
| invWriter.putContent(reader); | ||
|
|
||
| } | ||
|
|
||
| private void startWorkFlow (final NodeRef user) { | ||
|
|
||
| WorkflowDefinition workflowDefinition = workflowService.getDefinitionByName("activiti$resetPasswordFlex"); | ||
|
|
||
| if(workflowDefinition == null) { | ||
| logger.error("Workflow definition is not found activiti$resetPasswordFlex"); | ||
| throw new WebScriptException(500, "Sorry. Internal error. Try later"); | ||
| } | ||
|
|
||
| Map<QName, Serializable> param = new HashMap<>(); | ||
|
|
||
| logger.debug("Try to set assignee"); | ||
|
|
||
| param.put(WorkflowModel.ASSOC_ASSIGNEE, user); | ||
|
|
||
| logger.debug("Assignee: {}", param.get(WorkflowModel.ASSOC_ASSIGNEE)); | ||
|
|
||
| logger.debug("Try to set description"); | ||
|
|
||
| param.put(WorkflowModel.PROP_DESCRIPTION, "Request to change password"); | ||
|
|
||
| logger.debug("Try to set owner"); | ||
|
|
||
| param.put(ContentModel.PROP_OWNER, AuthenticationUtil.getAdminUserName()); | ||
|
|
||
| logger.debug("Try to start workflow..."); | ||
|
|
||
| workflowService.startWorkflow(workflowDefinition.getId(), param); | ||
| } | ||
|
|
||
| private NodeRef getUserByUserName (final String userName) { | ||
|
|
||
| return AuthenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<NodeRef>() { | ||
| @Override | ||
| public NodeRef doWork () throws Exception { | ||
| NodeRef user = personService.getPersonOrNull(userName); | ||
| if(user == null) { | ||
| logger.error("Failed to find user with username={}", userName); | ||
| throw new WebScriptException(404, "error.userNotFound"); | ||
| } | ||
| return user; | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| private String getUserNameFromRequest (WebScriptRequest request) { | ||
| Content content = request.getContent(); | ||
|
|
||
| String userName; | ||
|
|
||
| try { | ||
| JSONObject jsonObject = new JSONObject(content.getContent()); | ||
|
|
||
| userName = jsonObject.getString("userName"); | ||
|
|
||
| } catch (JSONException | IOException e) { | ||
| logger.error(e.getMessage()); | ||
| throw new WebScriptException(500, "Sorry. Internal error. Try later"); | ||
| } | ||
|
|
||
| return userName; | ||
| } | ||
|
|
||
| public void setWorkflowService (WorkflowService workflowService) { | ||
| this.workflowService = workflowService; | ||
| } | ||
|
|
||
| public void setPersonService (PersonService personService) { | ||
| this.personService = personService; | ||
| } | ||
| } |
| @@ -0,0 +1,54 @@ | ||
| package com.flexsolution.resetpassword.webscripts; | ||
|
|
||
| import com.flexsolution.resetpassword.util.WorkflowHelper; | ||
| import org.activiti.engine.history.HistoricTaskInstance; | ||
| import org.alfresco.error.AlfrescoRuntimeException; | ||
| import org.json.JSONObject; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
| import org.springframework.extensions.surf.util.Content; | ||
| import org.springframework.extensions.webscripts.Cache; | ||
| import org.springframework.extensions.webscripts.DeclarativeWebScript; | ||
| import org.springframework.extensions.webscripts.Status; | ||
| import org.springframework.extensions.webscripts.WebScriptRequest; | ||
|
|
||
| import java.util.HashMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
|
|
||
| public class TaskIsCompletePost extends DeclarativeWebScript { | ||
|
|
||
| private static final Logger logger = LoggerFactory.getLogger(TaskIsCompletePost.class); | ||
|
|
||
| @Override | ||
| protected Map<String, Object> executeImpl (WebScriptRequest req, Status status, Cache cache) { | ||
|
|
||
| try { | ||
| Content content = req.getContent(); | ||
| final JSONObject jsonObject = new JSONObject(content.getContent()); | ||
| final String resetPasswordToken = jsonObject.getString("token"); | ||
|
|
||
| if(resetPasswordToken == null) { | ||
| logger.error("Invalid 'change password' request received. User token is null"); | ||
| throw new AlfrescoRuntimeException("Request to change password is not valid"); | ||
| } | ||
|
|
||
| List<HistoricTaskInstance> cnadidateTasks = WorkflowHelper.getResetPassTasksByUserTokenAcrossTenants(resetPasswordToken); | ||
|
|
||
| if(cnadidateTasks.isEmpty()) { | ||
| logger.error("Invalid 'change password' request received. Process by token={} does not exist or has been finished", resetPasswordToken); | ||
| throw new AlfrescoRuntimeException("Request to change password is not valid"); | ||
| } | ||
| if(cnadidateTasks.size() != 1) { | ||
| logger.error("Found more than one process by token={}", resetPasswordToken); | ||
| throw new AlfrescoRuntimeException("Request to change password is not valid"); | ||
| } | ||
| return new HashMap<String, Object>(){{ | ||
| put("message", "OK"); | ||
| }}; | ||
| } catch (Exception e) { | ||
| logger.error(e.getMessage()); | ||
| throw new AlfrescoRuntimeException("Failed to retrieve data from json"); | ||
| } | ||
| } | ||
| } |
| @@ -29,15 +29,6 @@ | ||
| <title>Review</title> | ||
| <parent>bpm:workflowTask</parent> | ||
| <properties> | ||
| <property name="fs-reset:token"> | ||
| <type>d:text</type> | ||
| <mandatory>true</mandatory> | ||
| @@ -0,0 +1,4 @@ | ||
| alfresco.host=localhost | ||
| share.host=localhost | ||
|
|
||
| fs.passreset.salt=$2a$11$sGL4KAlXWsxR3ysNL8HOmu |
| @@ -0,0 +1,12 @@ | ||
| <beans xmlns="http://www.springframework.org/schema/beans" | ||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| xmlns:context="http://www.springframework.org/schema/context" | ||
| xsi:schemaLocation="http://www.springframework.org/schema/beans | ||
| http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> | ||
|
|
||
| <!-- This is filtered by Maven at build time, so that module name is single sourced. --> | ||
| <import resource="classpath:alfresco/module/resetPassword/context/*-context.xml" /> | ||
|
|
||
| <context:component-scan base-package="com.flexsolution"/> | ||
|
|
||
| </beans> |
| @@ -0,0 +1,4 @@ | ||
| module.id=${project.artifactId} | ||
| module.title=${project.name} | ||
| module.description=${project.description} | ||
| module.version=${project.version} |
| @@ -0,0 +1,30 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| <modelVersion>4.0.0</modelVersion> | ||
|
|
||
| <groupId>com.flex-solution</groupId> | ||
| <artifactId>resetpassword</artifactId> | ||
| <version>1.2.0.RC1</version> | ||
| <name>all in one</name> | ||
| <packaging>pom</packaging> | ||
|
|
||
|
|
||
| <repositories> | ||
| <repository> | ||
| <id>alfresco-public</id> | ||
| <url>https://artifacts.alfresco.com/nexus/content/groups/public</url> | ||
| </repository> | ||
| </repositories> | ||
| <pluginRepositories> | ||
| <pluginRepository> | ||
| <id>alfresco-public</id> | ||
| <name>Alfresco</name> | ||
| <url>http://artifacts.alfresco.com/nexus/content/repositories/releases</url> | ||
| </pluginRepository> | ||
| </pluginRepositories> | ||
|
|
||
| <modules> | ||
| <module>alfresco-resetpassword</module> | ||
| <module>share-resetpassword</module> | ||
| </modules> | ||
| </project> |