CVE-2020-15181: Other (NVD-CWE-Other)
value: The Alfresco Reset Password add-on before version 1.2.0 relies on untrusted inputs in a security decision. Intruders can get admin's access to the system using the vulnerability in the project. Impacts all servers where this add-on is installed. The problem is fixed in version 1.2.0
CWE description: NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
CVSS v3 base score: 9.8
Published: 2020-09-18T18:15Z
Repository: https://github.com/FlexSolution/AlfrescoResetPassword
Language: XML
Files changed: .gitignore, README.md, _docker/REFRESH-WS-intelij.http, _docker/docker-compose.yml, _docker/linux/start.sh, _docker/linux/stop-all.sh, _docker/windows/start.bat, _docker/windows/stop-all.bat, alfresco-resetpassword/Dockerfile, alfresco-resetpassword/amp.xml, alfresco-resetpassword/context.xml, alfresco-resetpassword/pom.xml, alfresco-resetpassword/src/main/config/alfresco/module/resetPassword/bootstrap/emailTemplate/reset-password.ftl, alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/exceptions/EmailTemplateException.java, alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/namespaces/ResetPasswordNameSpace.java, alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/patch/AlfrescoFileVisitor.java, alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/patch/CopyStructurePatch.java, alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/services/script/ScriptPasswordService.java, alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/util/EmailHelper.java, alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/util/Sudo.java, alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/util/TokenGenerator.java, alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/util/WorkflowHelper.java, alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/webscripts/ApplyChangedPasswordPost.java, alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/webscripts/ResetPasswordPost.java, alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/webscripts/TaskIsCompletePost.java, alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/applyChangedPassword/applyChangedPassword.post.desc.xml, alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/applyChangedPassword/applyChangedPassword.post.json.ftl, alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/reset-pass-lib.js, alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/reset-password/reset-password.post.desc.xml, alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/reset-password/reset-password.post.json.ftl, alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/reset-password/reset-password.post.properties, alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/taskIsComplete/taskIsComplete.post.desc.xml, alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/taskIsComplete/taskIsComplete.post.json.ftl, alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/taskIsComplete/taskIsComplete.post.properties, alfresco-resetpassword/src/main/resources/alfresco/extension/workflow/ResetPasswordProcess.bpmn20.xml, alfresco-resetpassword/src/main/resources/alfresco/extension/workflow/js/reset-password.js, alfresco-resetpassword/src/main/resources/alfresco/extension/workflow/reset-workflow-model.xml, alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/alfresco-global.properties, alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/context/bootstrap-context.xml, alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/context/service-context.xml, alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/context/webscript-context.xml, alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/context/workflow-context.xml, alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/module-context.xml, alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/module.properties, pom.xml, share-resetpassword/Dockerfile, share-resetpassword/amp.xml, share-resetpassword/context.xml, share-resetpassword/pom.xml, share-resetpassword/src/main/resources/alfresco/web-extension/messages/resetPassword-share.properties, share-resetpassword/src/main/resources/alfresco/web-extension/messages/resetPassword-share_de.properties, share-resetpassword/src/main/resources/alfresco/web-extension/share-resetpassword-slingshot-application-context.xml, share-resetpassword/src/main/resources/alfresco/web-extension/share-resetpassword/share-config-custom.xml, share-resetpassword/src/main/resources/alfresco/web-extension/site-data/extensions/resetPassword-extensions.xml, share-resetpassword/src/main/resources/alfresco/web-extension/site-data/pages/changePassWF.xml, share-resetpassword/src/main/resources/alfresco/web-extension/site-data/pages/update-password.xml, share-resetpassword/src/main/resources/alfresco/web-extension/site-data/template-instances/changePassWF.xml, share-resetpassword/src/main/resources/alfresco/web-extension/site-data/template-instances/update-password.xml, share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/com/flex-solution/resetPassword/changePassWF/changePassWF.get.desc.xml, share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/com/flex-solution/resetPassword/changePassWF/changePassWF.get.html.ftl, share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/com/flex-solution/resetPassword/changePassWF/changePassWF.get.js, share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/com/flex-solution/resetPassword/components/form/controls/passConfirmation.ftl, share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/com/flex-solution/resetPassword/components/form/controls/wfTransitions.ftl, share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/com/flex-solution/resetPassword/components/guest/login.get.html.ftl, share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/org/alfresco/README.md, share-resetpassword/src/main/resources/alfresco/web-extension/templates/com/flex-solution/changePassWF/changePassWF.ftl, share-resetpassword/src/main/resources/alfresco/web-extension/templates/com/flex-solution/login/simple-guest.ftl, share-resetpassword/src/main/resources/alfresco/web-extension/templates/com/flex-solution/password/update-password.ftl, share-resetpassword/src/main/resources/file-mapping.properties, share-resetpassword/src/main/resources/module.properties, share-resetpassword/src/main/resources/web/extras/com/flex-solution/components/ResetPassword.css, share-resetpassword/src/main/resources/web/extras/com/flex-solution/components/ResetPassword.js, share-resetpassword/src/main/resources/web/extras/com/flex-solution/components/changePassWF.css, share-resetpassword/src/main/resources/web/extras/com/flex-solution/components/changePassWF.js, share-resetpassword/src/main/resources/web/extras/com/flex-solution/components/confirmPassword.js, share-resetpassword/src/main/resources/web/extras/com/flex-solution/components/wfTransitions.js
Commit date: 2020-09-16 14:25:28 +0300
Commit message: Merge remote-tracking branch 'remotes/origin/candidate' into release

# Conflicts:
#	resetPassword-share/pom.xml
#	resetPassword/pom.xml

Diff: diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..039cbe4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+*.iml
+/.idea/
+/_docker/logs.txt
+/alfresco-resetpassword/target/
+/share-resetpassword/target/
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6a0f3ce
--- /dev/null
+++ b/README.md
@@ -0,0 +1,109 @@
+# AlfrescoResetPassword
+
+Alfresco Reset Password add-on is a great thing that allows you to solve missing capability of Alfresco to reset passwords for users who forgot owns password without contacting Administrator.
+
+# Features
+ - Sends email notification with a confirmation link
+ - Reset password link will be expired after 24h
+ - It works fine even if multitenancy feature is enabled
+
+# User's reset password manual
+
+ - Click Forgot Password button
+ 
+ ![](readme_resources/1-config.png)
+ 
+ - Enter username
+ 
+ ![](readme_resources/4-config.png)
+ 
+ - You'll receive email notification with link for changing password
+ 
+ ![](readme_resources/2-config.png)
+ 
+ - Enter your new password and confirm it.
+ 
+ ![](readme_resources/3-config.png)
+
+# Compatibility was tested on versions:
+ - Alfresco 5.0d
+ - Alfresco 5.1
+ - Alfresco 5.2
+ - Alfresco 6.0.7-ga
+ - Alfresco 6.2.0-ga
+
+# How to build
+### The following tools must be installed:
+- Java11
+- Maven3
+
+### Required settings
+  - You have to configure Alfresco Outbound SMTP in {ALFRESCO LOCATION}/tomcat/shared/classes/alfresco-global.properties.
+  ```sh
+   mail.host={mail host}
+   mail.port={mail port}
+   mail.username={mail username}
+   mail.password={mail password}
+   mail.protocol=smtps
+   mail.smtps.starttls.enable=true
+   mail.smtps.auth=true
+   mail.from.enabled=false
+   mail.from.default={default sending email}
+   ```
+- It is highly recommended to change secure salt property as well that us used in token generation.
+To generate new one use any BCrypt hash generator. First 29 characters is salt
+```fs.passreset.salt=```
+
+### Build steps
+1. Clone repo
+```sh
+$ git clone https://github.com/FlexSolution/AlfrescoResetPassword.git
+```
+2. Compile repo amp 
+
+```sh
+$ cd ResetPasswordAddon/resetPassword
+$ mvn clean package
+```
+
+3. Compile share amp
+
+```sh
+$ cd ../resetPassword-share
+$ mvn clean package
+```
+
+# How to deploy add-on to Alfresco
+
+1. Compile AMPs for alfresco and share how described above
+2. Stop Alfresco
+3. Copy file ResetPasswordAddon/resetPassword/target/reset-password-repo-${version}.amp into ${Alfresco_HOME}/amps folder
+4. Copy file ResetPasswordAddon/resetPassword-share/target/reset-password-share-${version}.amp into ${Alfresco_HOME}/amps_share folder
+5. Apply AMPs by executing command from command line
+```sh
+$ cd ${Alfresco_HOME}/bin
+$ apply_amps.sh -force
+```
+6. Start Alfresco
+
+##How to setup dev environment:
+You need to have installed docker and configured to use without root privileges https://docs.docker.com/install
+
+1. Add **Remote** Debugger cofiguration.
+![](readme_resources/1-configuration.png)
+
+1. Configure **Before launch: Tool window** and point at **start.sh** or **start.bat** as executable script.
+(files within _docker folder should be executable).
+![](readme_resources/4-configuration.png)
+![](readme_resources/2-configuration.png)
+
+1. Point at **logs.txt** as logs file.
+![](readme_resources/3-configuration.png)
+
+All alfresco configuration placed in **docker-compose.yml**. You can place additional configuration there if you need
+
+To stop docker containers use **stop-all.sh** or **stop-all.bat** script.
+**start.sh|bat** script also stops containers and starts again
+
+To debug Share app java code create new **Remote** configuration. Use port 5006
+
diff --git a/_docker/REFRESH-WS-intelij.http b/_docker/REFRESH-WS-intelij.http
new file mode 100755
index 0000000..46f919c
--- /dev/null
+++ b/_docker/REFRESH-WS-intelij.http
@@ -0,0 +1,7 @@
+POST http://localhost:8080/share/page/index
+Content-Type: application/x-www-form-urlencoded
+Authorization: Basic YWRtaW46YWRtaW4=
+
+reset=on
+
+###
\ No newline at end of file
diff --git a/_docker/docker-compose.yml b/_docker/docker-compose.yml
new file mode 100755
index 0000000..b4a95a7
--- /dev/null
+++ b/_docker/docker-compose.yml
@@ -0,0 +1,142 @@
+
+version: "2"
+
+services:
+    alfresco-resetpassword:
+        # image: alfresco/alfresco-content-repository-community:6.2.0-ga
+        build: ./../alfresco-resetpassword
+        environment:
+            CATALINA_OPTS: "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=0.0.0.0:8888"
+            JAVA_OPTS : "
+                -Ddb.driver=org.postgresql.Driver
+                -Ddb.username=alfresco
+                -Ddb.password=alfresco
+                -Ddb.url=jdbc:postgresql://postgres-resetpassword:5432/alfresco
+                -Dsolr.host=solr6-resetpassword
+                -Dsolr.port=8983
+                -Dsolr.secureComms=none
+                -Dsolr.base.url=/solr
+                -Dindex.subsystem.name=solr6
+
+                -Ddeployment.method=DOCKER_COMPOSE
+                -Dcsrf.filter.enabled=false
+                -Dmessaging.subsystem.autoStart=false
+
+                -Dlocal.transform.service.enabled=true
+                -DlocalTransform.pdfrenderer.url=http://alfresco-pdf-renderer:8090/
+                -DlocalTransform.imagemagick.url=http://imagemagick:8090/
+                -DlocalTransform.libreoffice.url=http://libreoffice:8090/
+                -DlocalTransform.tika.url=http://tika:8090/
+                -DlocalTransform.misc.url=http://transform-misc:8090/
+                -Dlegacy.transform.service.enabled=true
+                -Dalfresco-pdf-renderer.url=http://alfresco-pdf-renderer:8090/
+                -Djodconverter.url=http://libreoffice:8090/
+                -Dimg.url=http://imagemagick:8090/
+                -Dtika.url=http://tika:8090/
+                -Dtransform.misc.url=http://transform-misc:8090/
+
+                -Xms1500m -Xmx1500m
+                "
+        ports:
+            - 8082:8080 #Browser port
+            - 5005:8888
+            - 2121:2121
+        links:
+            - postgres-resetpassword
+        volumes:
+            - alf_data-resetpassword:/usr/local/tomcat/alf_data
+            - ./../alfresco-resetpassword/_lib:/usr/local/tomcat/webapps/alfresco/development/lib
+            - ./../alfresco-resetpassword/target/classes:/usr/local/tomcat/webapps/alfresco/development/classes
+            - ./../alfresco-resetpassword/context.xml:/usr/local/tomcat/webapps/alfresco/META-INF/context.xml
+
+    alfresco-pdf-renderer:
+        image: alfresco/alfresco-pdf-renderer:2.1.0
+        mem_limit: 1g
+        environment:
+            JAVA_OPTS: " -Xms256m -Xmx512m"
+        expose:
+            - 8090
+
+    imagemagick:
+        image: alfresco/alfresco-imagemagick:2.1.0
+        mem_limit: 1g
+        environment:
+            JAVA_OPTS: " -Xms256m -Xmx512m"
+        expose:
+            - 8090
+
+    libreoffice:
+        image: alfresco/alfresco-libreoffice:2.1.0
+        mem_limit: 1g
+        environment:
+            JAVA_OPTS: " -Xms256m -Xmx512m"
+        expose:
+            - 8090
+
+    tika:
+        image: alfresco/alfresco-tika:2.1.0
+        mem_limit: 1g
+        environment:
+            JAVA_OPTS: " -Xms256m -Xmx512m"
+        expose:
+            - 8090
+
+    transform-misc:
+        image: alfresco/alfresco-transform-misc:2.1.0
+        mem_limit: 1g
+        environment:
+            JAVA_OPTS: " -Xms256m -Xmx512m"
+        expose:
+            - 8090
+
+
+    share-resetpassword:
+        # image: alfresco/alfresco-share:6.2.0
+        build: ./../share-resetpassword
+        environment:
+            - REPO_HOST=alfresco-resetpassword
+            - REPO_PORT=8080
+            - JAVA_OPTS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5006
+        ports:
+            - 8080:8080
+            - 5006:5006
+        volumes:
+            - ./../share-resetpassword/_lib:/usr/local/tomcat/webapps/share/development/lib
+            - ./../share-resetpassword/target/classes:/usr/local/tomcat/webapps/share/development/classes
+            - ./../share-resetpassword/context.xml:/usr/local/tomcat/webapps/share/META-INF/context.xml
+            - ./../share-resetpassword/src/main/resources/web:/usr/local/tomcat/webapps/share/development/web
+
+    postgres-resetpassword:
+        image: postgres:11.4
+        environment:
+            - POSTGRES_PASSWORD=alfresco
+            - POSTGRES_USER=alfresco
+            - POSTGRES_DB=alfresco
+        command: postgres -c max_connections=300 -c log_min_messages=LOG
+        ports:
+            - 5435:5432
+
+    solr6-resetpassword:
+        image: alfresco/alfresco-search-services:1.4.0
+        environment:
+            #Solr needs to know how to register itself with Alfresco
+            - SOLR_ALFRESCO_HOST=alfresco-resetpassword
+            - SOLR_ALFRESCO_PORT=8080
+            #Alfresco needs to know how to call solr
+            - SOLR_SOLR_HOST=solr6-resetpassword
+            - SOLR_SOLR_PORT=8983
+            #Create the default alfresco and archive cores
+            - SOLR_CREATE_ALFRESCO_DEFAULTS=alfresco,archive
+            - ALFRESCO_SECURE_COMMS=none
+        ports:
+            - 8983
+
+    activemq-resetpassword:
+        image: alfresco/alfresco-activemq:5.15.8
+        ports:
+            - 8161 # Web Console
+            - 5672 # AMQP
+            - 61616 # OpenWire
+            - 61613 # STOMP
+volumes:
+    alf_data-resetpassword:
\ No newline at end of file
diff --git a/_docker/linux/docker-compose-linux b/_docker/linux/docker-compose-linux
new file mode 100755
index 0000000..2a9e942
Binary files /dev/null and b/_docker/linux/docker-compose-linux differ
diff --git a/_docker/linux/start.sh b/_docker/linux/start.sh
new file mode 100755
index 0000000..ee572b5
--- /dev/null
+++ b/_docker/linux/start.sh
@@ -0,0 +1,8 @@
+mvn -f ../../pom.xml clean package
+
+rm ../logs.txt
+./docker-compose-linux -f ../docker-compose.yml stop
+./docker-compose-linux -f ../docker-compose.yml up --no-start --no-recreate
+./docker-compose-linux -f ../docker-compose.yml start
+./docker-compose-linux -f ../docker-compose.yml logs -f --tail=0  >> ../logs.txt &
+sleep 5
\ No newline at end of file
diff --git a/_docker/linux/stop-all.sh b/_docker/linux/stop-all.sh
new file mode 100755
index 0000000..2dc8950
--- /dev/null
+++ b/_docker/linux/stop-all.sh
@@ -0,0 +1,2 @@
+BASEDIR=$(dirname "$0")
+$BASEDIR/docker-compose-linux -f $BASEDIR/../docker-compose.yml stop
\ No newline at end of file
diff --git a/_docker/windows/docker-compose-Windows-x86_64.exe b/_docker/windows/docker-compose-Windows-x86_64.exe
new file mode 100755
index 0000000..020955c
Binary files /dev/null and b/_docker/windows/docker-compose-Windows-x86_64.exe differ
diff --git a/_docker/windows/start.bat b/_docker/windows/start.bat
new file mode 100755
index 0000000..165023b
--- /dev/null
+++ b/_docker/windows/start.bat
@@ -0,0 +1,11 @@
+@ECHO OFF
+chcp 65001
+set BASEDIR=%~dp0
+
+call %BASEDIR%docker-compose-Windows-x86_64.exe -f %BASEDIR%..\docker-compose.yml stop
+call mvn -f %BASEDIR%..\..\pom.xml clean package
+del %BASEDIR%..\logs.txt
+call %BASEDIR%docker-compose-Windows-x86_64.exe -f %BASEDIR%..\docker-compose.yml up  --no-recreate --no-start
+start /B %BASEDIR%docker-compose-Windows-x86_64.exe -f %BASEDIR%..\docker-compose.yml start
+PING localhost -n 20 >NUL
+start /B %BASEDIR%docker-compose-Windows-x86_64.exe -f %BASEDIR%..\docker-compose.yml logs -f --tail=0 > %BASEDIR%..\logs.txt
diff --git a/_docker/windows/stop-all.bat b/_docker/windows/stop-all.bat
new file mode 100755
index 0000000..6bae767
--- /dev/null
+++ b/_docker/windows/stop-all.bat
@@ -0,0 +1,2 @@
+set BASEDIR=%~dp0
+call %BASEDIR%docker-compose-Windows-x86_64.exe -f %BASEDIR%..\docker-compose.yml stop
\ No newline at end of file
diff --git a/alfresco-resetpassword/Dockerfile b/alfresco-resetpassword/Dockerfile
new file mode 100644
index 0000000..fab363d
--- /dev/null
+++ b/alfresco-resetpassword/Dockerfile
@@ -0,0 +1,13 @@
+FROM alfresco/alfresco-content-repository-community:6.2.0-ga
+
+ARG TOMCAT_DIR=/usr/local/tomcat
+
+USER root
+
+
+# Copy Dockerfile to avoid an error if no AMPs exist
+COPY Dockerfile _amps/*.amp $TOMCAT_DIR/amps/
+RUN java -jar $TOMCAT_DIR/alfresco-mmt/alfresco-mmt*.jar install \
+              $TOMCAT_DIR/amps $TOMCAT_DIR/webapps/alfresco -directory -nobackup -force
+
+USER ${USERNAME}
\ No newline at end of file
diff --git a/alfresco-resetpassword/_amps/README.txt b/alfresco-resetpassword/_amps/README.txt
new file mode 100644
index 0000000..e69de29
diff --git a/alfresco-resetpassword/_lib/README.txt b/alfresco-resetpassword/_lib/README.txt
new file mode 100644
index 0000000..e69de29
diff --git a/alfresco-resetpassword/amp.xml b/alfresco-resetpassword/amp.xml
new file mode 100644
index 0000000..f6d7e91
--- /dev/null
+++ b/alfresco-resetpassword/amp.xml
@@ -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>
\ No newline at end of file
diff --git a/alfresco-resetpassword/context.xml b/alfresco-resetpassword/context.xml
new file mode 100644
index 0000000..607ec28
--- /dev/null
+++ b/alfresco-resetpassword/context.xml
@@ -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 &quot;true TRUE, false FALSE&quot;."/>
+<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>
+
diff --git a/alfresco-resetpassword/pom.xml b/alfresco-resetpassword/pom.xml
new file mode 100644
index 0000000..c87624b
--- /dev/null
+++ b/alfresco-resetpassword/pom.xml
@@ -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>
diff --git a/alfresco-resetpassword/src/main/config/README.txt b/alfresco-resetpassword/src/main/config/README.txt
new file mode 100644
index 0000000..e69de29
diff --git a/alfresco-resetpassword/src/main/config/alfresco/module/resetPassword/bootstrap/emailTemplate/reset-password.ftl b/alfresco-resetpassword/src/main/config/alfresco/module/resetPassword/bootstrap/emailTemplate/reset-password.ftl
new file mode 100755
index 0000000..7117e0b
--- /dev/null
+++ b/alfresco-resetpassword/src/main/config/alfresco/module/resetPassword/bootstrap/emailTemplate/reset-password.ftl
@@ -0,0 +1,90 @@
+<html>
+<head>
+    <style type="text/css"><!--
+    body {
+        font-family: Arial, sans-serif;
+        font-size: 14px;
+        color: #4c4c4c;
+    }
+
+    a, a:visited {
+        color: #0072cf;
+    }
+
+    --></style>
+</head>
+
+<body bgcolor="#dddddd">
+<table width="100%" cellpadding="20" cellspacing="0" border="0" bgcolor="#dddddd">
+    <tr>
+        <td width="100%" align="center">
+            <table width="70%" cellpadding="0" cellspacing="0" bgcolor="white"
+                   style="background-color: white; border: 1px solid #aaaaaa;">
+                <tr>
+                    <td width="100%">
+                        <table width="100%" cellpadding="0" cellspacing="0" border="0">
+                            <tr>
+                                <td style="padding: 10px 30px 0px;">
+                                    <table width="100%" cellpadding="0" cellspacing="0" border="0">
+                                        <tr>
+                                            <td>
+                                                <table cellpadding="0" cellspacing="0" border="0">
+                                                    <tr>
+                                                        <td>
+                                                            <img src="${shareUrl}/res/components/images/no-user-photo-64.png"
+                                                                 alt="" width="64" height="64" border="0"
+                                                                 style="padding-right: 20px;"/>
+                                                        </td>
+                                                        <td>
+                                                            <div style="font-size: 22px; padding-bottom: 4px;">
+                                                                Change password for Alfresco ${productName!""} account
+                                                            </div>
+                                                            <div style="font-size: 13px;">
+                                                            ${date?datetime?string.full}
+                                                            </div>
+                                                        </td>
+                                                    </tr>
+                                                </table>
+                                                <div style="font-size: 14px; margin: 12px 0px 24px 0px; padding-top: 10px; border-top: 1px solid #aaaaaa;">
+                                                    <p>Dear ${assignee.firstname},</p>
+
+                                                    <p>we received a request for password change
+                                                        at ${shareUrl}</p>
+
+                                                    <br/><a href="${shareUrl}/page/changePassWF?token=${token}">Go to
+                                                    this link</a> to set your new password.
+                                                    The link will be active for 24 hours.</p>
+
+                                                    <p>Sincerely,<br/>
+                                                        Alfresco ${productName!""}</p>
+                                                </div>
+                                            </td>
+                                        </tr>
+                                    </table>
+                                </td>
+                            </tr>
+                            <tr>
+                                <td>
+                                    <div style="border-top: 1px solid #aaaaaa;">&nbsp;</div>
+                                </td>
+                            </tr>
+                            <tr>
+                                <td>
+                                    <div style="border-bottom: 1px solid #aaaaaa;">&nbsp;</div>
+                                </td>
+                            </tr>
+                            <tr>
+                                <td style="padding: 10px 30px;">
+                                    <img src="${shareUrl}/themes/default/images/app-logo.png" alt="" width="145"
+                                         height="32" border="0"/>
+                                </td>
+                            </tr>
+                        </table>
+                    </td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+</table>
+</body>
+</html>
\ No newline at end of file
diff --git a/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/exceptions/EmailTemplateException.java b/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/exceptions/EmailTemplateException.java
new file mode 100644
index 0000000..433270c
--- /dev/null
+++ b/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/exceptions/EmailTemplateException.java
@@ -0,0 +1,10 @@
+package com.flexsolution.resetpassword.exceptions;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+
+public class EmailTemplateException extends AlfrescoRuntimeException {
+
+    public EmailTemplateException(String msgId) {
+        super(msgId);
+    }
+}
diff --git a/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/namespaces/ResetPasswordNameSpace.java b/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/namespaces/ResetPasswordNameSpace.java
new file mode 100644
index 0000000..ac1ed81
--- /dev/null
+++ b/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/namespaces/ResetPasswordNameSpace.java
@@ -0,0 +1,16 @@
+package com.flexsolution.resetpassword.namespaces;
+
+import org.alfresco.service.namespace.QName;
+
+public abstract class ResetPasswordNameSpace {
+
+    private ResetPasswordNameSpace() {}
+
+    private static final String MODEL_URI = "https://flex-solution.com/model/workflow/1.0";
+
+    public static final QName QNAME_TOKEN = QName.createQName(MODEL_URI, "token");
+
+    public static final QName QNAME_PASSWORD = QName.createQName(MODEL_URI, "password");
+
+    public static final QName QNAME_CONFIRM_PASS = QName.createQName(MODEL_URI, "confirmPass");
+}
diff --git a/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/patch/AlfrescoFileVisitor.java b/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/patch/AlfrescoFileVisitor.java
new file mode 100644
index 0000000..a225ee6
--- /dev/null
+++ b/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/patch/AlfrescoFileVisitor.java
@@ -0,0 +1,174 @@
+package com.flexsolution.resetpassword.patch;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.model.ContentModel;
+import org.alfresco.service.cmr.model.FileFolderService;
+import org.alfresco.service.cmr.model.FileInfo;
+import org.alfresco.service.cmr.repository.*;
+import org.alfresco.service.namespace.QName;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.extensions.surf.util.I18NUtil;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.*;
+
+public class AlfrescoFileVisitor extends SimpleFileVisitor<Path> {
+
+    private final Logger LOGGER = LoggerFactory.getLogger(AlfrescoFileVisitor.class);
+
+    private boolean ignoreRoot;
+
+    public AlfrescoFileVisitor (NodeService nodeService, FileFolderService fileFolderService,
+                                ContentService contentService,
+                                MimetypeService mimetypeService,
+                                Properties globalProps,
+                                Map<String, NodeRef> startLocationEntry,
+                                Set<String> ignoreFiles,
+                                boolean ignoreRoot) {
+        this.nodeService = nodeService;
+        this.fileFolderService = fileFolderService;
+        this.contentService = contentService;
+        this.mimetypeService = mimetypeService;
+        this.globalProps = globalProps;
+        this.ignoreFiles = ignoreFiles;
+        this.ignoreRoot = ignoreRoot;
+
+        IMPORTED.putAll(startLocationEntry);
+    }
+
+    private final String SPACES_PROPS_PATH = "alfresco/messages/bootstrap-spaces";
+
+    private ResourceBundle bundle;
+
+    private final NodeService nodeService;
+    private final FileFolderService fileFolderService;
+    private final ContentService contentService;
+    private final MimetypeService mimetypeService;
+    private final Properties globalProps;
+    private final Set<String> ignoreFiles;
+
+    private final Map<String, NodeRef> IMPORTED = new HashMap<>();
+
+    @Override
+    public FileVisitResult preVisitDirectory (Path dir, BasicFileAttributes attrs) throws IOException {
+        return copyToNode(dir, ContentModel.TYPE_FOLDER);
+    }
+
+    private String getLocalizedName (String fileNameExpr) {
+        if(this.bundle == null) {
+            Locale bindingLocale = I18NUtil.getLocale();
+            this.bundle = ResourceBundle.getBundle(SPACES_PROPS_PATH, bindingLocale);
+        }
+
+        return this.bundle.getString(fileNameExpr);
+    }
+
+    private FileVisitResult copyToNode (Path fileOrDir, QName type) {
+        if(!IMPORTED.containsKey(fileOrDir.toString()) || ignoreRoot == false) {
+
+            NodeRef parentNode;
+
+            if(ignoreRoot == false){
+                parentNode = IMPORTED.get(fileOrDir.toString());
+                ignoreRoot = true;
+            }else {
+                parentNode = IMPORTED.get(fileOrDir.getParent().toString());
+            }
+
+            String importedFileName = fileOrDir.getFileName().toString();
+
+            if(importedFileName.startsWith("$")) {
+                importedFileName = getLocalizedName(importedFileName.substring(1));
+            }
+
+            NodeRef targetNode = fileFolderService.searchSimple(parentNode, importedFileName);
+
+            if(targetNode == null) {
+
+                final FileInfo newNode = fileFolderService.create(parentNode, importedFileName, type);
+
+                targetNode = newNode.getNodeRef();
+
+                if(ContentModel.TYPE_CONTENT.equals(type)) {
+                    copyContent(fileOrDir, newNode);
+                }
+                LOGGER.debug("Node {} has been imported. File/folder name {}", targetNode, importedFileName);
+            } else {
+                LOGGER.debug("Node {} already exists (SKIP). File/folder name {}", targetNode, importedFileName);
+            }
+
+            setNodeTitle(targetNode, importedFileName);
+
+            IMPORTED.put(fileOrDir.toString(), targetNode);
+
+        }
+
+        LOGGER.debug("Imported file/dir path={}", fileOrDir.toString());
+        return FileVisitResult.CONTINUE;
+    }
+
+    private void copyContent (Path file, FileInfo newNode) {
+        try {
+            final ContentWriter writer = contentService.getWriter(newNode.getNodeRef(), ContentModel.PROP_CONTENT, true);
+
+            final String importedFileName = file.getFileName().toString();
+            writer.setMimetype(mimetypeService.getMimetype(FilenameUtils.getExtension(importedFileName)));
+
+            try (
+                    final InputStream is = Files.newInputStream(file);
+                    final OutputStream os = writer.getContentOutputStream();
+            ) {
+                IOUtils.copy(is, os);
+            }
+        } catch (IOException e) {
+            throw new AlfrescoRuntimeException(e.getMessage());
+        }
+
+        LOGGER.debug( "Content copied file path={}", file.toString());
+    }
+
+
+    private void setNodeTitle (NodeRef nodeRef, String filename) {
+        final String title = this.globalProps.getProperty("title." + filename);
+
+        LOGGER.debug("Title for file/folder {} is {}", filename, title);
+
+//        if(title != null){
+//            this.nodeService.setProperty(nodeRef, ContentModel.PROP_TITLE, title);
+//        }
+
+        Optional.ofNullable(title)
+                .ifPresent(t -> this.nodeService.setProperty(nodeRef, ContentModel.PROP_TITLE, t));
+
+    }
+
+    @Override
+    public FileVisitResult visitFile (Path file, BasicFileAttributes attrs) throws IOException {
+
+        if(ignoreFiles != null && ignoreFiles.contains(file.getFileName().toString())) {
+            LOGGER.info("File {} has been ignored", file.getFileName().toString());
+            return FileVisitResult.CONTINUE;
+        }
+        return copyToNode(file, ContentModel.TYPE_CONTENT);
+    }
+
+    @Override
+    public FileVisitResult visitFileFailed (Path file, IOException exc) throws IOException {
+        throw new AlfrescoRuntimeException(exc.getMessage());
+    }
+
+    @Override
+    public FileVisitResult postVisitDirectory (Path dir, IOException exc) throws IOException {
+        return super.postVisitDirectory(dir, exc);
+    }
+}
diff --git a/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/patch/CopyStructurePatch.java b/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/patch/CopyStructurePatch.java
new file mode 100644
index 0000000..845a28c
--- /dev/null
+++ b/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/patch/CopyStructurePatch.java
@@ -0,0 +1,110 @@
+package com.flexsolution.resetpassword.patch;
+
+import org.alfresco.repo.admin.patch.AbstractPatch;
+import org.alfresco.repo.model.Repository;
+import org.alfresco.service.cmr.model.FileFolderService;
+import org.alfresco.service.cmr.repository.ContentService;
+import org.alfresco.service.cmr.repository.MimetypeService;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.io.DefaultResourceLoader;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
+
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.Properties;
+import java.util.Set;
+
+public class CopyStructurePatch extends AbstractPatch {
+
+    private final Logger LOGGER = LoggerFactory.getLogger(CopyStructurePatch.class);
+
+    protected String classpathLocaiton;
+    protected String copyToXpath;
+
+    protected Repository repository;
+    protected FileFolderService fileFolderService;
+    protected ContentService contentService;
+    protected MimetypeService mimetypeService;
+    protected Properties globalProps;
+    protected Set<String> ignoreFiles;
+    protected boolean ignoreRoot = true;
+
+    @Override
+    public String applyInternal () throws Exception {
+
+
+        NodeRef companyHome = repository.getCompanyHome();
+
+        NodeRef dataDictionary = searchService.selectNodes(companyHome, copyToXpath, null, namespaceService, false).get(0);
+
+
+        ResourceLoader resourceLoader = new DefaultResourceLoader(getClass().getClassLoader());
+        Resource bootstrapFolder = resourceLoader.getResource(classpathLocaiton);
+
+        final String startLocationPath = bootstrapFolder.getFile().getPath();
+        Files.walkFileTree(Paths.get(startLocationPath),
+                new AlfrescoFileVisitor(
+                        nodeService,
+                        fileFolderService,
+                        contentService,
+                        mimetypeService,
+                        globalProps,
+                        Collections.singletonMap(startLocationPath, dataDictionary),
+                        ignoreFiles,
+                        ignoreRoot
+                )
+        );
+
+
+        return "STRUCTURE COPY PATCH FINISHED SUCCESSFULLY";
+    }
+
+    public Repository getRepository() {
+        return repository;
+    }
+
+    public void setRepository (Repository repository) {
+        this.repository = repository;
+    }
+
+    public void setFileFolderService (FileFolderService fileFolderService) {
+        this.fileFolderService = fileFolderService;
+    }
+
+    public void setContentService (ContentService contentService) {
+        this.contentService = contentService;
+    }
+
+    public void setMimetypeService (MimetypeService mimetypeService) {
+        this.mimetypeService = mimetypeService;
+    }
+
+    public void setClasspathLocaiton (String classpathLocaiton) {
+        this.classpathLocaiton = classpathLocaiton;
+    }
+
+
+    public void setCopyToXpath (String copyToXpath) {
+        this.copyToXpath = copyToXpath;
+    }
+
+    public void setGlobalProps (Properties globalProps) {
+        this.globalProps = globalProps;
+    }
+
+    public void setIgnoreFiles (Set<String> ignoreFiles) {
+        this.ignoreFiles = ignoreFiles;
+    }
+
+    public void setIgnoreRoot (boolean ignoreRoot) {
+        this.ignoreRoot = ignoreRoot;
+    }
+
+    public boolean isIgnoreRoot () {
+        return ignoreRoot;
+    }
+}
diff --git a/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/services/script/ScriptPasswordService.java b/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/services/script/ScriptPasswordService.java
new file mode 100644
index 0000000..8461b96
--- /dev/null
+++ b/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/services/script/ScriptPasswordService.java
@@ -0,0 +1,17 @@
+package com.flexsolution.resetpassword.services.script;
+
+import org.alfresco.repo.jscript.BaseScopableProcessorExtension;
+import org.alfresco.service.cmr.security.MutableAuthenticationService;
+
+public class ScriptPasswordService extends BaseScopableProcessorExtension {
+
+    private MutableAuthenticationService authenticationService;
+
+    public void setPassword(String userName, String password) {
+            authenticationService.setAuthentication(userName, password.toCharArray());
+    }
+
+    public void setAuthenticationService(MutableAuthenticationService authenticationService) {
+        this.authenticationService = authenticationService;
+    }
+}
\ No newline at end of file
diff --git a/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/util/EmailHelper.java b/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/util/EmailHelper.java
new file mode 100644
index 0000000..d7c19ea
--- /dev/null
+++ b/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/util/EmailHelper.java
@@ -0,0 +1,38 @@
+package com.flexsolution.resetpassword.util;
+
+import com.flexsolution.resetpassword.exceptions.EmailTemplateException;
+import org.alfresco.repo.jscript.BaseScopableProcessorExtension;
+import org.alfresco.repo.jscript.ScriptNode;
+import org.alfresco.repo.model.Repository;
+import org.alfresco.service.ServiceRegistry;
+import org.alfresco.service.cmr.repository.NodeRef;
+
+import java.util.List;
+
+public class EmailHelper extends BaseScopableProcessorExtension {
+
+    private ServiceRegistry services;
+    private Repository repositoryHelper;
+
+    public ScriptNode getLocalizedEmailTemplate(String xpath) {
+
+        List<NodeRef> nodes = services.getSearchService().selectNodes(repositoryHelper.getCompanyHome(),
+                xpath, null, services.getNamespaceService(), true);
+
+        if (nodes.size()!=1) {
+            throw new EmailTemplateException("Email template doesn't exist by the following XPATH: " + xpath);
+        }
+
+        NodeRef emailTemplate = services.getFileFolderService().getLocalizedSibling(nodes.get(0));
+
+        return new ScriptNode(emailTemplate, services, this.getScope());
+    }
+
+    public void setServiceRegistry(ServiceRegistry serviceRegistry) {
+        this.services = serviceRegistry;
+    }
+
+    public void setRepositoryHelper(Repository repositoryHelper) {
+        this.repositoryHelper = repositoryHelper;
+    }
+}
diff --git a/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/util/Sudo.java b/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/util/Sudo.java
new file mode 100644
index 0000000..b150cca
--- /dev/null
+++ b/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/util/Sudo.java
@@ -0,0 +1,61 @@
+package com.flexsolution.resetpassword.util;
+
+import org.alfresco.repo.jscript.BaseScopableProcessorExtension;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.tenant.TenantUtil;
+import org.alfresco.util.Pair;
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Function;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+
+
+public class Sudo extends BaseScopableProcessorExtension {
+
+    private final Logger logger = Logger.getLogger(Sudo.class);
+
+    public Object su(final Function func) {
+
+        final Scriptable scope = getScope();
+
+        return su(func, scope);
+    }
+
+    public Object su(final Function func, final Scriptable scope, final Object... objects) {
+
+        final Context cx = Context.getCurrentContext();
+
+        AuthenticationUtil.RunAsWork<Object> raw = new AuthenticationUtil.RunAsWork<Object>() {
+            public Object doWork() throws Exception {
+                return func.call(cx, ScriptableObject.getTopLevelScope(scope), scope, objects);
+            }
+        };
+
+        return AuthenticationUtil.runAs(raw, AuthenticationUtil.getSystemUserName());
+    }
+
+    public Object asTenant(final String requestName, final Function func, final Scriptable scope) {
+
+        if(logger.isDebugEnabled()) {
+            logger.debug("AsTenant starts . Username: " + requestName);
+            logger.debug("AsTenant function " + func.toString());
+        }
+
+        Pair<String, String> userTenant = AuthenticationUtil.getUserTenant(requestName);
+        final String tenantDomain = userTenant.getSecond();
+
+        if (StringUtils.isBlank(tenantDomain)) {
+            return su(func, scope);
+        }
+
+        TenantUtil.TenantRunAsWork<Object> work = new TenantUtil.TenantRunAsWork<Object>() {
+            public Object doWork() throws Exception {
+                return su(func, scope);
+            }
+        };
+
+        return TenantUtil.runAsUserTenant(work, requestName, tenantDomain);
+    }
+}
diff --git a/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/util/TokenGenerator.java b/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/util/TokenGenerator.java
new file mode 100755
index 0000000..1ca5659
--- /dev/null
+++ b/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/util/TokenGenerator.java
@@ -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;
+    }
+}
+
+
diff --git a/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/util/WorkflowHelper.java b/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/util/WorkflowHelper.java
new file mode 100755
index 0000000..f335895
--- /dev/null
+++ b/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/util/WorkflowHelper.java
@@ -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;
+    }
+}
\ No newline at end of file
diff --git a/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/webscripts/ApplyChangedPasswordPost.java b/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/webscripts/ApplyChangedPasswordPost.java
new file mode 100755
index 0000000..39a6f9c
--- /dev/null
+++ b/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/webscripts/ApplyChangedPasswordPost.java
@@ -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;
+    }
+}
\ No newline at end of file
diff --git a/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/webscripts/ResetPasswordPost.java b/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/webscripts/ResetPasswordPost.java
new file mode 100644
index 0000000..fe793a5
--- /dev/null
+++ b/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/webscripts/ResetPasswordPost.java
@@ -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;
+    }
+}
diff --git a/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/webscripts/TaskIsCompletePost.java b/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/webscripts/TaskIsCompletePost.java
new file mode 100755
index 0000000..50d2474
--- /dev/null
+++ b/alfresco-resetpassword/src/main/java/com/flexsolution/resetpassword/webscripts/TaskIsCompletePost.java
@@ -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");
+        }
+    }
+}
\ No newline at end of file
diff --git a/alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/applyChangedPassword/applyChangedPassword.post.desc.xml b/alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/applyChangedPassword/applyChangedPassword.post.desc.xml
new file mode 100644
index 0000000..e27f13a
--- /dev/null
+++ b/alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/applyChangedPassword/applyChangedPassword.post.desc.xml
@@ -0,0 +1,8 @@
+<webscript>
+    <shortname>apply-changed-password</shortname>
+    <description>Apply password changes</description>
+    <url>/com/flex-solution/applyChangedPassword</url>
+    <format default="json"/>
+    <authentication runas="admin">none</authentication>
+    <transaction>required</transaction>
+</webscript>
\ No newline at end of file
diff --git a/alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/applyChangedPassword/applyChangedPassword.post.json.ftl b/alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/applyChangedPassword/applyChangedPassword.post.json.ftl
new file mode 100644
index 0000000..cc97f71
--- /dev/null
+++ b/alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/applyChangedPassword/applyChangedPassword.post.json.ftl
@@ -0,0 +1 @@
+<#--logic in controller-->
\ No newline at end of file
diff --git a/alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/reset-pass-lib.js b/alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/reset-pass-lib.js
new file mode 100644
index 0000000..4dae3c1
--- /dev/null
+++ b/alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/reset-pass-lib.js
@@ -0,0 +1,15 @@
+const PASS_MIN_LENGTH = 3;
+
+function sendCallback(code, message) {
+    status.code = code;
+    status.message = message;
+    status.redirect = true;
+}
+
+function isValid(pass, pass2){
+    if(PASS_MIN_LENGTH > pass.length){
+        return false;
+    }
+
+    return pass.equals(pass2);
+}
\ No newline at end of file
diff --git a/alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/reset-password/reset-password.post.desc.xml b/alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/reset-password/reset-password.post.desc.xml
new file mode 100644
index 0000000..44b2b3d
--- /dev/null
+++ b/alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/reset-password/reset-password.post.desc.xml
@@ -0,0 +1,8 @@
+<webscript>
+    <shortname>reset-password</shortname>
+    <description>Forgot password process webscript </description>
+    <url>/com/flex-solution/reset-password</url>
+    <format default="json"/>
+    <authentication runas="admin">none</authentication>
+    <transaction>required</transaction>
+</webscript>
\ No newline at end of file
diff --git a/alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/reset-password/reset-password.post.json.ftl b/alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/reset-password/reset-password.post.json.ftl
new file mode 100644
index 0000000..cc97f71
--- /dev/null
+++ b/alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/reset-password/reset-password.post.json.ftl
@@ -0,0 +1 @@
+<#--logic in controller-->
\ No newline at end of file
diff --git a/alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/reset-password/reset-password.post.properties b/alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/reset-password/reset-password.post.properties
new file mode 100644
index 0000000..31dcac6
--- /dev/null
+++ b/alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/reset-password/reset-password.post.properties
@@ -0,0 +1,3 @@
+workflow.desc=Reset password workflow
+user.not.found=User disabled or does not exist
+user.email.number.wrong=There are more than one user with same e-mail. Please, enter a user name
\ No newline at end of file
diff --git a/alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/taskIsComplete/taskIsComplete.post.desc.xml b/alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/taskIsComplete/taskIsComplete.post.desc.xml
new file mode 100644
index 0000000..cd1d1d4
--- /dev/null
+++ b/alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/taskIsComplete/taskIsComplete.post.desc.xml
@@ -0,0 +1,8 @@
+<webscript>
+    <shortname>get-userName</shortname>
+    <description>Return change password task assignee</description>
+    <url>/com/flex-solution/taskIsComplete</url>
+    <format default="json"/>
+    <authentication runas="admin">none</authentication>
+    <transaction>required</transaction>
+</webscript>
\ No newline at end of file
diff --git a/alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/taskIsComplete/taskIsComplete.post.json.ftl b/alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/taskIsComplete/taskIsComplete.post.json.ftl
new file mode 100644
index 0000000..cc97f71
--- /dev/null
+++ b/alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/taskIsComplete/taskIsComplete.post.json.ftl
@@ -0,0 +1 @@
+<#--logic in controller-->
\ No newline at end of file
diff --git a/alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/taskIsComplete/taskIsComplete.post.properties b/alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/taskIsComplete/taskIsComplete.post.properties
new file mode 100644
index 0000000..4ce8118
--- /dev/null
+++ b/alfresco-resetpassword/src/main/resources/alfresco/extension/templates/webscripts/com/flex-solution/taskIsComplete/taskIsComplete.post.properties
@@ -0,0 +1 @@
+task.not-exist=Task does not exist
\ No newline at end of file
diff --git a/alfresco-resetpassword/src/main/resources/alfresco/extension/workflow/ResetPasswordProcess.bpmn20.xml b/alfresco-resetpassword/src/main/resources/alfresco/extension/workflow/ResetPasswordProcess.bpmn20.xml
new file mode 100755
index 0000000..f3c2bde
--- /dev/null
+++ b/alfresco-resetpassword/src/main/resources/alfresco/extension/workflow/ResetPasswordProcess.bpmn20.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
+             xmlns:activiti="http://activiti.org/bpmn"
+             xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
+             xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
+             xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema"
+             expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://alfresco.org">
+
+    <process id="resetPasswordFlex" name="Reset Password" isExecutable="true">
+
+        <startEvent id="start"
+                    activiti:formKey="fs-reset:start"/>
+        <sequenceFlow id='flow1'
+                      sourceRef='start'
+                      targetRef='review'/>
+
+        <userTask id="review" name="Review" activiti:formKey="fs-reset:review" activiti:assignee="${bpm_assignee.properties.userName}">
+            <extensionElements>
+
+                <activiti:taskListener event="create"
+                                       class="org.alfresco.repo.workflow.activiti.tasklistener.ScriptTaskListener">
+                    <activiti:field name="script">
+                        <activiti:string><![CDATA[<import resource="classpath:alfresco/extension/workflow/js/reset-password.js">
+                        var obj = new ResetPassword();
+                        sudo.asTenant(bpm_assignee.properties.userName, obj.reviewCreate, obj);
+                        ]]></activiti:string>
+                    </activiti:field>
+                </activiti:taskListener>
+
+            </extensionElements>
+        </userTask>
+
+        <boundaryEvent id="timer" name="Timer" attachedToRef="review">
+            <timerEventDefinition>
+                <timeDuration>PT24H</timeDuration>
+            </timerEventDefinition>
+        </boundaryEvent>
+
+        <sequenceFlow id='flow2'
+                      sourceRef='review'
+                      targetRef='theEnd'/>
+
+        <sequenceFlow id="flow3" sourceRef="timer" targetRef="theEnd"/>
+
+        <endEvent id="theEnd"/>
+
+    </process>
+
+</definitions>
\ No newline at end of file
diff --git a/alfresco-resetpassword/src/main/resources/alfresco/extension/workflow/js/reset-password.js b/alfresco-resetpassword/src/main/resources/alfresco/extension/workflow/js/reset-password.js
new file mode 100755
index 0000000..ebea4ca
--- /dev/null
+++ b/alfresco-resetpassword/src/main/resources/alfresco/extension/workflow/js/reset-password.js
@@ -0,0 +1,49 @@
+<import resource="classpath:alfresco/extension/templates/webscripts/com/flex-solution/reset-pass-lib.js">
+
+var ResetPassword = function () {
+
+    this.reviewCreate = function () {
+        if (typeof bpm_workflowDueDate != 'undefined') task.dueDate = bpm_workflowDueDate;
+        if (typeof bpm_workflowPriority != 'undefined') task.priority = bpm_workflowPriority;
+
+        //Send Email
+        this._sendEmail("reset-password.ftl", bpm_assignee.properties.email, {})
+    };
+
+    this._sendEmail = function (templateName, to) {
+        try {
+            var template = emailHelper.getLocalizedEmailTemplate("app:dictionary/app:email_templates/cm:workflownotification/cm:" + templateName);
+        } catch (e) {
+            throw e.javaException.getMessage();
+        }
+        
+        var mail = actions.create("mail");
+        var currentToken = token.genToken(bpm_assignee.properties.userName);
+
+        mail.parameters.template = template;
+        mail.parameters.to = to;
+        mail.parameters.subject = template.properties.title || "Reset Password";
+        mail.parameters.template_model = this.prepareTemplateProps(bpm_assignee, task, currentToken);
+        task.setVariable("fs-reset:token", currentToken);
+
+        try {
+
+            mail.execute(bpm_assignee);
+
+        } catch (ex) {
+
+            throw "error.outBound";
+        }
+
+    };
+
+    this.prepareTemplateProps = function (assignee, task, currentToken) {
+        var templateProps = {};
+        templateProps["assignee"] = {
+            firstname: assignee.properties["firstName"],
+            lastname: assignee.properties["lastName"]
+        };
+        templateProps["token"] = currentToken;
+        return templateProps;
+    }
+};
\ No newline at end of file
diff --git a/alfresco-resetpassword/src/main/resources/alfresco/extension/workflow/reset-workflow-model.xml b/alfresco-resetpassword/src/main/resources/alfresco/extension/workflow/reset-workflow-model.xml
new file mode 100755
index 0000000..34513d6
--- /dev/null
+++ b/alfresco-resetpassword/src/main/resources/alfresco/extension/workflow/reset-workflow-model.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<model name="fs-reset:resetWorkflowModel" xmlns="http://www.alfresco.org/model/dictionary/1.0">
+
+    <description>Reset Workflow Model</description>
+    <author>s.palyukh</author>
+    <version>1.0</version>
+
+    <imports>
+        <!-- Import Alfresco Dictionary Definitions -->
+        <import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d"/>
+        <!-- Import Alfresco Workflow Model Definitions -->
+        <import uri="http://www.alfresco.org/model/bpm/1.0" prefix="bpm"/>
+    </imports>
+
+    <namespaces>
+        <namespace uri="https://flex-solution.com/model/workflow/1.0" prefix="fs-reset"/>
+    </namespaces>
+
+    <types>
+
+        <type name="fs-reset:start">
+            <parent>bpm:startTask</parent>
+            <mandatory-aspects>
+                <aspect>bpm:assignee</aspect>
+            </mandatory-aspects>
+        </type>
+
+        <type name="fs-reset:review">
+            <title>Review</title>
+            <parent>bpm:workflowTask</parent>
+            <properties>
+                <property name="fs-reset:token">
+                    <type>d:text</type>
+                    <mandatory>true</mandatory>
+                </property>
+
+            </properties>
+            <overrides>
+                <property name="bpm:reassignable">
+                    <default>false</default>
+                </property>
+            </overrides>
+        </type>
+    </types>
+</model>
+
diff --git a/alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/alfresco-global.properties b/alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/alfresco-global.properties
new file mode 100755
index 0000000..61fa983
--- /dev/null
+++ b/alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/alfresco-global.properties
@@ -0,0 +1,4 @@
+alfresco.host=localhost
+share.host=localhost
+
+fs.passreset.salt=$2a$11$sGL4KAlXWsxR3ysNL8HOmu
\ No newline at end of file
diff --git a/alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/context/bootstrap-context.xml b/alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/context/bootstrap-context.xml
new file mode 100644
index 0000000..37f7fd3
--- /dev/null
+++ b/alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/context/bootstrap-context.xml
@@ -0,0 +1,25 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+          http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
+
+    <bean id="patch.copyEmailTemplate" class="com.flexsolution.resetpassword.patch.CopyStructurePatch"
+          parent="basePatch">
+        <property name="id" value="patch.copyEmailTemplate"/>
+        <property name="description" value="Copy EmailTemplate"/>
+        <property name="fixesFromSchema" value="0"/>
+        <property name="fixesToSchema" value="${version.schema}"/>
+        <property name="targetSchema" value="100005"/>
+        <property name="fileFolderService" ref="FileFolderService"/>
+        <property name="repository" ref="repositoryHelper"/>
+        <property name="contentService" ref="ContentService"/>
+        <property name="mimetypeService" ref="mimetypeService"/>
+        <property name="globalProps" ref="global-properties"/>
+        <property name="classpathLocaiton" value="alfresco/module/resetPassword/bootstrap/emailTemplate"/>
+        <property name="copyToXpath" value="/${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.templates.email.childname}/cm:workflownotification"/>
+
+    </bean>
+
+
+</beans>
diff --git a/alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/context/service-context.xml b/alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/context/service-context.xml
new file mode 100755
index 0000000..09e51be
--- /dev/null
+++ b/alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/context/service-context.xml
@@ -0,0 +1,33 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
+<beans>
+
+    <bean class="com.flexsolution.resetpassword.util.EmailHelper" parent="baseJavaScriptExtension">
+        <property name="extensionName" value="emailHelper"/>
+        <property name="serviceRegistry" ref="ServiceRegistry"/>
+        <property name="repositoryHelper" ref="repositoryHelper"/>
+    </bean>
+
+    <bean class="com.flexsolution.resetpassword.services.script.ScriptPasswordService" parent="baseJavaScriptExtension">
+        <property name="extensionName" value="passwordService"/>
+        <property name="authenticationService" ref="AuthenticationService" />
+    </bean>
+
+    <bean id="genToken" parent="baseJavaScriptExtension" class="com.flexsolution.resetpassword.util.TokenGenerator">
+        <property name="extensionName">
+            <value>token</value>
+        </property>
+        <property name="globalProps" ref="global-properties"/>
+    </bean>
+
+    <bean class="com.flexsolution.resetpassword.util.WorkflowHelper">
+        <property name="workflowService" ref="WorkflowService"/>
+        <property name="activitiHistoryService" ref="activitiHistoryService"/>
+    </bean>
+
+
+    <bean id="resetPassword.Sudo" class="com.flexsolution.resetpassword.util.Sudo" parent="baseJavaScriptExtension">
+        <property name="extensionName" value="sudo"/>
+    </bean>
+
+</beans>
\ No newline at end of file
diff --git a/alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/context/webscript-context.xml b/alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/context/webscript-context.xml
new file mode 100755
index 0000000..6a66b90
--- /dev/null
+++ b/alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/context/webscript-context.xml
@@ -0,0 +1,19 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
+
+<beans>
+    <bean id="webscript.com.flex-solution.reset-password.reset-password.post" parent="webscript" class="com.flexsolution.resetpassword.webscripts.ResetPasswordPost">
+        <property name="workflowService" ref="WorkflowService"/>
+        <property name="personService" ref="PersonService"/>
+    </bean>
+
+    <bean id="webscript.com.flex-solution.taskIsComplete.taskIsComplete.post" parent="webscript" class="com.flexsolution.resetpassword.webscripts.TaskIsCompletePost">
+    </bean>
+
+    <bean id="webscript.com.flex-solution.applyChangedPassword.applyChangedPassword.post" parent="webscript" class="com.flexsolution.resetpassword.webscripts.ApplyChangedPasswordPost">
+        <property name="workflowService" ref="WorkflowService"/>
+        <property name="authenticationService" ref="AuthenticationService" />
+        <property name="tokenGenerator" ref="genToken" />
+    </bean>
+
+</beans>
\ No newline at end of file
diff --git a/alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/context/workflow-context.xml b/alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/context/workflow-context.xml
new file mode 100644
index 0000000..cb71540
--- /dev/null
+++ b/alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/context/workflow-context.xml
@@ -0,0 +1,23 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
+
+<beans>
+
+    <bean id="resetPassword.workflowBootstrap" parent="workflowDeployer">
+        <property name="workflowDefinitions">
+            <list>
+                <props>
+                    <prop key="engineId">activiti</prop>
+                    <prop key="location">alfresco/extension/workflow/ResetPasswordProcess.bpmn20.xml</prop>
+                    <prop key="mimetype">text/xml</prop>
+                    <prop key="redeploy">false</prop>
+                </props>
+            </list>
+        </property>
+        <property name="models">
+            <list>
+                <value>alfresco/extension/workflow/reset-workflow-model.xml</value>
+            </list>
+        </property>
+    </bean>
+</beans>
diff --git a/alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/module-context.xml b/alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/module-context.xml
new file mode 100755
index 0000000..3881ef2
--- /dev/null
+++ b/alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/module-context.xml
@@ -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>
diff --git a/alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/module.properties b/alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/module.properties
new file mode 100644
index 0000000..9663ecf
--- /dev/null
+++ b/alfresco-resetpassword/src/main/resources/alfresco/module/resetPassword/module.properties
@@ -0,0 +1,4 @@
+module.id=${project.artifactId}
+module.title=${project.name}
+module.description=${project.description}
+module.version=${project.version}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..4bea08e
--- /dev/null
+++ b/pom.xml
@@ -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>
\ No newline at end of file
diff --git a/readme_resources/1-config.png b/readme_resources/1-config.png
new file mode 100644
index 0000000..e789d2a
Binary files /dev/null and b/readme_resources/1-config.png differ
diff --git a/readme_resources/1-configuration.png b/readme_resources/1-configuration.png
new file mode 100644
index 0000000..e5ad538
Binary files /dev/null and b/readme_resources/1-configuration.png differ
diff --git a/readme_resources/2-config.png b/readme_resources/2-config.png
new file mode 100644
index 0000000..4128fc6
Binary files /dev/null and b/readme_resources/2-config.png differ
diff --git a/readme_resources/2-configuration.png b/readme_resources/2-configuration.png
new file mode 100644
index 0000000..049da7a
Binary files /dev/null and b/readme_resources/2-configuration.png differ
diff --git a/readme_resources/3-config.png b/readme_resources/3-config.png
new file mode 100644
index 0000000..bdbdf1e
Binary files /dev/null and b/readme_resources/3-config.png differ
diff --git a/readme_resources/3-configuration.png b/readme_resources/3-configuration.png
new file mode 100644
index 0000000..2625874
Binary files /dev/null and b/readme_resources/3-configuration.png differ
diff --git a/readme_resources/4-config.png b/readme_resources/4-config.png
new file mode 100644
index 0000000..6730e57
Binary files /dev/null and b/readme_resources/4-config.png differ
diff --git a/readme_resources/4-configuration.png b/readme_resources/4-configuration.png
new file mode 100644
index 0000000..2adaea7
Binary files /dev/null and b/readme_resources/4-configuration.png differ
diff --git a/share-resetpassword/Dockerfile b/share-resetpassword/Dockerfile
new file mode 100644
index 0000000..cf5da8e
--- /dev/null
+++ b/share-resetpassword/Dockerfile
@@ -0,0 +1,13 @@
+FROM alfresco/alfresco-share:6.2.0
+
+ARG TOMCAT_DIR=/usr/local/tomcat
+
+USER root
+
+
+# Copy Dockerfile to avoid an error if no AMPs exist
+COPY Dockerfile _amps/*.amp $TOMCAT_DIR/amps_share/
+RUN java -jar $TOMCAT_DIR/alfresco-mmt/alfresco-mmt*.jar install \
+              $TOMCAT_DIR/amps_share $TOMCAT_DIR/webapps/share -directory -nobackup -force
+
+USER ${USERNAME}
\ No newline at end of file
diff --git a/share-resetpassword/_amps/README.txt b/share-resetpassword/_amps/README.txt
new file mode 100644
index 0000000..e69de29
diff --git a/share-resetpassword/_lib/README.txt b/share-resetpassword/_lib/README.txt
new file mode 100644
index 0000000..e69de29
diff --git a/share-resetpassword/amp.xml b/share-resetpassword/amp.xml
new file mode 100644
index 0000000..49896c4
--- /dev/null
+++ b/share-resetpassword/amp.xml
@@ -0,0 +1,48 @@
+<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/module.properties</source>
+            <filtered>true</filtered>
+        </file>
+        <file>
+            <source>src/main/resources/file-mapping.properties</source>
+            <filtered>true</filtered>
+        </file>
+    </files>
+
+    <fileSets>
+        <fileSet>
+            <directory>src/main/config</directory>
+            <outputDirectory>config</outputDirectory>
+            <filtered>false</filtered>
+        </fileSet>
+        <fileSet>
+            <directory>src/main/resources/web</directory>
+            <outputDirectory>web</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>
\ No newline at end of file
diff --git a/share-resetpassword/context.xml b/share-resetpassword/context.xml
new file mode 100644
index 0000000..7c59a54
--- /dev/null
+++ b/share-resetpassword/context.xml
@@ -0,0 +1,16 @@
+<?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/share/development/classes"
+                      webAppMount="/WEB-INF/classes"/>
+        <PreResources className="org.apache.catalina.webresources.DirResourceSet"
+                      base="/usr/local/tomcat/webapps/share/development/web"
+                      webAppMount="/"/>
+        <JarResources className="org.apache.catalina.webresources.DirResourceSet"
+                      base="/usr/local/tomcat/webapps/share/development/lib"
+                      webAppMount="/WEB-INF/lib"/>
+    </Resources>
+</Context>
+
diff --git a/share-resetpassword/pom.xml b/share-resetpassword/pom.xml
new file mode 100644
index 0000000..87a15c5
--- /dev/null
+++ b/share-resetpassword/pom.xml
@@ -0,0 +1,133 @@
+<?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-share</artifactId>
+    <version>1.2.0.RC1</version>
+    <name>Reset Password Share AMP</name>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.alfresco</groupId>
+            <artifactId>share</artifactId>
+            <version>6.0.b</version>
+            <scope>provided</scope>
+            <type>pom</type>
+        </dependency>
+        <dependency>
+            <groupId>org.alfresco</groupId>
+            <artifactId>share</artifactId>
+            <version>6.0.b</version>
+            <scope>provided</scope>
+            <type>war</type>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+                <filtering>true</filtering>
+                <excludes>
+                    <exclude>web/**</exclude>
+                </excludes>
+            </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-resources-plugin</artifactId>
+                <version>3.0.2</version>
+                <executions>
+                    <execution>
+                        <id>copy-resources</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>copy-resources</goal>
+                        </goals>
+                        <configuration>
+                            <outputDirectory>${project.build.outputDirectory}</outputDirectory>
+                            <resources>
+                                <resource>
+                                    <directory>src/main/resources/web</directory>
+                                    <includes>
+                                        <include>**/**</include>
+                                    </includes>
+                                    <targetPath>../web</targetPath>
+                                    <filtering>false</filtering>
+                                </resource>
+                            </resources>
+                        </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>
diff --git a/share-resetpassword/src/main/config/README.txt b/share-resetpassword/src/main/config/README.txt
new file mode 100644
index 0000000..e69de29
diff --git a/share-resetpassword/src/main/resources/alfresco/web-extension/messages/resetPassword-share.properties b/share-resetpassword/src/main/resources/alfresco/web-extension/messages/resetPassword-share.properties
new file mode 100644
index 0000000..a4f6d56
--- /dev/null
+++ b/share-resetpassword/src/main/resources/alfresco/web-extension/messages/resetPassword-share.properties
@@ -0,0 +1,30 @@
+form.label.new-password=Enter new password
+form.help.new-password=Please concoct new password and type it
+form.label.new-password-confirm=Confirm password
+form.help.new-password-confirm=Please enter your password again
+form.field.length=Password length must be  more than 2 characters
+form.field.confirm=Confirm password does not match password
+form.field.mandatory=Field can not be empty
+form.success.message=Password has been changed
+form.button.submit=Submit
+form.button.cancel=Cancel
+form.button.sign.in=Sign in
+
+prop_fs-reset_password=New password
+prop_fs-reset_confirmPass=Confirm password
+
+button.resetPassword=Forgot Password?
+dialog.title=Reset Password Dialog
+dialog.text=Username:
+dialog.okButton.label=Reset
+spinner.message=Please wait
+error.title=Failure
+error.userNotFound=Seems, there is no user with appropriate username
+error.outBound=Failed to send email. Please, check outbound email configuration.
+error.password=Confirm password does not match password or password length too short
+error.server-side=Sorry. Internal error. Try later
+success.text=Please check your email. You should receive email with instructions how to reset password
+
+page.updatepassword.title=Update user password
+page.updatepassword.description=Page allows user reset his password
+
diff --git a/share-resetpassword/src/main/resources/alfresco/web-extension/messages/resetPassword-share_de.properties b/share-resetpassword/src/main/resources/alfresco/web-extension/messages/resetPassword-share_de.properties
new file mode 100644
index 0000000..8113c61
--- /dev/null
+++ b/share-resetpassword/src/main/resources/alfresco/web-extension/messages/resetPassword-share_de.properties
@@ -0,0 +1,3 @@
+button.resetPassword=Passwort vergessen?
+error.userNotFound=Anscheinend gibt es keinen Benutzer mit dem entsprechenden Benutzernamen
+error.outBound=E-Mail konnte nicht gesendet werden. Bitte überprüfen Sie die Konfiguration der ausgehenden E-Mails.
\ No newline at end of file
diff --git a/share-resetpassword/src/main/resources/alfresco/web-extension/share-resetpassword-slingshot-application-context.xml b/share-resetpassword/src/main/resources/alfresco/web-extension/share-resetpassword-slingshot-application-context.xml
new file mode 100644
index 0000000..1b0e911
--- /dev/null
+++ b/share-resetpassword/src/main/resources/alfresco/web-extension/share-resetpassword-slingshot-application-context.xml
@@ -0,0 +1,27 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+                http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
+
+
+    <bean id="com.flexsolution.resetPassword-share.config" class="org.springframework.extensions.config.ConfigBootstrap"
+          init-method="register">
+        <property name="configService" ref="web.config"/>
+        <property name="configs">
+            <list>
+                <value>classpath:alfresco/web-extension/share-resetpassword/share-config-custom.xml</value>
+            </list>
+        </property>
+    </bean>
+
+    <bean id="com.flexsolution.resetPassword-share.resources"
+          class="org.springframework.extensions.surf.util.ResourceBundleBootstrapComponent">
+        <property name="resourceBundles">
+            <list>
+                <value>alfresco.web-extension.messages.resetPassword-share</value>
+            </list>
+        </property>
+    </bean>
+
+</beans>
\ No newline at end of file
diff --git a/share-resetpassword/src/main/resources/alfresco/web-extension/share-resetpassword/share-config-custom.xml b/share-resetpassword/src/main/resources/alfresco/web-extension/share-resetpassword/share-config-custom.xml
new file mode 100755
index 0000000..ea5c222
--- /dev/null
+++ b/share-resetpassword/src/main/resources/alfresco/web-extension/share-resetpassword/share-config-custom.xml
@@ -0,0 +1,36 @@
+<alfresco-config>
+
+    <config evaluator="string-compare" condition="Workflow">
+        <hidden-workflows>
+            <workflow name="activiti$resetPasswordFlex"/>
+        </hidden-workflows>
+        <hidden-tasks>
+            <task type="fs-reset:*"/>
+        </hidden-tasks>
+    </config>
+
+    <config evaluator="string-compare" condition="activiti$resetPasswordFlex">
+        <forms>
+            <form>
+                <field-visibility>
+                    <show id="bpm:workflowDescription"/>
+                    <show id="bpm:assignee"/>
+                </field-visibility>
+            </form>
+        </forms>
+    </config>
+
+    <config evaluator="task-type" condition="fs-reset:review">
+        <forms>
+            <form>
+                <field-visibility>
+                    <show id="transitions"/>
+                </field-visibility>
+
+                <appearance>
+                    <field id="transitions"/>
+                </appearance>
+            </form>
+        </forms>
+    </config>
+</alfresco-config>
diff --git a/share-resetpassword/src/main/resources/alfresco/web-extension/site-data/extensions/resetPassword-extensions.xml b/share-resetpassword/src/main/resources/alfresco/web-extension/site-data/extensions/resetPassword-extensions.xml
new file mode 100644
index 0000000..bac7dce
--- /dev/null
+++ b/share-resetpassword/src/main/resources/alfresco/web-extension/site-data/extensions/resetPassword-extensions.xml
@@ -0,0 +1,15 @@
+<extension>
+  <modules>
+    <module>
+      <id>Reset-pass patch</id>
+      <version>1.0</version>
+      <auto-deploy>true</auto-deploy>
+      <customizations>
+        <customization>
+          <targetPackageRoot>org.alfresco.components.guest</targetPackageRoot>
+          <sourcePackageRoot>com.flex-solution.resetPassword.components.guest</sourcePackageRoot>
+        </customization>
+      </customizations>
+    </module>
+  </modules>
+</extension>
diff --git a/share-resetpassword/src/main/resources/alfresco/web-extension/site-data/pages/changePassWF.xml b/share-resetpassword/src/main/resources/alfresco/web-extension/site-data/pages/changePassWF.xml
new file mode 100644
index 0000000..1e4154f
--- /dev/null
+++ b/share-resetpassword/src/main/resources/alfresco/web-extension/site-data/pages/changePassWF.xml
@@ -0,0 +1,8 @@
+<page>
+    <title>Change password</title>
+    <!--<title-id>page.changePassword.title</title-id>-->
+    <description>Page for changing password </description>
+    <!--<description-id>page.changePassword.description</description-id>-->
+    <template-instance>changePassWF</template-instance>
+    <authentication>none</authentication>
+</page>
\ No newline at end of file
diff --git a/share-resetpassword/src/main/resources/alfresco/web-extension/site-data/pages/update-password.xml b/share-resetpassword/src/main/resources/alfresco/web-extension/site-data/pages/update-password.xml
new file mode 100644
index 0000000..0069ce1
--- /dev/null
+++ b/share-resetpassword/src/main/resources/alfresco/web-extension/site-data/pages/update-password.xml
@@ -0,0 +1,8 @@
+<page>
+    <title>Update password page</title>
+    <title-id>page.updatepassword.title</title-id>
+    <description>Page allows user reset his password</description>
+    <description-id>page.updatepassword.description</description-id>
+    <template-instance>update-password</template-instance>
+    <authentication>none</authentication>
+</page>
diff --git a/share-resetpassword/src/main/resources/alfresco/web-extension/site-data/template-instances/changePassWF.xml b/share-resetpassword/src/main/resources/alfresco/web-extension/site-data/template-instances/changePassWF.xml
new file mode 100644
index 0000000..de5bdb1
--- /dev/null
+++ b/share-resetpassword/src/main/resources/alfresco/web-extension/site-data/template-instances/changePassWF.xml
@@ -0,0 +1,13 @@
+<template-instance>
+    <template-type>com/flex-solution/changePassWF/changePassWF</template-type>
+
+    <components>
+        <component>
+            <scope>template</scope>
+            <region-id>changePassForm</region-id>
+            <source-id>changePassForm</source-id>
+            <url>/com/flex-solution/resetPassword/changePassWF</url>
+        </component>
+    </components>
+
+</template-instance>
\ No newline at end of file
diff --git a/share-resetpassword/src/main/resources/alfresco/web-extension/site-data/template-instances/update-password.xml b/share-resetpassword/src/main/resources/alfresco/web-extension/site-data/template-instances/update-password.xml
new file mode 100644
index 0000000..b352ff8
--- /dev/null
+++ b/share-resetpassword/src/main/resources/alfresco/web-extension/site-data/template-instances/update-password.xml
@@ -0,0 +1,3 @@
+<template-instance>
+    <template-type>com/flex-solution/password/update-password</template-type>
+</template-instance>
\ No newline at end of file
diff --git a/share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/com/flex-solution/resetPassword/changePassWF/changePassWF.get.desc.xml b/share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/com/flex-solution/resetPassword/changePassWF/changePassWF.get.desc.xml
new file mode 100644
index 0000000..afd0b40
--- /dev/null
+++ b/share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/com/flex-solution/resetPassword/changePassWF/changePassWF.get.desc.xml
@@ -0,0 +1,7 @@
+<webscript>
+    <shortname>Change password form</shortname>
+    <description>Return change password form</description>
+    <url>/com/flex-solution/resetPassword/changePassWF</url>
+    <authentication>none</authentication>
+    <format default="html"/>
+</webscript>
\ No newline at end of file
diff --git a/share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/com/flex-solution/resetPassword/changePassWF/changePassWF.get.html.ftl b/share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/com/flex-solution/resetPassword/changePassWF/changePassWF.get.html.ftl
new file mode 100755
index 0000000..2b0099c
--- /dev/null
+++ b/share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/com/flex-solution/resetPassword/changePassWF/changePassWF.get.html.ftl
@@ -0,0 +1,60 @@
+<#if !showError>
+
+    <#assign id="rp-comp"/>
+
+
+    <@link href="${url.context}/res/extras/com/flex-solution/components/changePassWF.css" group="form"/>
+    <@script type="text/javascript" src="${url.context}/res/components/form/form.js" group="form"/>
+    <@script type="text/javascript" src="${url.context}/res/extras/com/flex-solution/components/changePassWF.js"/>
+
+
+
+<script type="text/javascript">//<![CDATA[
+
+new Alfresco.component.ChangePassWF("${id}");
+
+//]]></script>
+
+
+<div id="${id}-parent" class="parent-block">
+
+<div id="${id}" class="fields">
+    <form id="${id}-form" class="formaa"
+          action="${url.context}/proxy/alfresco-noauth/com/flex-solution/applyChangedPassword"
+          method="post">
+        <div id="comp-logo" class="theme-company-logo logo-com"></div>
+        <div class="row">
+                        <span class="label"><label for="${id}-new-password">${msg("form.label.new-password")}
+                            :</label></span>
+            <span class="formField"><input type="password" maxlength="255" id="${id}-new-password" name="new-password"/></span>
+        </div>
+        <div class="row">
+                        <span class="label"><label
+                                for="${id}-new-password-confirm">${msg("form.label.new-password-confirm")}
+                            :</label></span>
+            <span class="formField"><input type="password" maxlength="255" id="${id}-new-password-confirm"
+                                           name="new-password-confirm"/></span>
+        </div>
+        <input type="hidden" name="userToken" value="${token}">
+
+        <div class="buttons">
+            <button id="${id}-button-ok" name="save">${msg("form.button.submit")}</button>
+            <button id="${id}-button-cancel" name="cancel">${msg("form.button.cancel")}</button>
+        </div>
+    </form>
+</div>
+</div>
+
+
+<#else>
+<script type="application/javascript">
+    window.location.href = Alfresco.constants.URL_CONTEXT + "page/error404";
+</script>
+</#if>
+
+
+
+
+
+
+
diff --git a/share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/com/flex-solution/resetPassword/changePassWF/changePassWF.get.js b/share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/com/flex-solution/resetPassword/changePassWF/changePassWF.get.js
new file mode 100755
index 0000000..d3f1ff0
--- /dev/null
+++ b/share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/com/flex-solution/resetPassword/changePassWF/changePassWF.get.js
@@ -0,0 +1,11 @@
+model.token = page.url.args["token"];
+
+var jsonObj = {
+    token: model.token
+};
+
+var connector = remote.connect("alfresco-noauth");
+
+var resp = connector.post("/com/flex-solution/taskIsComplete", jsonUtils.toJSONString(jsonObj), "application/json");
+
+model.showError = resp.status == 500;
\ No newline at end of file
diff --git a/share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/com/flex-solution/resetPassword/components/form/controls/passConfirmation.ftl b/share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/com/flex-solution/resetPassword/components/form/controls/passConfirmation.ftl
new file mode 100644
index 0000000..d057164
--- /dev/null
+++ b/share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/com/flex-solution/resetPassword/components/form/controls/passConfirmation.ftl
@@ -0,0 +1,39 @@
+
+<div class="form-field">
+<#if form.mode == "view">
+    <div class="viewmode-field">
+        <#if field.mandatory && !(field.value?is_number) && field.value == "">
+        <span class="incomplete-warning"><img src="${url.context}/res/components/form/images/warning-16.png" title="${msg("form.field.incomplete")}" /><span>
+        </#if>
+        <span class="viewmode-label">${field.label?html}:</span>
+        <#if field.control.params.activateLinks?? && field.control.params.activateLinks == "true">
+            <#assign fieldValue=field.value?html?replace("((http|ftp|https):\\/\\/[\\w\\-_]+(\\.[\\w\\-_]+)+([\\w\\-\\.,@?\\^=%&:\\/~\\+#]*[\\w\\-\\@?\\^=%&\\/~\\+#])?)", "<a href=\"$1\" target=\"_blank\">$1</a>", "r")>
+        <#else>
+            <#if field.value?is_number>
+                <#assign fieldValue=field.value?c>
+            <#else>
+                <#assign fieldValue=field.value?html>
+            </#if>
+        </#if>
+        <span class="viewmode-value"><#if fieldValue == "">${msg("form.control.novalue")}<#else>${fieldValue}</#if></span>
+    </div>
+<#else>
+    <label for="${fieldHtmlId}">${field.label?html}:<#if field.mandatory><span class="mandatory-indicator">${msg("form.required.fields.marker")}</span></#if></label>
+    <input id="${fieldHtmlId}" name="${field.name}" tabindex="0"
+           <#if field.control.params.password??>type="password"<#else>type="text"</#if>
+           <#if field.control.params.styleClass??>class="${field.control.params.styleClass}"</#if>
+           <#if field.control.params.style??>style="${field.control.params.style}"</#if>
+           <#if field.value?is_number>value="${field.value?c}"<#else>value="${field.value?html}"</#if>
+           <#if field.description??>title="${field.description}"</#if>
+           <#if field.control.params.maxLength??>maxlength="${field.control.params.maxLength}"<#else>maxlength="1024"</#if>
+           <#if field.control.params.size??>size="${field.control.params.size}"</#if>
+           <#if field.disabled && !(field.control.params.forceEditable?? && field.control.params.forceEditable == "true")>disabled="true"</#if> />
+    <@formLib.renderFieldHelp field=field />
+</#if>
+</div>
+
+<@script type="text/javascript" src="${url.context}/res/extras/com/flex-solution/components/confirmPassword.js"/>
+
+<script type="application/javascript">//<![CDATA[
+    new Alfresco.component.ConfirmPassword("${fieldHtmlId}");
+//]]></script>
\ No newline at end of file
diff --git a/share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/com/flex-solution/resetPassword/components/form/controls/wfTransitions.ftl b/share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/com/flex-solution/resetPassword/components/form/controls/wfTransitions.ftl
new file mode 100644
index 0000000..e8ed1bb
--- /dev/null
+++ b/share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/com/flex-solution/resetPassword/components/form/controls/wfTransitions.ftl
@@ -0,0 +1,18 @@
+<@script type="text/javascript" src="${url.context}/res/extras/com/flex-solution/components/wfTransitions.js"/>
+
+<#if form.mode == "edit" >
+<script type="text/javascript">//<![CDATA[
+(function()
+{
+    new Alfresco.Transitions.ChangePassword("${fieldHtmlId}").setOptions(
+            {
+                currentValue: "${field.value?js_string}"
+            }).setMessages(${messages});
+})();
+//]]></script>
+
+<div class="form-field suggested-actions" id="${fieldHtmlId}">
+    <div id="${fieldHtmlId}-buttons">
+    </div>
+</div>
+</#if>
\ No newline at end of file
diff --git a/share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/com/flex-solution/resetPassword/components/guest/login.get.html.ftl b/share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/com/flex-solution/resetPassword/components/guest/login.get.html.ftl
new file mode 100644
index 0000000..f7ddf66
--- /dev/null
+++ b/share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/com/flex-solution/resetPassword/components/guest/login.get.html.ftl
@@ -0,0 +1,20 @@
+<@markup id="reset-pass-css" target="js" action="after">
+<#-- CSS Dependencies -->
+    <@link href="${url.context}/res/extras/com/flex-solution/components/ResetPassword.css" group="login"/>
+
+<script type="text/javascript">
+    new FlexSolution.component.ResetPassword("${args.htmlid}-resetPassword");
+</script>
+</@markup>
+
+<@markup id="reset-pass-js">
+<#-- JavaScript Dependencies -->
+    <@script src="${url.context}/res/extras/com/flex-solution/components/ResetPassword.js" group="login"/>
+</@markup>
+
+<@markup id="reset-password" target="buttons" action="after" scope="page">
+<div class="form-field">
+    <input type="button" id="${args.htmlid}-resetPassword" class="login-button hidden"
+           value="${msg("button.resetPassword")}"/>
+</div>
+</@>
diff --git a/share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/org/alfresco/README.md b/share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/org/alfresco/README.md
new file mode 100644
index 0000000..f2c0a4f
--- /dev/null
+++ b/share-resetpassword/src/main/resources/alfresco/web-extension/site-webscripts/org/alfresco/README.md
@@ -0,0 +1,6 @@
+## If you are overriding out-of-the-box Alfresco web scripts, put these files here
+## in the correct org.alfresco... package.
+## If you are defining a new custom web script, add it under
+## resources/alfresco/web-extension/site-webscripts/{your domain path}.
+##
+
diff --git a/share-resetpassword/src/main/resources/alfresco/web-extension/templates/com/flex-solution/changePassWF/changePassWF.ftl b/share-resetpassword/src/main/resources/alfresco/web-extension/templates/com/flex-solution/changePassWF/changePassWF.ftl
new file mode 100644
index 0000000..976a7af
--- /dev/null
+++ b/share-resetpassword/src/main/resources/alfresco/web-extension/templates/com/flex-solution/changePassWF/changePassWF.ftl
@@ -0,0 +1,10 @@
+<#include "/org/alfresco/include/alfresco-template.ftl" />
+
+<@templateHeader/>
+
+<@templateBody type="alfresco-guest">
+    <@region id="changePassForm" scope="template" />
+</@>
+
+<@templateFooter/>
+
diff --git a/share-resetpassword/src/main/resources/alfresco/web-extension/templates/com/flex-solution/login/simple-guest.ftl b/share-resetpassword/src/main/resources/alfresco/web-extension/templates/com/flex-solution/login/simple-guest.ftl
new file mode 100644
index 0000000..2acb5ab
--- /dev/null
+++ b/share-resetpassword/src/main/resources/alfresco/web-extension/templates/com/flex-solution/login/simple-guest.ftl
@@ -0,0 +1 @@
+<@region id="extraRegion" target="components" action="before" scope="page" />
\ No newline at end of file
diff --git a/share-resetpassword/src/main/resources/alfresco/web-extension/templates/com/flex-solution/password/update-password.ftl b/share-resetpassword/src/main/resources/alfresco/web-extension/templates/com/flex-solution/password/update-password.ftl
new file mode 100644
index 0000000..8df7b3b
--- /dev/null
+++ b/share-resetpassword/src/main/resources/alfresco/web-extension/templates/com/flex-solution/password/update-password.ftl
@@ -0,0 +1,21 @@
+<#include "/org/alfresco/include/alfresco-template.ftl" />
+<@templateHeader></@>
+<@templateBody>
+    <@markup id="alf-hd">
+    <div id="alf-hd">
+        <@region scope="global" id="share-header" chromeless="true"/>
+    </div>
+    </@>
+    <@markup id="bd">
+    <div id="bd">
+        <h1>This is just a test page. Hello World!</h1>
+    </div>
+    </@>
+</@>
+<@templateFooter>
+    <@markup id="alf-ft">
+    <div id="alf-ft">
+        <@region id="footer" scope="global" />
+    </div>
+    </@>
+</@>
\ No newline at end of file
diff --git a/share-resetpassword/src/main/resources/file-mapping.properties b/share-resetpassword/src/main/resources/file-mapping.properties
new file mode 100644
index 0000000..6acc63f
--- /dev/null
+++ b/share-resetpassword/src/main/resources/file-mapping.properties
@@ -0,0 +1,14 @@
+# Custom AMP to WAR location mappings
+
+#
+# The following property can be used to include the standard set of mappings.
+# The contents of this file will override any defaults.  The default is
+# 'true', i.e. the default mappings will be augmented or modified by values in
+# this file.
+#
+include.default=true
+
+#
+# Custom mappings.  If 'include.default' is false, then this is the complete set.
+#
+/web=/
diff --git a/share-resetpassword/src/main/resources/module.properties b/share-resetpassword/src/main/resources/module.properties
new file mode 100644
index 0000000..2afa1ba
--- /dev/null
+++ b/share-resetpassword/src/main/resources/module.properties
@@ -0,0 +1,48 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+
+# SDK Sample module
+
+# ==== Beginning of Alfresco required/optional properties ====== #
+# NB: These properties are filtered at build time by Maven, single
+# sourcing from POM properties  
+module.id=${project.artifactId}
+#module.aliases=myModule-123, my-module
+module.title=${project.name}
+module.description=${project.description}
+module.version=${noSnapshotVersion}
+
+# The following optional properties can be used to prevent the module from being added
+# to inappropriate versions of the WAR file.
+# module.repo.version.min=2.0
+# module.repo.version.max=2.1
+
+# FIXME: This dependencies should come out of mvn dependencies on amp
+
+# The following describe dependencies on other modules
+# Depends on net.sf.myproject.module.SupportModuleA version ${version} or later
+# module.depends.net.sf.myproject.module.SupportModuleA=${version}-*
+# Depends on net.sf.myproject.module.SupportModuleA version ${version} to 2.0
+# module.depends.net.sf.myproject.module.SupportModuleB=${version}-2.0
+# Depends on net.sf.myproject.module.SupportModuleC - any version
+# module.depends.net.sf.myproject.module.SupportModuleB=*
+
+
+# ==== End of Alfresco required/optional properties ======= #
+
+
+# ==== Beginning of module required properties/optional ====== #
\ No newline at end of file
diff --git a/share-resetpassword/src/main/resources/web/extras/com/flex-solution/components/ResetPassword.css b/share-resetpassword/src/main/resources/web/extras/com/flex-solution/components/ResetPassword.css
new file mode 100644
index 0000000..c88e407
--- /dev/null
+++ b/share-resetpassword/src/main/resources/web/extras/com/flex-solution/components/ResetPassword.css
@@ -0,0 +1,5 @@
+.copy{
+    text-align: center;
+    position: relative;
+    top: -30px;
+}
\ No newline at end of file
diff --git a/share-resetpassword/src/main/resources/web/extras/com/flex-solution/components/ResetPassword.js b/share-resetpassword/src/main/resources/web/extras/com/flex-solution/components/ResetPassword.js
new file mode 100644
index 0000000..924b572
--- /dev/null
+++ b/share-resetpassword/src/main/resources/web/extras/com/flex-solution/components/ResetPassword.js
@@ -0,0 +1,144 @@
+var namespace = function (identifier) {
+    var klasses = arguments[1] || false;
+    var ns = window;
+
+    if (identifier !== '') {
+        var parts = identifier.split(".");
+        for (var i = 0; i < parts.length; i++) {
+            if (!ns[parts[i]]) {
+                ns[parts[i]] = {};
+            }
+            ns = ns[parts[i]];
+        }
+    }
+
+    if (klasses) {
+        for (var klass in klasses) {
+            if (klasses.hasOwnProperty(klass)) {
+                ns[klass] = klasses[klass];
+            }
+        }
+    }
+
+    return ns;
+
+};
+
+(function () {
+    namespace("FlexSolution.component");
+
+    var Dom = YAHOO.util.Dom;
+
+    FlexSolution.component.ResetPassword = function Login_constructor(htmlId) {
+        FlexSolution.component.ResetPassword.superclass.constructor.call(this, "FlexSolution.component.ResetPassword", htmlId, ["button"]);
+
+        return this;
+    };
+
+    YAHOO.extend(FlexSolution.component.ResetPassword, Alfresco.component.Base,
+        {
+            options: {
+                spinner: null
+            },
+
+            onReady: function Login_onReady() {
+                var resetPasswordButton = Alfresco.util.createYUIButton(this, "show", this.showDialog, {type: "button"}, this.id);
+
+                var resetPasswordElement = Dom.get(resetPasswordButton);
+
+                var parent = resetPasswordElement.parentElement;
+
+                this.getFormElement().appendChild(parent);
+            },
+
+            showDialog: function (p_event, p_obj) {
+
+                Alfresco.util.PopupManager.getUserInput({
+                    title: Alfresco.util.message("dialog.title"),
+                    text: Alfresco.util.message("dialog.text"),
+                    okButtonText: Alfresco.util.message("dialog.okButton.label"),
+                    input: "text",
+                    callback: {
+                        fn: this.resetPassword,
+                        scope: this
+                    }
+                });
+            },
+
+            resetPassword: function (text) {
+
+                this.showSpinner();
+
+                Alfresco.util.Ajax.jsonPost({
+                    url: Alfresco.constants.URL_CONTEXT + "proxy/alfresco-noauth/com/flex-solution/reset-password",
+                    dataObj: {
+                        userName: text
+                    },
+                    successCallback: {
+                        fn: this.successCallback,
+                        scope: this
+                    },
+                    failureCallback: {
+                        fn: this.failureCallback,
+                        scope: this
+                    }
+                });
+
+            },
+
+            successCallback: function () {
+                
+                this.hideSpinner();
+
+                Alfresco.util.PopupManager.displayMessage({
+                    text: Alfresco.util.message("success.text"),
+                    displayTime: 5
+                })
+
+            },
+
+            failureCallback: function (resp) {
+
+                this.hideSpinner();
+
+                var errMessage;
+
+                // We do this because server response is something like 503004 Wrapped Exception with status error.outBound.
+                if(resp.json.message.includes('error.outBound')){
+                    errMessage = Alfresco.util.message("error.outBound");
+                }else if(resp.json.message.includes('error.userNotFound')){
+                    errMessage = Alfresco.util.message("error.userNotFound");
+                }else{
+                    errMessage = resp.json.message;
+                }
+                Alfresco.util.PopupManager.displayPrompt(
+                    {
+                        title: Alfresco.util.message("error.title"),
+                        text: resp.json ? errMessage : resp.serverResponse || Alfresco.util.message("error.server-side")
+                        // text: resp.json ? resp.json.message : resp.serverResponse || Alfresco.util.message("error.server-side")
+                    });
+            },
+
+
+            getFormElement: function () {
+
+                var loginComponent = Alfresco.util.ComponentManager.findFirst("Alfresco.component.Login");
+
+                return loginComponent.widgets.submitButton.getForm();
+            },
+
+            showSpinner: function () {
+
+                this.options.spinner = Alfresco.util.PopupManager.displayMessage({
+                    text: Alfresco.util.message("spinner.message"),
+                    displayTime: 0,
+                    spanClass: "wait"
+                });
+            },
+
+            hideSpinner: function () {
+                this.options.spinner.destroy();
+            }
+
+        });
+})();
\ No newline at end of file
diff --git a/share-resetpassword/src/main/resources/web/extras/com/flex-solution/components/changePassWF.css b/share-resetpassword/src/main/resources/web/extras/com/flex-solution/components/changePassWF.css
new file mode 100644
index 0000000..5a7f0f1
--- /dev/null
+++ b/share-resetpassword/src/main/resources/web/extras/com/flex-solution/components/changePassWF.css
@@ -0,0 +1,44 @@
+
+.parent-block {
+    top: 50%;
+    left: 50%;
+    width: 30em;
+    height: 14em;
+    position: absolute;
+    margin-left: -15em;
+    margin-top: -7em;
+    padding: 1em;
+    box-shadow: 0.33px 2px 8px;
+    border-radius: 6px;
+    border: 1px solid #ccc;
+    background: #ffffff;
+}
+
+.fields {
+    width: 100%;
+    float: left;
+    max-width: 30em;
+}
+
+.row {
+    clear: both;
+    text-align: right;
+    padding: 5px;
+    line-height:25px;
+}
+
+.label {
+    float: left;
+    padding-right:10px;
+}
+
+
+div.buttons{
+    margin-top: 1em;
+}
+
+div#comp-logo{
+    width: 100%;
+    background-size: contain;
+    margin-bottom: 1em;
+}
diff --git a/share-resetpassword/src/main/resources/web/extras/com/flex-solution/components/changePassWF.js b/share-resetpassword/src/main/resources/web/extras/com/flex-solution/components/changePassWF.js
new file mode 100644
index 0000000..cb69ed2
--- /dev/null
+++ b/share-resetpassword/src/main/resources/web/extras/com/flex-solution/components/changePassWF.js
@@ -0,0 +1,78 @@
+
+(function () {
+
+    Alfresco.component.ChangePassWF = function (htmlId) {
+        Alfresco.component.ChangePassWF.superclass.constructor.call(this, "Alfresco.component.ChangePassWF", htmlId);
+
+        return this;
+    };
+
+    YAHOO.extend(Alfresco.component.ChangePassWF, Alfresco.component.Base,
+        {
+
+
+            onCancel: function () {
+                window.location.href = Alfresco.constants.URL_CONTEXT;
+            },
+
+
+            initForm: function () {
+
+                var form = new Alfresco.forms.Form(this.id + "-form");
+                var passField = YAHOO.util.Dom.get(this.id + "-new-password");
+                var confPassField = YAHOO.util.Dom.get(this.id + "-new-password-confirm");
+
+                Alfresco.util.createYUIButton(this, "button-ok", null,
+                    {
+                        type: "submit"
+                    });
+
+                Alfresco.util.createYUIButton(this, "button-cancel", this.onCancel);
+
+                form.addValidation(passField.id, Alfresco.forms.validation.mandatory, null, "keyup,mouseover", Alfresco.util.message("form.field.mandatory"));
+                form.addValidation(passField.id, Alfresco.forms.validation.length, {minLength: 3}, "keyup,mouseover", Alfresco.util.message("form.field.length"));
+                form.addValidation(confPassField.id, Alfresco.forms.validation.passwordMatch, {el: passField}, "keyup,mouseover", Alfresco.util.message("form.field.confirm"));
+                form.addValidation(confPassField.id, Alfresco.forms.validation.mandatory, null, "keyup,mouseover", Alfresco.util.message("form.field.mandatory"));
+
+                form.setSubmitAsJSON(true);
+                form.setAJAXSubmit(true,
+                    {
+                        successCallback: {
+                            fn: function (obj) {
+
+                                //redirect to home page on success
+                                function onClickBut() {
+                                    window.location.href = Alfresco.constants.URL_CONTEXT;
+                                }
+
+                                Alfresco.util.PopupManager.displayPrompt({
+                                    text: Alfresco.util.message("form.success.message"),
+                                    buttons: [{
+                                        text: Alfresco.util.message("form.button.sign.in"),
+                                        handler: onClickBut
+                                    }]
+                                });
+                            },
+                            scope: this
+                        },
+
+                        failureCallback: {
+                            fn: function (obj) {
+                                Alfresco.util.PopupManager.displayPrompt({
+                                    text: !obj.json ? obj.serverResponse.statusText : obj.json.message
+                                });
+                            },
+                            scope: this
+                        }
+                    });
+
+                form.init();
+            },
+
+
+            onReady: function () {
+                this.initForm();
+            }
+        }
+    )
+})();
\ No newline at end of file
diff --git a/share-resetpassword/src/main/resources/web/extras/com/flex-solution/components/confirmPassword.js b/share-resetpassword/src/main/resources/web/extras/com/flex-solution/components/confirmPassword.js
new file mode 100644
index 0000000..8dc6a14
--- /dev/null
+++ b/share-resetpassword/src/main/resources/web/extras/com/flex-solution/components/confirmPassword.js
@@ -0,0 +1,27 @@
+(function () {
+
+    Alfresco.component.ConfirmPassword = function (htmlId) {
+        Alfresco.component.ConfirmPassword.superclass.constructor.call(this, "Alfresco.component.ConfirmPassword", htmlId);
+
+        return this;
+    };
+
+    YAHOO.extend(Alfresco.component.ConfirmPassword, Alfresco.component.Base,
+        {
+
+
+            getPassField: function () {
+                return YAHOO.util.Dom.get(this.id.replace(/_prop_fs-reset_confirmPass/g, "_prop_fs-reset_password"));
+            },
+
+            addValidation: function (event, args) {
+                args[1].runtime.addValidation(this.id, Alfresco.forms.validation.passwordMatch, {el: this.getPassField()}, "keyup,mouseover", Alfresco.util.message("form.field.confirm"));
+            },
+
+            onReady: function () {
+
+                YAHOO.Bubbling.on("afterFormRuntimeInit", this.addValidation, this);
+            }
+        }
+    )
+})();
\ No newline at end of file
diff --git a/share-resetpassword/src/main/resources/web/extras/com/flex-solution/components/wfTransitions.js b/share-resetpassword/src/main/resources/web/extras/com/flex-solution/components/wfTransitions.js
new file mode 100644
index 0000000..ad6a70e
--- /dev/null
+++ b/share-resetpassword/src/main/resources/web/extras/com/flex-solution/components/wfTransitions.js
@@ -0,0 +1,62 @@
+/**
+ * Transitions form component.
+ *
+ * @namespace Alfresco
+ * @class Alfresco.Transitions
+ */
+(function () {
+    /**
+     * YUI Library aliases
+     */
+    var Dom = YAHOO.util.Dom,
+        Event = YAHOO.util.Event;
+
+    /**
+     * Alfresco Slingshot aliases
+     */
+    var $html = Alfresco.util.encodeHTML;
+
+    /**
+     * Transitions constructor.
+     *
+     * @param {String} htmlId The HTML id of the parent element
+     * @return {Alfresco.Transitions} The new Transitions instance
+     * @constructor
+     */
+    Alfresco.Transitions.ChangePassword = function (htmlId) {
+        Alfresco.Transitions.superclass.constructor.call(this, "Alfresco.Transitions.ChangePassword", htmlId, ["button", "container"]);
+
+        return this;
+    };
+
+    YAHOO.extend(Alfresco.Transitions.ChangePassword, Alfresco.Transitions,
+        {
+
+            onClick: function Transitions_onClick(e, p_obj) {
+
+                //listen form validation event
+                YAHOO.Bubbling.on("formValidationError", function () {
+                    p_obj.set("disabled", false);
+                });
+
+                //MNT-2196 fix, disable transition button to prevent multiple execution
+                p_obj.set("disabled", true);
+
+                // determine what button was pressed by it's id
+                var buttonId = p_obj.get("id");
+                var transitionId = buttonId.substring(this.id.length + 1);
+
+                // get the hidden field
+                var hiddenField = this._getHiddenField();
+
+                // set the hidden field value
+                Dom.setAttribute(hiddenField, "value", transitionId);
+
+                if (Alfresco.logger.isDebugEnabled())
+                    Alfresco.logger.debug("Set transitions hidden field to: " + transitionId);
+
+                // attempt to submit the form
+                Alfresco.util.submitForm(p_obj.getForm());
+            },
+        });
+})();
\ No newline at end of file

Fixed code: *.iml
/.idea/
/_docker/logs.txt
/alfresco-resetpassword/target/
/share-resetpassword/target/
# AlfrescoResetPassword

Alfresco Reset Password add-on is a great thing that allows you to solve missing capability of Alfresco to reset passwords for users who forgot owns password without contacting Administrator.

# Features
 - Sends email notification with a confirmation link
 - Reset password link will be expired after 24h
 - It works fine even if multitenancy feature is enabled

# User's reset password manual

 - Click Forgot Password button
 
 ![](readme_resources/1-config.png)
 
 - Enter username
 
 ![](readme_resources/4-config.png)
 
 - You'll receive email notification with link for changing password
 
 ![](readme_resources/2-config.png)
 
 - Enter your new password and confirm it.
 
 ![](readme_resources/3-config.png)

# Compatibility was tested on versions:
 - Alfresco 5.0d
 - Alfresco 5.1
 - Alfresco 5.2
 - Alfresco 6.0.7-ga
 - Alfresco 6.2.0-ga

# How to build
### The following tools must be installed:
- Java11
- Maven3

### Required settings
  - You have to configure Alfresco Outbound SMTP in {ALFRESCO LOCATION}/tomcat/shared/classes/alfresco-global.properties.
  ```sh
   mail.host={mail host}
   mail.port={mail port}
   mail.username={mail username}
   mail.password={mail password}
   mail.protocol=smtps
   mail.smtps.starttls.enable=true
   mail.smtps.auth=true
   mail.from.enabled=false
   mail.from.default={default sending email}
   ```
- It is highly recommended to change secure salt property as well that us used in token generation.
To generate new one use any BCrypt hash generator. First 29 characters is salt
```fs.passreset.salt=```

### Build steps
1. Clone repo
```sh
$ git clone https://github.com/FlexSolution/AlfrescoResetPassword.git
```
2. Compile repo amp 

```sh
$ cd ResetPasswordAddon/resetPassword
$ mvn clean package
```

3. Compile share amp

```sh
$ cd ../resetPassword-share
$ mvn clean package
```

# How to deploy add-on to Alfresco

1. Compile AMPs for alfresco and share how described above
2. Stop Alfresco
3. Copy file ResetPasswordAddon/resetPassword/target/reset-password-repo-${version}.amp into ${Alfresco_HOME}/amps folder
4. Copy file ResetPasswordAddon/resetPassword-share/target/reset-password-share-${version}.amp into ${Alfresco_HOME}/amps_share folder
5. Apply AMPs by executing command from command line
```sh
$ cd ${Alfresco_HOME}/bin
$ apply_amps.sh -force
```
6. Start Alfresco

##How to setup dev environment:
You need to have installed docker and configured to use without root privileges https://docs.docker.com/install

1. Add **Remote** Debugger cofiguration.
![](readme_resources/1-configuration.png)

1. Configure **Before launch: Tool window** and point at **start.sh** or **start.bat** as executable script.
(files within _docker folder should be executable).
![](readme_resources/4-configuration.png)
![](readme_resources/2-configuration.png)

1. Point at **logs.txt** as logs file.
![](readme_resources/3-configuration.png)

All alfresco configuration placed in **docker-compose.yml**. You can place additional configuration there if you need

To stop docker containers use **stop-all.sh** or **stop-all.bat** script.
**start.sh|bat** script also stops containers and starts again

To debug Share app java code create new **Remote** configuration. Use port 5006

POST http://localhost:8080/share/page/index
Content-Type: application/x-www-form-urlencoded
Authorization: Basic YWRtaW46YWRtaW4=

reset=on

###

version: "2"

services:
    alfresco-resetpassword:
        # image: alfresco/alfresco-content-repository-community:6.2.0-ga
        build: ./../alfresco-resetpassword
        environment:
            CATALINA_OPTS: "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=0.0.0.0:8888"
            JAVA_OPTS : "
                -Ddb.driver=org.postgresql.Driver
                -Ddb.username=alfresco
                -Ddb.password=alfresco
                -Ddb.url=jdbc:postgresql://postgres-resetpassword:5432/alfresco
                -Dsolr.host=solr6-resetpassword
                -Dsolr.port=8983
                -Dsolr.secureComms=none
                -Dsolr.base.url=/solr
                -Dindex.subsystem.name=solr6

                -Ddeployment.method=DOCKER_COMPOSE
                -Dcsrf.filter.enabled=false
                -Dmessaging.subsystem.autoStart=false

                -Dlocal.transform.service.enabled=true
                -DlocalTransform.pdfrenderer.url=http://alfresco-pdf-renderer:8090/
                -DlocalTransform.imagemagick.url=http://imagemagick:8090/
                -DlocalTransform.libreoffice.url=http://libreoffice:8090/
                -DlocalTransform.tika.url=http://tika:8090/
                -DlocalTransform.misc.url=http://transform-misc:8090/
                -Dlegacy.transform.service.enabled=true
                -Dalfresco-pdf-renderer.url=http://alfresco-pdf-renderer:8090/
                -Djodconverter.url=http://libreoffice:8090/
                -Dimg.url=http://imagemagick:8090/
                -Dtika.url=http://tika:8090/
                -Dtransform.misc.url=http://transform-misc:8090/

                -Xms1500m -Xmx1500m
                "
        ports:
            - 8082:8080 #Browser port
            - 5005:8888
            - 2121:2121
        links:
            - postgres-resetpassword
        volumes:
            - alf_data-resetpassword:/usr/local/tomcat/alf_data
            - ./../alfresco-resetpassword/_lib:/usr/local/tomcat/webapps/alfresco/development/lib
            - ./../alfresco-resetpassword/target/classes:/usr/local/tomcat/webapps/alfresco/development/classes
            - ./../alfresco-resetpassword/context.xml:/usr/local/tomcat/webapps/alfresco/META-INF/context.xml

    alfresco-pdf-renderer:
        image: alfresco/alfresco-pdf-renderer:2.1.0
        mem_limit: 1g
        environment:
            JAVA_OPTS: " -Xms256m -Xmx512m"
        expose:
            - 8090

    imagemagick:
        image: alfresco/alfresco-imagemagick:2.1.0
        mem_limit: 1g
        environment:
            JAVA_OPTS: " -Xms256m -Xmx512m"
        expose:
            - 8090

    libreoffice:
        image: alfresco/alfresco-libreoffice:2.1.0
        mem_limit: 1g
        environment:
            JAVA_OPTS: " -Xms256m -Xmx512m"
        expose:
            - 8090

    tika:
        image: alfresco/alfresco-tika:2.1.0
        mem_limit: 1g
        environment:
            JAVA_OPTS: " -Xms256m -Xmx512m"
        expose:
            - 8090

    transform-misc:
        image: alfresco/alfresco-transform-misc:2.1.0
        mem_limit: 1g
        environment:
            JAVA_OPTS: " -Xms256m -Xmx512m"
        expose:
            - 8090


    share-resetpassword:
        # image: alfresco/alfresco-share:6.2.0
        build: ./../share-resetpassword
        environment:
            - REPO_HOST=alfresco-resetpassword
            - REPO_PORT=8080
            - JAVA_OPTS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5006
        ports:
            - 8080:8080
            - 5006:5006
        volumes:
            - ./../share-resetpassword/_lib:/usr/local/tomcat/webapps/share/development/lib
            - ./../share-resetpassword/target/classes:/usr/local/tomcat/webapps/share/development/classes
            - ./../share-resetpassword/context.xml:/usr/local/tomcat/webapps/share/META-INF/context.xml
            - ./../share-resetpassword/src/main/resources/web:/usr/local/tomcat/webapps/share/development/web

    postgres-resetpassword:
        image: postgres:11.4
        environment:
            - POSTGRES_PASSWORD=alfresco
            - POSTGRES_USER=alfresco
            - POSTGRES_DB=alfresco
        command: postgres -c max_connections=300 -c log_min_messages=LOG
        ports:
            - 5435:5432

    solr6-resetpassword:
        image: alfresco/alfresco-search-services:1.4.0
        environment:
            #Solr needs to know how to register itself with Alfresco
            - SOLR_ALFRESCO_HOST=alfresco-resetpassword
            - SOLR_ALFRESCO_PORT=8080
            #Alfresco needs to know how to call solr
            - SOLR_SOLR_HOST=solr6-resetpassword
            - SOLR_SOLR_PORT=8983
            #Create the default alfresco and archive cores
            - SOLR_CREATE_ALFRESCO_DEFAULTS=alfresco,archive
            - ALFRESCO_SECURE_COMMS=none
        ports:
            - 8983

    activemq-resetpassword:
        image: alfresco/alfresco-activemq:5.15.8
        ports:
            - 8161 # Web Console
            - 5672 # AMQP
            - 61616 # OpenWire
            - 61613 # STOMP
volumes:
    alf_data-resetpassword:
mvn -f ../../pom.xml clean package

rm ../logs.txt
./docker-compose-linux -f ../docker-compose.yml stop
./docker-compose-linux -f ../docker-compose.yml up --no-start --no-recreate
./docker-compose-linux -f ../docker-compose.yml start
./docker-compose-linux -f ../docker-compose.yml logs -f --tail=0  >> ../logs.txt &
sleep 5
BASEDIR=$(dirname "$0")
$BASEDIR/docker-compose-linux -f $BASEDIR/../docker-compose.yml stop
@ECHO OFF
chcp 65001
set BASEDIR=%~dp0

call %BASEDIR%docker-compose-Windows-x86_64.exe -f %BASEDIR%..\docker-compose.yml stop
call mvn -f %BASEDIR%..\..\pom.xml clean package
del %BASEDIR%..\logs.txt
call %BASEDIR%docker-compose-Windows-x86_64.exe -f %BASEDIR%..\docker-compose.yml up  --no-recreate --no-start
start /B %BASEDIR%docker-compose-Windows-x86_64.exe -f %BASEDIR%..\docker-compose.yml start
PING localhost -n 20 >NUL
start /B %BASEDIR%docker-compose-Windows-x86_64.exe -f %BASEDIR%..\docker-compose.yml logs -f --tail=0 > %BASEDIR%..\logs.txt
set BASEDIR=%~dp0
call %BASEDIR%docker-compose-Windows-x86_64.exe -f %BASEDIR%..\docker-compose.yml stop
FROM alfresco/alfresco-content-repository-community:6.2.0-ga

ARG TOMCAT_DIR=/usr/local/tomcat

USER root


# Copy Dockerfile to avoid an error if no AMPs exist
COPY Dockerfile _amps/*.amp $TOMCAT_DIR/amps/
RUN java -jar $TOMCAT_DIR/alfresco-mmt/alfresco-mmt*.jar install \
              $TOMCAT_DIR/amps $TOMCAT_DIR/webapps/alfresco -directory -nobackup -force

USER ${USERNAME}
<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>
<?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 &quot;true TRUE, false FALSE&quot;."/>
<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>

<?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>
<html>
<head>
    <style type="text/css"><!--
    body {
        font-family: Arial, sans-serif;
        font-size: 14px;
        color: #4c4c4c;
    }

    a, a:visited {
        color: #0072cf;
    }

    --></style>
</head>

<body bgcolor="#dddddd">
<table width="100%" cellpadding="20" cellspacing="0" border="0" bgcolor="#dddddd">
    <tr>
        <td width="100%" align="center">
            <table width="70%" cellpadding="0" cellspacing="0" bgcolor="white"
                   style="background-color: white; border: 1px solid #aaaaaa;">
                <tr>
                    <td width="100%">
                        <table width="100%" cellpadding="0" cellspacing="0" border="0">
                            <tr>
                                <td style="padding: 10px 30px 0px;">
                                    <table width="100%" cellpadding="0" cellspacing="0" border="0">
                                        <tr>
                                            <td>
                                                <table cellpadding="0" cellspacing="0" border="0">
                                                    <tr>
                                                        <td>
                                                            <img src="${shareUrl}/res/components/images/no-user-photo-64.png"
                                                                 alt="" width="64" height="64" border="0"
                                                                 style="padding-right: 20px;"/>
                                                        </td>
                                                        <td>
                                                            <div style="font-size: 22px; padding-bottom: 4px;">
                                                                Change password for Alfresco ${productName!""} account
                                                            </div>
                                                            <div style="font-size: 13px;">
                                                            ${date?datetime?string.full}
                                                            </div>
                                                        </td>
                                                    </tr>
                                                </table>
                                                <div style="font-size: 14px; margin: 12px 0px 24px 0px; padding-top: 10px; border-top: 1px solid #aaaaaa;">
                                                    <p>Dear ${assignee.firstname},</p>

                                                    <p>we received a request for password change
                                                        at ${shareUrl}</p>

                                                    <br/><a href="${shareUrl}/page/changePassWF?token=${token}">Go to
                                                    this link</a> to set your new password.
                                                    The link will be active for 24 hours.</p>

                                                    <p>Sincerely,<br/>
                                                        Alfresco ${productName!""}</p>
                                                </div>
                                            </td>
                                        </tr>
                                    </table>
                                </td>
                            </tr>
                            <tr>
                                <td>
                                    <div style="border-top: 1px solid #aaaaaa;">&nbsp;</div>
                                </td>
                            </tr>
                            <tr>
                                <td>
                                    <div style="border-bottom: 1px solid #aaaaaa;">&nbsp;</div>
                                </td>
                            </tr>
                            <tr>
                                <td style="padding: 10px 30px;">
                                    <img src="${shareUrl}/themes/default/images/app-logo.png" alt="" width="145"
                                         height="32" border="0"/>
                                </td>
                            </tr>
                        </table>
                    </td>
                </tr>
            </table>
        </td>
    </tr>
</table>
</body>
</html>
package com.flexsolution.resetpassword.exceptions;

import org.alfresco.error.AlfrescoRuntimeException;

public class EmailTemplateException extends AlfrescoRuntimeException {

    public EmailTemplateException(String msgId) {
        super(msgId);
    }
}
package com.flexsolution.resetpassword.namespaces;

import org.alfresco.service.namespace.QName;

public abstract class ResetPasswordNameSpace {

    private ResetPasswordNameSpace() {}

    private static final String MODEL_URI = "https://flex-solution.com/model/workflow/1.0";

    public static final QName QNAME_TOKEN = QName.createQName(MODEL_URI, "token");

    public static final QName QNAME_PASSWORD = QName.createQName(MODEL_URI, "password");

    public static final QName QNAME_CONFIRM_PASS = QName.createQName(MODEL_URI, "confirmPass");
}
package com.flexsolution.resetpassword.patch;

import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.*;
import org.alfresco.service.namespace.QName;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.extensions.surf.util.I18NUtil;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;

public class AlfrescoFileVisitor extends SimpleFileVisitor<Path> {

    private final Logger LOGGER = LoggerFactory.getLogger(AlfrescoFileVisitor.class);

    private boolean ignoreRoot;

    public AlfrescoFileVisitor (NodeService nodeService, FileFolderService fileFolderService,
                                ContentService contentService,
                                MimetypeService mimetypeService,
                                Properties globalProps,
                                Map<String, NodeRef> startLocationEntry,
                                Set<String> ignoreFiles,
                                boolean ignoreRoot) {
        this.nodeService = nodeService;
        this.fileFolderService = fileFolderService;
        this.contentService = contentService;
        this.mimetypeService = mimetypeService;
        this.globalProps = globalProps;
        this.ignoreFiles = ignoreFiles;
        this.ignoreRoot = ignoreRoot;

        IMPORTED.putAll(startLocationEntry);
    }

    private final String SPACES_PROPS_PATH = "alfresco/messages/bootstrap-spaces";

    private ResourceBundle bundle;

    private final NodeService nodeService;
    private final FileFolderService fileFolderService;
    private final ContentService contentService;
    private final MimetypeService mimetypeService;
    private final Properties globalProps;
    private final Set<String> ignoreFiles;

    private final Map<String, NodeRef> IMPORTED = new HashMap<>();

    @Override
    public FileVisitResult preVisitDirectory (Path dir, BasicFileAttributes attrs) throws IOException {
        return copyToNode(dir, ContentModel.TYPE_FOLDER);
    }

    private String getLocalizedName (String fileNameExpr) {
        if(this.bundle == null) {
            Locale bindingLocale = I18NUtil.getLocale();
            this.bundle = ResourceBundle.getBundle(SPACES_PROPS_PATH, bindingLocale);
        }

        return this.bundle.getString(fileNameExpr);
    }

    private FileVisitResult copyToNode (Path fileOrDir, QName type) {
        if(!IMPORTED.containsKey(fileOrDir.toString()) || ignoreRoot == false) {

            NodeRef parentNode;

            if(ignoreRoot == false){
                parentNode = IMPORTED.get(fileOrDir.toString());
                ignoreRoot = true;
            }else {
                parentNode = IMPORTED.get(fileOrDir.getParent().toString());
            }

            String importedFileName = fileOrDir.getFileName().toString();

            if(importedFileName.startsWith("$")) {
                importedFileName = getLocalizedName(importedFileName.substring(1));
            }

            NodeRef targetNode = fileFolderService.searchSimple(parentNode, importedFileName);

            if(targetNode == null) {

                final FileInfo newNode = fileFolderService.create(parentNode, importedFileName, type);

                targetNode = newNode.getNodeRef();

                if(ContentModel.TYPE_CONTENT.equals(type)) {
                    copyContent(fileOrDir, newNode);
                }
                LOGGER.debug("Node {} has been imported. File/folder name {}", targetNode, importedFileName);
            } else {
                LOGGER.debug("Node {} already exists (SKIP). File/folder name {}", targetNode, importedFileName);
            }

            setNodeTitle(targetNode, importedFileName);

            IMPORTED.put(fileOrDir.toString(), targetNode);

        }

        LOGGER.debug("Imported file/dir path={}", fileOrDir.toString());
        return FileVisitResult.CONTINUE;
    }

    private void copyContent (Path file, FileInfo newNode) {
        try {
            final ContentWriter writer = contentService.getWriter(newNode.getNodeRef(), ContentModel.PROP_CONTENT, true);

            final String importedFileName = file.getFileName().toString();
            writer.setMimetype(mimetypeService.getMimetype(FilenameUtils.getExtension(importedFileName)));

            try (
                    final InputStream is = Files.newInputStream(file);
                    final OutputStream os = writer.getContentOutputStream();
            ) {
                IOUtils.copy(is, os);
            }
        } catch (IOException e) {
            throw new AlfrescoRuntimeException(e.getMessage());
        }

        LOGGER.debug( "Content copied file path={}", file.toString());
    }


    private void setNodeTitle (NodeRef nodeRef, String filename) {
        final String title = this.globalProps.getProperty("title." + filename);

        LOGGER.debug("Title for file/folder {} is {}", filename, title);

//        if(title != null){
//            this.nodeService.setProperty(nodeRef, ContentModel.PROP_TITLE, title);
//        }

        Optional.ofNullable(title)
                .ifPresent(t -> this.nodeService.setProperty(nodeRef, ContentModel.PROP_TITLE, t));

    }

    @Override
    public FileVisitResult visitFile (Path file, BasicFileAttributes attrs) throws IOException {

        if(ignoreFiles != null && ignoreFiles.contains(file.getFileName().toString())) {
            LOGGER.info("File {} has been ignored", file.getFileName().toString());
            return FileVisitResult.CONTINUE;
        }
        return copyToNode(file, ContentModel.TYPE_CONTENT);
    }

    @Override
    public FileVisitResult visitFileFailed (Path file, IOException exc) throws IOException {
        throw new AlfrescoRuntimeException(exc.getMessage());
    }

    @Override
    public FileVisitResult postVisitDirectory (Path dir, IOException exc) throws IOException {
        return super.postVisitDirectory(dir, exc);
    }
}
package com.flexsolution.resetpassword.patch;

import org.alfresco.repo.admin.patch.AbstractPatch;
import org.alfresco.repo.model.Repository;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.Properties;
import java.util.Set;

public class CopyStructurePatch extends AbstractPatch {

    private final Logger LOGGER = LoggerFactory.getLogger(CopyStructurePatch.class);

    protected String classpathLocaiton;
    protected String copyToXpath;

    protected Repository repository;
    protected FileFolderService fileFolderService;
    protected ContentService contentService;
    protected MimetypeService mimetypeService;
    protected Properties globalProps;
    protected Set<String> ignoreFiles;
    protected boolean ignoreRoot = true;

    @Override
    public String applyInternal () throws Exception {


        NodeRef companyHome = repository.getCompanyHome();

        NodeRef dataDictionary = searchService.selectNodes(companyHome, copyToXpath, null, namespaceService, false).get(0);


        ResourceLoader resourceLoader = new DefaultResourceLoader(getClass().getClassLoader());
        Resource bootstrapFolder = resourceLoader.getResource(classpathLocaiton);

        final String startLocationPath = bootstrapFolder.getFile().getPath();
        Files.walkFileTree(Paths.get(startLocationPath),
                new AlfrescoFileVisitor(
                        nodeService,
                        fileFolderService,
                        contentService,
                        mimetypeService,
                        globalProps,
                        Collections.singletonMap(startLocationPath, dataDictionary),
                        ignoreFiles,
                        ignoreRoot
                )
        );


        return "STRUCTURE COPY PATCH FINISHED SUCCESSFULLY";
    }

    public Repository getRepository() {
        return repository;
    }

    public void setRepository (Repository repository) {
        this.repository = repository;
    }

    public void setFileFolderService (FileFolderService fileFolderService) {
        this.fileFolderService = fileFolderService;
    }

    public void setContentService (ContentService contentService) {
        this.contentService = contentService;
    }

    public void setMimetypeService (MimetypeService mimetypeService) {
        this.mimetypeService = mimetypeService;
    }

    public void setClasspathLocaiton (String classpathLocaiton) {
        this.classpathLocaiton = classpathLocaiton;
    }


    public void setCopyToXpath (String copyToXpath) {
        this.copyToXpath = copyToXpath;
    }

    public void setGlobalProps (Properties globalProps) {
        this.globalProps = globalProps;
    }

    public void setIgnoreFiles (Set<String> ignoreFiles) {
        this.ignoreFiles = ignoreFiles;
    }

    public void setIgnoreRoot (boolean ignoreRoot) {
        this.ignoreRoot = ignoreRoot;
    }

    public boolean isIgnoreRoot () {
        return ignoreRoot;
    }
}
package com.flexsolution.resetpassword.services.script;

import org.alfresco.repo.jscript.BaseScopableProcessorExtension;
import org.alfresco.service.cmr.security.MutableAuthenticationService;

public class ScriptPasswordService extends BaseScopableProcessorExtension {

    private MutableAuthenticationService authenticationService;

    public void setPassword(String userName, String password) {
            authenticationService.setAuthentication(userName, password.toCharArray());
    }

    public void setAuthenticationService(MutableAuthenticationService authenticationService) {
        this.authenticationService = authenticationService;
    }
}
package com.flexsolution.resetpassword.util;

import com.flexsolution.resetpassword.exceptions.EmailTemplateException;
import org.alfresco.repo.jscript.BaseScopableProcessorExtension;
import org.alfresco.repo.jscript.ScriptNode;
import org.alfresco.repo.model.Repository;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeRef;

import java.util.List;

public class EmailHelper extends BaseScopableProcessorExtension {

    private ServiceRegistry services;
    private Repository repositoryHelper;

    public ScriptNode getLocalizedEmailTemplate(String xpath) {

        List<NodeRef> nodes = services.getSearchService().selectNodes(repositoryHelper.getCompanyHome(),
                xpath, null, services.getNamespaceService(), true);

        if (nodes.size()!=1) {
            throw new EmailTemplateException("Email template doesn't exist by the following XPATH: " + xpath);
        }

        NodeRef emailTemplate = services.getFileFolderService().getLocalizedSibling(nodes.get(0));

        return new ScriptNode(emailTemplate, services, this.getScope());
    }

    public void setServiceRegistry(ServiceRegistry serviceRegistry) {
        this.services = serviceRegistry;
    }

    public void setRepositoryHelper(Repository repositoryHelper) {
        this.repositoryHelper = repositoryHelper;
    }
}
package com.flexsolution.resetpassword.util;

import org.alfresco.repo.jscript.BaseScopableProcessorExtension;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.tenant.TenantUtil;
import org.alfresco.util.Pair;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;


public class Sudo extends BaseScopableProcessorExtension {

    private final Logger logger = Logger.getLogger(Sudo.class);

    public Object su(final Function func) {

        final Scriptable scope = getScope();

        return su(func, scope);
    }

    public Object su(final Function func, final Scriptable scope, final Object... objects) {

        final Context cx = Context.getCurrentContext();

        AuthenticationUtil.RunAsWork<Object> raw = new AuthenticationUtil.RunAsWork<Object>() {
            public Object doWork() throws Exception {
                return func.call(cx, ScriptableObject.getTopLevelScope(scope), scope, objects);
            }
        };

        return AuthenticationUtil.runAs(raw, AuthenticationUtil.getSystemUserName());
    }

    public Object asTenant(final String requestName, final Function func, final Scriptable scope) {

        if(logger.isDebugEnabled()) {
            logger.debug("AsTenant starts . Username: " + requestName);
            logger.debug("AsTenant function " + func.toString());
        }

        Pair<String, String> userTenant = AuthenticationUtil.getUserTenant(requestName);
        final String tenantDomain = userTenant.getSecond();

        if (StringUtils.isBlank(tenantDomain)) {
            return su(func, scope);
        }

        TenantUtil.TenantRunAsWork<Object> work = new TenantUtil.TenantRunAsWork<Object>() {
            public Object doWork() throws Exception {
                return su(func, scope);
            }
        };

        return TenantUtil.runAsUserTenant(work, requestName, tenantDomain);
    }
}
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;
    }
}


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;
    }
}
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;
    }
}
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;
    }
}
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");
        }
    }
}
<webscript>
    <shortname>apply-changed-password</shortname>
    <description>Apply password changes</description>
    <url>/com/flex-solution/applyChangedPassword</url>
    <format default="json"/>
    <authentication runas="admin">none</authentication>
    <transaction>required</transaction>
</webscript>
<#--logic in controller-->
const PASS_MIN_LENGTH = 3;

function sendCallback(code, message) {
    status.code = code;
    status.message = message;
    status.redirect = true;
}

function isValid(pass, pass2){
    if(PASS_MIN_LENGTH > pass.length){
        return false;
    }

    return pass.equals(pass2);
}
<webscript>
    <shortname>reset-password</shortname>
    <description>Forgot password process webscript </description>
    <url>/com/flex-solution/reset-password</url>
    <format default="json"/>
    <authentication runas="admin">none</authentication>
    <transaction>required</transaction>
</webscript>
<#--logic in controller-->
workflow.desc=Reset password workflow
user.not.found=User disabled or does not exist
user.email.number.wrong=There are more than one user with same e-mail. Please, enter a user name
<webscript>
    <shortname>get-userName</shortname>
    <description>Return change password task assignee</description>
    <url>/com/flex-solution/taskIsComplete</url>
    <format default="json"/>
    <authentication runas="admin">none</authentication>
    <transaction>required</transaction>
</webscript>
<#--logic in controller-->
task.not-exist=Task does not exist
<?xml version="1.0" encoding="UTF-8" ?>

<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
             xmlns:activiti="http://activiti.org/bpmn"
             xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
             xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
             xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema"
             expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://alfresco.org">

    <process id="resetPasswordFlex" name="Reset Password" isExecutable="true">

        <startEvent id="start"
                    activiti:formKey="fs-reset:start"/>
        <sequenceFlow id='flow1'
                      sourceRef='start'
                      targetRef='review'/>

        <userTask id="review" name="Review" activiti:formKey="fs-reset:review" activiti:assignee="${bpm_assignee.properties.userName}">
            <extensionElements>

                <activiti:taskListener event="create"
                                       class="org.alfresco.repo.workflow.activiti.tasklistener.ScriptTaskListener">
                    <activiti:field name="script">
                        <activiti:string><![CDATA[<import resource="classpath:alfresco/extension/workflow/js/reset-password.js">
                        var obj = new ResetPassword();
                        sudo.asTenant(bpm_assignee.properties.userName, obj.reviewCreate, obj);
                        ]]></activiti:string>
                    </activiti:field>
                </activiti:taskListener>

            </extensionElements>
        </userTask>

        <boundaryEvent id="timer" name="Timer" attachedToRef="review">
            <timerEventDefinition>
                <timeDuration>PT24H</timeDuration>
            </timerEventDefinition>
        </boundaryEvent>

        <sequenceFlow id='flow2'
                      sourceRef='review'
                      targetRef='theEnd'/>

        <sequenceFlow id="flow3" sourceRef="timer" targetRef="theEnd"/>

        <endEvent id="theEnd"/>

    </process>

</definitions>
<import resource="classpath:alfresco/extension/templates/webscripts/com/flex-solution/reset-pass-lib.js">

var ResetPassword = function () {

    this.reviewCreate = function () {
        if (typeof bpm_workflowDueDate != 'undefined') task.dueDate = bpm_workflowDueDate;
        if (typeof bpm_workflowPriority != 'undefined') task.priority = bpm_workflowPriority;

        //Send Email
        this._sendEmail("reset-password.ftl", bpm_assignee.properties.email, {})
    };

    this._sendEmail = function (templateName, to) {
        try {
            var template = emailHelper.getLocalizedEmailTemplate("app:dictionary/app:email_templates/cm:workflownotification/cm:" + templateName);
        } catch (e) {
            throw e.javaException.getMessage();
        }
        
        var mail = actions.create("mail");
        var currentToken = token.genToken(bpm_assignee.properties.userName);

        mail.parameters.template = template;
        mail.parameters.to = to;
        mail.parameters.subject = template.properties.title || "Reset Password";
        mail.parameters.template_model = this.prepareTemplateProps(bpm_assignee, task, currentToken);
        task.setVariable("fs-reset:token", currentToken);

        try {

            mail.execute(bpm_assignee);

        } catch (ex) {

            throw "error.outBound";
        }

    };

    this.prepareTemplateProps = function (assignee, task, currentToken) {
        var templateProps = {};
        templateProps["assignee"] = {
            firstname: assignee.properties["firstName"],
            lastname: assignee.properties["lastName"]
        };
        templateProps["token"] = currentToken;
        return templateProps;
    }
};
<?xml version="1.0" encoding="UTF-8"?>
<model name="fs-reset:resetWorkflowModel" xmlns="http://www.alfresco.org/model/dictionary/1.0">

    <description>Reset Workflow Model</description>
    <author>s.palyukh</author>
    <version>1.0</version>

    <imports>
        <!-- Import Alfresco Dictionary Definitions -->
        <import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d"/>
        <!-- Import Alfresco Workflow Model Definitions -->
        <import uri="http://www.alfresco.org/model/bpm/1.0" prefix="bpm"/>
    </imports>

    <namespaces>
        <namespace uri="https://flex-solution.com/model/workflow/1.0" prefix="fs-reset"/>
    </namespaces>

    <types>

        <type name="fs-reset:start">
            <parent>bpm:startTask</parent>
            <mandatory-aspects>
                <aspect>bpm:assignee</aspect>
            </mandatory-aspects>
        </type>

        <type name="fs-reset:review">
            <title>Review</title>
            <parent>bpm:workflowTask</parent>
            <properties>
                <property name="fs-reset:token">
                    <type>d:text</type>
                    <mandatory>true</mandatory>
                </property>

            </properties>
            <overrides>
                <property name="bpm:reassignable">
                    <default>false</default>
                </property>
            </overrides>
        </type>
    </types>
</model>

alfresco.host=localhost
share.host=localhost

fs.passreset.salt=$2a$11$sGL4KAlXWsxR3ysNL8HOmu
<?xml version='1.0' encoding='UTF-8'?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

    <bean id="patch.copyEmailTemplate" class="com.flexsolution.resetpassword.patch.CopyStructurePatch"
          parent="basePatch">
        <property name="id" value="patch.copyEmailTemplate"/>
        <property name="description" value="Copy EmailTemplate"/>
        <property name="fixesFromSchema" value="0"/>
        <property name="fixesToSchema" value="${version.schema}"/>
        <property name="targetSchema" value="100005"/>
        <property name="fileFolderService" ref="FileFolderService"/>
        <property name="repository" ref="repositoryHelper"/>
        <property name="contentService" ref="ContentService"/>
        <property name="mimetypeService" ref="mimetypeService"/>
        <property name="globalProps" ref="global-properties"/>
        <property name="classpathLocaiton" value="alfresco/module/resetPassword/bootstrap/emailTemplate"/>
        <property name="copyToXpath" value="/${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.templates.email.childname}/cm:workflownotification"/>

    </bean>


</beans>
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>

    <bean class="com.flexsolution.resetpassword.util.EmailHelper" parent="baseJavaScriptExtension">
        <property name="extensionName" value="emailHelper"/>
        <property name="serviceRegistry" ref="ServiceRegistry"/>
        <property name="repositoryHelper" ref="repositoryHelper"/>
    </bean>

    <bean class="com.flexsolution.resetpassword.services.script.ScriptPasswordService" parent="baseJavaScriptExtension">
        <property name="extensionName" value="passwordService"/>
        <property name="authenticationService" ref="AuthenticationService" />
    </bean>

    <bean id="genToken" parent="baseJavaScriptExtension" class="com.flexsolution.resetpassword.util.TokenGenerator">
        <property name="extensionName">
            <value>token</value>
        </property>
        <property name="globalProps" ref="global-properties"/>
    </bean>

    <bean class="com.flexsolution.resetpassword.util.WorkflowHelper">
        <property name="workflowService" ref="WorkflowService"/>
        <property name="activitiHistoryService" ref="activitiHistoryService"/>
    </bean>


    <bean id="resetPassword.Sudo" class="com.flexsolution.resetpassword.util.Sudo" parent="baseJavaScriptExtension">
        <property name="extensionName" value="sudo"/>
    </bean>

</beans>
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>

<beans>
    <bean id="webscript.com.flex-solution.reset-password.reset-password.post" parent="webscript" class="com.flexsolution.resetpassword.webscripts.ResetPasswordPost">
        <property name="workflowService" ref="WorkflowService"/>
        <property name="personService" ref="PersonService"/>
    </bean>

    <bean id="webscript.com.flex-solution.taskIsComplete.taskIsComplete.post" parent="webscript" class="com.flexsolution.resetpassword.webscripts.TaskIsCompletePost">
    </bean>

    <bean id="webscript.com.flex-solution.applyChangedPassword.applyChangedPassword.post" parent="webscript" class="com.flexsolution.resetpassword.webscripts.ApplyChangedPasswordPost">
        <property name="workflowService" ref="WorkflowService"/>
        <property name="authenticationService" ref="AuthenticationService" />
        <property name="tokenGenerator" ref="genToken" />
    </bean>

</beans>
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>

<beans>

    <bean id="resetPassword.workflowBootstrap" parent="workflowDeployer">
        <property name="workflowDefinitions">
            <list>
                <props>
                    <prop key="engineId">activiti</prop>
                    <prop key="location">alfresco/extension/workflow/ResetPasswordProcess.bpmn20.xml</prop>
                    <prop key="mimetype">text/xml</prop>
                    <prop key="redeploy">false</prop>
                </props>
            </list>
        </property>
        <property name="models">
            <list>
                <value>alfresco/extension/workflow/reset-workflow-model.xml</value>
            </list>
        </property>
    </bean>
</beans>
<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>
module.id=${project.artifactId}
module.title=${project.name}
module.description=${project.description}
module.version=${project.version}
<?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>
FROM alfresco/alfresco-share:6.2.0

ARG TOMCAT_DIR=/usr/local/tomcat

USER root


# Copy Dockerfile to avoid an error if no AMPs exist
COPY Dockerfile _amps/*.amp $TOMCAT_DIR/amps_share/
RUN java -jar $TOMCAT_DIR/alfresco-mmt/alfresco-mmt*.jar install \
              $TOMCAT_DIR/amps_share $TOMCAT_DIR/webapps/share -directory -nobackup -force

USER ${USERNAME}
<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/module.properties</source>
            <filtered>true</filtered>
        </file>
        <file>
            <source>src/main/resources/file-mapping.properties</source>
            <filtered>true</filtered>
        </file>
    </files>

    <fileSets>
        <fileSet>
            <directory>src/main/config</directory>
            <outputDirectory>config</outputDirectory>
            <filtered>false</filtered>
        </fileSet>
        <fileSet>
            <directory>src/main/resources/web</directory>
            <outputDirectory>web</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>
<?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/share/development/classes"
                      webAppMount="/WEB-INF/classes"/>
        <PreResources className="org.apache.catalina.webresources.DirResourceSet"
                      base="/usr/local/tomcat/webapps/share/development/web"
                      webAppMount="/"/>
        <JarResources className="org.apache.catalina.webresources.DirResourceSet"
                      base="/usr/local/tomcat/webapps/share/development/lib"
                      webAppMount="/WEB-INF/lib"/>
    </Resources>
</Context>

<?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-share</artifactId>
    <version>1.2.0.RC1</version>
    <name>Reset Password Share AMP</name>
    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>org.alfresco</groupId>
            <artifactId>share</artifactId>
            <version>6.0.b</version>
            <scope>provided</scope>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>org.alfresco</groupId>
            <artifactId>share</artifactId>
            <version>6.0.b</version>
            <scope>provided</scope>
            <type>war</type>
        </dependency>
    </dependencies>

    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
                <excludes>
                    <exclude>web/**</exclude>
                </excludes>
            </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-resources-plugin</artifactId>
                <version>3.0.2</version>
                <executions>
                    <execution>
                        <id>copy-resources</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-resources</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.outputDirectory}</outputDirectory>
                            <resources>
                                <resource>
                                    <directory>src/main/resources/web</directory>
                                    <includes>
                                        <include>**/**</include>
                                    </includes>
                                    <targetPath>../web</targetPath>
                                    <filtering>false</filtering>
                                </resource>
                            </resources>
                        </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>
form.label.new-password=Enter new password
form.help.new-password=Please concoct new password and type it
form.label.new-password-confirm=Confirm password
form.help.new-password-confirm=Please enter your password again
form.field.length=Password length must be  more than 2 characters
form.field.confirm=Confirm password does not match password
form.field.mandatory=Field can not be empty
form.success.message=Password has been changed
form.button.submit=Submit
form.button.cancel=Cancel
form.button.sign.in=Sign in

prop_fs-reset_password=New password
prop_fs-reset_confirmPass=Confirm password

button.resetPassword=Forgot Password?
dialog.title=Reset Password Dialog
dialog.text=Username:
dialog.okButton.label=Reset
spinner.message=Please wait
error.title=Failure
error.userNotFound=Seems, there is no user with appropriate username
error.outBound=Failed to send email. Please, check outbound email configuration.
error.password=Confirm password does not match password or password length too short
error.server-side=Sorry. Internal error. Try later
success.text=Please check your email. You should receive email with instructions how to reset password

page.updatepassword.title=Update user password
page.updatepassword.description=Page allows user reset his password

button.resetPassword=Passwort vergessen?
error.userNotFound=Anscheinend gibt es keinen Benutzer mit dem entsprechenden Benutzernamen
error.outBound=E-Mail konnte nicht gesendet werden. Bitte überprüfen Sie die Konfiguration der ausgehenden E-Mails.
<?xml version='1.0' encoding='UTF-8'?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">


    <bean id="com.flexsolution.resetPassword-share.config" class="org.springframework.extensions.config.ConfigBootstrap"
          init-method="register">
        <property name="configService" ref="web.config"/>
        <property name="configs">
            <list>
                <value>classpath:alfresco/web-extension/share-resetpassword/share-config-custom.xml</value>
            </list>
        </property>
    </bean>

    <bean id="com.flexsolution.resetPassword-share.resources"
          class="org.springframework.extensions.surf.util.ResourceBundleBootstrapComponent">
        <property name="resourceBundles">
            <list>
                <value>alfresco.web-extension.messages.resetPassword-share</value>
            </list>
        </property>
    </bean>

</beans>
<alfresco-config>

    <config evaluator="string-compare" condition="Workflow">
        <hidden-workflows>
            <workflow name="activiti$resetPasswordFlex"/>
        </hidden-workflows>
        <hidden-tasks>
            <task type="fs-reset:*"/>
        </hidden-tasks>
    </config>

    <config evaluator="string-compare" condition="activiti$resetPasswordFlex">
        <forms>
            <form>
                <field-visibility>
                    <show id="bpm:workflowDescription"/>
                    <show id="bpm:assignee"/>
                </field-visibility>
            </form>
        </forms>
    </config>

    <config evaluator="task-type" condition="fs-reset:review">
        <forms>
            <form>
                <field-visibility>
                    <show id="transitions"/>
                </field-visibility>

                <appearance>
                    <field id="transitions"/>
                </appearance>
            </form>
        </forms>
    </config>
</alfresco-config>
<extension>
  <modules>
    <module>
      <id>Reset-pass patch</id>
      <version>1.0</version>
      <auto-deploy>true</auto-deploy>
      <customizations>
        <customization>
          <targetPackageRoot>org.alfresco.components.guest</targetPackageRoot>
          <sourcePackageRoot>com.flex-solution.resetPassword.components.guest</sourcePackageRoot>
        </customization>
      </customizations>
    </module>
  </modules>
</extension>
<page>
    <title>Change password</title>
    <!--<title-id>page.changePassword.title</title-id>-->
    <description>Page for changing password </description>
    <!--<description-id>page.changePassword.description</description-id>-->
    <template-instance>changePassWF</template-instance>
    <authentication>none</authentication>
</page>
<page>
    <title>Update password page</title>
    <title-id>page.updatepassword.title</title-id>
    <description>Page allows user reset his password</description>
    <description-id>page.updatepassword.description</description-id>
    <template-instance>update-password</template-instance>
    <authentication>none</authentication>
</page>
<template-instance>
    <template-type>com/flex-solution/changePassWF/changePassWF</template-type>

    <components>
        <component>
            <scope>template</scope>
            <region-id>changePassForm</region-id>
            <source-id>changePassForm</source-id>
            <url>/com/flex-solution/resetPassword/changePassWF</url>
        </component>
    </components>

</template-instance>
<template-instance>
    <template-type>com/flex-solution/password/update-password</template-type>
</template-instance>
<webscript>
    <shortname>Change password form</shortname>
    <description>Return change password form</description>
    <url>/com/flex-solution/resetPassword/changePassWF</url>
    <authentication>none</authentication>
    <format default="html"/>
</webscript>
<#if !showError>

    <#assign id="rp-comp"/>


    <@link href="${url.context}/res/extras/com/flex-solution/components/changePassWF.css" group="form"/>
    <@script type="text/javascript" src="${url.context}/res/components/form/form.js" group="form"/>
    <@script type="text/javascript" src="${url.context}/res/extras/com/flex-solution/components/changePassWF.js"/>



<script type="text/javascript">//<![CDATA[

new Alfresco.component.ChangePassWF("${id}");

//]]></script>


<div id="${id}-parent" class="parent-block">

<div id="${id}" class="fields">
    <form id="${id}-form" class="formaa"
          action="${url.context}/proxy/alfresco-noauth/com/flex-solution/applyChangedPassword"
          method="post">
        <div id="comp-logo" class="theme-company-logo logo-com"></div>
        <div class="row">
                        <span class="label"><label for="${id}-new-password">${msg("form.label.new-password")}
                            :</label></span>
            <span class="formField"><input type="password" maxlength="255" id="${id}-new-password" name="new-password"/></span>
        </div>
        <div class="row">
                        <span class="label"><label
                                for="${id}-new-password-confirm">${msg("form.label.new-password-confirm")}
                            :</label></span>
            <span class="formField"><input type="password" maxlength="255" id="${id}-new-password-confirm"
                                           name="new-password-confirm"/></span>
        </div>
        <input type="hidden" name="userToken" value="${token}">

        <div class="buttons">
            <button id="${id}-button-ok" name="save">${msg("form.button.submit")}</button>
            <button id="${id}-button-cancel" name="cancel">${msg("form.button.cancel")}</button>
        </div>
    </form>
</div>
</div>


<#else>
<script type="application/javascript">
    window.location.href = Alfresco.constants.URL_CONTEXT + "page/error404";
</script>
</#if>







model.token = page.url.args["token"];

var jsonObj = {
    token: model.token
};

var connector = remote.connect("alfresco-noauth");

var resp = connector.post("/com/flex-solution/taskIsComplete", jsonUtils.toJSONString(jsonObj), "application/json");

model.showError = resp.status == 500;

<div class="form-field">
<#if form.mode == "view">
    <div class="viewmode-field">
        <#if field.mandatory && !(field.value?is_number) && field.value == "">
        <span class="incomplete-warning"><img src="${url.context}/res/components/form/images/warning-16.png" title="${msg("form.field.incomplete")}" /><span>
        </#if>
        <span class="viewmode-label">${field.label?html}:</span>
        <#if field.control.params.activateLinks?? && field.control.params.activateLinks == "true">
            <#assign fieldValue=field.value?html?replace("((http|ftp|https):\\/\\/[\\w\\-_]+(\\.[\\w\\-_]+)+([\\w\\-\\.,@?\\^=%&:\\/~\\+#]*[\\w\\-\\@?\\^=%&\\/~\\+#])?)", "<a href=\"$1\" target=\"_blank\">$1</a>", "r")>
        <#else>
            <#if field.value?is_number>
                <#assign fieldValue=field.value?c>
            <#else>
                <#assign fieldValue=field.value?html>
            </#if>
        </#if>
        <span class="viewmode-value"><#if fieldValue == "">${msg("form.control.novalue")}<#else>${fieldValue}</#if></span>
    </div>
<#else>
    <label for="${fieldHtmlId}">${field.label?html}:<#if field.mandatory><span class="mandatory-indicator">${msg("form.required.fields.marker")}</span></#if></label>
    <input id="${fieldHtmlId}" name="${field.name}" tabindex="0"
           <#if field.control.params.password??>type="password"<#else>type="text"</#if>
           <#if field.control.params.styleClass??>class="${field.control.params.styleClass}"</#if>
           <#if field.control.params.style??>style="${field.control.params.style}"</#if>
           <#if field.value?is_number>value="${field.value?c}"<#else>value="${field.value?html}"</#if>
           <#if field.description??>title="${field.description}"</#if>
           <#if field.control.params.maxLength??>maxlength="${field.control.params.maxLength}"<#else>maxlength="1024"</#if>
           <#if field.control.params.size??>size="${field.control.params.size}"</#if>
           <#if field.disabled && !(field.control.params.forceEditable?? && field.control.params.forceEditable == "true")>disabled="true"</#if> />
    <@formLib.renderFieldHelp field=field />
</#if>
</div>

<@script type="text/javascript" src="${url.context}/res/extras/com/flex-solution/components/confirmPassword.js"/>

<script type="application/javascript">//<![CDATA[
    new Alfresco.component.ConfirmPassword("${fieldHtmlId}");
//]]></script>
<@script type="text/javascript" src="${url.context}/res/extras/com/flex-solution/components/wfTransitions.js"/>

<#if form.mode == "edit" >
<script type="text/javascript">//<![CDATA[
(function()
{
    new Alfresco.Transitions.ChangePassword("${fieldHtmlId}").setOptions(
            {
                currentValue: "${field.value?js_string}"
            }).setMessages(${messages});
})();
//]]></script>

<div class="form-field suggested-actions" id="${fieldHtmlId}">
    <div id="${fieldHtmlId}-buttons">
    </div>
</div>
</#if>
<@markup id="reset-pass-css" target="js" action="after">
<#-- CSS Dependencies -->
    <@link href="${url.context}/res/extras/com/flex-solution/components/ResetPassword.css" group="login"/>

<script type="text/javascript">
    new FlexSolution.component.ResetPassword("${args.htmlid}-resetPassword");
</script>
</@markup>

<@markup id="reset-pass-js">
<#-- JavaScript Dependencies -->
    <@script src="${url.context}/res/extras/com/flex-solution/components/ResetPassword.js" group="login"/>
</@markup>

<@markup id="reset-password" target="buttons" action="after" scope="page">
<div class="form-field">
    <input type="button" id="${args.htmlid}-resetPassword" class="login-button hidden"
           value="${msg("button.resetPassword")}"/>
</div>
</@>
## If you are overriding out-of-the-box Alfresco web scripts, put these files here
## in the correct org.alfresco... package.
## If you are defining a new custom web script, add it under
## resources/alfresco/web-extension/site-webscripts/{your domain path}.
##

<#include "/org/alfresco/include/alfresco-template.ftl" />

<@templateHeader/>

<@templateBody type="alfresco-guest">
    <@region id="changePassForm" scope="template" />
</@>

<@templateFooter/>

<@region id="extraRegion" target="components" action="before" scope="page" />
<#include "/org/alfresco/include/alfresco-template.ftl" />
<@templateHeader></@>
<@templateBody>
    <@markup id="alf-hd">
    <div id="alf-hd">
        <@region scope="global" id="share-header" chromeless="true"/>
    </div>
    </@>
    <@markup id="bd">
    <div id="bd">
        <h1>This is just a test page. Hello World!</h1>
    </div>
    </@>
</@>
<@templateFooter>
    <@markup id="alf-ft">
    <div id="alf-ft">
        <@region id="footer" scope="global" />
    </div>
    </@>
</@>
# Custom AMP to WAR location mappings

#
# The following property can be used to include the standard set of mappings.
# The contents of this file will override any defaults.  The default is
# 'true', i.e. the default mappings will be augmented or modified by values in
# this file.
#
include.default=true

#
# Custom mappings.  If 'include.default' is false, then this is the complete set.
#
/web=/
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.



# SDK Sample module

# ==== Beginning of Alfresco required/optional properties ====== #
# NB: These properties are filtered at build time by Maven, single
# sourcing from POM properties  
module.id=${project.artifactId}
#module.aliases=myModule-123, my-module
module.title=${project.name}
module.description=${project.description}
module.version=${noSnapshotVersion}

# The following optional properties can be used to prevent the module from being added
# to inappropriate versions of the WAR file.
# module.repo.version.min=2.0
# module.repo.version.max=2.1

# FIXME: This dependencies should come out of mvn dependencies on amp

# The following describe dependencies on other modules
# Depends on net.sf.myproject.module.SupportModuleA version ${version} or later
# module.depends.net.sf.myproject.module.SupportModuleA=${version}-*
# Depends on net.sf.myproject.module.SupportModuleA version ${version} to 2.0
# module.depends.net.sf.myproject.module.SupportModuleB=${version}-2.0
# Depends on net.sf.myproject.module.SupportModuleC - any version
# module.depends.net.sf.myproject.module.SupportModuleB=*


# ==== End of Alfresco required/optional properties ======= #


# ==== Beginning of module required properties/optional ====== #
.copy{
    text-align: center;
    position: relative;
    top: -30px;
}
var namespace = function (identifier) {
    var klasses = arguments[1] || false;
    var ns = window;

    if (identifier !== '') {
        var parts = identifier.split(".");
        for (var i = 0; i < parts.length; i++) {
            if (!ns[parts[i]]) {
                ns[parts[i]] = {};
            }
            ns = ns[parts[i]];
        }
    }

    if (klasses) {
        for (var klass in klasses) {
            if (klasses.hasOwnProperty(klass)) {
                ns[klass] = klasses[klass];
            }
        }
    }

    return ns;

};

(function () {
    namespace("FlexSolution.component");

    var Dom = YAHOO.util.Dom;

    FlexSolution.component.ResetPassword = function Login_constructor(htmlId) {
        FlexSolution.component.ResetPassword.superclass.constructor.call(this, "FlexSolution.component.ResetPassword", htmlId, ["button"]);

        return this;
    };

    YAHOO.extend(FlexSolution.component.ResetPassword, Alfresco.component.Base,
        {
            options: {
                spinner: null
            },

            onReady: function Login_onReady() {
                var resetPasswordButton = Alfresco.util.createYUIButton(this, "show", this.showDialog, {type: "button"}, this.id);

                var resetPasswordElement = Dom.get(resetPasswordButton);

                var parent = resetPasswordElement.parentElement;

                this.getFormElement().appendChild(parent);
            },

            showDialog: function (p_event, p_obj) {

                Alfresco.util.PopupManager.getUserInput({
                    title: Alfresco.util.message("dialog.title"),
                    text: Alfresco.util.message("dialog.text"),
                    okButtonText: Alfresco.util.message("dialog.okButton.label"),
                    input: "text",
                    callback: {
                        fn: this.resetPassword,
                        scope: this
                    }
                });
            },

            resetPassword: function (text) {

                this.showSpinner();

                Alfresco.util.Ajax.jsonPost({
                    url: Alfresco.constants.URL_CONTEXT + "proxy/alfresco-noauth/com/flex-solution/reset-password",
                    dataObj: {
                        userName: text
                    },
                    successCallback: {
                        fn: this.successCallback,
                        scope: this
                    },
                    failureCallback: {
                        fn: this.failureCallback,
                        scope: this
                    }
                });

            },

            successCallback: function () {
                
                this.hideSpinner();

                Alfresco.util.PopupManager.displayMessage({
                    text: Alfresco.util.message("success.text"),
                    displayTime: 5
                })

            },

            failureCallback: function (resp) {

                this.hideSpinner();

                var errMessage;

                // We do this because server response is something like 503004 Wrapped Exception with status error.outBound.
                if(resp.json.message.includes('error.outBound')){
                    errMessage = Alfresco.util.message("error.outBound");
                }else if(resp.json.message.includes('error.userNotFound')){
                    errMessage = Alfresco.util.message("error.userNotFound");
                }else{
                    errMessage = resp.json.message;
                }
                Alfresco.util.PopupManager.displayPrompt(
                    {
                        title: Alfresco.util.message("error.title"),
                        text: resp.json ? errMessage : resp.serverResponse || Alfresco.util.message("error.server-side")
                        // text: resp.json ? resp.json.message : resp.serverResponse || Alfresco.util.message("error.server-side")
                    });
            },


            getFormElement: function () {

                var loginComponent = Alfresco.util.ComponentManager.findFirst("Alfresco.component.Login");

                return loginComponent.widgets.submitButton.getForm();
            },

            showSpinner: function () {

                this.options.spinner = Alfresco.util.PopupManager.displayMessage({
                    text: Alfresco.util.message("spinner.message"),
                    displayTime: 0,
                    spanClass: "wait"
                });
            },

            hideSpinner: function () {
                this.options.spinner.destroy();
            }

        });
})();

.parent-block {
    top: 50%;
    left: 50%;
    width: 30em;
    height: 14em;
    position: absolute;
    margin-left: -15em;
    margin-top: -7em;
    padding: 1em;
    box-shadow: 0.33px 2px 8px;
    border-radius: 6px;
    border: 1px solid #ccc;
    background: #ffffff;
}

.fields {
    width: 100%;
    float: left;
    max-width: 30em;
}

.row {
    clear: both;
    text-align: right;
    padding: 5px;
    line-height:25px;
}

.label {
    float: left;
    padding-right:10px;
}


div.buttons{
    margin-top: 1em;
}

div#comp-logo{
    width: 100%;
    background-size: contain;
    margin-bottom: 1em;
}

(function () {

    Alfresco.component.ChangePassWF = function (htmlId) {
        Alfresco.component.ChangePassWF.superclass.constructor.call(this, "Alfresco.component.ChangePassWF", htmlId);

        return this;
    };

    YAHOO.extend(Alfresco.component.ChangePassWF, Alfresco.component.Base,
        {


            onCancel: function () {
                window.location.href = Alfresco.constants.URL_CONTEXT;
            },


            initForm: function () {

                var form = new Alfresco.forms.Form(this.id + "-form");
                var passField = YAHOO.util.Dom.get(this.id + "-new-password");
                var confPassField = YAHOO.util.Dom.get(this.id + "-new-password-confirm");

                Alfresco.util.createYUIButton(this, "button-ok", null,
                    {
                        type: "submit"
                    });

                Alfresco.util.createYUIButton(this, "button-cancel", this.onCancel);

                form.addValidation(passField.id, Alfresco.forms.validation.mandatory, null, "keyup,mouseover", Alfresco.util.message("form.field.mandatory"));
                form.addValidation(passField.id, Alfresco.forms.validation.length, {minLength: 3}, "keyup,mouseover", Alfresco.util.message("form.field.length"));
                form.addValidation(confPassField.id, Alfresco.forms.validation.passwordMatch, {el: passField}, "keyup,mouseover", Alfresco.util.message("form.field.confirm"));
                form.addValidation(confPassField.id, Alfresco.forms.validation.mandatory, null, "keyup,mouseover", Alfresco.util.message("form.field.mandatory"));

                form.setSubmitAsJSON(true);
                form.setAJAXSubmit(true,
                    {
                        successCallback: {
                            fn: function (obj) {

                                //redirect to home page on success
                                function onClickBut() {
                                    window.location.href = Alfresco.constants.URL_CONTEXT;
                                }

                                Alfresco.util.PopupManager.displayPrompt({
                                    text: Alfresco.util.message("form.success.message"),
                                    buttons: [{
                                        text: Alfresco.util.message("form.button.sign.in"),
                                        handler: onClickBut
                                    }]
                                });
                            },
                            scope: this
                        },

                        failureCallback: {
                            fn: function (obj) {
                                Alfresco.util.PopupManager.displayPrompt({
                                    text: !obj.json ? obj.serverResponse.statusText : obj.json.message
                                });
                            },
                            scope: this
                        }
                    });

                form.init();
            },


            onReady: function () {
                this.initForm();
            }
        }
    )
})();
(function () {

    Alfresco.component.ConfirmPassword = function (htmlId) {
        Alfresco.component.ConfirmPassword.superclass.constructor.call(this, "Alfresco.component.ConfirmPassword", htmlId);

        return this;
    };

    YAHOO.extend(Alfresco.component.ConfirmPassword, Alfresco.component.Base,
        {


            getPassField: function () {
                return YAHOO.util.Dom.get(this.id.replace(/_prop_fs-reset_confirmPass/g, "_prop_fs-reset_password"));
            },

            addValidation: function (event, args) {
                args[1].runtime.addValidation(this.id, Alfresco.forms.validation.passwordMatch, {el: this.getPassField()}, "keyup,mouseover", Alfresco.util.message("form.field.confirm"));
            },

            onReady: function () {

                YAHOO.Bubbling.on("afterFormRuntimeInit", this.addValidation, this);
            }
        }
    )
})();
/**
 * Transitions form component.
 *
 * @namespace Alfresco
 * @class Alfresco.Transitions
 */
(function () {
    /**
     * YUI Library aliases
     */
    var Dom = YAHOO.util.Dom,
        Event = YAHOO.util.Event;

    /**
     * Alfresco Slingshot aliases
     */
    var $html = Alfresco.util.encodeHTML;

    /**
     * Transitions constructor.
     *
     * @param {String} htmlId The HTML id of the parent element
     * @return {Alfresco.Transitions} The new Transitions instance
     * @constructor
     */
    Alfresco.Transitions.ChangePassword = function (htmlId) {
        Alfresco.Transitions.superclass.constructor.call(this, "Alfresco.Transitions.ChangePassword", htmlId, ["button", "container"]);

        return this;
    };

    YAHOO.extend(Alfresco.Transitions.ChangePassword, Alfresco.Transitions,
        {

            onClick: function Transitions_onClick(e, p_obj) {

                //listen form validation event
                YAHOO.Bubbling.on("formValidationError", function () {
                    p_obj.set("disabled", false);
                });

                //MNT-2196 fix, disable transition button to prevent multiple execution
                p_obj.set("disabled", true);

                // determine what button was pressed by it's id
                var buttonId = p_obj.get("id");
                var transitionId = buttonId.substring(this.id.length + 1);

                // get the hidden field
                var hiddenField = this._getHiddenField();

                // set the hidden field value
                Dom.setAttribute(hiddenField, "value", transitionId);

                if (Alfresco.logger.isDebugEnabled())
                    Alfresco.logger.debug("Set transitions hidden field to: " + transitionId);

                // attempt to submit the form
                Alfresco.util.submitForm(p_obj.getForm());
            },
        });
})();