In a migration from WebForms to MVC, some .aspx pages remain. Authentication for these is currently file-based and happens through Web.config. MVC authentication happens by adding an AuthorizeAttribute to GlobalFilters.Filters and to the controllers/actions as needed.
The Web.config authentication currently looks like this:
<authentication mode="Forms">
<forms loginUrl="~/SignIn.aspx" protection="All" path="/" timeout="10080" />
</authentication>
<authorization>
<deny users="?" />
<allow users="*" />
</authorization>
<location path="SomePage.aspx">
<system.web>
<authorization>
<allow roles="Administrator, BasicUser" />
<deny users="*" />
</authorization>
</system.web>
</location>
I would however like to move the WebForms authentication away from Web.config and into an MVC filter that performs a check using a method call. I have been unable to find a single example of this. Is this even possible, and are there any undesired implications of doing it?
I am aware that .aspx files are not handled by the MVC hooks by default.
I am looking for a way to grab all .aspx files regardless of what their code-behind's base class is.
In Global.asax.cs,
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
// Since MVC does not handle authentication for .aspx pages, do this here.
AuthController.AuthenticateAspxPage(this.Context);
}
and in AuthController, for some type UserRole,
public const string ErrorRedirectUrl = "~/Auth/Error";
private static readonly IDictionary<string, UserRole[]> _aspxAccessRoles =
new Dictionary<string, UserRole[]>()
{
{ "~/SomePage.aspx", new UserRole[] { UserRole.Administrator,
UserRole.BasicUser } },
...
};
and then,
[NonAction]
public static void AuthenticateAspxPage(HttpContext context)
{
string ext = context.Request.CurrentExecutionFilePathExtension;
string aspxPage = context.Request.AppRelativeCurrentExecutionFilePath;
ClaimsPrincipal principal = context.User as ClaimsPrincipal;
if ((ext == ".aspx" || ext == ".ashx") && !HasAspxAccess(aspxPage, principal))
{
context.Response.Redirect(ErrorRedirectUrl);
}
}
[NonAction]
public static bool HasAspxAccess(string aspxPage, ClaimsPrincipal principal)
{
if (principal == null || principal.Claims == null)
{
return false;
}
string[] userRoles = principal.Claims
.Where(claim => claim.Type == ClaimTypes.Role)
.Select(claim => claim.Value)
.ToArray();
UserRole[] accessRoles;
return _aspxAccessRoles.TryGetValue(aspxPage, out accessRoles)
&& accessRoles.Any(role => userRoles.Contains(role.ToString()));
}
Related
I need to redirect a user to a specific page when they sign into my web site.
In my Web.Config file I have:
<configuration>
<location path="User_personal_a.aspx">
<system.web>
<authorization>
<allow users="User1" />
<deny users="*" />
</authorization>
</system.web>
</location>
And then when they login I have:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
if (User.Identity.IsAuthenticated)
{
if (!string.IsNullOrEmpty(Request.QueryString["ReturnUrl"]))
{
Response.Redirect("UnauthorizedAccess.aspx");
}
if (UserName.Text.Trim() == "User1")
{
Response.Redirect("Client Reports/User_personal_a.aspx");
}
else
{
Response.Redirect("Admin/Dashboard.aspx");
}
StatusText.Text = string.Format("Hello {0}!!",
User.Identity.GetUserName());
LogoutButton.Visible = true;
}
else
{
LoginForm.Visible = true;
}
}
}
But this unfortunately does not work :-(
Need some help on this please.
Thanks
there is a bug / bad design for sure
this part
if (UserName.Text.Trim() == "User1")
is always false, because the text box UserName is probably empty - especial before the post back that you have set before reach that line of code.
What you most probably looking for there must be the
if (HttpContext.Current.User.Identity.Name == "User1")
but, this is base of your code logic.
I have a file directory that can be access directly from the URL
e.g.
http://website.com/DirectoryName/folder/downloadpdf.pdf
I need to control whom can access the files so I have created a ASHX handler so I check permissions of the user.
however, when I access http://website.com/DirectoryName/ the handler doesnt get called.
I believe its something to do with my config, I have
<location path="DirectoryName">
<system.webServer>
<handlers>
<add name="PSHandler" path="DirectoryName/*" verb="*" type="ProjectName.PSHandler"/>
</handlers>
</system.webServer>
</location>
Different way - IIS 6?
<location path="DirectoryName">
<system.web>
<httpHandlers>
<add path="DirectoryName/*" verb="*" type="ProjectName.PSHandler, PSHandler"/>
</httpHandlers>
</system.web>
</location>
Is there something wrong with the above?
the ashx file
<%# WebHandler Language="VB" Class="PSHandler" %>
Imports System
Imports System.Web
Public Class PSHandler : Implements IHttpHandler
Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
context.Response.ContentType = "text/plain"
context.Response.Write("Hello World")
End Sub
Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
Get
Return False
End Get
End Property
End Class
FYI this is by running locally using IIS Express
Thanks
I think you are very close. One thing is you will need to write file to response.
public class PSHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
string fileName = Path.GetFileName(context.Request.Path);
string filePath = context.Server.MapPath("~/DirectoryName/" + fileName);
// Check permission of user
context.Response.ContentType = "application/pdf";
context.Response.WriteFile(filePath);
}
public bool IsReusable
{
get { return false; }
}
}
<system.webServer>
<handlers>
<add name="PS" path="DirectoryName/*" verb="*"
type="ProjectName.PSHandler, ProjectName"
preCondition="integratedMode"/>
</handlers>
</system.webServer>
Update
First of all, replace httpHandlers with authorization tag.
<location path="DirectoryName">
<system.web>
<authorization>
<deny users="*"/>
</authorization>
</system.web>
</location>
Then add handler inside application's web.config.
<system.webServer>
<handlers>
<add name="PS" path="DirectoryName/*" verb="*"
type="ProjectName.PSHandler, ProjectName"
preCondition="integratedMode"/>
</handlers>
</system.webServer>
Then copy and paste the following PSHandler code.
Public Class PSHandler
Implements IHttpHandler
Public Sub ProcessRequest(context As HttpContext)
Dim fileName As String = Path.GetFileName(context.Request.Path)
Dim filePath As String = context.Server.MapPath(Convert.ToString("~/DirectoryName/") & fileName)
' Check permission of user. If permission denied, display 404 -
' context.Server.Transfer("~/404.aspx")
context.Response.ContentType = "application/pdf"
context.Response.WriteFile(filePath)
End Sub
Public ReadOnly Property IsReusable() As Boolean
Get
Return False
End Get
End Property
End Class
Reqeust URL should be like this -
http://www.yourwebsite.com/DirectoryName/downloadpdf.pdf
OR
http://localhost:xxxx/DirectoryName/downloadpdf.pdf
Please make sure downloadpdf.pdf file exists in DirectoryName folder.
If it is still doesn't work, please create a new Web Application, and test the above code. I have tested and it works fine.
Intranet application that needs to use Windows AD for authorization. Anyone will have access to the site, but only members of a specific AD group will have access to certain pages and API calls. The group name is in the web.config as it will vary by environment. I want to be able to use attributes on API controllers and methods:
[MyAdminAttribute]
public class MyController : ApiController
Here is my custom attribute:
public class MyAdminAttribute: AuthorizeAttribute
{
protected override bool IsAuthorized(HttpActionContext actionContext)
{
var user = ((ApiController)actionContext.ControllerContext.Controller).User; // THIS USER IS ALWAYS NULL, WHY?
var adminGroup = WebConfigurationManager.AppSettings["MyAdminGroup"];
var isInAdminGroup = user.IsInRole(adminGroup);
var roles = ((ClaimsIdentity)user.Identity).Claims
.Where(c => c.Type == ClaimTypes.Role)
.Select(c => c.Value).ToList(); // Just checking here, 0 roles found.
return isInAdminGroup;
}
}
I also need to be able to check that the user is in the role within a Razor view:
#if (User.IsInRole(WebConfigurationManager.AppSettings["MyAdminGroup"])) {
Here are the relevent web.config settings:
<authentication mode="Windows"/>
<roleManager enabled="true" defaultProvider="AspNetWindowsTokenRoleProvider">
<providers>
<clear />
<add
name="AspNetWindowsTokenRoleProvider"
type="System.Web.Security.WindowsTokenRoleProvider"
applicationName="/" />
</providers>
</roleManager>
However, the User is always null and User.IsInRole() is always false. Why would this be occuring?
I added Elmah tool to my Sitecore Solution, actually I did it like here
http://newguid.net/sitecore/2011/using-elmah-for-error-logging-within-sitecore/
Just one rolls I have enabled is allowRemoteAccess property,
<elmah>
<security allowRemoteAccess="1" />
</elmah>
Now logs available for every one from any place, but I want to show it just for user who is login (authorized) to sitecore (sitecore users)
How I can mange it ?
Thanks.
Not sure what Elmah had built in now, but I used to do this:
<location path="elmah.axd">
<system.web>
<authorization>
<allow roles="SITECORE_USERS"/> <!---put your role here-->
<deny users="*"/>
</authorization>
</system.web>
</location>
Securing Elmah RSS Feeds in ASP.NET website
I found solution after research of elmah source code and this article http://dotnetslackers.com/articles/aspnet/Securing-ELMAH-with-Independent-HTTP-Authentication.aspx. User can implement IRequestAuthorizationHandler for custom IHttpModule
I did next:
public class SitecoreAuthModule :IHttpModule, IRequestAuthorizationHandler
{
public bool Authorize(HttpContext context)
{
return SC.Context.User.IsAdministrator;
}
public void Init(HttpApplication context)
{
context.AuthenticateRequest += new EventHandler(ContextAuthenticateRequest); ;
}
void ContextAuthenticateRequest(object sender, EventArgs e)
{
var context = sender as HttpApplication;
if (context.Request.Path.IndexOf("elmah.axd",
StringComparison.InvariantCultureIgnoreCase) < 0)
return;
}
public void Dispose()
{
}
}
Having a code-blind moment.
ASP.NET 4.0.
Web.config:
<?xml version="1.0"?>
<configuration>
<system.web>
<authentication mode="Forms">
<forms name="DataViewer" loginUrl="login.aspx">
<credentials passwordFormat="Clear">
<user name="devuser" password="test" />
</credentials>
</forms>
</authentication>
<authorization>
<deny users="?" />
</authorization>
</system.web>
and a login control:
<asp:Login ID="login" runat="server" />
If I enter a username and password, and click Login, it hangs.
If I break, I can see in the call stack that login.AuthenticateUsingMembershipProvider() is in the middle of calling SqlMembershipProvider.ValidateUser(). There is no database defined or involved in this project at all, and I haven't specified that SqlMembershipProvider should be used.
So my question is, what membership provider should I use to get ASP.NET to use the usernames and passwords in the <credentials> element of web.config?
I'm amazed that considering how the framework designers went to the trouble of defining a <credentials /> element that they didn't implement any code to consume it.
I found a sort-of-working implementation of this here which I have fixed up and included below. All other members of MembershipProvider throw NotImplementedException.
using System.Configuration;
using System.Web.Configuration;
using System.Web.Security;
public class WebConfigMembershipProvider : MembershipProvider
{
private FormsAuthenticationUserCollection _users = null;
private FormsAuthPasswordFormat _passwordFormat;
public override void Initialize(string name,
System.Collections.Specialized.NameValueCollection config)
{
base.Initialize(name, config);
_passwordFormat = getPasswordFormat();
}
public override bool ValidateUser(string username, string password)
{
var user = getUsers()[username];
if (user == null) return false;
if (_passwordFormat == FormsAuthPasswordFormat.Clear)
{
if (user.Password == password)
{
return true;
}
}
else
{
if (user.Password == FormsAuthentication.HashPasswordForStoringInConfigFile(password,
_passwordFormat.ToString()))
{
return true;
}
}
return false;
}
protected FormsAuthenticationUserCollection getUsers()
{
if (_users == null)
{
AuthenticationSection section = getAuthenticationSection();
FormsAuthenticationCredentials creds = section.Forms.Credentials;
_users = section.Forms.Credentials.Users;
}
return _users;
}
protected AuthenticationSection getAuthenticationSection()
{
Configuration config = WebConfigurationManager.OpenWebConfiguration("~");
return (AuthenticationSection)config.GetSection("system.web/authentication");
}
protected FormsAuthPasswordFormat getPasswordFormat()
{
return getAuthenticationSection().Forms.Credentials.PasswordFormat;
}
}
You are going to need to write your own provider for this. It should be relatively straightforward to take the sample ReadOnlyXmlMembershipProvider in the MSDN documentation and change it to read users and credentials from web.config, instead of an external XML file.
I'm not sure if you have tried but....
The FormsAuthentication.Authenticate is in charge to do that for you (although it is deprecated now because the recommended behavior is to use the Membership object)
From MSDN:
The Authenticate method verifies user credentials that are stored in the credentials section of the application configuration file. Alternatively, you can use ASP.NET membership to store user credentials and call the ValidateUser to verify the credentials.
You can also remove the membership providers (because even when you do not declare them on your web.config, they are inherited from the machine.config file)
<membership>
<providers>
<remove name="AspNetSqlMembershipProvider"/>
</providers>
</membership>
Try using this method. I hope it helps. http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.login.onauthenticate.aspx