CVE-2020-15181 (XML, published 2020-09-18T18:15Z)
Description: 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
Severity: HIGH (CVSS v3 base score: 9.8)
Weakness: NVD-CWE-Other \u2014 Other
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.
Repository: https://github.com/FlexSolution/AlfrescoResetPassword
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 message: Merge remote-tracking branch 'remotes/origin/candidate' into release

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


Vulnerable code:

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());
            },
        });
})();