Having trouble figuring out how to impersonate another user while using aspnet_personalization with Windows authentication.
I am using asp.net's personalization framework on my page:
<asp:WebPartManager ID="WebPartManager1" runat="server" >
<Personalization Enabled="True" />
</asp:WebPartManager>
My page is in an existing web app that uses AuthenticationMode="Windows" (which I cannot change, and IIS is also set to Windows Authentication). This web app has a page that allows super-users to impersonate any other user. This is accomplished by manipulating a connection string that is built dynamically every time we hit the database.
My problem is that aspnet_personalization still uses windows authentication. When I try to edit the connection string used by aspnet_personalization dynamically, aspnet does not seem to use it - it still uses the username of the current NT account.
I have the following in my web.config's configuration tag:
<connectionStrings>
<remove name="LocalSqlServer"/>
<add name="AnotherConnectionString" connectionString="Data Source=serverName;Initial Catalog=dbName;Integrated Security=True" providerName="System.Data.SqlClient"/>
</connectionStrings>
<system.web>
<membership>
<providers>
<remove name="AspNetSqlMembershipProvider"/>
<add name="AspNetSqlMembershipProvider"
type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a"
applicationName="/"
connectionStringName="AnotherConnectionString" />
</providers>
</membership>
<profile enabled="true" defaultProvider="TableProfileProvider">
<providers>
<clear />
<add name="TableProfileProvider" type="Microsoft.Samples.SqlTableProfileProvider" connectionStringName="AnotherConnectionString" table="aspnet_Profile" applicationName="/" />
</providers>
<!--<properties></properties>-->
</profile>
I tried editing the connection string by adding code as follows whenever we impersonate a user, hoping asp.net would use it, but it does not:
Dim settings = ConfigurationManager.ConnectionStrings("AnotherConnectionString")
Dim fi = GetType(ConfigurationElement).GetField("_bReadOnly", BindingFlags.Instance Or BindingFlags.NonPublic)
fi.SetValue(settings, False)
settings.ConnectionString = "data source=serverName;database=dbName;user id=" & login & "; password=" & password
I did confirm on page_load of the page containing the webPartManager that "AnotherConnectionString" does contain the desired username, but asp.net_personalization still calls its stored procs using the NT username.
ANSWER:
Stackoverflow won't let me post an answer. That's ridiculous, so after I post the answer inside the question, I'm deleting my account and will encourage others to do the same.
I solved the problem by extending SqlPersonalizationProvider:
Imports System.Web.UI.WebControls.WebParts
Public Class MySqlPersonalizationProvider
Inherits SqlPersonalizationProvider
Protected Overrides Sub LoadPersonalizationBlobs(ByVal webPartManager As System.Web.UI.WebControls.WebParts.WebPartManager, ByVal path As String, ByVal userName As String, ByRef sharedDataBlob() As Byte, ByRef userDataBlob() As Byte)
Dim impersonatedName As String = MySession.UserLoginName()
path = path & "|" & impersonatedName
userName = impersonatedName
MyBase.LoadPersonalizationBlobs(webPartManager, path, userName, sharedDataBlob, userDataBlob)
End Sub
Protected Overrides Sub SavePersonalizationBlob(ByVal webPartManager As System.Web.UI.WebControls.WebParts.WebPartManager, ByVal path As String, ByVal userName As String, ByVal dataBlob() As Byte)
Dim impersonatedName As String = MySession.UserLoginName()
path = path & "|" & impersonatedName
userName = impersonatedName
MyBase.SavePersonalizationBlob(webPartManager, path, userName, dataBlob)
End Sub
End Class
And then in web.config:
<system.web>
<webParts>
<personalization defaultProvider="AspNetSqlPersonalizationProvider">
<providers>
<remove name="AspNetSqlPersonalizationProvider"/>
<add name="AspNetSqlPersonalizationProvider"
type="MyProject.MySqlPersonalizationProvider"
connectionStringName="MyConnectionString"
applicationName="/"></add>
</providers>
</personalization>
</webParts>
<profile enabled="true" defaultProvider="TableProfileProvider">
<providers>
<clear />
<add name="TableProfileProvider" type="Microsoft.Samples.SqlTableProfileProvider" connectionStringName="iCaseHomepage" table="aspnet_Profile" applicationName="/" />
</providers>
</profile>
</system.web>
<connectionStrings>
<remove name="LocalSqlServer"/>
<add name="MyConnectionString" connectionString="Data Source=MyServerName;Initial Catalog=MyDatabaseName;Integrated Security=True" providerName="System.Data.SqlClient"/>
</connectionStrings>
and on the page:
<asp:WebPartManager ID="WebPartManager1" runat="server" >
<Personalization Enabled="True" />
</asp:WebPartManager>
Looks like I'm allowed to post an answer now. I guess I won't delete my account after all. But geez, SO isn't very friendly to noobs.
Anyway, I solved the problem by extending SqlPersonalizationProvider:
Imports System.Web.UI.WebControls.WebParts
Public Class MySqlPersonalizationProvider
Inherits SqlPersonalizationProvider
Protected Overrides Sub LoadPersonalizationBlobs(ByVal webPartManager As System.Web.UI.WebControls.WebParts.WebPartManager, ByVal path As String, ByVal userName As String, ByRef sharedDataBlob() As Byte, ByRef userDataBlob() As Byte)
Dim impersonatedName As String = MySession.UserLoginName()
path = path & "|" & impersonatedName
userName = impersonatedName
MyBase.LoadPersonalizationBlobs(webPartManager, path, userName, sharedDataBlob, userDataBlob)
End Sub
Protected Overrides Sub SavePersonalizationBlob(ByVal webPartManager As System.Web.UI.WebControls.WebParts.WebPartManager, ByVal path As String, ByVal userName As String, ByVal dataBlob() As Byte)
Dim impersonatedName As String = MySession.UserLoginName()
path = path & "|" & impersonatedName
userName = impersonatedName
MyBase.SavePersonalizationBlob(webPartManager, path, userName, dataBlob)
End Sub
End Class
And then in web.config:
<system.web>
<webParts>
<personalization defaultProvider="AspNetSqlPersonalizationProvider">
<providers>
<remove name="AspNetSqlPersonalizationProvider"/>
<add name="AspNetSqlPersonalizationProvider"
type="MyProject.MySqlPersonalizationProvider"
connectionStringName="MyConnectionString"
applicationName="/"></add>
</providers>
</personalization>
</webParts>
<profile enabled="true" defaultProvider="TableProfileProvider">
<providers>
<clear />
<add name="TableProfileProvider" type="Microsoft.Samples.SqlTableProfileProvider" connectionStringName="iCaseHomepage" table="aspnet_Profile" applicationName="/" />
</providers>
</profile>
</system.web>
<connectionStrings>
<remove name="LocalSqlServer"/>
<add name="MyConnectionString" connectionString="Data Source=MyServerName;Initial Catalog=MyDatabaseName;Integrated Security=True" providerName="System.Data.SqlClient"/>
</connectionStrings>
and on the page:
<asp:WebPartManager ID="WebPartManager1" runat="server" >
<Personalization Enabled="True" />
</asp:WebPartManager>
Related
When i created a profile and when i add items it always says not declared in the code behind!!
I tried to change the Framework of the project from Framework 4.0 to Framework 3.5 and it still didn't work.
It says FirstNamep , LastNamep are not declared .
And in the Web.config :
<profile defaultProvider="CustomProfileProvider" enabled="true">
<providers>
</providers>
<!-- Define the properties for Profile... -->
<properties>
<add name="FirstNamep" type="String" />
<add name="LastNamep" type="String" />
</properties>
</profile>
Behind the Code:
Profile.FirstNamep = FirstNameTextBox.Text
Profile.LastNamep = LastNameTextBox.Text
The properties are dynamically generated at runtime, which means you can't access them from code-behind. What you can do is access them from your .ASPX pages using a script block (if that works for you). Like this.
<%# Page Language="C#" %>
<script runat="server">
public void Page_Init()
{
Profile.FirstNamep = "some dood";
}
</script>
<div>Your name is <%= Profile.FirstNamep %></div>
It seems to be sort of "by design" that the Profile is available to .aspx pages, but not to the code behind.
If you've defined the default provider as CustomProfileProvider, then that has to be a class that inherits System.Web.Profile.ProfileProvider. Otherwise, you should use the default SQL profile provider.
<connectionStrings>
<add name="ApplicationServices" connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\aspnetdb.mdf;User Instance=true" providerName="System.Data.SqlClient" />
</connectionStrings>
<membership>
<providers>
<clear/>
<add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="ApplicationServices" applicationName="/" />
</providers>
I have a simple create user wizard and custom membership provider which was taken from here
Now I am following this tutorial by scott Mitchell and creating new user using wizard and able to send email by setting Disable create property user to "False" so that whenever user recieves the activation link he needs to click that and verifies his account.
Now the problem is when he creates new user it is working fine and when he tried to login immediately he gets message that he needs to ativate the link first in order to login.
And after registration he gets email and when he clicks the link it gives me error that there is no user in the database.
As you can see below that user gets activation link
When the user tried to click it he gets that he is not found in the database
And if i check in the administration tool If I check the user is available without a tick beside it.
Here is my web.config:
<?xml version="1.0"?>
<configuration>
<connectionStrings>
<add name="HDIConnectionString"
connectionString="Data Source=.\SQLExpress;Integrated Security=True;User Instance=True;AttachDBFilename=|DataDirectory|HDIMembershipProvider.mdf"/>
</connectionStrings>
<system.web>
<roleManager defaultProvider="CustomProvider">
<providers>
<add connectionStringName="HDIConnectionString" name="CustomProvider"
type="System.Web.Security.SqlRoleProvider" />
</providers>
</roleManager>
<membership defaultProvider="HDIMembershipProvider">
<providers>
<clear/>
<add name="HDIMembershipProvider" type="HDI.AspNet.Membership.HDIMembershipProvider" connectionStringName="HDIConnectionString" enablePasswordRetrieval="true" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" passwordFormat="Clear"/>
</providers>
</membership>
<machineKey validationKey="C50B3C89CB21F4F1422FF158A5B42D0E8DB8CB5CDA1742572A487D9401E3400267682B202B746511891C1BAF47F8D25C07F6C39A104696DB51F17C529AD3CABE" decryptionKey="8A9BE8FD67AF6979E7D20198CFEA50DD3D3799C77AF2B72F" validation="SHA1"/>
<authentication mode="Forms">
<forms name=".ASPXFORMSAUTH" loginUrl="Login.aspx" />
</authentication>
<compilation debug="true" strict="false" explicit="true" targetFramework="4.0">
<assemblies>
<add assembly="System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="System.Web.Extensions.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
</assemblies>
</compilation>
</system.web>
<appSettings>
<add key="adminEmail" value="noreply#xyz.com"/>
</appSettings>
<system.net>
<mailSettings>
<smtp from="xyz#gmail.com">
<network host="smtp.gmail.com" password="password" port="587" userName="xyz#gmail.com"/>
</smtp>
</mailSettings>
</system.net>
</configuration>
And code behind for createuser.aspx:
Protected Sub CreateUserWizard1_SendingMail(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.MailMessageEventArgs) Handles CreateUserWizard1.SendingMail
Dim userInfo As MembershipUser = Membership.GetUser(CreateUserWizard1.UserName)
'Construct the verification URL
Dim verifyUrl As String = Request.Url.GetLeftPart(UriPartial.Authority) & Page.ResolveUrl("~/Verify.aspx?ID=" & userInfo.ProviderUserKey.ToString())
'Replace <%VerifyUrl%> placeholder with verifyUrl value
e.Message.Body = e.Message.Body.Replace("<%VerifyUrl%>", verifyUrl)
End Sub
Verify Page_Load:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
'Make sure that a valid querystring value was passed through
If String.IsNullOrEmpty(Request.QueryString("ID")) OrElse Not Regex.IsMatch(Request.QueryString("ID"), "[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}") Then
InformationLabel.Text = "An invalid ID value was passed in through the querystring."
Else
'ID exists and is kosher, see if this user is already approved
'Get the ID sent in the querystring
Dim userId As Guid = New Guid(Request.QueryString("ID"))
'Get information about the user
Dim userInfo As MembershipUser = Membership.GetUser(userId)
If userInfo Is Nothing Then
'Could not find user!
InformationLabel.Text = "The user account could not be found in the membership database."
Else
'User is valid, approve them
userInfo.IsApproved = True
Membership.UpdateUser(userInfo)
'Display a message
InformationLabel.Text = "Your account has been verified and you can now log into the site."
End If
End If
And here is the database screenshot:
#Tim and Baldy-I have finally got working but not with UserID.I don't know what's wrong with the GUID and I tried it with username and it's working perfectly.
So if any modifications with the GUID please let me know.
You are passing a guid type to the GetUser method of the Membership class.
UPDATE Have tested this now. Passing a GUID does call the correct overload - GetUser(object providerUserKey). So this answer is not relevant.
How can you be sure that this is being inferred to the correct overload at runtime?
GetUser has both string and object single parameter overloads, therefore it would make sense to pass the guid in as an object so you are explicitly stating which overload you want to call.
The framework may be calling ToString() on your guid, which would invoke the overload that looks up the username rather than the provider key.
Not at a computer right now, but it should go like this...
Dim key as new object()
'put the guid in the object type
key = Userid
Dim user = Membership.GetUser(key)
I'm trying to create a routine in my asp.net's main page that will see if the current user is a member of a Windows domain group. The site is hosted in IIS and is visible through our intranet.
GlenFerrieLive listed this code (which I'd like to use) in an earlier post:
UserName = System.Environment.UserName
If Roles.IsUserInRole(UserName, "MyDomain\MyGroup") Then
Dim UserExists As Boolean = True
End If
When trying that code, I got the above-mentioned error. So I plugged in the roleManager tag in my Web.config like so:
<roleManager enabled="true" cacheRolesInCookie="true" defaultProvider="ActiveDirectoryMembershipProvider" cookieName=".ASPXROLES" cookiePath="/" cookieTimeout="480" cookieRequireSSL="false" cookieSlidingExpiration="true" createPersistentCookie="false" cookieProtection="All" />
Problem is, now I'm getting the configuration error 'Default Role Provider could not be found'.
How can I get around this? I just need to see if the current user exists in a specific domain group.
Any help would be greatly appreciated.
Thanks,
Jason
Look into this page:http://msdn.microsoft.com/en-us/library/ff648345.aspx
You need something like this in your webconfig specifying where the default role provider points to
<connectionStrings>
<add name="ADConnectionString"
connectionString=
"LDAP://domain.testing.com/CN=Users,DC=domain,DC=testing,DC=com" />
</connectionStrings>
<system.web>
...
<membership defaultProvider="MembershipADProvider">
<providers>
<add
name="MembershipADProvider"
type="System.Web.Security.ActiveDirectoryMembershipProvider, System.Web,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
connectionStringName="ADConnectionString"
connectionUsername="<domainName>\administrator"
connectionPassword="password"/>
</providers>
</membership>
...
</system.web>
I ended up using this:
Private Function ValidateActiveDirectoryLogin(ByVal Domain As String, ByVal Username As String, ByVal Password As String) As Boolean
Dim Success As Boolean = False
Dim Entry As New System.DirectoryServices.DirectoryEntry("LDAP://" & Domain, Username, Password)
Dim Searcher As New System.DirectoryServices.DirectorySearcher(Entry)
Searcher.SearchScope = DirectoryServices.SearchScope.OneLevel
Try
Dim Results As System.DirectoryServices.SearchResult = Searcher.FindOne
Success = Not (Results Is Nothing)
Catch
Success = False
End Try
Return Success
End Function
Worked like a charm when this was in my web.config:
<authentication mode="Windows"/>
<roleManager enabled="true" cacheRolesInCookie="true" defaultProvider="AspNetWindowsTokenRoleProvider" cookieName=".ASPXROLES" cookiePath="/" cookieTimeout="480" cookieRequireSSL="false" cookieSlidingExpiration="true" createPersistentCookie="false" cookieProtection="All" />
I created a File Cleanup Provider that is inherits system.web.management.webeventprovider.
Public Class FileCleanupProvider
Inherits System.Web.Management.WebEventProvider
Private Const StateFIleFolderPath As String = "StateData/"
Public Overrides Sub Initialize(ByVal name As String, ByVal config As System.Collections.Specialized.NameValueCollection)
MyBase.Initialize(name, config)
End Sub
Public Overrides Sub Flush()
' not required
End Sub
Public Overrides Sub ProcessEvent(ByVal raisedEvent As System.Web.Management.WebBaseEvent)
Dim DateTimeRaised As DateTime = raisedEvent.EventTime
' Remove files
Dim FilePath As String = IO.Path.Combine(HttpRuntime.AppDomainAppPath, StateFIleFolderPath)
For Each FileName In IO.Directory.GetFiles(FilePath)
If (DateTimeRaised - IO.File.GetCreationTime(FileName)) > TimeSpan.FromHours(6) Then
IO.File.Delete(FileName)
End If
Next
End Sub
Public Overrides Sub ShutDown()
'Clean up on shut down
Dim FilePath As String = IO.Path.Combine(HttpRuntime.AppDomainAppPath, StateFIleFolderPath)
For Each FileName In IO.Directory.GetFiles(FilePath)
IO.File.Delete(FileName)
Next
End Sub
End Class
And I configure in web.config this
<healthMonitoring enabled="true" heartbeatInterval="5">
<providers>
<add name="FileCleanupProvider" type="System.Web.Management.WebEventProvider"/>
</providers>
<profiles>
<remove name="Default"/>
<add name="Default" minInstances="1" maxLimit="Infinite" minInterval="00:01:00" custom=""/>
</profiles>
<eventMappings>
<remove name="Heartbeats"/>
<add name="Heartbeats" type="System.Web.Management.WebHeartbeatEvent,System.Web,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a" startEventCode="0" endEventCode="2147483647" />
</eventMappings>
<!--<eventMappings>
<add name="FileCleanupEvent" type="System.Web.Management.WebHeartbeatEvent" startEventCode="0" endEventCode="2147483647"/>
</eventMappings>-->
<rules>
<clear/>
<add name="FileCleanupRule" eventName="Heartbeats" provider="FileCleanupProvider" profile="Default"/>
</rules>
</healthMonitoring>
But I got one problem that.
How can I solve that?
You need to specify your class in the configuration:
<add name="FileCleanupProvider" type="YourNameSpace.FileCleanupProvider,AssemblyName,Version=0.0.0.0,Culture=neutral,PublicKeyToken=PublicKeyHere"/>
The .NET is creating instance of the class on the fly, so the class must be "valid".
I would like to know how I can verify a user's credential against an existing asp.net membership database. The short story is that we want provide single sign on access.
So what I've done is to connect directly to the membership database and tried to run a sql query against the aspnet_Membership table:
private bool CanLogin(string userName, string password)
{
// Check DB to see if the credential is correct
try
{
string passwordHash = FormsAuthentication.HashPasswordForStoringInConfigFile(password, "SHA1");
string sql = string.Format("select 1 from aspnet_Users a inner join aspnet_Membership b on a.UserId = b.UserId and a.applicationid = b.applicationid where a.username = '{0}' and b.password='{1}'", userName.ToLowerInvariant(), passwordHash);
using (SqlConnection sqlConn = new SqlConnection(ConfigurationManager.ConnectionStrings["LocalSqlServer"].ConnectionString))
using (SqlCommand sqlCmd = new SqlCommand(sql, sqlConn))
{
sqlConn.Open();
int count = sqlCmd.ExecuteNonQuery();
return count == 1;
}
}
catch (Exception ex)
{
return false;
}
}
The problem is the password value, does anyone know how the password it is hashed?
if you have two asp.net apps on the same IIS server, you can do SSO like this. I asked this question and answered it myself.
here
Once you have both apps pointing at your asp_membership database by placing the following in the system.web section of your web config
<authentication mode="Forms" />
<membership>
<providers>
<clear/>
<add name="AspNetSqlMembershipProvider"
type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
connectionStringName="membership"
applicationName="/"
/>
</providers>
</membership>
<roleManager enabled="true" />
make sure both have the same applicationname property set.
I was using IIS 6 so I configured it to autogenerate a machine key for both applications. Because both of these applications live on the same machine the key would be identical, this is the critical part to making the SSO work. After setting up IIS the following was added to my web.config
<machineKey decryptionKey="AutoGenerate" validation="SHA1" validationKey="AutoGenerate" />
That was all there was to it. Once that was done I could log into app1 and then browse to app2 and keep my security credentials.
The problem is the password value,
does anyone know how the password it
is hashed?
Yes - you do! Check your web.config file for something like this:
<membership defaultProvider="MembershipSqlProvider"
userIsOnlineTimeWindow="15">
<providers>
<add name="MembershipSqlProvider"
type="System.Web.Security.SqlMembershipProvider, System.Web,
Version=1.2.3400.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a"
PasswordFormat="Hashed" />
</providers>
</membership>
The PasswordFormat is what you are looking for. It can have the following three values:
Clear
Encrypted
Hashed
And, Microsoft sets the default value to Hashed for PasswordFormat.
Why don't check it automatically via System.Web.Security.Membership.ValidateUser() ?
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<membership defaultProvider="MyMembershipProvider">
<providers>
<clear />
<add name="MyMembershipProvider" type="MyApplication.MyMembershipProvider" connectionStringName="MyConnString" />
</providers>
</membership>
</system.web>
</configuration>