Skip to content

Commit 805f47f

Browse files
authored
Merge pull request #108 from alejoroman0605/superusers-impersonate-other-user
Ability for Superusers to Impersonate Other User Accounts
2 parents 5647b6b + 13103b9 commit 805f47f

File tree

6 files changed

+194
-3
lines changed

6 files changed

+194
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<root>
3+
<!--
4+
Microsoft ResX Schema
5+
6+
Version 2.0
7+
8+
The primary goals of this format is to allow a simple XML format
9+
that is mostly human readable. The generation and parsing of the
10+
various data types are done through the TypeConverter classes
11+
associated with the data types.
12+
13+
Example:
14+
15+
... ado.net/XML headers & schema ...
16+
<resheader name="resmimetype">text/microsoft-resx</resheader>
17+
<resheader name="version">2.0</resheader>
18+
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
19+
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
20+
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
21+
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
22+
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
23+
<value>[base64 mime encoded serialized .NET Framework object]</value>
24+
</data>
25+
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
26+
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
27+
<comment>This is a comment</comment>
28+
</data>
29+
30+
There are any number of "resheader" rows that contain simple
31+
name/value pairs.
32+
33+
Each data row contains a name, and value. The row also contains a
34+
type or mimetype. Type corresponds to a .NET class that support
35+
text/value conversion through the TypeConverter architecture.
36+
Classes that don't support this are serialized and stored with the
37+
mimetype set.
38+
39+
The mimetype is used for serialized objects, and tells the
40+
ResXResourceReader how to depersist the object. This is currently not
41+
extensible. For a given mimetype the value must be set accordingly:
42+
43+
Note - application/x-microsoft.net.object.binary.base64 is the format
44+
that the ResXResourceWriter will generate, however the reader can
45+
read any of the formats listed below.
46+
47+
mimetype: application/x-microsoft.net.object.binary.base64
48+
value : The object must be serialized with
49+
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
50+
: and then encoded with base64 encoding.
51+
52+
mimetype: application/x-microsoft.net.object.soap.base64
53+
value : The object must be serialized with
54+
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
55+
: and then encoded with base64 encoding.
56+
57+
mimetype: application/x-microsoft.net.object.bytearray.base64
58+
value : The object must be serialized into a byte array
59+
: using a System.ComponentModel.TypeConverter
60+
: and then encoded with base64 encoding.
61+
-->
62+
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
63+
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
64+
<xsd:element name="root" msdata:IsDataSet="true">
65+
<xsd:complexType>
66+
<xsd:choice maxOccurs="unbounded">
67+
<xsd:element name="metadata">
68+
<xsd:complexType>
69+
<xsd:sequence>
70+
<xsd:element name="value" type="xsd:string" minOccurs="0" />
71+
</xsd:sequence>
72+
<xsd:attribute name="name" use="required" type="xsd:string" />
73+
<xsd:attribute name="type" type="xsd:string" />
74+
<xsd:attribute name="mimetype" type="xsd:string" />
75+
<xsd:attribute ref="xml:space" />
76+
</xsd:complexType>
77+
</xsd:element>
78+
<xsd:element name="assembly">
79+
<xsd:complexType>
80+
<xsd:attribute name="alias" type="xsd:string" />
81+
<xsd:attribute name="name" type="xsd:string" />
82+
</xsd:complexType>
83+
</xsd:element>
84+
<xsd:element name="data">
85+
<xsd:complexType>
86+
<xsd:sequence>
87+
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
88+
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
89+
</xsd:sequence>
90+
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
91+
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
92+
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
93+
<xsd:attribute ref="xml:space" />
94+
</xsd:complexType>
95+
</xsd:element>
96+
<xsd:element name="resheader">
97+
<xsd:complexType>
98+
<xsd:sequence>
99+
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
100+
</xsd:sequence>
101+
<xsd:attribute name="name" type="xsd:string" use="required" />
102+
</xsd:complexType>
103+
</xsd:element>
104+
</xsd:choice>
105+
</xsd:complexType>
106+
</xsd:element>
107+
</xsd:schema>
108+
<resheader name="resmimetype">
109+
<value>text/microsoft-resx</value>
110+
</resheader>
111+
<resheader name="version">
112+
<value>2.0</value>
113+
</resheader>
114+
<resheader name="reader">
115+
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
116+
</resheader>
117+
<resheader name="writer">
118+
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
119+
</resheader>
120+
<data name="AreYouSure.Text" xml:space="preserve">
121+
<value>Are you sure you want to perform this action?</value>
122+
</data>
123+
<data name="Impersonate.Text" xml:space="preserve">
124+
<value>Impersonate</value>
125+
</data>
126+
<data name="ImpersonateAnotherUserAccountConfirmation.Text" xml:space="preserve">
127+
<value>You are about to impersonate another user account. If you confirm, he website will attempt to log you in as if they logged in themselves. You will need to logout and log back in to restore your superuser access. If you cancel, nothing will happen.</value>
128+
</data>
129+
<data name="ImpersonateAnotherUserAccountConfirmationAlert.Text" xml:space="preserve">
130+
<value>Technical Limitation: If you are using another authentication provider other than the built-in DNN provider, this feature may or may not work, depending on the type of authentication you have installed/enabled.</value>
131+
</data>
132+
</root>

Modules/UserManager/App_LocalResources/Shared.resx

+3
Original file line numberDiff line numberDiff line change
@@ -180,4 +180,7 @@
180180
<data name="OnlySuperUserIsAllowedBulkDeleteUserAccounts.Text" xml:space="preserve">
181181
<value>Only a superuser is allowed to bulk delete user accounts</value>
182182
</data>
183+
<data name="Cancel.Text" xml:space="preserve">
184+
<value>Cancel</value>
185+
</data>
183186
</root>

Modules/UserManager/Controllers/UserManageController.cs

+22
Original file line numberDiff line numberDiff line change
@@ -534,5 +534,27 @@ public ActionResult PasswordResetLink(int itemId)
534534
TempData["Message"] = UserRepository.SendPasswordResetLink(portalId, itemId, portalSettings);
535535
return RedirectToAction("Index");
536536
}
537+
538+
/// <summary>
539+
/// Impersonate a user
540+
/// </summary>
541+
/// <param name="itemId"></param>
542+
/// <returns></returns>
543+
public ActionResult ImpersonateUserById(int itemId)
544+
{
545+
if (_currentUser.IsSuperUser)
546+
{
547+
var user = UserController.GetUserById(PortalSettings.PortalId, itemId);
548+
if (user != null)
549+
{
550+
// Perform impersonation
551+
UserController.UserLogin(PortalSettings.PortalId, user, PortalSettings.PortalName, Request.UserHostAddress, false);
552+
}
553+
return Redirect(Url.Content("~/"));
554+
}
555+
string errorMessage = Localization.GetString("NotPermissions.Text", ResourceFile);
556+
ViewBag.ErrorMessage = errorMessage;
557+
return View("Error");
558+
}
537559
}
538560
}

Modules/UserManager/Module.css

+5
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@
206206
.checkbox {
207207
font-size: 16px;
208208
}
209+
209210
.admin-manager .checkbox {
210211
margin-top: 20px;
211212
}
@@ -726,4 +727,8 @@
726727
line-height: 26px;
727728
border-top-left-radius: 0;
728729
border-bottom-left-radius: 0;
730+
}
731+
732+
.AM-white, .AM-white a, .AM-white a:visited {
733+
color: white;
729734
}

Modules/UserManager/Upendo.Modules.UserManager.csproj

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<?xml version="1.0" encoding="utf-8"?>
1+
<?xml version="1.0" encoding="utf-8"?>
22
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
33
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
44
<PropertyGroup>
@@ -186,6 +186,9 @@
186186
<ItemGroup>
187187
<Content Include="App_LocalResources\BulkDelete.resx" />
188188
</ItemGroup>
189+
<ItemGroup>
190+
<Content Include="App_LocalResources\Impersonate.resx" />
191+
</ItemGroup>
189192
<PropertyGroup>
190193
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
191194
<VSToolsPath Condition="'$(VSToolsPath)' == ''">

Modules/UserManager/Views/UserManage/Index.cshtml

+28-2
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,14 @@
218218
<span class="fa fa-trash color-white" aria-hidden="true"></span>
219219
</a>
220220
</span>
221+
@if (ViewBag.IsCurrentUserSuperUser)
222+
{
223+
<span id="dialogs-impersonate" class="dnnActions" style="display: initial;">
224+
<a data-hint="Delete" class="confirm AM-btn AM-btn-primary AM-btn-sm" data-placement="top" data-singleton="true" href="@Url.Action("ImpersonateUserById", "UserManage", new {itemId = item.UserId})">
225+
<span class="fas fa-eye color-white" aria-hidden="true"></span>
226+
</a>
227+
</span>
228+
}
221229
</td>
222230
</tr>
223231
}
@@ -249,20 +257,38 @@
249257
var deleteText = '@DotNetNuke.Services.Localization.Localization.GetString("DeleteConfirmation.Text", "~/DesktopModules/MVC/Upendo.Modules.UserManager/App_LocalResources/Index.resx")';
250258
var deleteUnauthorizedText = '@DotNetNuke.Services.Localization.Localization.GetString("DeleteUnauthorized.Text", "~/DesktopModules/MVC/Upendo.Modules.UserManager/App_LocalResources/Index.resx")';
251259
var removeDeleteText = '@DotNetNuke.Services.Localization.Localization.GetString("RemoveDelete.Text", "~/DesktopModules/MVC/Upendo.Modules.UserManager/App_LocalResources/Index.resx")';
260+
var impersonateText = '@DotNetNuke.Services.Localization.Localization.GetString("Impersonate.Text", "~/DesktopModules/MVC/Upendo.Modules.UserManager/App_LocalResources/Impersonate.resx")';
261+
var impersonateAnotherUserAccountConfirmation = '@DotNetNuke.Services.Localization.Localization.GetString("ImpersonateAnotherUserAccountConfirmation.Text", "~/DesktopModules/MVC/Upendo.Modules.UserManager/App_LocalResources/Impersonate.resx")';
262+
var impersonateAnotherUserAccountConfirmationAlert = '@DotNetNuke.Services.Localization.Localization.GetString("ImpersonateAnotherUserAccountConfirmationAlert.Text", "~/DesktopModules/MVC/Upendo.Modules.UserManager/App_LocalResources/Impersonate.resx")';
263+
var areYouSure = '@DotNetNuke.Services.Localization.Localization.GetString("AreYouSure.Text", "~/DesktopModules/MVC/Upendo.Modules.UserManager/App_LocalResources/Impersonate.resx")';
264+
var cancel = '@DotNetNuke.Services.Localization.Localization.GetString("Cancel.Text", "~/DesktopModules/MVC/Upendo.Modules.UserManager/App_LocalResources/Shared.resx")';
265+
var deleteBtnText = '@DotNetNuke.Services.Localization.Localization.GetString("Delete.Text", "~/DesktopModules/MVC/Upendo.Modules.UserManager/App_LocalResources/Shared.resx")';
266+
252267
$(function () {
253268
$('.dropdown-toggle').dropdown();
254269
});
255270
256271
$(function () {
257272
$('#dialogs-delete .confirm').dnnConfirm({
258273
title: "",
259-
yesText: "Delete",
260-
noText: "Cancel",
274+
yesText: deleteBtnText,
275+
noText: cancel,
261276
dialogClass: 'dnnFormPopup',
262277
text: '<p style="font-size:20px;">' + deleteText + '</p>',
263278
});
264279
});
265280
281+
$(function () {
282+
$('#dialogs-impersonate .confirm').dnnConfirm({
283+
title: areYouSure,
284+
yesText: impersonateText,
285+
noText: cancel,
286+
dialogClass: 'dnnFormPopup',
287+
width: 700,
288+
text: '<div class="admin-manager"> <div class="red"> <p>' + impersonateAnotherUserAccountConfirmation + '</p>' + '<p>' + impersonateAnotherUserAccountConfirmationAlert + '</p> </div> </div>',
289+
});
290+
});
291+
266292
$(function () {
267293
$('#dialogs-DeleteUnauthorized .confirm').dnnConfirm({
268294
title: "",

0 commit comments

Comments
 (0)