We are developing an ASP.NET MVC web application that is dependent on another system for some of it's data. (The merits of this choice are outside the scope of this question)
When our web application starts up we need it to check the status of the other system. We do this by logging in to it using a HTTPCLient request.
If the system does not respond or the credentials are incorrect, then our system should not start either, and redirect the user to an Error page. If the login is successful, we grab a load of data from it, and put it into our local cache.
The problem i'm having is that the user is not directed to the error page, the are directed to our application login page instead.
Here's my Global ASAX.
private bool _externalSystemAvailable;
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AutomapperConfiguration.Configure();
_externalSystemAvailable = ExternalSystem.Login();
}
protected void Application_BeginRequest(object source, EventArgs e)
{
var app = (HttpApplication) source;
var ctx = app.Context;
FirstRequestInitialisation.Initialise(ctx, _externalSystemAvailable);
}
I have another class based on this that checks whether the application is initialised already and performs the necessary post initialisation routines. I have this class so that the check is not performed on every request.
public class FirstRequestInitialisation
{
private static bool _alreadyInitialized = false;
private static object _lock = new object();
public static void Initialise(HttpContext context, bool _xternalSystemAvailable)
{
if (_alreadyInitialized)
{
return;
}
lock (_lock)
{
if (_alreadyInitialized)
{
return;
}
}
if ( !externalSystemAvailable)
{
HttpContext.Current.RewritePath("/Home/Error");
}
_alreadyInitialized = true;
}
}
the HttpContext.Current.RewritePath("/Home/Error");is being hit, but the user is not being redirected.
You can redirect user in Application_BeginRequest
protected void Application_BeginRequest(object source, EventArgs e)
{
if (!externalSystemAvailable)
{
Response.Redirect("/Home/Error", false);
Response.StatusCode = 301;
}
}
But there is problem with above code and that is by calling Response.Redirect you create new page request which means that event fires again and again and again and it fall into infinite loop.
I think better place for doing that is Session_Start:
protected void Session_Start(object source, EventArgs e)
{
if (Session.IsNewSession && !externalSystemAvailable)
{
Response.Redirect("/Home/Error", false);
Response.StatusCode = 301;
}
}
Stupid mistake on my part. The Home Controller was restricted to authenticated users.
Related
I'm trying to add Application Insights to a WPF app using this documentation: https://learn.microsoft.com/en-us/azure/azure-monitor/app/windows-desktop. The basic integration is working. I am now trying to remove the RoleInstance property from the submitted data, as described in the documentation, as this contains the user's computer name (personally identifiable information). Here's my code, based on the documentation above:
Telemetry.cs
public static class Telemetry
{
public static TelemetryClient Client;
public static void Close()
{
if (Client != null)
{
Client.Flush();
System.Threading.Thread.Sleep(1000);
}
}
public static void Initialize()
{
TelemetryConfiguration.Active.InstrumentationKey = "xxxxxxxx";
TelemetryConfiguration.Active.TelemetryInitializers.Add(new MyTelemetryInitializer());
Client = new TelemetryClient(TelemetryConfiguration.Active);
Client.Context.Session.Id = Guid.NewGuid().ToString();
Client.Context.Device.OperatingSystem = Environment.OSVersion.ToString();
}
private class MyTelemetryInitializer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
if (string.IsNullOrEmpty(telemetry.Context.Cloud.RoleName))
{
telemetry.Context.Cloud.RoleInstance = null;
}
}
}
}
App.xaml.cs
public partial class App : Application
{
protected override void OnExit(ExitEventArgs e)
{
Telemetry.Close();
base.OnExit(e);
}
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Telemetry.Initialize();
}
}
When I call Telemetry.Client.TrackEvent(), Telemetry.Initialize() runs, and RoleInstance is null from the start. But, the data sent to AI contains my computer name as the RoleInstance. Not sure how to debug further.
Note: the documentation describes integration into WinForms, and I'm in WPF, so I've guessed at using App.OnStartup instead of Main.
I just found something interesting, if you set a dummy value for RoleInstance, it really takes effect. Maybe the null/empty value will be checked and replaced by the real RoleInstance in the backend. As a workaround, you can just specify a dummy value instead of null/empty value.
Here is my test result with a dummy value:
in azure portal:
ASP.NET C#
I have a question, how can I make an access control to a request provided by a botton, to stop the execution of the function, I need something generic in which it can be configured and say that roles or profiles can access to certain functions request Executed by a button.
I don't want something like that
protected void DownloadFile_ServerClick(object sender, EventArgs e)
{
if (RoleAdmin)
{
// do something
}
}
I need something that directly validates in the request of the pag when the method is executed, to see if that profile matches with the method stored in the base, so I do for all pag and do not have to put it in hard in each one of the executed methods.
I need the name of fucntion that is request.
public class PageBase : Page
{
protected override void OnLoad(EventArgs e)
{
***How to capture the function name of request ???***
if (User.Identity.IsAuthenticated == false) { Response.Redirect("~/Account/login.aspx?ReturnUrl=/admin"); };
if (!(User.IsInRole("admin") || User.IsInRole("super user"))) { Response.Redirect("/"); };
}
}
Maybe with this:
public class StaticObjects
{
public static string UserRole
{
get
{
try
{
return (string)HttpContext.Current.Session["UserRole"];
}
catch (Exception)
{
return "";
}
}
set
{
HttpContext.Current.Session["UserRole"]=value;
}
}
public static bool AuthorizeExecution(EventHandler method)
{
bool autorized = YourDataBaseQuery(UserRole, method.Method.Name);
return autorized;
}
}
////////////////////////////// ANOTHER FILE /////////////////
public static void DownloadFile_ServerClick(object sender, EventArgs e)
{
//You send the method itself because it fits the delegate "EventHandler"
if(!StaticObjects.AuthorizeExecution(DownloadFile_ServerClick))
return;
}
I have an application, an asp.net mvc 4 application. I want to use rabbitmq with easynetq. In my local computer, it works perfectly. But in the production environment which is windows server 2012, it does not send any message.
I could not understand, why it does not work. In log message, nothing unusual. IIS and rabbitmq is in the same machine.
protected void Application_Start()
{
...
using (var bus = RabbitHutch.CreateBus("host=localhost"))
{
bus.Publish(new SystemLog() { Code = "startapplication", Message = "nomessage" });
}
...
}
void Session_End(object sender, EventArgs e)
{
...
using (var bus = RabbitHutch.CreateBus("host=localhost"))
{
bus.Publish(new SystemLog() { Code = "sessionends", Message = "somenumber"});
};
...
}
Thanks in advance
Don't put it in a using statement, that will dispose the bus instance immediately when startup is complete, you want to keep the same instance during the life time of your application.
Instead save the instance somewhere (like a static variable), and dispose it in the application_end event, not session_end.
So more like this:
protected void Application_Start()
{
_bus = RabbitHutch.CreateBus("host=localhost"))
_bus.Publish(new SystemLog() { Code = "startapplication", Message = "nomessage" });
}
void Session_End(object sender, EventArgs e)
{
_bus.Publish(new SystemLog() { Code = "sessionends", Message = "somenumber"});
}
protected void Application_End()
{
if(_bus!=null)
_bus.Dispose();
}
I'm attempting to use the new ASP.NET Identity 2.0 authentication system(s) in a WebForms application, but I'm having trouble validating a user before allowing the data source for users to save.
The trouble stems from calling IIdentityValidator.ValidateAsync from the data source's OnUpdating event. The markup is functionally identical to the default Dynamic Data templates (except for the addition of Async="true"), with a few customizations in the code behind. Basically, I manually set the MetaTable for the request (since this page is a replacement for one of my dynamic data routes, but I'd like to keep the benefit of scaffolded properties) and I've added the DetailsDataSource_Updating event. Though the code sample below successfully saves the user to our database, the following error is usually thrown before returning to the client:
"An asynchronous module or handler completed while an asynchronous operation was still pending."
I've spent a considerable amount of time attempting to get this to work, but have yet to find a solution that does not lock up the page or throw the above error. I fear that I am completely misunderstanding async/await in WebForms, or worse, that async/await is only really usable for database queries/binding outside of MVC.
public partial class Edit : System.Web.UI.Page
{
protected UserManager manager;
protected CustomMetaTable table;
protected void Page_Init(object sender, EventArgs e)
{
manager = UserManager.GetManager(Context.GetOwinContext());
table = Global.DefaultModel.GetTable(typeof(User)) as CustomMetaTable;
DynamicDataRouteHandler.SetRequestMetaTable(Context, table);
FormView1.SetMetaTable(table);
DetailsDataSource.EntityTypeFilter = table.EntityType.Name;
}
protected void Page_Load(object sender, EventArgs e)
{
Title = table.EntityName;
DetailsDataSource.Include = table.ForeignKeyColumnsNames;
}
protected void FormView1_ItemCommand(object sender, FormViewCommandEventArgs e)
{
if (e.CommandName == DataControlCommands.CancelCommandName)
{
Response.Redirect(table.ListActionPath);
}
}
protected void FormView1_ItemUpdated(object sender, FormViewUpdatedEventArgs e)
{
if (e.Exception == null || e.ExceptionHandled)
{
Response.Redirect(table.ListActionPath);
}
}
protected async void DetailsDataSource_Updating(object sender, Microsoft.AspNet.EntityDataSource.EntityDataSourceChangingEventArgs e)
{
IdentityResult result = await manager.UserValidator.ValidateAsync(e.Entity as User);
if (!result.Succeeded)
{
e.Cancel = true;
}
}
In the process of writing a new UserValidator with a synchronous Validate method, I found a class in the Identity assembly which is used in all the synchronous wrappers for UserManager and RoleManager. I copied this class into my project and it has allowed me to consume async methods synchronously with only a few exceptions (the primary exception seems to be avoided by assigning the result to a variable before referencing it elsewhere).
internal static class AsyncHelper
{
private static readonly TaskFactory _myTaskFactory = new TaskFactory(
CancellationToken.None,
TaskCreationOptions.None,
TaskContinuationOptions.None,
TaskScheduler.Default);
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
return _myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
}
public static void RunSync(Func<Task> func)
{
_myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
}
}
Usage:
AsyncHelper.RunSync(() => manager.UserValidator.ValidateAsync(e.Entity as User));
I have a HTTP module that I have written that needs to access the session. I have done the following:
Module is registered in web.config
Module attaches my method call to the PostAcquireRequestState event
The module implement IRequiresSessionState
However, when my page doesn't have an extension (i.e. as when htp://www.mywebsite.com) the session is not available and my code fails. If the page does have an aspx extension then all is ok.
You need to have an item that is processed by ASP.NET in order for your module to be part of the request life-cycle. Serving a page like index.html won't accomplish that. An ASPX page will.
The code from the following thread does the trick (1):
public class Module : IHttpModule, IRequiresSessionState
{
public void Dispose()
{
}
void OnPostMapRequestHandler(object source, EventArgs e)
{
HttpApplication app = (HttpApplication)source;
if (app.Context.Handler is IReadOnlySessionState || app.Context.Handler is IRequiresSessionState)
return;
app.Context.Handler = new MyHttpHandler(app.Context.Handler);
}
void OnPostAcquireRequestState(object source, EventArgs e)
{
HttpApplication app = (HttpApplication)source;
MyHttpHandler resourceHttpHandler = HttpContext.Current.Handler as MyHttpHandler;
if (resourceHttpHandler != null)
HttpContext.Current.Handler = resourceHttpHandler.OriginalHandler;
}
public void Init(HttpApplication httpApp)
{
httpApp.PostAcquireRequestState += new EventHandler(OnPostAcquireRequestState);
httpApp.PostMapRequestHandler += new EventHandler(OnPostMapRequestHandler);
}
public class MyHttpHandler : IHttpHandler, IRequiresSessionState
{
internal readonly IHttpHandler OriginalHandler;
public void ProcessRequest(HttpContext context)
{
throw new InvalidOperationException("MyHttpHandler cannot process requests.");
}
public MyHttpHandler(IHttpHandler originalHandler)
{
OriginalHandler = originalHandler;
}
public bool IsReusable
{
get { return false; }
}
}
}
It turns out its an II7 issue, see here:
http://forum.umbraco.org/yaf_postst9997_ContextSession-always-null-on-top-umbraco-page.aspx