MembershipProvider and RequiresQuestionAndAnswer - asp.net

We provide a website template for our customers to use as the basis of their websites. Our website has a custom membership provider.
We have had a problem raised by one customer. The customer sends out invitations to prospective members by email with a url to login the member. During registration they set their security question / answer.
However sometimes the prospective member loses the email (and therefore their password) but still tries to join the site.
The customer requested that the member be allowed to reset their password without the usual security question / answer when registration was not complete.
Unfortunately the MembershipProvider doesn't provide the username when requesting whether the question / answer are required. However it does call GetUser() just before.
To get this feature working I added a method (StartingPasswordRecovery) to my MembershipProvider to flag that password reset was active, calling it from the OnVerifyingUser event in the PasswordRecovery page.
While this code works I'm not convinced that it's very robust.
Can anyone point me towards a better solution.
Here's the relevant code I added to my membership provider.
Private _hasUserDefinedQuestionAndAnswer As Boolean
Private _isResettingPassword As Boolean
Public Overloads Overrides Function GetUser(ByVal username As String, ByVal userIsOnline As Boolean) As System.Web.Security.MembershipUser
...
_hasUserDefinedQuestionAndAnswer = ...
...
End Function
Public Overrides ReadOnly Property RequiresQuestionAndAnswer() As Boolean
Get
If Me._isResettingPassword Then
Me._isResettingPassword = False
Return Me.pRequiresQuestionAndAnswer And Me._hasUserDefinedQuestionAndAnswer
End If
Return Me.pRequiresQuestionAndAnswer
End Get
End Property
Public Sub StartingPasswordRecovery()
Me._isResettingPassword = True
End Sub

I'm not sure if i've understood you correctly, but couldn't you use the User-Profile to determine if a user requires question and answer or not?
web.config:
<profile defaultProvider="YourProfileProvider">
<providers>
<clear/>
<add name="YourProfileProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="ConnectionStringToDB" applicationName="/YourApp"></add>
</providers>
<properties>
<add name="RequiresQuestionAndAnswer" defaultValue="false" />
</properties>
</profile>
Custom membership-provider:
Public Overrides ReadOnly Property RequiresQuestionAndAnswer As Boolean
Get
If HttpContext.Current.User.Identity.IsAuthenticated Then
Dim userRequiresQuestionAndAnswer = _
CType(HttpContext.Current.Profile.GetPropertyValue("RequiresQuestionAndAnswer"), Boolean)
Return userRequiresQuestionAndAnswer
Else
Return MyBase.RequiresQuestionAndAnswer
End If
End Get
End Property
You could set it in your user-management page for every user individually:
HttpContext.Current.Profile.SetPropertyValue("RequiresQuestionAndAnswer", userRequiresQuestionAndAnswer)
HttpContext.Current.Profile.Save()
Edit:
according to your comment, i've modified the code a little bit. I hope that helps to get it working:
in custom membership-provider:
Public Overloads Overrides ReadOnly Property RequiresQuestionAndAnswer As Boolean
Get
If HttpContext.Current.User.Identity.IsAuthenticated Then
Return RequiresQuestionAndAnswer(Membership.GetUser.UserName)
Else
Return MyBase.RequiresQuestionAndAnswer
End If
End Get
End Property
Public Overloads ReadOnly Property RequiresQuestionAndAnswer(ByVal userName As String) As Boolean
Get
Dim profile As ProfileBase = ProfileBase.Create(userName)
If Not profile Is Nothing Then
Dim userRequiresQuestionAndAnswer = _
CType(profile.GetPropertyValue("RequiresQuestionAndAnswer"), Boolean)
Return userRequiresQuestionAndAnswer
Else
Return MyBase.RequiresQuestionAndAnswer
End If
End Get
End Property
where your PasswordRecovery-Control is:
Protected Sub VerifyingUser(ByVal sender As Object, ByVal e As LoginCancelEventArgs)
Dim login As WebControls.Login = DirectCast(Me.LoginView1.FindControl("Login1"), WebControls.Login)
Dim userName = DirectCast(login.FindControl("PwdRecovery"), PasswordRecovery).UserName
Dim RequiresQuestionAndAnswer = DirectCast(Membership.Provider, YourMembershipProvider).RequiresQuestionAndAnswer(userName)
'....'
End Sub

Related

.NET 4.0 FormsAuthentication.SetAuthCookie FAIL

I know there are a few questions out there on this already but none seem to help my problem.
I am debugging a VB.NET webForms app and I cannot Get FormsAuthentication.SetAuthCookie to work (with a non-persistent cookie). It seems to create an HttpContext.Current.User object when I check for it in a watch window it seems to have created the object, but not its "Identity" property.
I've read a bunch of SO posts checked the basic things, like seeing if my browser supports cookies, etc... This project is a direct port from an earlier project of ours, which uses the same code for all things listed here, and it works just fine, relatively speaking. Where this throws an exception is where it's called from my BLL code that is supposed to get it.
Here is the code that calls the FormsAuthentication method...:
'When participant logs in having already created records in DB.
Protected Sub btnGo_Click(ByVal sender As Object, ByVal e As System.Web.UI.ImageClickEventArgs) Handles btnGo.Click
If Me.txtUsername.Text.Trim.Length <> 0 AndAlso Me.txtPassword.Text.Trim.Length <> 0 Then
If Membership.ValidateUser(Me.txtUsername.Text, Me.txtPassword.Text) Then
FormsAuthentication.SetAuthCookie(Me.txtUsername.Text, False)
'This is where we run into trouble; the property checks with the forms auth...
MyBLL.Common.CurrentUser = New MyBLL.User(Me.txtUsername.Text)
'set site property..
If Site_ IsNot Nothing Then
MyBLL.Common.CurrentUser.Site = Me.Site_
End If
MyBLL.Common.CurrentParticpant = Nothing
MyBLL.Common.CurrentParticpantVisitID = -1
Response.Redirect("~/Apps/Dashboard.aspx", True)
Else
Me.lblLoginMsg.Visible = True
End If
Else
Me.lblLoginMsg.Visible = True
End If
End Sub
Here is the code for the BLL object (which has a shared property calling user from HttpContext...)
Public Shared Property CurrentUser() As MyBLL.User
Get
Dim objUser As MyBLL.User
If Not IsNothing(HttpContext.Current.Session("currentSiteUser")) Then
objUser = CType(HttpContext.Current.Session("currentSiteUser"), MyBLL.User)
If objUser.Username <> HttpContext.Current.User.Identity.Name Then
objUser = New MyBLL.User(HttpContext.Current.User.Identity.Name)
HttpContext.Current.Session("currentSiteUser") = objUser
End If
Else
objUser = New MyBLL.User(HttpContext.Current.User.Identity.Name)
HttpContext.Current.Session("currentSiteUser") = objUser
End If
Return objUser
End Get
Set(ByVal value As MyBLL.User)
'_CurrentUser = value
HttpContext.Current.Session("currentSiteUser") = value
End Set
End Property
Here is the Forms element from my webConfig; everything seems alright here to me...
<authentication mode="Forms">
<forms loginUrl="~/Public/Default2.aspx" defaultUrl="~/Public/Default2.aspx" timeout="60"/>
</authentication>
You should immediately redirect after callaing the SetAuthCookie method and only on subsequent requests you may hope to get the full IPrincipal to be initialized. Do not try to access HttpContext.Current.User.Identity.Name in the same controller action in which you called the SetAuthCookie method. It won't have any effect. The redirect is important so that on the next request the forms authentication module will built the principal from the request cookie.
In your CurrentUser method you seem to be calling the HttpContext.Current.User.Identity.Name property but this is not available until you redirect.

How to retrieve custom settings from web.config

I have a web.config with some Custom Settings in it(not in appsettings) which look like this:
<ldapSettings>
<add key="server" value="xxxxxx"/>
<add key="portNumber" value="28400"/>
<add key="protocolVersion" value="3"/>
<add key="secure" value="true"/>
</ldapSettings>
How can I use the server address for my code?
I tried following
dim pfad As String
pfad = System.Configuration.ConfigurationManager.GetSection("ldapSettings")
Dim blas As String
blas =pfad["server"]
But it doesn't work. What am I missing?
You need to cast the return value of GetSection("ldapSettings") because it does not return string:
Dim ldap As ldapSettings = CType(ConfigurationManager.GetSection("ldapSettings"), ldapSettings)
Dim server As String = ldapSettings.server
First of all, you will need to define a class for your custom configuration section in order to tell ASP.NET what properties it has, like so:
Public Class ldapSettings
Inherits ConfigurationSection
Private Shared LSettings As ldapSettings = TryCast(ConfigurationManager.GetSection("ldapSettings"), ldapSettings)
Public Shared ReadOnly Property Settings() As ldapSettings
Get
Return LSettings
End Get
End Property
<ConfigurationProperty("server")>
Public Property Server() As String
Get
Return Me("server")
End Get
Set(value As String)
Me("server") = value
End Set
End Property
<ConfigurationProperty("portNumber")>
Public Property PortNumber() As String
Get
Return Me("portNumber")
End Get
Set(value As String)
Me("portNumber") = value
End Set
End Property
<ConfigurationProperty("protocolVersion")>
Public Property ProtocolVersion() As String
Get
Return Me("protocolVersion")
End Get
Set(value As String)
Me("protocolVersion") = value
End Set
End Property
<ConfigurationProperty("secure")>
Public Property Secure() As Boolean
Get
Return Me("secure")
End Get
Set(value As Boolean)
Me("secure") = value
End Set
End Property
End Class
Then, you will need to change your web.config file slightly. The XML layout of the custom section should look like this instead:
<configSections>
<section name="ldapSettings" type="Your_Assembly_Name.ldapSettings"/>
</configSections>
<ldapSettings
server="xxxxxx"
portNumber="28400"
protocolVersion="3"
secure="true"
/>
And then finally, you can get a setting using the following line:
Dim Secure As Boolean = ldapSettings.Settings.Secure
Sorry about the VB.NET, you can use this tool to convert if you need to: http://www.developerfusion.com/tools/convert/csharp-to-vb/
Info mainly sourced from here:
http://haacked.com/archive/2007/03/11/custom-configuration-sections-in-3-easy-steps.aspx
I found out a much simpler solution
here is what i did:
Private config As NameValueCollection
config = DirectCast(ConfigurationManager.GetSection("ldapSettings"), NameValueCollection)
Dim server As String
server = config.[Get]("server")
ConfigurationManager.AppSettings("keyname")
usually works for me
You can get help from following link
http://msdn.microsoft.com/en-us/library/610xe886%28v=vs.80%29.aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1

How to get email from Google OpenID Provider (in VB)

Salam to all
I am using the DotNetOpenAuth control for authentication from google. This is the code that I am using.
<rp:OpenIdLogin ID="OID" runat=server Identifier="https://www.google.com/accounts/o8/id" RequestEmail="Require" ></rp:OpenIdLogin>
To get the response from the provider for the email ID I am using this code in the page load event of default.aspx
Public Email As String = "N/A"
Public FullName As String = "N/A"
Public Country As String = "N/A"
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim openid As OpenIdRelyingParty = New OpenIdRelyingParty
Dim response = openid.GetResponse
If (Not (response) Is Nothing) Then
Select Case (response.Status)
Case AuthenticationStatus.Authenticated
Dim fetch = response.GetExtension
Dim email As String = String.Empty
If (Not (fetch) Is Nothing) Then
email = fetch.GetAttributeValue(WellKnownAttributes.Contact.Email)
End If
FormsAuthentication.RedirectFromLoginPage(response.ClaimedIdentifier, False)
End Select
End If
End Sub
I am able be authenticated with google, but there is no response of the email id from google.
Please tell me what exactly I am missing that is causing this problem.
Update
<configSections>
<section name="dotNetOpenAuth" type="DotNetOpenAuth.Configuration.DotNetOpenAuthSection" requirePermission="false" allowLocation="true"/>
</configSections>
<dotNetOpenAuth>
<openid>
<relyingParty>
<behaviors>
<!-- The following OPTIONAL behavior allows RPs to use SREG only, but be compatible
with OPs that use Attribute Exchange (in various formats). -->
<add type="DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform, DotNetOpenAuth" />
</behaviors>
</relyingParty>
</openid>
</dotNetOpenAuth>
You're likely missing the appropriate "behavior" in your web.config file. Please study this page and apply it to your site: https://github.com/DotNetOpenAuth/DotNetOpenAuth/wiki/Enhancements
Also, when using this behavior, you should be looking for the ClaimsResponse extension in the positive authentication response rather than FetchResponse.
As a side note, you have a lot of boilerplate code in your code-behind's Page_Load method that you don't need. The OpenIdControl you're using has a LoggedIn method that does most of what you're doing here (it gets you all the way to the body of your Case block.

Connect reusable ASP.NET WebControl to a method for loading data

I'm trying to create a control that can extend other webcontrols and set some properties like visible and enabled, based on user permissions.
Here's an example where your user role would need to include the "CanSave" permission:
<asp:Button ID="btn1" runat="server" Text="Save"/>
<myControls:PermissionsExtender runat="server" ControlToSet="btn1" Permission="CanSave"/>
I'm trying to keep this reusable, that's why the PermissionExtender is in a separate project that can not have any dependencies to other projects. To make a decision, the control of course needs to get this info from somewhere else (database or something). I made another control and, using events, the above extender will be set by a master control, so only that needs to know where to look up the information.
The master control now needs to be configured to know where the information about roles and permissions will be coming from. My idea was to have an interface inside the reusable project, and implement that somewhere else, then configure my control to go and find the class that implements the method I need and load it through reflection. But I'm unclear how this could work. I would probably place the master control in the masterpage and supply it a class name like PermissionClass="SecurityLibrary.PermissionsClass". Kinda like ObjectDatasource does it, but other suggestions are welcome.
The method signature would be like:
bool HasPermission(string permission)
It would know the current users role and using that combination, looks up if the role includes the permission.
How can I wire up a call from the control to a method inside my main project that can supply the necessary information without making them dependent.
I think I've got something that will work for you (tested fine for me but I may have misunderstood part of what you were looking for). With this implementation the asp.net designer code will look like this:
<web:PermissionMasterControl runat="server" ID="masterController" PermissionClass="SecurityLibrary.RandomPermissionClass" />
<asp:Button ID="btnSave" runat="server" Text="save" />
<web:PermissionExtender runat="server" ControlToSet="btnSave" Permission="CanSave" MasterControllerID="masterController" />
Now for the SecurityLibrary. Pretty straight forward, I included a simple "RandomPermissionClass" that randomly returns true/false.
Namespace SecurityLibrary
Public MustInherit Class PermissionClass
Public MustOverride Function HasPermission(ByVal permission As String) As Boolean
End Class
Public Class RandomPermissionClass
Inherits PermissionClass
Private rand As New Random()
Public Overrides Function HasPermission(permission As String) As Boolean
Return If(rand.Next(2) = 0, False, True)
End Function
End Class
End Namespace
Now we have the "myControls" library, which contains no references to SecurityLibrary. I created two controls and a delegate. The controls are "PermissionMasterControl" and "PermissionExtender". The delegate is what is used to actually perform the check against the reflected object.
Namespace myControls
Public Delegate Function HasPermissionDelegate(ByVal permission As String) As Boolean
Public Class PermissionMasterControl
Inherits System.Web.UI.Control
Public Property PermissionClass As String
Get
Return If(ViewState("PermissionClass") Is Nothing, "", ViewState("PermissionClass").ToString())
End Get
Set(value As String)
ViewState("PermissionClass") = value
End Set
End Property
Private ReadOnly Property PermissionDelegate As HasPermissionDelegate
Get
If _permissionDel Is Nothing Then
If Not String.IsNullOrEmpty(PermissionClass) Then
Dim t = Type.GetType(PermissionClass, False)
If t IsNot Nothing Then
_permissionObj = Activator.CreateInstance(t)
Dim mi As MethodInfo = _
t.GetMethod("HasPermission", BindingFlags.Public Or BindingFlags.Instance)
_permissionDel = [Delegate].CreateDelegate(GetType(HasPermissionDelegate), _permissionObj, mi)
End If
End If
End If
Return _permissionDel
End Get
End Property
Private _permissionObj As Object = Nothing
Private _permissionDel As HasPermissionDelegate = Nothing
Public Function HasPermission(ByVal permission As String) As Boolean
If PermissionDelegate Is Nothing Then
Throw New NullReferenceException("The specified permission class (" + PermissionClass + ") could not be loaded/found.")
End If
Return PermissionDelegate(permission)
End Function
End Class
Public Class PermissionExtender
Inherits System.Web.UI.Control
Public Property ControlToSet As String
Get
Return If(ViewState("ControlToSet") Is Nothing, "", ViewState("ControlToSet").ToString())
End Get
Set(value As String)
ViewState("ControlToSet") = value
End Set
End Property
Public Property Permission As String
Get
Return If(ViewState("Permission") Is Nothing, "", ViewState("Permission").ToString())
End Get
Set(value As String)
ViewState("Permission") = value
End Set
End Property
Public Property MasterControllerID As String
Get
Return If(ViewState("MasterControllerID") Is Nothing, "", ViewState("MasterControllerID").ToString())
End Get
Set(value As String)
ViewState("MasterControllerID") = value
End Set
End Property
Protected ReadOnly Property MasterController As PermissionMasterControl
Get
If _mastercontroller Is Nothing Then
_mastercontroller = Me.Page.FindControl(MasterControllerID)
End If
Return _mastercontroller
End Get
End Property
Protected ReadOnly Property ManagedControl As Control
Get
If _controlToSet Is Nothing Then
_controlToSet = Me.NamingContainer.FindControl(ControlToSet)
End If
Return _controlToSet
End Get
End Property
Private _controlToSet As Control = Nothing
Private _mastercontroller As PermissionMasterControl = Nothing
Protected Overrides Sub OnLoad(e As System.EventArgs)
MyBase.OnLoad(e)
Dim bResult As Boolean = MasterController.HasPermission(Permission)
ManagedControl.Visible = bResult
End Sub
End Class
End Namespace

Asp.Net Button Event on refresh fires again??? GUID?

The obvious issue here is that on refresh a button event is recalled and duplicate posts are created in the database. I have read other questions on this site about the very same issue.
Apparently the answer is for every post create a GUID and check to make sure the GUID is unique? I am not really sure how to implement this.
Does that mean on refresh, it will try and create a duplicate post with the same GUID?
How do you implement a GUID into your database? Or if this is not the answer, what is?
Thank you!
The idea is that you create a unique number for the form, and when the form is posted you save this unique number in the database in the record that you are editing/creating. Before saving you check if that number has already been used, in that case it's a form that has been reposted by refreshing.
If you are updating a record, you only have to check if that record has been saved with the same unique number, but if you are adding a new record you have to check if any other record has that number.
A Guid is a good number to use as it's very unlikely that you get a duplicate. A 31 bit random number that the Random class can produce is also pretty unlikely to give duplicates, but the 128 bits of a Guid makes it a lot more unlikely.
You don't have to create the Guid value in the database, just use Guid.NewGuid() in the code that initialises the form. You can put the Guid in a hidden field in the form. In the database you only need a field that can store a Guid value, either a Guid data type if available or just a text field large enough to hold the text representation of the Guid.
You can use the ToString method to get the string representation of a Guid value (so that you can put it in the form). Using id.ToString("N") gives the most compact format, i.e. 32 hexadecimal digits without separators. Using id.ToString("B") gives the more recognisable format "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}". To get the Guid back from a string (either format), you just use new Guid(str).
Here's a RefreshValidator control that I use. Just drop it on your page, and check Page.IsValid before saving to the database. You can add an error message like other validators, or catch the Refreshed event if you want to do something special. Since it's a Validator, GridViews and the like will already take notice of it - except for Delete or Cancel actions (yeah, I have a custom GridView that solves that too...)
The code is pretty simple - store a GUID into ControlState, and a copy in Session. On load, compare the 2. If they're not the same - then it's a refresh. Rinse, repeat, and create a new GUID and start over.
''' <summary>
''' A validator control that detects if the page has been refreshed
''' </summary>
''' <remarks>If <see cref="SessionState.HttpSessionState" /> is not available or is reset, validator will return Valid</remarks>
Public Class RefreshValidator
Inherits BaseValidator
Private isRefreshed As Boolean
Protected Overrides Sub OnInit(ByVal e As System.EventArgs)
MyBase.OnInit(e)
Page.RegisterRequiresControlState(Me)
End Sub
Protected Overrides Function SaveControlState() As Object
Dim obj As Object = MyBase.SaveControlState()
Return New System.Web.UI.Pair(_pageHashValue, obj)
End Function
Protected Overrides Sub LoadControlState(ByVal savedState As Object)
Dim pair As System.Web.UI.Pair = TryCast(savedState, System.Web.UI.Pair)
If pair IsNot Nothing Then
_pageHashValue = TryCast(pair.First, String)
MyBase.LoadControlState(pair.Second)
Else
MyBase.LoadControlState(savedState)
End If
End Sub
Private _pageHashValue As String
Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
MyBase.OnLoad(e)
If HttpContext.Current Is Nothing OrElse HttpContext.Current.Session Is Nothing Then
isRefreshed = False
Return
End If
' Get hash value from session
Dim currHashValue As String = CType(HttpContext.Current.Session(Me.UniqueID & ":pageHashValue"), String)
If _pageHashValue Is Nothing OrElse currHashValue Is Nothing Then
' No page hash value - must be first render
' No current hash value. Session reset?
isRefreshed = False
ElseIf currHashValue = _pageHashValue Then
' Everything OK
isRefreshed = False
Else
' Was refreshed
isRefreshed = True
End If
' Build new values for form hash
Dim newHashValue As String = Guid.NewGuid().ToString()
_pageHashValue = newHashValue
HttpContext.Current.Session(Me.UniqueID & ":pageHashValue") = newHashValue
End Sub
Protected Overrides Function ControlPropertiesValid() As Boolean
Return True
End Function
Protected Overrides Function EvaluateIsValid() As Boolean
If isRefreshed Then OnRefreshed(EventArgs.Empty)
Return Not isRefreshed
End Function
Protected Overridable Sub OnRefreshed(ByVal e As EventArgs)
RaiseEvent Refreshed(Me, e)
End Sub
''' <summary>
''' Fires when page is detected as a refresh
''' </summary>
Public Event Refreshed As EventHandler
End Class

Resources