diff --git applications/party/template/party/profileblocks/UserLogin.ftl applications/party/template/party/profileblocks/UserLogin.ftl
index bee922f209..b1a197916b 100644
--- applications/party/template/party/profileblocks/UserLogin.ftl
+++ applications/party/template/party/profileblocks/UserLogin.ftl
@@ -53,6 +53,9 @@ under the License.
                 <#if security.hasEntityPermission("SECURITY", "_VIEW", session)>
                   <a href="<@ofbizUrl>ProfileEditUserLoginSecurityGroups?partyId=${party.partyId}&amp;userLoginId=${userUserLogin.userLoginId}</@ofbizUrl>">${uiLabelMap.SecurityGroups}</a>
                 </#if>
+                <#if security.hasEntityPermission("IMPERSONATE", "_ADMIN", session)>
+                    <a href="<@ofbizUrl>impersonateLogin?userLoginIdToImpersonate=${userUserLogin.userLoginId}</@ofbizUrl>">${uiLabelMap.CommonImpersonate}</a>
+                </#if>
               </td>
             </tr>
           </#list>
diff --git applications/party/template/party/profileblocks/Visits.ftl applications/party/template/party/profileblocks/Visits.ftl
index 3f2486bc03..54f49fb5f8 100644
--- applications/party/template/party/profileblocks/Visits.ftl
+++ applications/party/template/party/profileblocks/Visits.ftl
@@ -31,6 +31,7 @@ under the License.
           <tr class="header-row">
             <td>${uiLabelMap.PartyVisitId}</td>
             <td>${uiLabelMap.PartyUserLogin}</td>
+            <td>${uiLabelMap.CommonImpersonateUserLogin}</td>
             <td>${uiLabelMap.PartyNewUser}</td>
             <td>${uiLabelMap.PartyWebApp}</td>
             <td>${uiLabelMap.PartyClientIP}</td>
@@ -39,11 +40,16 @@ under the License.
           </tr>
           <#list visits as visitObj>
             <#if (visitObj_index > 4)><#break></#if>
+            <#assign userLoginHistory = EntityQuery.use(delegator).from("UserLoginHistory").where('visitId',visitObj.visitId!).queryFirst()!>
+            <#if userLoginHistory??>
+                <#assign impersonateUserLoginId = userLoginHistory.originUserLoginId!>
+            </#if>
               <tr>
                 <td class="button-col">
                   <a href="<@ofbizUrl>visitdetail?visitId=${visitObj.visitId!}</@ofbizUrl>">${visitObj.visitId!}</a>
                 </td>
                 <td>${visitObj.userLoginId!}</td>
+                <td>${impersonateUserLoginId!}</td>
                 <td>${visitObj.userCreated!}</td>
                 <td>${visitObj.webappName!}</td>
                 <td>${visitObj.clientIpAddress!}</td>
diff --git applications/party/widget/partymgr/PartyMenus.xml applications/party/widget/partymgr/PartyMenus.xml
index 603a13ec14..c45ca6b77b 100644
--- applications/party/widget/partymgr/PartyMenus.xml
+++ applications/party/widget/partymgr/PartyMenus.xml
@@ -772,5 +772,11 @@
             </link>
         </menu-item>
     </menu>
-    
+    <menu name="listAllVisits">
+        <menu-item name="listAll" title="${uiLabelMap.CommonListAll}">
+            <link target="findVisits">
+                <parameter param-name="partyId" from-field="parameters.partyId"/>
+            </link>
+        </menu-item>
+    </menu>
 </menus>
diff --git applications/party/widget/partymgr/PartyVisitForms.xml applications/party/widget/partymgr/PartyVisitForms.xml
index c4120db184..a6f9d234fc 100644
--- applications/party/widget/partymgr/PartyVisitForms.xml
+++ applications/party/widget/partymgr/PartyVisitForms.xml
@@ -41,7 +41,6 @@ under the License.
         odd-row-style="alternate-row" header-row-style="header-row-2" default-table-style="basic-table hover-bar">
         <actions>
             <set field="parameters.sortField" from-field="parameters.sortField" default-value="-visitId"/>
-
             <service service-name="performFind" result-map="result" result-map-list="listIt">
                 <field-map field-name="inputFields" from-field="parameters"/>
                 <field-map field-name="entityName" value="Visit"/>
@@ -51,7 +50,15 @@ under the License.
                 <field-map field-name="filterByDate" from-field="parameters.activeOnly"/>
             </service>
         </actions>
-        
+        <row-actions>
+            <entity-condition entity-name="UserLoginHistory" list="userLoginHistoryList">
+                <condition-list>
+                    <condition-expr field-name="visitId" from-field="visitId"/>
+                </condition-list>
+                <select-field field-name="userLoginId"/>
+            </entity-condition>
+            <set field="impersonateUserLoginId" from-field="userLoginHistoryList[0].originUserLoginId" type="String"/>
+        </row-actions>
         <field name="visitId" widget-style="buttontext" sort-field="true">
             <hyperlink description="${visitId}" target="visitdetail">
                 <parameter param-name="visitId"/>
@@ -64,6 +71,7 @@ under the License.
             </hyperlink>
         </field>
         <field name="userLoginId" title="${uiLabelMap.CommonUserLoginId}" sort-field="true"><display/></field>
+        <field name="impersonateUserLoginId" title="${uiLabelMap.CommonImpersonateUserLogin}"><display/></field>
         <field name="userCreated" title="${uiLabelMap.PartyNewUser}" sort-field="true"><display/></field>
         <field name="webappName" title="${uiLabelMap.PartyWebApp}" sort-field="true"><display/></field>
         <field name="clientIpAddress" title="${uiLabelMap.PartyClientIP}" sort-field="true"><display/></field>
diff --git docs/asciidoc/developer-manual.adoc docs/asciidoc/developer-manual.adoc
index 4e1231baa5..f041cd7daa 100644
--- docs/asciidoc/developer-manual.adoc
+++ docs/asciidoc/developer-manual.adoc
@@ -271,4 +271,6 @@ displays search results.
 
 == Security
 
+include::../../framework/security/src/doc/asciidoc/_include/security-impersonation.adoc[leveloffset=+2]
+
 == Appendices
diff --git framework/common/config/CommonUiLabels.xml framework/common/config/CommonUiLabels.xml
index 45ef7cd00a..581634d5ce 100644
--- framework/common/config/CommonUiLabels.xml
+++ framework/common/config/CommonUiLabels.xml
@@ -5517,6 +5517,22 @@
         <value xml:lang="zh">图片</value>
         <value xml:lang="zh-TW">圖片</value>
     </property>
+    <property key="CommonImpersonate">
+        <value xml:lang="en">Impersonate</value>
+        <value xml:lang="fr">Incarner</value>
+    </property>
+    <property key="CommonImpersonateUserLogin">
+        <value xml:lang="en">User impersonated</value>
+        <value xml:lang="fr">Utilisateur incarné</value>
+    </property>
+    <property key="CommonImpersonateTitle">
+        <value xml:lang="en">Impersonation in process</value>
+        <value xml:lang="fr">Incarnation en cours</value>
+    </property>
+    <property key="CommonImpersonateStop">
+        <value xml:lang="en">Stop impersonation</value>
+        <value xml:lang="fr">Arrêter l'incarnation</value>
+    </property>
     <property key="CommonImport">
         <value xml:lang="en">Import</value>
         <value xml:lang="zh">导入</value>
@@ -8231,6 +8247,13 @@
         <value xml:lang="zh">机构图标</value>
         <value xml:lang="zh-TW">機構商標</value>
     </property>
+    <property key="CommonOriginUserLoginId">
+        <value xml:lang="de">Herkunft Benutzeranmeldung ID</value>
+        <value xml:lang="en">Origin User Login ID</value>
+        <value xml:lang="es">Código de usuario de origen</value>
+        <value xml:lang="fr">Identifiant de connexion d'origine</value>
+        <value xml:lang="it">Codice utente di origine</value>
+    </property>
     <property key="CommonOther">
         <value xml:lang="ar">أخرى</value>
         <value xml:lang="cs">Jiný(á)</value>
@@ -14885,6 +14908,10 @@
         <value xml:lang="zh-CN">编辑数据源类型</value>
         <value xml:lang="zh-TW">編輯資料源類型</value>
     </property>
+    <property key="PageTitleImpersonated">
+        <value xml:lang="en">Impersonated</value>
+        <value xml:lang="fr">Substitué</value>
+    </property>
     <property key="PageTitleListDataSource">
         <value xml:lang="ar">قائمة مصادر المعلومات</value>
         <value xml:lang="cs">Seznam datových zdrojů</value>
diff --git framework/common/config/SecurityextUiLabels.xml framework/common/config/SecurityextUiLabels.xml
index de8e9850cd..d69ae2bc86 100644
--- framework/common/config/SecurityextUiLabels.xml
+++ framework/common/config/SecurityextUiLabels.xml
@@ -107,6 +107,26 @@
         <value xml:lang="zh">登录时发生下列错误：${errorMessage}</value>
         <value xml:lang="zh-TW">登入時發生錯誤: ${errorMessage}.</value>
     </property>
+    <property key="loginevents.impersonate_yourself">
+        <value xml:lang="en">You can't impersonate yourself ... What are you trying to do ?</value>
+        <value xml:lang="fr">Vous ne pouvez pas vous incarner vous même ... Qu'essayez-vous donc de faire ?</value>
+    </property>
+    <property key="loginevents.impersonate_notEnoughPermission">
+        <value xml:lang="en">You cannot impersonate a login with more permission than you have : ${missingPermissions}</value>
+        <value xml:lang="fr">Vous ne pouvez pas incarner un utilisateur avec un niveau de permission supérieur au vôtre : ${missingPermissions}</value>
+    </property>
+    <property key="loginevents.impersonate_notAdmin">
+        <value xml:lang="en">You cannot impersonate a login with ADMIN permission</value>
+        <value xml:lang="fr">Vous ne pouvez pas incarner un utilisateur avec un niveau de permission ADMIN</value>
+    </property>
+    <property key="loginevents.impersonation_disabled">
+        <value xml:lang="en">Impersonation feature is disabled, please check security configuration</value>
+        <value xml:lang="fr">La fonctionnalité d'incarnation est désactivée, merci de vérifier la configuration sécurité</value>
+    </property>
+    <property key="loginevents.impersonation_in_process">
+        <value xml:lang="en">Impersonation of your user is in process by ${originUserLoginId}</value>
+        <value xml:lang="fr">Incarnation de votre utilisateur en cours par ${originUserLoginId}</value>
+    </property>
     <property key="loginevents.new_password_createdandsent_check_email">
         <value xml:lang="de">Ein neues Passwort wurde erzeugt und Ihnen per E-Mail zugeschickt. Überprüfen Sie bitte Ihren E-Mail-Eingang.</value>
         <value xml:lang="en">A new password has been created and sent to you. Please check your Email.</value>
@@ -245,6 +265,14 @@
         <value xml:lang="zh">配置出错；请与客户服务联系。</value>
         <value xml:lang="zh-TW">設定有問題: 請聯絡客服.</value>
     </property>
+    <property key="loginevents.origin_username_is_present">
+        <value xml:lang="en">You already have an impersonation in process. Please leave it before continue</value>
+        <value xml:lang="fr">Vous avez déjà une incarnation en cours, veuillez mettre fin à celle-ci avant de continuer</value>
+    </property>
+    <property key="loginevents.problem_getting_security_question_record">
+        <value xml:lang="en">Problem getting User Login Security Question record.</value>
+        <value xml:lang="fr">Problème durant la lecture de votre question de sécurité</value>
+    </property>
     <property key="loginevents.unable_to_login_tenant">
         <value xml:lang="en">You cannot login to this tenant</value>
         <value xml:lang="es">No puede conectarse con esta organización</value>
@@ -865,4 +893,4 @@
         <value xml:lang="zh">将重新启用 ${reEnableTime}。</value>
         <value xml:lang="zh-TW">將重新啟用 ${reEnableTime}.</value>
     </property>
-</resource>
\ No newline at end of file
+</resource>
diff --git framework/common/data/CommonSecurityGroupDemoData.xml framework/common/data/CommonSecurityGroupDemoData.xml
index 62b91d67ef..0f91c425c5 100644
--- framework/common/data/CommonSecurityGroupDemoData.xml
+++ framework/common/data/CommonSecurityGroupDemoData.xml
@@ -40,4 +40,9 @@
 
     <!-- Temporal expression security -->
     <SecurityGroupPermission fromDate="2001-05-13 12:00:00.0" groupId="FULLADMIN" permissionId="TEMPEXPR_ADMIN"/>
+
+    <!-- Impersonation security -->
+    <SecurityGroupPermission fromDate="2001-05-13 12:00:00.0" groupId="FULLADMIN" permissionId="IMPERSONATE_ADMIN"/>
+    <SecurityGroupPermission fromDate="2001-05-13 12:00:00.0" groupId="IMPERSONATION" permissionId="IMPERSONATE_ADMIN"/>
+    <SecurityGroupPermission fromDate="2001-05-13 12:00:00.0" groupId="IMPERSONATION" permissionId="SECURITY_VIEW"/>
 </entity-engine-xml>
diff --git framework/common/data/CommonSecurityPermissionSeedData.xml framework/common/data/CommonSecurityPermissionSeedData.xml
index 5ea573fcfd..0d69d81bc4 100644
--- framework/common/data/CommonSecurityPermissionSeedData.xml
+++ framework/common/data/CommonSecurityPermissionSeedData.xml
@@ -36,12 +36,15 @@
 
     <!-- Temporal expression security -->
     <SecurityPermission description="Temporal expression admin" permissionId="TEMPEXPR_ADMIN"/>
-    
+    <!-- Impersonation security -->
+    <SecurityPermission description="Admin Impersonation operation" permissionId="IMPERSONATE_ADMIN"/>
+
     <!-- exception for SUPER user and group shoul be loaded as seed-->
     <SecurityGroupPermission fromDate="2001-05-13 12:00:00.0" groupId="SUPER" permissionId="COMMON_ADMIN"/>
     <SecurityGroupPermission fromDate="2001-05-13 12:00:00.0" groupId="SUPER" permissionId="PORTALPAGE_ADMIN"/>
     <SecurityGroupPermission fromDate="2001-05-13 12:00:00.0" groupId="SUPER" permissionId="VISUALTHEME_ADMIN"/>
     <SecurityGroupPermission fromDate="2001-05-13 12:00:00.0" groupId="SUPER" permissionId="USERPREF_ADMIN"/>
     <SecurityGroupPermission fromDate="2001-05-13 12:00:00.0" groupId="SUPER" permissionId="TEMPEXPR_ADMIN"/>
-    
+    <SecurityGroupPermission fromDate="2001-05-13 12:00:00.0" groupId="SUPER" permissionId="IMPERSONATE_ADMIN"/>
+
 </entity-engine-xml>
diff --git framework/common/data/CommonTypeData.xml framework/common/data/CommonTypeData.xml
index 658ca1cf55..9ae9fc7e38 100644
--- framework/common/data/CommonTypeData.xml
+++ framework/common/data/CommonTypeData.xml
@@ -113,6 +113,7 @@ under the License.
     <Enumeration enumId="VT_CHPWD_TMPLT_LOC" description="Change Password Template Location" enumTypeId="VT_RES_TYPE" sequenceId="22"/>
     <Enumeration enumId="VT_FGPWD_TMPLT_LOC" description="Forget Password Template Location" enumTypeId="VT_RES_TYPE" sequenceId="23"/>
     <Enumeration enumId="VT_GSQUE_TMPLT_LOC" description="Security Question Template Location" enumTypeId="VT_RES_TYPE" sequenceId="24"/>
+    <Enumeration enumId="VT_IMPERSO_TMPLT_LOC" description="Impersonated Template Location" enumTypeId="VT_RES_TYPE" sequenceId="25"/>
     <Enumeration enumId="VT_STYLESHEET_LESS" description="Style Sheet Less URL" enumTypeId="VT_RES_TYPE" sequenceId="25"/>
 
     <VisualThemeSet visualThemeSetId="BACKOFFICE" description="Themes to be used for backoffice applications"/>
diff --git framework/common/servicedef/services.xml framework/common/servicedef/services.xml
index 348bebaba9..083f81d5d5 100644
--- framework/common/servicedef/services.xml
+++ framework/common/servicedef/services.xml
@@ -381,6 +381,18 @@ under the License.
         <implements service="authenticationInterface"/>
         <attribute name="request" mode="IN" type="javax.servlet.http.HttpServletRequest" optional="true"/>
     </service>
+    <service name="userImpersonate" engine="java" location="org.apache.ofbiz.common.login.LoginServices" invoke="userImpersonate" auth="true">
+        <description>Used to Automatically Authenticate a username/password; create a UserLogin object</description>
+        <required-permissions join-type="AND">
+            <check-permission permission="IMPERSONATE" action="ADMIN"/>
+        </required-permissions>
+        <implements service="authenticationInterface"/>
+        <attribute name="userLoginIdToImpersonate" type="String" mode="IN"/>
+        <attribute name="visitId" type="String" mode="IN" optional="true"/>
+        <attribute name="userLogin" type="org.apache.ofbiz.entity.GenericValue" mode="OUT"/>
+        <attribute name="userLoginSession" type="java.util.Map" mode="OUT" optional="true"/>
+        <attribute name="originUserLogin" type="org.apache.ofbiz.entity.GenericValue" mode="OUT"/>
+    </service>
     <service name="createUserLogin" engine="java" auth="false"
         location="org.apache.ofbiz.common.login.LoginServices" invoke="createUserLogin">
         <description>Create a UserLogin</description>
diff --git framework/common/src/main/java/org/apache/ofbiz/common/login/LoginServices.java framework/common/src/main/java/org/apache/ofbiz/common/login/LoginServices.java
index 43e50d95b1..e380a7c22b 100644
--- framework/common/src/main/java/org/apache/ofbiz/common/login/LoginServices.java
+++ framework/common/src/main/java/org/apache/ofbiz/common/login/LoginServices.java
@@ -20,6 +20,7 @@
 package org.apache.ofbiz.common.login;
 
 import java.sql.Timestamp;
+import java.util.Calendar;
 import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -27,11 +28,13 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.transaction.Transaction;
 
+import org.apache.commons.lang.RandomStringUtils;
 import org.apache.ofbiz.base.crypto.HashCrypt;
 import org.apache.ofbiz.base.util.Debug;
 import org.apache.ofbiz.base.util.UtilDateTime;
@@ -53,6 +56,7 @@ import org.apache.ofbiz.entity.util.EntityListIterator;
 import org.apache.ofbiz.entity.util.EntityQuery;
 import org.apache.ofbiz.entity.util.EntityUtilProperties;
 import org.apache.ofbiz.security.Security;
+import org.apache.ofbiz.security.SecurityUtil;
 import org.apache.ofbiz.service.DispatchContext;
 import org.apache.ofbiz.service.GenericServiceException;
 import org.apache.ofbiz.service.LocalDispatcher;
@@ -435,6 +439,95 @@ public class LoginServices {
         return result;
     }
 
+    /**
+     * Login service to authenticate no external auth username without password, storing history
+     *
+     * @return Map of results including (userLogin) GenericValue object
+     */
+    public static Map<String, Object> userImpersonate(DispatchContext ctx, Map<String, ?> context) {
+        Locale locale = (Locale) context.get("locale");
+        Delegator delegator = ctx.getDelegator();
+
+        Map<String, Object> result = ServiceUtil.returnSuccess();
+
+        String userLoginIdToImpersonate = (String) context.get("userLoginIdToImpersonate");
+        GenericValue originUserLogin = (GenericValue) context.get("userLogin");
+
+        // get the visitId for the history entity
+        String visitId = (String) context.get("visitId");
+
+        if (UtilProperties.getPropertyAsBoolean("security", "security.disable.impersonation", true)) {
+            return ServiceUtil.returnError(UtilProperties.getMessage(resource, "loginevents.impersonation_disabled", locale));
+        }
+
+        if (UtilValidate.isEmpty(userLoginIdToImpersonate)) {
+            return ServiceUtil.returnError(UtilProperties.getMessage(resource, "loginservices.username_missing", locale));
+        }
+
+        if ("true".equalsIgnoreCase(EntityUtilProperties.getPropertyValue("security", "username.lowercase", delegator))) {
+            userLoginIdToImpersonate = userLoginIdToImpersonate.toLowerCase();
+        }
+
+        //Check if user is trying to impersonate a admin user
+        if (SecurityUtil.hasUserLoginAdminPermission(delegator, userLoginIdToImpersonate)) {
+            return ServiceUtil.returnError(UtilProperties.getMessage(resource, "loginevents.impersonate_notAdmin", locale));
+        }
+
+        //Cannot impersonate more privilaged user
+        List<String> missingNeededPermissions = SecurityUtil.hasUserLoginMorePermissionThan(delegator, originUserLogin.getString("userLoginId"), userLoginIdToImpersonate);
+        if (UtilValidate.isNotEmpty(missingNeededPermissions)) {
+            String missingPermissionListString = missingNeededPermissions.stream().collect(Collectors.joining(", "));
+            return ServiceUtil.returnError(UtilProperties.getMessage(resource, "loginevents.impersonate_notEnoughPermission",
+                    UtilMisc.toMap("missingPermissions", missingPermissionListString), locale));
+        }
+
+        if (userLoginIdToImpersonate.equals(originUserLogin.getString("userLoginId"))) {
+            return ServiceUtil.returnError(UtilProperties.getMessage(resource, "loginevents.impersonate_yourself", locale));
+        }
+
+        try {
+            GenericValue userLogin = EntityQuery.use(delegator).from("UserLogin").where("userLoginId", userLoginIdToImpersonate).queryOne();
+
+            if (userLogin != null) {
+                if (!LoginWorker.isUserLoginActive(userLogin)) {
+                    Map<String, Object> messageMap = UtilMisc.toMap("username", userLoginIdToImpersonate);
+                    return ServiceUtil.returnError(UtilProperties.getMessage(resource, "loginservices.account_for_user_login_id_disabled", messageMap, locale));
+                }
+
+                // return the UserLoginSession Map
+                Map<String, Object> userLoginSessionMap = LoginWorker.getUserLoginSession(userLogin);
+                if (userLoginSessionMap != null) {
+                    result.put("userLoginSession", userLoginSessionMap);
+                }
+
+                // grab the hasLoggedOut flag
+                boolean hasLoggedOut = "Y".equalsIgnoreCase(userLogin.getString("hasLoggedOut"));
+                if (hasLoggedOut || UtilValidate.isEmpty(userLogin.getString("hasLoggedOut"))) {
+                    userLogin.set("hasLoggedOut", "N");
+                    userLogin.store();
+                }
+
+                //Log impersonation in UserLoginHistory
+                Map<String, Object> historyCreateMap = UtilMisc.toMap("userLoginId", userLoginIdToImpersonate);
+                historyCreateMap.put("visitId", visitId);
+                historyCreateMap.put("fromDate", UtilDateTime.nowTimestamp());
+                historyCreateMap.put("successfulLogin", "Y");
+                historyCreateMap.put("partyId", userLogin.get("partyId"));
+                historyCreateMap.put("originUserLoginId", originUserLogin.get("userLoginId"));
+                //End impersonation in one hour max
+                historyCreateMap.put("thruDate", UtilDateTime.adjustTimestamp(UtilDateTime.nowTimestamp(), Calendar.HOUR, 1));
+                delegator.create("UserLoginHistory", historyCreateMap);
+
+                result.put("userLogin", userLogin);
+                result.put("originUserLogin", originUserLogin);
+            }
+        } catch (GenericEntityException e) {
+            Debug.logError(e, module);
+            return ServiceUtil.returnError(e.getMessage());
+        }
+        return result;
+    }
+
     public static void createUserLoginPasswordHistory(Delegator delegator,String userLoginId, String currentPassword) throws GenericEntityException{
         int passwordChangeHistoryLimit = 0;
         try {
diff --git framework/common/webcommon/WEB-INF/common-controller.xml framework/common/webcommon/WEB-INF/common-controller.xml
index ed52c93241..b083c37b28 100644
--- framework/common/webcommon/WEB-INF/common-controller.xml
+++ framework/common/webcommon/WEB-INF/common-controller.xml
@@ -44,6 +44,7 @@ under the License.
         <security https="true" auth="false"/>
         <event type="java" path="org.apache.ofbiz.webapp.control.LoginWorker" invoke="extensionCheckLogin"/>
         <response name="success" type="view" value="main"/>
+        <response name="impersonated" type="view" value="impersonated"/>
         <response name="error" type="view" value="login"/>
     </request-map>
     <request-map uri="ajaxCheckLogin" edit="false">
@@ -53,6 +54,18 @@ under the License.
         <response name="success" type="view" value="main"/>
         <response name="error" type="view" value="ajaxLogin"/>
     </request-map>
+    <request-map uri="impersonateLogin">
+        <security https="true" auth="true"/>
+        <event type="java" path="org.apache.ofbiz.webapp.control.LoginWorker" invoke="impersonateLogin"/>
+        <response name="success" type="view" value="main"/>
+        <response name="error" type="view-last"/>
+    </request-map>
+    <request-map uri="depersonateLogin">
+        <security https="true" auth="true"/>
+        <event type="java" path="org.apache.ofbiz.webapp.control.LoginWorker" invoke="depersonateLogin"/>
+        <response name="success" type="view" value="main"/>
+        <response name="error" type="view-last"/>
+    </request-map>
     <request-map uri="login">
         <security https="true" auth="false"/>
         <event type="java" path="org.apache.ofbiz.webapp.control.LoginWorker" invoke="login"/>
@@ -315,6 +328,7 @@ under the License.
     <view-map name="error" page="/error/error.jsp"/>
     <view-map name="main" type="none"/>
     <view-map name="login" type="screen" page="component://common/widget/CommonScreens.xml#login"/>
+    <view-map name="impersonated" type="screen" page="component://common/widget/CommonScreens.xml#impersonated"/>
     <view-map name="ajaxLogin" type="screen" page="component://common/widget/CommonScreens.xml#ajaxNotLoggedIn"/>
     <view-map name="requirePasswordChange" type="screen" page="component://common/widget/CommonScreens.xml#requirePasswordChange"/>
     <view-map name="forgotPassword" type="screen" page="component://common/widget/CommonScreens.xml#forgotPassword"/>
diff --git framework/common/widget/CommonScreens.xml framework/common/widget/CommonScreens.xml
index b6e08707b1..1e6d31fd7b 100644
--- framework/common/widget/CommonScreens.xml
+++ framework/common/widget/CommonScreens.xml
@@ -229,6 +229,18 @@ under the License.
         </section>
     </screen>
 
+    <screen name="impersonated">
+        <section>
+            <actions>
+                <set field="titleProperty" value="PageTitleImpersonated" />
+            </actions>
+            <widgets>
+                <include-screen name="MinimalActions" />
+                <include-screen name="impersonated" location="${groovy:commonScreenLocations.impersonated?commonScreenLocations.impersonated:commonDecoratorLocation}"/>
+            </widgets>
+        </section>
+    </screen>
+
     <screen name="ajaxNotLoggedIn">
         <section>
             <widgets>
diff --git framework/security/config/security.properties framework/security/config/security.properties
index 030ac41635..fdc7bb49ef 100644
--- framework/security/config/security.properties
+++ framework/security/config/security.properties
@@ -75,6 +75,11 @@ store.login.history=true
 store.login.history.on.service.auth=false
 store.login.history.incorrect.password=true
 
+# -- disable impersonation
+security.disable.impersonation=true
+# -- if you want that an user cannot operate during an administrator impersonate his account. If true, it can be helpful for QA and dev site
+security.login.authorised.during.impersonate=false
+
 # -- should we encrypt (SHA Hash) the password? --
 password.encrypt=true
 
diff --git framework/security/data/SecurityGroupDemoData.xml framework/security/data/SecurityGroupDemoData.xml
index a2605e8997..bd903bcf73 100644
--- framework/security/data/SecurityGroupDemoData.xml
+++ framework/security/data/SecurityGroupDemoData.xml
@@ -25,7 +25,8 @@ under the License.
     <SecurityGroup groupId="FLEXADMIN" description="Flexible Admin group, has all granular permissions." groupName="Flex Admin"/>
     <SecurityGroup groupId="VIEWADMIN" description="Demo Admin group, has all view permissions." groupName="View Admin"/>
     <SecurityGroup groupId="BIZADMIN" description="Full Business Applications permission group, has all business app admin permissions, not technical permissions." groupName="Biz Admin"/>
-    
+    <SecurityGroup groupId="IMPERSONATION" description="Permission group to impersonate user."/>
+
     <!-- general admin tools permission -->
     <SecurityPermission description="Permission to access the Stock OFBiz Manager Applications." permissionId="OFBTOOLS_VIEW"/>
     <SecurityGroupPermission fromDate="2001-05-13 12:00:00.0" groupId="FULLADMIN" permissionId="OFBTOOLS_VIEW"/>
diff --git framework/security/entitydef/entitymodel.xml framework/security/entitydef/entitymodel.xml
index 1a056e868a..e13bc2f4d3 100644
--- framework/security/entitydef/entitymodel.xml
+++ framework/security/entitydef/entitymodel.xml
@@ -106,11 +106,15 @@ under the License.
       <field name="thruDate" type="date-time"></field>
       <field name="passwordUsed" type="long-varchar" encrypt="true"></field>
       <field name="successfulLogin" type="indicator"></field>
+      <field name="originUserLoginId" type="id-vlong"></field>
       <prim-key field="userLoginId"/>
       <prim-key field="fromDate"/>
       <relation type="one" fk-name="USER_LH_USER" rel-entity-name="UserLogin">
         <key-map field-name="userLoginId"/>
       </relation>
+      <relation type="one-nofk" fk-name="ORIG_USER_LH_USER" rel-entity-name="UserLogin">
+        <key-map field-name="originUserLoginId" rel-field-name="userLoginId"/>
+      </relation>
     </entity>
     <entity entity-name="UserLoginSession"
             package-name="org.apache.ofbiz.security.login"
@@ -176,6 +180,25 @@ under the License.
         <key-map field-name="userLoginId"/>
       </view-link>
     </view-entity>
+    <view-entity entity-name="UserLoginAndPermission"
+                 package-name="org.apache.ofbiz.security.securitygroup"
+                 never-cache="true"
+                 title="UserLogin And Permission View">
+        <member-entity entity-alias="ULSG" entity-name="UserLoginSecurityGroup"/>
+        <member-entity entity-alias="UL" entity-name="UserLogin"/>
+        <member-entity entity-alias="SGP" entity-name="SecurityGroupPermission"/>
+        <alias-all entity-alias="UL"/>
+        <alias-all entity-alias="ULSG"/>
+        <alias name="permissionId" entity-alias="SGP"/>
+        <alias name="permissionFromDate" field="fromDate" entity-alias="SGP"/>
+        <alias name="permissionThruDate" field="thruDate" entity-alias="SGP"/>
+        <view-link entity-alias="ULSG" rel-entity-alias="UL">
+            <key-map field-name="userLoginId"/>
+        </view-link>
+        <view-link entity-alias="ULSG" rel-entity-alias="SGP">
+            <key-map field-name="groupId"/>
+        </view-link>
+    </view-entity>
     <entity entity-name="UserLoginSecurityGroup"
             package-name="org.apache.ofbiz.security.securitygroup"
             title="Security Component - User Login Security Group">
diff --git framework/security/src/doc/asciidoc/_include/security-impersonation.adoc framework/security/src/doc/asciidoc/_include/security-impersonation.adoc
new file mode 100644
index 0000000000..13751d414c
--- /dev/null
+++ framework/security/src/doc/asciidoc/_include/security-impersonation.adoc
@@ -0,0 +1,127 @@
+////
+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.
+////
+
+= Impersonation
+== What is Impersonation in Apache OFBiz
+The Apache OFBiz Project
+Release 17.12
+
+:imagesdir: ../../themes/common-theme/webapp/images/img/
+ifdef::backend-pdf[]
+:title-logo-image: image::OFBiz-Logo.svg[Apache OFBiz Logo, pdfwidth=4.25in, align=center]
+:source-highlighter: rouge
+endif::[]
+
+=== Introduction to User impersonation
+
+User Impersonation is a feature that offer a way to select a user login and impersonate it, i.e. see what the user could
+see navigating through the application in his name.
+
+=== How do this work ?
+
+An authorized user _(see <<Security,security>> and <<Controls,controls>> section for configuration)_, can select a user
+that will be impersonated.
+
+The impersonation start, if everything is well configured, in current application (partymgr for the demo).
+Everything appears like if we were logged in with the userLoginId and the valid password (though we know nothing about it)
+
+The only thing showing that we currently are impersonating a user is the little bottom-right image :
+
+image::impersonate-ico.png[Impersonate icon, pdfwidth=0.5in, align=right]
+
+This icon indicates, when clicking on it, the user impersonated, and offer a way to depersonate.
+
+The impersonate period is stored for audit purpose, and if the impersonator forgot to depersonate, the period
+is terminated _one hour_ after impersonation start.
+
+=== Security
+
+This feature can draw some concerns about security aspect. This paragraph will introduce every controls and properties
+that have been implemented around the impersonation feature.
+
+[CAUTION]
+These configuration steps are not to be neglected for a *production environment* since this feature offer a way to act
+ in place of another user.
+
+==== Properties
+
+The _security.properties_ file introduce two properties that control impersonation feature :
+
+
+[source]
+security.disable.impersonation = true
+
+This property, set by default to *true*, controls the activation of impersonation feature. If no configuration is done
+any user trying to use impersonation will face an error message, indicating that the feature is disabled.
+
+To enable impersonation this property need to be set to *false*
+
+
+[source]
+security.login.authorised.during.impersonate = false
+
+This property controls the way impersonation occurred to the impersonated user :
+
+In default configuration, the impersonated user see nothing and can use the application without knowing that he is
+currently impersonated. Several authorized user can impersonate a same login without any issue.
+
+[NOTE]
+This configuration is intended for testing/QA environment allowing any authorized user to impersonate a login
+to validate its configuration, test the application etc.
+
+Set to *true*, this configuration improve the control of the data generated by the impersonated user. Indeed, Only one
+authorized user can impersonate a login at the same time, and during the impersonation process, the impersonated user
+is unable to act within the application.
+
+Since the impersonation period is stored in database, the actions done by the
+authorized user can be identified if there is the need to do so.
+[NOTE]
+This configuration is intended for production environment
+
+
+==== Controls
+
+The permission::
+
+First, to be able to use impersonation, a user need to possess _IMPERSONATE_ADMIN_ permissions. Demo data offer
+_IMPERSONATION_ security group for this purpose. +
+In demo data, _FULLADMIN_ security group also possess the permission.
+
+
+Permission based user restriction::
+
+An authorized user cannot impersonate any user. There are two main controls that will restrict the impersonation feature.
+
+Cannot impersonate Admin user:::
+
+It is impossible to impersonate a user that is granted any of the admin permission :
+
+            "IMPERSONATE_ADMIN"
+            "ARTIFACT_INFO_VIEW"
+            "SERVICE_MAINT"
+            "ENTITY_MAINT"
+            "UTIL_CACHE_VIEW"
+            "UTIL_DEBUG_VIEW"
+
+Cannot impersonate more privileged user:::
+
+It is impossible to impersonate a user that has more permission than your user. Even if the missing persmission is
+a minor one.
+
+
diff --git framework/security/src/main/java/org/apache/ofbiz/security/SecurityUtil.java framework/security/src/main/java/org/apache/ofbiz/security/SecurityUtil.java
new file mode 100644
index 0000000000..2e7c7fee70
--- /dev/null
+++ framework/security/src/main/java/org/apache/ofbiz/security/SecurityUtil.java
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * 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.
+ *******************************************************************************/
+package org.apache.ofbiz.security;
+
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.apache.ofbiz.base.util.Debug;
+import org.apache.ofbiz.base.util.StringUtil;
+import org.apache.ofbiz.base.util.UtilMisc;
+import org.apache.ofbiz.base.util.UtilValidate;
+import org.apache.ofbiz.entity.Delegator;
+import org.apache.ofbiz.entity.GenericEntityException;
+import org.apache.ofbiz.entity.condition.EntityCondition;
+import org.apache.ofbiz.entity.condition.EntityOperator;
+import org.apache.ofbiz.entity.util.EntityQuery;
+import org.apache.ofbiz.entity.util.EntityUtil;
+
+/**
+ * A <code>Security</code> util.
+ */
+public final class SecurityUtil {
+
+    public static final String module = SecurityUtil.class.getName();
+    private static final List<String> adminPermissions = UtilMisc.toList(
+            "IMPERSONATE_ADMIN",
+            "ARTIFACT_INFO_VIEW",
+            "SERVICE_MAINT",
+            "ENTITY_MAINT",
+            "UTIL_CACHE_VIEW",
+            "UTIL_DEBUG_VIEW");
+
+    /**
+     * Return true if given userLogin possess at least one of the adminPermission
+     *
+     * @param delegator
+     * @param userLoginId
+     * @return
+     */
+    public static boolean hasUserLoginAdminPermission(Delegator delegator, String userLoginId) {
+        if (UtilValidate.isEmpty(userLoginId)) return false;
+        try {
+            return EntityQuery.use(delegator)
+                    .from("UserLoginAndPermission")
+                    .where(EntityCondition.makeCondition(
+                            EntityCondition.makeCondition("userLoginId", userLoginId),
+                            EntityCondition.makeCondition("permissionId", EntityOperator.IN, adminPermissions)))
+                    .filterByDate("fromDate", "thruDate", "permissionFromDate", "permissionThruDate")
+                    .queryCount() != 0;
+        } catch (GenericEntityException e) {
+            Debug.logError("Failed to resolve user permissions", module);
+        }
+        return false;
+    }
+
+    /**
+     * Return the list of missing permission, if toUserLoginId has more permission thant userLoginId, emptyList either.
+     *
+     * @param delegator
+     * @param userLoginId
+     * @param toUserLoginId
+     * @return
+     */
+    public static List<String> hasUserLoginMorePermissionThan(Delegator delegator, String userLoginId, String toUserLoginId) {
+        ArrayList<String> returnList = new ArrayList<>();
+        if (UtilValidate.isEmpty(userLoginId) || UtilValidate.isEmpty(toUserLoginId)) return returnList;
+        List<String> userLoginPermissionIds;
+        List<String> toUserLoginPermissionIds;
+        try {
+            userLoginPermissionIds = EntityUtil.getFieldListFromEntityList(
+                    EntityQuery.use(delegator)
+                            .from("UserLoginAndPermission")
+                            .where("userLoginId", userLoginId)
+                            .filterByDate("fromDate", "thruDate", "permissionFromDate", "permissionThruDate")
+                            .queryList(), "permissionId", true);
+            toUserLoginPermissionIds = EntityUtil.getFieldListFromEntityList(
+                    EntityQuery.use(delegator)
+                            .from("UserLoginAndPermission")
+                            .filterByDate("fromDate", "thruDate", "permissionFromDate", "permissionThruDate")
+                            .where("userLoginId", toUserLoginId)
+                            .queryList(), "permissionId", true);
+        } catch (GenericEntityException e) {
+            Debug.logError("Failed to resolve user permissions", module);
+            return returnList;
+        }
+
+        if (UtilValidate.isEmpty(userLoginPermissionIds)) return toUserLoginPermissionIds;
+        if (UtilValidate.isEmpty(toUserLoginPermissionIds)) return returnList;
+
+        //Resolve all ADMIN permissions associated with the origin user
+        List<String> adminPermissions = userLoginPermissionIds.stream()
+                .filter(perm -> perm.endsWith("_ADMIN"))
+                .map(perm -> StringUtil.replaceString(perm, "_ADMIN", ""))
+                .collect(Collectors.toList());
+
+        //if toUserLoginPermissionIds contains at least one permission that is not in admin permission or userLoginPermissionIds
+        // return the list of missing permission
+        return toUserLoginPermissionIds.stream()
+                .filter(perm ->
+                        !userLoginPermissionIds.contains(perm)
+                                && !adminPermissions.contains(perm.substring(0, perm.lastIndexOf("_"))))
+                .collect(Collectors.toList());
+    }
+}
\ No newline at end of file
diff --git framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/LoginWorker.java framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/LoginWorker.java
index ca3515cfe7..0a8088eb0b 100644
--- framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/LoginWorker.java
+++ framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/LoginWorker.java
@@ -26,8 +26,10 @@ import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.ServiceLoader;
 import java.util.regex.Matcher;
@@ -59,6 +61,7 @@ import org.apache.ofbiz.base.util.UtilValidate;
 import org.apache.ofbiz.entity.Delegator;
 import org.apache.ofbiz.entity.DelegatorFactory;
 import org.apache.ofbiz.entity.EntityCryptoException;
+import org.apache.ofbiz.entity.GenericEntity;
 import org.apache.ofbiz.entity.GenericEntityException;
 import org.apache.ofbiz.entity.GenericValue;
 import org.apache.ofbiz.entity.condition.EntityCondition;
@@ -215,6 +218,52 @@ public class LoginWorker {
         return userLogin;
     }
 
+    /**
+     * Return the active {@link GenericValue} of a current impersonation UserLoginHistory of current userLogin session,
+     * only if not the impersonator himself.
+     *
+     * @param request The HTTP request object for the current JSP or Servlet request.
+     * @param response The HTTP response object for the current JSP or Servlet request.
+     * @return GenericValue
+     */
+    public static GenericValue checkImpersonationInProcess(HttpServletRequest request, HttpServletResponse response) {
+        HttpSession session = request.getSession();
+        GenericValue userLogin = (GenericValue) session.getAttribute("userLogin");
+        GenericValue originUserLogin = (GenericValue) session.getAttribute("originUserLogin");
+
+        //if originUserLogin is present, it's the impersonator session
+        if (originUserLogin != null) {
+            return null;
+        }
+
+        //Check if a visit in enable with a impersonation
+        GenericValue userLoginHistory = null;
+        if (userLogin != null) {
+            try {
+                userLoginHistory = EntityQuery.use(userLogin.getDelegator())
+                        .from("UserLoginHistory")
+                        .where(EntityCondition.makeCondition("userLoginId", userLogin.get("userLoginId")),
+                                EntityCondition.makeCondition("originUserLoginId", EntityOperator.NOT_EQUAL, null))
+                        .filterByDate()
+                        .queryFirst();
+            } catch (GenericEntityException e) {
+                Debug.logError(e, "impossible to resolve userLogin history", module);
+            }
+        }
+        if (userLoginHistory != null) {
+            List<Object> errorMessageList = UtilGenerics.checkList(request.getAttribute("_ERROR_MESSAGE_LIST"));
+            if (errorMessageList == null) {
+                errorMessageList = new LinkedList<>();
+                request.setAttribute("_ERROR_MESSAGE_LIST_", errorMessageList);
+            }
+            HashMap<String, Object> messageMap = new HashMap<>();
+            messageMap.putAll(userLoginHistory.getAllFields());
+            String errMsg = UtilProperties.getMessage(resourceWebapp, "loginevents.impersonation_in_process", messageMap, UtilHttp.getLocale(request));
+            errorMessageList.add(errMsg);
+        }
+        return userLoginHistory;
+    }
+
     /** This WebEvent allows for java 'services' to hook into the login path.
      * This method loads all instances of {@link LoginCheck}, and calls the
      * {@link LoginCheck#associate} method.  The first implementation to return
@@ -315,6 +364,16 @@ public class LoginWorker {
             }
         }
 
+        //Allow loggingOut when impersonated
+        boolean isLoggingOut = "logout".equals(RequestHandler.getRequestUri(request.getPathInfo()));
+        //Check if the user has an impersonation in process
+        boolean authoriseLoginDuringImpersonate = EntityUtilProperties.propertyValueEquals("security", "security.login.authorised.during.impersonate", "true");
+        if (!isLoggingOut && !authoriseLoginDuringImpersonate && checkImpersonationInProcess(request, response) != null) {
+            //remove error message that will be displayed in impersonated status screen
+            request.removeAttribute("_ERROR_MESSAGE_LIST_");
+            return "impersonated";
+        }
+
         return "success";
     }
 
@@ -533,6 +592,158 @@ public class LoginWorker {
         }
     }
 
+    /**
+     * An HTTP WebEvent handler to impersonate a given userLogin without using password. This should run before the security check.
+     *
+     * @param request The HTTP request object for the current JSP or Servlet request.
+     * @param response The HTTP response object for the current JSP or Servlet request.
+     * @return Return a boolean which specifies whether or not the calling Servlet or
+     *         JSP should generate its own content. This allows an event to override the default content.
+     */
+    public static String impersonateLogin(HttpServletRequest request, HttpServletResponse response) {
+        HttpSession session = request.getSession();
+        Delegator delegator = (Delegator) request.getAttribute("delegator");
+        String userLoginIdToImpersonate = request.getParameter("userLoginIdToImpersonate");
+        GenericValue userLogin = (GenericValue) session.getAttribute("userLogin");
+        LocalDispatcher dispatcher;
+
+        if (UtilProperties.getPropertyAsBoolean("security","security.disable.impersonation", true)) {
+            String errMsg = UtilProperties.getMessage(resourceWebapp, "loginevents.impersonation_disabled", UtilHttp.getLocale(request));
+            request.setAttribute("_ERROR_MESSAGE_", errMsg);
+            return "error";
+        }
+
+        //Check if user has impersonate permission
+        Security security = (Security) request.getAttribute("security");
+        if (!security.hasEntityPermission("IMPERSONATE", "_ADMIN", userLogin)) {
+            String errMsg = UtilProperties.getMessage(resourceWebapp, "loginevents.unable_to_login_this_application", UtilHttp.getLocale(request));
+            request.setAttribute("_ERROR_MESSAGE_", errMsg);
+            return "error";
+        }
+
+        List<String> errMsgList = new LinkedList<>();
+        if (UtilValidate.isNotEmpty(session.getAttribute("originUserLogin"))) {
+            errMsgList.add(UtilProperties.getMessage(resourceWebapp, "loginevents.origin_username_is_present", UtilHttp.getLocale(request)));
+        }
+        if (UtilValidate.isEmpty(userLoginIdToImpersonate)) {
+            errMsgList.add(UtilProperties.getMessage(resourceWebapp, "loginevents.username_was_empty_reenter", UtilHttp.getLocale(request)));
+        }
+
+        try {
+            GenericValue userLoginToImpersonate = delegator.findOne("UserLogin", false, "userLoginId", userLoginIdToImpersonate);
+            if (!hasBasePermission(userLoginToImpersonate, request)) {
+                errMsgList.add(UtilProperties.getMessage(resourceWebapp, "loginevents.unable_to_login_this_application", UtilHttp.getLocale(request)));
+            }
+        } catch (GenericEntityException e) {
+            String errMsg ="Error impersonating the userLoginId" + userLoginIdToImpersonate;
+            Debug.logError(e, errMsg, module);
+            errMsgList.add(errMsg);
+            request.setAttribute("_ERROR_MESSAGE_LIST_", errMsgList);
+            return  "error";
+        }
+        if (!errMsgList.isEmpty()) {
+            request.setAttribute("_ERROR_MESSAGE_LIST_", errMsgList);
+            return  "error";
+        }
+
+        // After this line, contrary to in the login method, multi-tenant is not handled
+        ServletContext servletContext = session.getServletContext();
+
+        // Set default delegator
+        Debug.logInfo("Setting default delegator", module);
+        String delegatorName = delegator.getDelegatorBaseName();
+        delegator = DelegatorFactory.getDelegator(delegatorName);
+        dispatcher = WebAppUtil.makeWebappDispatcher(servletContext, delegator);
+
+        Map<String, Object> result;
+        try {
+            // get the visit id to pass to the userLogin for history
+            String visitId = VisitHandler.getVisitId(session);
+            result = dispatcher.runSync("userImpersonate",
+                    UtilMisc.toMap("userLoginIdToImpersonate", userLoginIdToImpersonate,
+                            "userLogin", userLogin,"visitId", visitId, "locale", UtilHttp.getLocale(request)));
+        } catch (GenericServiceException e) {
+            Debug.logError(e, "Error calling userImpersonate service", module);
+            Map<String, String> messageMap = UtilMisc.toMap("errorMessage", e.getMessage());
+            String errMsg = UtilProperties.getMessage(resourceWebapp, "loginevents.following_error_occurred_during_login", messageMap, UtilHttp.getLocale(request));
+            request.setAttribute("_ERROR_MESSAGE_", errMsg);
+            return "error";
+        }
+
+        if (ModelService.RESPOND_SUCCESS.equals(result.get(ModelService.RESPONSE_MESSAGE))) {
+            userLogin = (GenericValue) result.get("userLogin");
+            GenericValue originUserLogin = (GenericValue) result.get("originUserLogin");
+
+            Map<String, Object> userLoginSession = checkMap(result.get("userLoginSession"), String.class, Object.class);
+
+            // check on JavaScriptEnabled
+            String javaScriptEnabled = "N";
+            if ("Y".equals(request.getParameter("JavaScriptEnabled"))) {
+                javaScriptEnabled = "Y";
+            }
+            try {
+                dispatcher.runSync("setUserPreference", UtilMisc.toMap("userPrefTypeId", "javaScriptEnabled",
+                        "userPrefGroupTypeId", "GLOBAL_PREFERENCES", "userPrefValue", javaScriptEnabled, "userLogin", userLogin));
+            } catch (GenericServiceException e) {
+                Debug.logError(e, "Error setting user preference", module);
+            }
+
+            //add originUserLogin in session
+            session.setAttribute("originUserLogin", originUserLogin);
+            // finally do the main login routine to set everything else up in the session, etc
+            return doMainLogin(request, response, userLogin, userLoginSession);
+        } else {
+            Map<String, String> messageMap = UtilMisc.toMap("errorMessage", result.get(ModelService.ERROR_MESSAGE));
+            String errMsg = UtilProperties.getMessage(resourceWebapp, "loginevents.following_error_occurred_during_login", messageMap, UtilHttp.getLocale(request));
+            request.setAttribute("_ERROR_MESSAGE_", errMsg);
+            return "error";
+        }
+    }
+
+    /**
+     * An HTTP WebEvent handler to reverse an impersonate login.
+     *
+     * @param request The HTTP request object for the current JSP or Servlet request.
+     * @param response The HTTP response object for the current JSP or Servlet request.
+     * @return Return a boolean which specifies whether or not the calling Servlet or
+     *         JSP should generate its own content. This allows an event to override the default content.
+     */
+    public static String depersonateLogin(HttpServletRequest request, HttpServletResponse response) {
+        HttpSession session = request.getSession();
+        GenericValue originUserLogin = (GenericValue) session.getAttribute("originUserLogin");
+        session.removeAttribute("originUserLogin");
+
+        List<String> errMsgList = new LinkedList<>();
+        if (null == originUserLogin) {
+            errMsgList.add(UtilProperties.getMessage(resourceWebapp, "loginevents.username_was_empty_reenter", UtilHttp.getLocale(request)));
+        }
+        if (!errMsgList.isEmpty()) {
+            request.setAttribute("_ERROR_MESSAGE_LIST_", errMsgList);
+            return "error";
+        }
+
+        //update the userLogin history, only one impersonation of this user can be active at the same time
+        EntityCondition conditions = EntityCondition.makeCondition(
+                EntityCondition.makeCondition("userLoginId", ((GenericValue) session.getAttribute("userLogin")).get("userLoginId")),
+                EntityCondition.makeCondition("originUserLoginId", originUserLogin.get("userLoginId")),
+                EntityUtil.getFilterByDateExpr());
+        try {
+            //check impersonation process existence to avoid depersonation abuse
+            if (EntityQuery.use(originUserLogin.getDelegator()).from("UserLoginHistory").where(conditions).queryCount() == 0) {
+                String errMsg = UtilProperties.getMessage(resourceWebapp, "loginevents.impersonate_NotInProcess", UtilHttp.getLocale(request));
+                request.setAttribute("_ERROR_MESSAGE_", errMsg);
+                return "error";
+            }
+            originUserLogin.getDelegator().storeByCondition("UserLoginHistory",
+                    UtilMisc.toMap("thruDate", UtilDateTime.nowTimestamp()), conditions);
+        } catch (GenericEntityException e) {
+            return "error";
+        }
+
+        // Log back the impersonating user
+        return doMainLogin(request, response, originUserLogin, null);
+    }
+
     protected static void setWebContextObjects(HttpServletRequest request, HttpServletResponse response, Delegator delegator, LocalDispatcher dispatcher) {
         HttpSession session = request.getSession();
         // NOTE: we do NOT want to set this in the servletContext, only in the request and session
@@ -561,6 +772,10 @@ public class LoginWorker {
 
     public static String doMainLogin(HttpServletRequest request, HttpServletResponse response, GenericValue userLogin, Map<String, Object> userLoginSession) {
         HttpSession session = request.getSession();
+        boolean authoriseLoginDuringImpersonate = EntityUtilProperties.propertyValueEquals("security", "security.login.authorised.during.impersonate", "true");
+        if (!authoriseLoginDuringImpersonate && checkImpersonationInProcess(request, response) != null) {
+            return "error";
+        }
         if (userLogin != null && hasBasePermission(userLogin, request)) {
             doBasicLogin(userLogin, request);
         } else {
@@ -1222,4 +1437,13 @@ public class LoginWorker {
         }
         return "success";
     }
+
+    /**
+     * Return true if userLogin has not been disabled
+     * @param userLogin
+     * @return
+     */
+    public static boolean isUserLoginActive(GenericValue userLogin) {
+        return !"N".equals(userLogin.getString("enabled")) && UtilValidate.isEmpty(userLogin.getString("disabledBy"));
+    }
 }
diff --git themes/bluelight/template/Header.ftl themes/bluelight/template/Header.ftl
index 2f21c89c71..e54f68c140 100644
--- themes/bluelight/template/Header.ftl
+++ themes/bluelight/template/Header.ftl
@@ -117,6 +117,7 @@ under the License.
 </#if>
 
 <body>
+  <#include "component://common-theme/template/ImpersonateBanner.ftl"/>
   <div id="wait-spinner" style="display:none">
     <div id="wait-spinner-image"></div>
   </div>
diff --git themes/common-theme/template/ImpersonateBanner.ftl themes/common-theme/template/ImpersonateBanner.ftl
new file mode 100644
index 0000000000..2dd3e9f2d1
--- /dev/null
+++ themes/common-theme/template/ImpersonateBanner.ftl
@@ -0,0 +1,11 @@
+<#if parameters.originUserLogin??>
+    <a href="#impersonateContent" title="${uiLabelMap.CommonImpersonateTitle}" id="impersonateBtn"><img src="/images/img/impersonate-ico.png" alt="${uiLabelMap.CommonImpersonateTitle}"/></a>
+    <div id="impersonateContent">
+        <div class="impersonateModal">
+            <a href="#" class="btn-close" title="${uiLabelMap.CommonClose}">×</a>
+            <h3>${uiLabelMap.CommonImpersonateTitle}</h3>
+            <p>${uiLabelMap.CommonImpersonateUserLogin} : <strong>${context.userLogin.userLoginId!}</strong></p>
+            <a href="depersonateLogin" class="btn" title="${uiLabelMap.CommonImpersonateStop}">${uiLabelMap.CommonImpersonateStop}</a>
+        </div>
+    </div>
+</#if>
diff --git themes/common-theme/template/Impersonated.ftl themes/common-theme/template/Impersonated.ftl
new file mode 100644
index 0000000000..b6117cefe3
--- /dev/null
+++ themes/common-theme/template/Impersonated.ftl
@@ -0,0 +1,33 @@
+<#--
+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.
+-->
+
+<#assign messageMap = Static["org.apache.ofbiz.base.util.UtilMisc"].toMap("originUserLoginId", impersonator)/>
+<#assign impersonationMessage= Static["org.apache.ofbiz.base.util.UtilProperties"].getMessage("SecurityextUiLabels", "loginevents.impersonation_in_process", messageMap, locale)/>
+<#assign fromDate = Static["org.apache.ofbiz.base.util.UtilFormatOut"].formatDateTime(impersonationFromDate, "", locale, timeZone)/>
+<div id="impersonateMode">
+  <div class="content">
+      <img src="/images/img/impersonate-ico.png" alt="${uiLabelMap.CommonImpersonateTitle}"/>
+      <p class="user">${impersonationMessage!}<p>
+      <p>${uiLabelMap.CommonSince} ${fromDate!}</p>
+      <p><a id="logout" class="user-pref-btn" href="<@ofbizUrl>logout</@ofbizUrl>">${uiLabelMap.CommonLogout}</a></p>
+  </div>
+</div>
+<script>
+    setInterval('window.location.reload()', 30000);
+</script>
diff --git themes/common-theme/webapp/common/css/impersonate.css themes/common-theme/webapp/common/css/impersonate.css
new file mode 100644
index 0000000000..276f26c23e
--- /dev/null
+++ themes/common-theme/webapp/common/css/impersonate.css
@@ -0,0 +1,180 @@
+#impersonateBtn {
+    position: fixed;
+    bottom: 37px;
+    right: 20px;
+    z-index: 10000;
+    width: 79px;
+    height: 60px;
+    cursor: pointer;
+}
+
+#impersonateBtn img {
+    width: 100%;
+    max-width: 79px;
+    height: auto;
+    opacity: 0.6;
+    transition: opacity .6s;
+}
+
+#impersonateBtn:hover img {
+    opacity: 1;
+}
+
+#impersonateContent:before {
+    content: "";
+    display: none;
+    background: rgba(225, 225, 225, 0.8);
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    z-index: 10;
+}
+
+#impersonateContent:target:before {
+    display: block;
+}
+
+#impersonateContent:target .impersonateModal {
+    opacity: 1;
+    bottom: 117px;
+    right: 20px;
+    -webkit-transform: translate(0, 0);
+    -ms-transform: translate(0, 0);
+    transform: translate(0, 0);
+}
+
+.impersonateModal {
+    opacity: 0;
+    position: fixed;
+    background: #FFF;
+    text-align: center;
+    padding: 30px 30px 15px 30px;
+    border-radius: 5px;
+    z-index: 11;
+    box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.15);
+    -webkit-transform: translate(0, -500%);
+    -ms-transform: translate(0, -500%);
+    transform: translate(0, -500%);
+    -webkit-transition: -webkit-transform 0.3s ease-out;
+    -moz-transition: -moz-transform 0.3s ease-out;
+    -o-transition: -o-transform 0.3s ease-out;
+    transition: transform 0.3s ease-out;
+}
+
+.impersonateModal:before {
+    content: "";
+    position: absolute;
+    bottom: -20px;
+    right: 37px;
+    border: 1em solid #FFF;
+    border-color: transparent transparent #FFF #FFF;
+    transform-origin: 0 0;
+    transform: rotate(-45deg);
+    box-shadow: -3px 3px 5px 0 rgba(0, 0, 0, 0.05);
+}
+
+#impersonateContent .btn-close {
+    color: #aaa;
+    font-size: 25px;
+    text-decoration: none;
+    position: absolute;
+    right: 10px;
+    top: 10px;
+}
+
+#impersonateContent .btn-close:hover {
+    color: #919191;
+}
+
+#impersonateContent h3 {
+    font-size: 26px;
+    font-weight: normal;
+    letter-spacing: 0.5px;
+    line-height: 24px;
+    color: #f08906;
+    border-bottom: 1px solid #F2F2F2;
+    padding-bottom: 10px;
+    margin: 15px 0 20px 0;
+}
+
+#impersonateContent p {
+    font-size: 18px;
+    color: #666;
+    letter-spacing: 0.3px;
+}
+
+#impersonateContent p strong {
+    color: #005982;
+    font-weight: bold;
+}
+
+#impersonateContent .btn {
+    border-radius: 2px;
+    border: 1px solid #005982;
+    padding: 7px 12px;
+    text-transform: uppercase;
+    letter-spacing: 1px;
+    color: #005982;
+    margin: 30px 0 10px 0;
+    line-height: 22px;
+    display: inline-block;
+    -webkit-transition: 0.2s ease-out;
+    -moz-transition: 0.2s ease-out;
+    -o-transition: 0.2s ease-out;
+    transition: 0.2s ease-out;
+}
+
+#impersonateContent .btn:hover {
+    color: #f08906;
+    border-color: #f08906;
+}
+
+#impersonateMode {
+    position: fixed;
+    top: 0;
+    left: 0;
+    height: 100%;
+    min-height: 100vh;
+    width: 100%;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    background: rgba(225, 225, 225, 0.8);
+    z-index: 11;
+    cursor: not-allowed;
+}
+
+#impersonateMode .content {
+    background: #FFF;
+    padding: 30px;
+    box-shadow: -3px 3px 5px 0 rgba(0, 0, 0, 0.05);
+    text-align: center;
+}
+
+#impersonateMode .content p.user {
+    margin-bottom: 15px;
+    color: #005982;
+    font-weight: bold;
+}
+
+#impersonateMode .content p {
+    font-size: 18px;
+    color: #666;
+    letter-spacing: 0.3px;
+}
+
+#impersonateMode .content img {
+    margin-bottom: 20px;
+}
+
+#impersonateMode .content .user-pref-btn {
+    width: auto;
+    border-radius: 3px;
+    display: inline-block;
+    padding: 5px 15px;
+    font-size: 15px;
+    text-transform: uppercase;
+    font-weight: 600;
+}
diff --git themes/common-theme/webapp/images/img/impersonate-ico.png themes/common-theme/webapp/images/img/impersonate-ico.png
new file mode 100644
index 0000000000..d12c252dfc
Binary files /dev/null and themes/common-theme/webapp/images/img/impersonate-ico.png differ
diff --git themes/common-theme/widget/CommonScreens.xml themes/common-theme/widget/CommonScreens.xml
index f0cf58efa1..8e674ec07a 100644
--- themes/common-theme/widget/CommonScreens.xml
+++ themes/common-theme/widget/CommonScreens.xml
@@ -155,6 +155,7 @@ under the License.
                         <set field="appbarCloseTemplateLocation" from-field="layoutSettings.VT_NAV_CLOSE_TMPLT" />
                         <set field="messagesTemplateLocation" from-field="layoutSettings.VT_MSG_TMPLT_LOC" />
                         <set field="layoutSettings.suppressTab" value="ofbizsetup"/><!-- diseable ofbiz setup by default -->
+                        <set field="layoutSettings.styleSheets[+0]" value="/common/css/impersonate.css" global="true" />
                     </actions>
                     <widgets />
                 </section>
@@ -546,6 +547,42 @@ under the License.
         </section>
     </screen>
 
+    <screen name="impersonated">
+        <section>
+            <actions>
+                <set field="titleProperty" value="PageTitleImpersonated" />
+                <entity-condition list="impersonated" entity-name="UserLoginHistory" filter-by-date="true">
+                    <condition-list>
+                        <condition-list>
+                            <condition-expr field-name="userLoginId" from-field="userLogin.userLoginId"/>
+                            <condition-expr field-name="originUserLoginId" operator="not-equals" from-field="nullField"/>
+                        </condition-list>
+                    </condition-list>
+                    <select-field field-name="originUserLoginId"/>
+                    <select-field field-name="fromDate"/>
+                    <order-by field-name="-fromDate"/>
+                </entity-condition>
+                <set field="impersonator" from-field="impersonated[0].originUserLoginId"/>
+                <set field="impersonationFromDate" from-field="impersonated[0].fromDate"/>
+            </actions>
+            <widgets>
+                <decorator-screen name="main-decorator" location="${parameters.mainDecoratorLocation}">
+                    <decorator-section name="body">
+                        <section>
+                            <actions>
+                                <set field="impersonatedTemplateLocation" from-field="layoutSettings.VT_IMPERSO_TMPLT_LOC" default-value="component://common-theme/template/Impersonated.ftl"/>
+                            </actions>
+                            <widgets />
+                        </section>
+                        <platform-specific>
+                            <html><html-template location="${impersonatedTemplateLocation}"/></html>
+                        </platform-specific>
+                    </decorator-section>
+                </decorator-screen>
+            </widgets>
+        </section>
+    </screen>
+
     <screen name="ajaxNotLoggedIn">
         <section>
             <widgets>
@@ -608,7 +645,7 @@ under the License.
                 <set field="questionEnumId" from-field="securityQuestions[0].questionEnumId" />
                 <entity-one entity-name="Enumeration" value-field="securityQuestion">
                     <field-map field-name="enumId" from-field="questionEnumId"/>
-                </entity-one> 
+                </entity-one>
             </actions>
             <widgets>
                 <decorator-screen name="main-decorator" location="${parameters.mainDecoratorLocation}">
diff --git themes/common-theme/widget/Theme.xml themes/common-theme/widget/Theme.xml
index 7de3df01b1..1ac0e2f2cc 100644
--- themes/common-theme/widget/Theme.xml
+++ themes/common-theme/widget/Theme.xml
@@ -148,6 +148,7 @@ under the License.
             <screen name="ajaxAutocompleteOptions"/>
             <screen name="FoError"/>
             <screen name="login"/>
+            <screen name="impersonated"/>
             <screen name="ajaxNotLoggedIn"/>
             <screen name="requirePasswordChange"/>
             <screen name="forgotPassword_step1"/>
diff --git themes/flatgrey/template/Header.ftl themes/flatgrey/template/Header.ftl
index f0a7eb6c1f..d86d5f41fd 100644
--- themes/flatgrey/template/Header.ftl
+++ themes/flatgrey/template/Header.ftl
@@ -97,6 +97,7 @@ under the License.
 </#if>
 <#assign organizationLogoLinkURL = "${layoutSettings.organizationLogoLinkUrl!}">
 <body>
+  <#include "component://common-theme/template/ImpersonateBanner.ftl"/>
   <div id="wait-spinner" style="display:none">
     <div id="wait-spinner-image"></div>
   </div>
diff --git themes/rainbowstone/template/includes/TopAppBar.ftl themes/rainbowstone/template/includes/TopAppBar.ftl
index f5e7ca9e7e..6faf99b27f 100644
--- themes/rainbowstone/template/includes/TopAppBar.ftl
+++ themes/rainbowstone/template/includes/TopAppBar.ftl
@@ -30,6 +30,7 @@ under the License.
     </#if>
 </#if>
 <body>
+<#include "component://common-theme/template/ImpersonateBanner.ftl"/>
 <div id="wait-spinner" style="display:none">
     <div id="wait-spinner-image"></div>
 </div>
diff --git themes/tomahawk/template/Header.ftl themes/tomahawk/template/Header.ftl
index e027c421ad..33766149ec 100644
--- themes/tomahawk/template/Header.ftl
+++ themes/tomahawk/template/Header.ftl
@@ -102,6 +102,7 @@ under the License.
 <#assign organizationLogoLinkURL = "${layoutSettings.organizationLogoLinkUrl!}">
 
 <body>
+  <#include "component://common-theme/template/ImpersonateBanner.ftl"/>
   <div id="wait-spinner" style="display:none">
     <div id="wait-spinner-image"></div>
   </div>
