ASP.NET Directory Authentication - c#

I have a .net 2.0 application using Forms Authentication with AD and have a directory for documents which has been configured using a web.config file -
<system.web>
<authorization>
<deny users="?"/>
<allow roles="Security Alerts - Admin"/>
<deny users="*"/>
</authorization>
</system.web>
When testing locally if I run the app and put the FQDN for a document /site/documents/Document1.pdf I am returned to the login page but when I have the site on a server I am able to open the PDFs without any problem. How can I force this so that if a user was to saves the URL of a document and tried to access it directly they would be forced to the login page to authenticate themselves first?
I have the same config for an ADMIN folder which includes aspx pages and works correctly and directs the users the Login page first, is it something to do with the doc type being a pdf as opposed to aspx pages.
Thanks in advance.

By default, .NET authentication does not work on static files such as pdfs.
You need to implement an HTTP Handler to serve your files if the user is authenticated.
It sound like your current authentication is set up and working correctly, so I won't go over the basics of setting that up.
Below is the relevant code which applies to your scenario taken from Kory Becker's helpful article here:
http://www.primaryobjects.com/2009/11/11/securing-pdf-files-in-asp-net-with-custom-http-handlers
You'll obviously have to alter the paths, namespaces and logic to suit your environment (e.g. IIS version) and/or specific file type requirements.
Step 1 - Create a FileProtectionHandler class which implements IHttpHandler
public class FileProtectionHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
switch (context.Request.HttpMethod)
{
case "GET":
{
// Is the user logged-in?
if (!context.User.Identity.IsAuthenticated)
{
FormsAuthentication.RedirectToLoginPage();
return;
}
string requestedFile = context.Server.MapPath(context.Request.FilePath);
// Verify the user has access to the User role.
if (context.User.IsInRole("Security Alerts - Admin"))
{
SendContentTypeAndFile(context, requestedFile);
}
else
{
// Deny access, redirect to error page or back to login page.
context.Response.Redirect("~/User/AccessDenied.aspx");
}
break;
}
}
}
public bool IsReusable { get; private set; }
private HttpContext SendContentTypeAndFile(HttpContext context, String strFile)
{
context.Response.ContentType = GetContentType(strFile);
context.Response.TransmitFile(strFile);
context.Response.End();
return context;
}
private string GetContentType(string filename)
{
// used to set the encoding for the reponse stream
string res = null;
FileInfo fileinfo = new FileInfo(filename);
if (fileinfo.Exists)
{
switch (fileinfo.Extension.Remove(0, 1).ToLower())
{
case "pdf":
{
res = "application/pdf";
break;
}
}
return res;
}
return null;
}
}
Step 2 - Add the following sections to your web.config file (with appropriate path/namespace modifications)
<httpHandlers>
...
<add path="*/User/Documents/*.pdf" verb="*" validate="true" type="CustomFileHandlerDemo.Handlers.FileProtectionHandler" />
</httpHandlers>
<system.webServer>
...
<handlers>
<add name="PDF" path="*.pdf" verb="*" type="CustomFileHandlerDemo.Handlers.FileProtectionHandler" resourceType="Unspecified" />
...
</handlers>
</system.webServer>

Related

SignalR unable to receive broadcast when in Microsoft Edge

We have problem getting message from Hub when we're in Microsoft Edge.
The connection is established, sending message from Client -> Server works as expected, but we're not receiving any response from server push. The same code works in Chrome & Firefox though.
Below is some code that we're using:
JS:
$.connection.hub.start()
.done(function () {
$.connection.myHub.server.broadcastMessage().done(function (data) {
console.log("broadcastMessage result: " + data); //work as expected when client request data from server, server does return the data
});
})
.fail(function () {
console.log("Connection failed!");
});
$.connection.myHub.client.showMessage = function (msg) {
alert(msg); //not working, in Microsoft Edge we're not receiving anything, this function is not triggered at all
};
C#:
public string BroadcastMessage() {
Clients.All.showMessage("ABC");
return "Hello World";
}
We're not completely unable to receive any broadcast 100% of the time though, however it does happens 95% of the time.
Although we're not able to receive any broadcast from server, but subsequent request from Client -> Server works as expected.
public override Task OnConnected() are not hit when we're in Edge too, but the code block does hit when we're in Chrome / Firefox.
Any idea? Is this a problem with SignalR or Edge?
P/S: We're using JQUERY 3.3.1 & SignalR 2.3.0
UPDATE 1:
We tried to remove everything and made a empty project to see if it's a problem with SignalR. Apparently if it's a completely new project, SignalR doesn't have this problem, but after I implemented Form Authentication, the problem starts to happen, I'm guessing is it because sometime when server trying to broadcast message to client, it's not authenticated or the cookies are not set?
Below is the code we used to implement our Forms Authentication:
Global.asax
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (HttpContext.Current.User != null)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
if (HttpContext.Current.User.Identity is FormsIdentity identity)
{
FormsIdentity id = identity;
FormsAuthenticationTicket ticket = id.Ticket;
string userData = ticket.UserData;
string[] roles = userData.Split(',');
HttpContext.Current.User = new GenericPrincipal(id, roles);
}
}
}
}
Web.Config
<authentication mode="Forms">
<forms name="LoginCookie" loginUrl="/Account/Login" protection="None" path="/" defaultUrl="/Account/Login" timeout="3600" />
</authentication>
Code in Web.Config to block folder access
<location path="CMS/Admin" allowOverride="true">
<system.web>
<authorization>
<allow roles="Admin" />
<deny users="*" />
</authorization>
</system.web>
</location>
The sample page were placed inside /CMS/Admin.
Try to change your c# code in:
public string BroadcastMessage() {
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<myHub>();
context.Clients.All.showMessage("ABC");
return "Hello World";
}
Without IHubContext context = GlobalHost.ConnectionManager.GetHubContext<myHub>(); i don't able to work with signalR

HttpHandler for prefix in url

I am trying to convert http call in aspx to https
Back Ground : i have a Aspx page that is in https site.on that page i have reference to script of google
Aspx page reference :
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js" type="text/javascript"></script>
i have created a HttpHandler for Prefix Http
IHttpHandler Interface implementation :
public class HttpToHttpsHandler : IHttpHandler
{
public bool IsReusable
{
get { return false; }
}
public void ProcessRequest(HttpContext context)
{
try
{
context.Response.ContentType = "text/plain";
if (context.Request.RawUrl.Contains("http:"))
{
string newUrl = context.Request.RawUrl.Replace("http", "https");
context.Server.Transfer(newUrl);
}
}
catch (Exception)
{
throw;
}
}
}
Web.Config file registration code :
<httpHandlers>
<add verb="*" path="http:*" type="HttpToHttpsHandler , App_Code"/>
</httpHandlers>
But i am not getting the control in Http handler class.what could be the possible error.
I am wondering if your assembly is called "App_Code". In your type declaration you must enter the assembly name, not the folder name of the C# file.
The path attribute as far as I know it is relative position and it only can take one of two values: the name/file-name or the extension/file-extension to map. Like
<add verb="*" path="*.SampleFileExtension"
type="Example1 " />
Or
<add verb="*" path="demo.*"
type="Example1 " />
I tried a combination of both these and it also worked, which says that anything that starts with test and for any extension will be handler by handler:
<add verb="*" path="test*.*"
name="HelloWorldHandler"
type="demo.HelloWorldHandler,App_Code" />
But please notice that it is a relative path, so it means it does not include the http or https values from the URL and therefore a Handler cannot be used to validate URLs.
You need to define your assembly name which contains the HttpToHandler class.
The handler is defined as the class HttpToHttpsHandler in the your assembly which if is in the same project then it will be your application name.
Go through this article
<httpHandlers>
<add verb="*" path="*.aspx"
type="HttpToHttpsHandler , AssemblyName" />
</httpHandlers>
</system.web>
if (!Request.IsLocal && !Request.IsSecureConnection)
{
string redirectUrl = Request.Url.ToString().Replace("http:", "https:");
Response.Redirect(redirectUrl);
}
HttpRequest.IsSecureConnection Property determines whether the HTTP connection uses secure sockets ( HTTPS) or not .-MSDN

Getting The Current User Name In ASP.NET Application

I am running a webpage that needs to be able to read the login id of the current user. Here is the code I am using:
string id = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
Currently this returns the correct login but when I use it in this method:
protected Boolean isPageOwner()
{
string id = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
alert("User: " + id);
if (id.Equals(pageOwnerID))
{
return true;
}
if (accessPermission.ContainsKey(id))
{
return true;
}
return false;
}
the method returns false even though the id returned is identical to pageOwnerID. I'm really not sure which part of this I am having a problem with.
On a side note, my login id is of the form string1/string2 but the code retrieves it as string1 + string2 without the slash.
Any advice is appreciated.
Regards.
Try using this to retrieve the username....
if (System.Web.HttpContext.Current.User.Identity.IsAuthenticated)
{
string username = System.Web.HttpContext.Current.User.Identity.Name;
}
It sounds like windows authentication is not being used - you need to disable anonymous access and enable windows integrated security.
Add this to your web.config...
<system.web>
<authentication mode="Windows"/>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
If you need the current logged in user's identity from within any layer (or Project in your solution) then use:
string userId = Thread.CurrentPrincipal.Identity.GetUserId();

DNN 6 Module - How to leverage asynchronous calls

DotNetNuke 6 does not appear to support WebMethods due to modules being developed as user controls, not aspx pages.
What is the recommended way to route, call and return JSON from a DNN user module to a page containing that module?
It appears the best way to handle this problem is custom Httphandlers. I used the example found in Chris Hammonds Article for a baseline.
The general idea is that you need to create a custom HTTP handler:
<system.webServer>
<handlers>
<add name="DnnWebServicesGetHandler" verb="*" path="svc/*" type="Your.Namespace.Handler, YourAssembly" preCondition="integratedMode" />
</handlers>
</system.webServer>
You also need the legacy handler configuration:
<system.web>
<httpHandlers>
<add verb="*" path="svc/*" type="Your.Namespace.Handler, YourAssembly" />
</httpHandlers>
</system.web>
The handler itself is very simple. You use the request url and parameters to infer the necessary logic. In this case I used Json.Net to return JSON data to the client.
public class Handler: IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
//because we're coming into a URL that isn't being handled by DNN we need to figure out the PortalId
SetPortalId(context.Request);
HttpResponse response = context.Response;
response.ContentType = "application/json";
string localPath = context.Request.Url.LocalPath;
if (localPath.Contains("/svc/time"))
{
response.Write(JsonConvert.SerializeObject(DateTime.Now));
}
}
public bool IsReusable
{
get { return true; }
}
///<summary>
/// Set the portalid, taking the current request and locating which portal is being called based on this request.
/// </summary>
/// <param name="request">request</param>
private void SetPortalId(HttpRequest request)
{
string domainName = DotNetNuke.Common.Globals.GetDomainName(request, true);
string portalAlias = domainName.Substring(0, domainName.IndexOf("/svc"));
PortalAliasInfo pai = PortalSettings.GetPortalAliasInfo(portalAlias);
if (pai != null)
{
PortalId = pai.PortalID;
}
}
protected int PortalId { get; set; }
}
A call to http://mydnnsite/svc/time is properly handled and returns JSON containing the current time.
does anyone else have an issue of accessing session state/updating user information via this module? I got the request/response to work, and i can access DNN interface, however, when i try to get the current user, it returns null; thus making it impossible to verify access roles.
//Always returns an element with null parameters; not giving current user
var currentUser = UserController.Instance.GetCurrentUserInfo();

Asp.Net Authentication Module

I have created an authentication module in ASP.Net but I do not want the logic in the authentication module to be executed if the resource is configured for anonymous access since the logic is expensive.
There are pages that require authentication in the same directory with pages that do not require authentication. I have no control over this. Is there an easy way to determine that a resource is configured to allow anonymous access prior to the URLAuthorizationModule?
Currently, I am doing the following which does "feel" right. Any help would be appreciated.
public static bool AllowEveryone()
{
bool rslt = false;
AuthorizationSection config = (AuthorizationSection)WebConfigurationManager.GetSection("system.web/authorization");
if (config.Rules != null && config.Rules.Count > 0)
{
AuthorizationRule r = config.Rules[0]; //doing this based on implementation of urlauthorization module in reflector...
if (r.Action == AuthorizationRuleAction.Allow && r.Users.Contains("*"))
{
return true;
}
//todo: check for allow anon ? case
}
return rslt;
}
I'm not sure how your code fits in with the Membership and Role provider system, but have you tried putting per-URL overrides in the web.config file?
<location path="MyAnonymousPage.aspx">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
In a regular ASP.Net site this can be accomplished with the following code:
IPrincipal anonUser = new GenericPrincipal(new GenericIdentity(string.Empty, string.Empty), new string[0]);
bool allowAnon = UrlAuthorizationModule.CheckUrlAccessForPrincipal(requestPath, anonUser, "get");
However, I am having problems getting it to behave as expected in SharePoint.

Categories