ASP.NET MVC C# global variable - asp.net

I need to save a value for all my website, is there a way to save it in a global variable in the server side like ViewData for example or is it better to save it in a cookie ?
This data is set using a dropdown list and cached in the controller.
Thanks.

In the Global.asax page
void Application_Start(object sender, EventArgs e)
{
// set your variable here
Application["myVar"] = "some value";
}
Inside the action
public ActionResult MyAction()
{
// get value
string value = Application["myValue"].ToString();
// change value
Application["myValue"] = "some NEW value";
}

You could store it in the Application state:
public ActionResult Foo()
{
HttpContext.Application["someKey"] = "some value";
...
}
and then later read from it:
string value = (string)HttpContext.Application["someKey"];
The values stored in the Application state are shared among all users of the website.
If you need to store user specific data you could use session or cookies depending on whether it is sensitive data or not.

Session would be the way if you are wanting to change the value, if the value is going to be static & is known before the application loads any data then you could store it in the Web.config and reference it from there.
Such as:
<appSettings>
<add key="MyStaticItem" value="Lulz" />
</appSettings>
So then if you want to retreive that string you can do:
Meh = ConfigurationManager.AppSettings["MyStaticItem"]
Meh would be Lulz

Can also use session like this:
Session["MyKey"] = "MyValue";
and retrieving like this:
var myVar = (string)Session["MyKey"];
if that's per user value.
Hope this is of help.

Related

Restrict access to all asp.net pages

I am mainlining one asp.net Project, this project is configured in IIS. The website is open for everyone, when i review the code in asp.net page, its checking window login "enterprise id" and allowing all users to view the all the aspx pages.
Now, my management team requested us to restrict those who are under junior level employees.(Junior engg, Developer, software engg).
I have written the query, passing enterprise id and validate grade, if its junior level , returning "0" values,else returning "1" values.
My questions is, I do not want go and edit each page and check this query and restrict each page.
can you please suggest , how can i implement simplest and best way to restric the users.
Thanks,
--------------------------------------- Update on 09/24/2015
Index.aspx
protected void Page_Load(object sender, EventArgs e)
{
string UserStatus = UtilFunctions.ValidateUser();
Response.Write(UserStatus);
if (UserStatus == "0")
{
Response.Write("<div><font color=red><h1>You are not authorized to view this page</h1></font></div>");
Response.End();
}
}
Utilifunctions.cs
public static String ValidateUser()
{
string CurrentUser = getLoggedOnUser();
using (System.Data.SqlClient.SqlConnection myConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["TestDB"].ConnectionString))
{
using (SqlCommand myCommand = myConnection.CreateCommand())
{
myConnection.Open();//Opens the Connection
myCommand.CommandText = "Select Permission From Temp_Validate Where EnterpriseId='" + CurrentUser + "'";
SqlDataReader IDReader = myCommand.ExecuteReader(); //Gets the ID
IDReader.Read();
string UserStatus = IDReader["Permission"].ToString();
IDReader.Close();
return UserStatus;
}
}
I implemented the above functionalite in my index.aspx page, if the userstatus equal to "0" , it will display the "You are not authrized to view this message" and it will end.
I have around 30 aspx page,its currently running in Production. I do not want go include the same code (index.aspx) in every page load to stop the user validation.
could you please suggest how can i implement without editing all pages.
Updated on 09/28 : Utilifunction.cs
public static String getLoggedOnUser()
{
String user = HttpContext.Current.User.Identity.Name.Substring(HttpContext.Current.User.Identity.Name.IndexOf("\\") + 1);
if (user == "") user = "anonymous";
string UserStatus = IsValidUser(user);
if (UserStatus == "0")
{
HttpContext.Current.Response.Redirect("PSF_Error.aspx", true);
}
return user;
}
public static String IsValidUser(string currentUser)
{
using (System.Data.SqlClient.SqlConnection myConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["Test"].ConnectionString))
{
using (SqlCommand myCommand = myConnection.CreateCommand())
{
//Gets email of the creator of current user
myConnection.Open();//Opens the Connection
myCommand.CommandText = "Select Permission From Temp_Validate Where EnterpriseId='" + currentUser + "'";
SqlDataReader IDReader = myCommand.ExecuteReader(); //Gets the ID
IDReader.Read();
string UserStatus = IDReader["Permission"].ToString();
IDReader.Close();
return UserStatus;
}
}
}
Index.aspx
Page_load
{
string CurrentUser = UtilFunctions.getLoggedOnUser();
}
You have a few options, here:
1) Set up role-based access with Owin or AspNet.Identity. This is probably your best option, but I couldn't find a good tutorial for you. Those packages are well-documented, however, and I'm sure you can figure them out with some effort.
2) Build a Roles table, and customize access yourself. The best example I found was here: http://www.codeproject.com/Articles/875547/Custom-Roles-Based-Access-Control-RBAC-in-ASP-NET
3) Redirect unauthorized users without the use of roles. So something like:
public ActionResult SecurePage(User u)
{
if(u.level == "junior"){
return RedirectToAction("CustomErrorPage");
} else {
return View();
}
}
I'm not sure that that option is terribly secure, but it should work.
Hope that helps!
after setting up roles you can use a web.config file in every directory specifying authorization and/or use the 'location' element in the web.config file.
First off, sorry about the confusing code. I've been using MVC, and you've clearly posted your code behind.
I don't think that you can achieve what you are trying to do, without adding your code to each page, or learning about roles. You could reduce some code duplication in a number of clever ways, but I can't think of anything that doesn't seem like a total hack.
If you want to, say, put all of your secure pages in the same directory, and restrict low-level access to that directory, you are going to have to filter by specific users or, if you can implement them, roles. As I understand it, the deny and allow nodes in your web.config file are setting server side (so IIS, probably) authorization rules, so the keywords and rules you can use are limited. Check this page out, for some basics:
http://weblogs.asp.net/gurusarkar/setting-authorization-rules-for-a-particular-page-or-folder-in-web-config
While it is likely POSSIBLE to build a rule based on values in your DB, doing so would probably be far more work than it would be worth.
Sorry that I can't offer a more satisfactory answer, but I would recommend: 1) Get to work, and add a check to the code behind for each page, or 2) (and I highly suggest this option) close this question, and post another, about implementing roles in .net, and assigning roles to users, in code. If, say, you can use your login page to assign every junior-level user the custom role of Junior, and place all of your secure pages in a directory named SecurePages you could add the following code to your web.config, and achieve exactly what you are trying to do:
<location path="SecurePages">
<system.web>
<authorization>
<deny roles="Junior">
<deny users="*">
</authorization></system.web></location>
Good luck!

ASP.NET localization

When localizing an ASP.NET app (MVC or webforms, does't matter), how do you handle HTML strings in your resource file? In particular, how do you handle something like a paragraph with an embedded dynamic link? My strategy so far has been to use some sort of placeholder for the href attribute value and replace it at runtime with the actual URL, but this seems hokey at best.
As an example, suppose my copy is:
Thank you for registering. Click
here
to update your preferences.
To login and begin using the app, click
here.
Using MVC (Razor), what could be a simple:
<p>#Resources.Strings.ThankYouMessage</p>
now turns into
<p>#Resources.Strings.ThankYouMessage
.Replace("{prefs_url}", Url.Action("Preferences", "User"))
.Replace("{login_url}", Url.Action("Login", "User"))</p>
It's not horrible, but I guess I'm just wondering if there's a better way?
There isn't really a better way, beyond some syntax and performance tweaks. For example, you might add a cache layer so that you aren't doing these string operations for every request. Something like this:
<p>#Resources.LocalizedStrings.ThankYouMessage</p>
which calls a function perhaps like this:
Localize("ThankYouMessage", Resources.Strings.ThankYouMessage)
which does a hashtable lookup by resource + culture:
//use Hashtable instead of Dictionary<> because DictionaryBase is not thread safe.
private static System.Collections.Hashtable _cache =
System.Collections.Hashtable.Synchronized(new Hashtable());
public static string Localize(string resourceName, string resourceContent) {
string cultureName = System.Threading.Thread.CurrentThread.CurrentCulture.Name;
if (string.IsNullOrEmpty(resourceName))
throw new ArgumentException("'resourceName' is null or empty.");
string cacheKey = resourceName + "/" + cultureName;
object o = _cache[cacheKey];
if (null == o) { //first generation; add it to the cache.
_cache[cacheKey] = o = ReplaceTokensWithValues(resourceContent);
}
return o as string;
}
Notice the call to ReplaceTokensWithValues(). That is the function that contains all the "not horrible" string-replacement fiffery:
public static string ReplaceTokensWithValues(string s) {
return s.Replace("{prefs_url}", Url.Action("Preferences", "User"))
.Replace("{login_url}", Url.Action("Login", "User")
.Replace("{any_other_stuff}", "random stuff");
}
By using a caching approach as above, ReplaceTokensWithValues() is only called once per culture, per resource for the lifetime of the application--instead of once per resource call. The difference may be on the order of 100 vs. 1,000,000.

How can I access session variables when the page is loaded using a SimpleWorkerRequest?

I'm reading an ASPX file as a string and using the returned HTML as the source for an email message. This is the code:
public string GetEmailHTML(int itemId)
{
string pageUrl = "HTMLEmail.aspx";
StringWriter stringWriter = new StringWriter();
HttpRuntime.ProcessRequest(new SimpleWorkerRequest(pageUrl, "ItemId=" + itemId.ToString(), stringWriter));
stringWriter.Flush();
stringWriter.Close();
return stringWriter.ToString();
}
HTMLEmail.aspx uses the ItemId query string variable to load data from a DB and populate the page with results. I need to secure the HTMLEmail.aspx page so a manipulated query string isn't going to allow just anybody to see the results.
I store the current user like this:
public User AuthenticatedUser
{
get { return Session["User"] as User; }
set { Session["User"] = value; }
}
Because the page request isn't made directly by the browser, but rather the SimpleWorkerRequest, there is no posted SessionId and therefore HTMLEmail.aspx cannot access any session variables. At least, I think that's the problem.
I've read the overview on session variables here: http://msdn.microsoft.com/en-us/library/ms178581.aspx
I'm wondering if I need to implement a custom session identifier. I can get the current SessionId inside the GetEmailHTML method and pass it as a query string param into HTMLEmail.aspx. If I have the SessionId inside HTMLEmail.aspx I could maybe use the custom session identifier to get access to the session variables.
That fix sounds messy. It also removes the encryption layer ASP automatically applies to the SessionId.
Anyone have a better idea?
As far as I can see, your best bet is to pass on all the values you need inside HTMLEmail.aspx to it via the query parameters, just like you do with ItemId.
Apart from that, you can probably get away with just sending in the UserId of the user to that page and make it hit the DB (or wherever you are storing your users) to the User object, instead of trying to read it off the Session variables.
Edit:
Why don't you use:
public string GetEmailHTML(int itemId)
{
string pageUrl = "HTMLEmail.aspx";
StringWriter stringWriter = new StringWriter();
Server.Execute(pageUrl, stringWriter);
stringWriter.Flush();
stringWriter.Close();
return stringWriter.ToString();
}
instead? As far as I can see Server.Execute inherits the same http request.

Asp.Net single control render for AJAX calls

I'm trying to implement something similar to this or this.
I've created a user control, a web service and a web method to return the rendered html of the control, executing the ajax calls via jQuery.
All works fine, but if I put something in the user control that uses a relative path (in my case an HyperLink with NavigateUrl="~/mypage.aspx") the resolution of relative path fails in my developing server.
I'm expecting:
http://localhost:999/MyApp/mypage.aspx
But I get:
http://localhost:999/mypage.aspx
Missing 'MyApp'...
I think the problem is on the creation of the Page used to load the control:
Page page = new Page();
Control control = page.LoadControl(userControlVirtualPath);
page.Controls.Add(control);
...
But I can't figure out why....
EDIT
Just for clarity
My user control is located at ~/ascx/mycontrol.ascx
and contains a really simple structure: by now just an hyperlink with NavigateUrl like "~/mypage.aspx".
And "mypage.aspx" really resides on the root.
Then I've made up a web service to return to ajax the partial rendered control:
[ScriptService]
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class wsAsynch : System.Web.Services.WebService
{
[WebMethod(EnableSession = true)]
public string GetControl(int parma1, int param2)
{
/* ...do some stuff with params... */
Page pageHolder = new Page();
UserControl viewControl = (UserControl)pageHolder.LoadControl("~/ascx/mycontrol.ascx");
Type viewControlType = viewControl.GetType();
/* ...set control properties with reflection... */
pageHolder.Controls.Add(viewControl);
StringWriter output = new StringWriter();
HttpContext.Current.Server.Execute(pageHolder, output, false);
return output.ToString();
}
}
The html is correctly rendered, but the relative path in the NavigateUrl of hyperlink is incorrectly resolved, because when I execute the project from developing server of VS2008, the root of my application is
http://localhost:999/MyApp/
and it's fine, but the NavigateUrl is resolved as
http://localhost:999/mypage.aspx
losing /MyApp/ .
Of Course if I put my ascx in a real page, instead of the pageHolder instance used in the ws, all works fine.
Another strange thing is that if I set the hl.NavigateUrl = Page.ResolveUrl("~/mypage.aspx") I get the correct url of the page:
http://localhost:999/MyApp/mypage.aspx
And by now I'll do that, but I would understand WHY it doesn't work in the normal way.
Any idea?
The problem is that the Page-class is not intented for instantiating just like that. If we fire up Reflector we'll quickly see that the Asp.Net internals sets an important property after instantiating a Page class an returning it as a IHttpHandler. You would have to set AppRelativeTemplateSourceDirectory. This is a property that exists on the Control class and internally it sets the TemplateControlVirtualDirectory property which is used by for instance HyperLink to resolve the correct url for "~" in a link.
Its important that you set this value before calling the LoadControl method, since the value of AppRelativeTemplateSourceDirectory is passed on to the controls created by your "master" control.
How to obtain the correct value to set on your property? Use the static AppDomainAppVirtualPath on the HttpRuntime class. Soo, to sum it up... this should work;
[WebMethod(EnableSession = true)]
public string GetControl(int parma1, int param2)
{
/* ...do some stuff with params... */
var pageHolder = new Page() { AppRelativeTemplateSourceDirectory = HttpRuntime.AppDomainAppVirtualPath };
var viewControl = (UserControl)pageHolder.LoadControl("~/ascx/mycontrol.ascx");
var viewControlType = viewControl.GetType();
/* ...set control properties with reflection... */
pageHolder.Controls.Add(viewControl);
var output = new StringWriter();
HttpContext.Current.Server.Execute(pageHolder, output, false);
return output.ToString();
}
The tildy pust the path in the root of the app, so its going to produce a the results you are seeing. You will want to use:
NavigateUrl="./whatever.aspx"
EDIT:
Here is a link that may also prove helpful...http://msdn.microsoft.com/en-us/library/ms178116.aspx
I find the /MyApp/ root causes all sorts of issues. It doesn't really answer your question 'why is doesn't work the normal way', but do you realize you can get rid of the /MyApp/ and host your website at http:/localhost/...?
Just set Virtual Path in the website properties to '/'.
This clears everything up, unless of course you are trying to host multiple apps on the development PC at the same time.
It might be that the new page object does not have "MyApp" as root, so it is resolved to the server root as default.
My question is rather why it works with Page.ResolveUrl(...).
Maybe ResolveUrl does some more investigation about the location of the usercontrol, and resolves based on that.
Weird, I recreated the example. The hyperlink renders as <a id="ctl00_hlRawr" href="Default.aspx"></a> for a given navigation url of ~/Default.aspx. My guess is that it has something to do with the RequestMethod. On a regular page it is "GET" but on a webservice call it is a "POST".
I was unable to recreate your results with hl.NavigateUrl = Page.ResolveUrl("~/mypage.aspx")
The control always rendered as <a id="ctl00_hlRawr" href="Default.aspx"></a> given a virtual path. (Page.ResolveUrl gives me "~/Default.aspx")
I would suggest doing something like this to avoid the trouble in the future.
protected void Page_Load(object sender, EventArgs e)
{
hlRawr.NavigateUrl = FullyQualifiedApplicationPath + "/Default.aspx";
}
public static string FullyQualifiedApplicationPath
{
get
{
//Return variable declaration
string appPath = null;
//Getting the current context of HTTP request
HttpContext context = HttpContext.Current;
//Checking the current context content
if (context != null)
{
//Formatting the fully qualified website url/name
appPath = string.Format("{0}://{1}{2}{3}",
context.Request.Url.Scheme,
context.Request.Url.Host,
(context.Request.Url.Port == 80 ? string.Empty : ":" + context.Request.Url.Port),
context.Request.ApplicationPath);
}
return appPath;
}
}
Regards,
It is hard to tell what you are trying to achieve without posting the line that actually sets the Url on of the HyperLink, but I think I understand your directory structure.
However, I have never run into a situation that couldn't be solved one way or another with the ResolveUrl() method. String parsing for a temporary path that won't be used in production is not recommended because it will add more complexity to your project.
This code will resolve in any object that inherits from page (including a usercontrol):
Page page = (Page)Context.Handler;
string Url = page.ResolveUrl("~/Anything.aspx");
Another thing you could try is something like this:
Me.Parent.ResolveUrl("~/Anything.aspx");
If these aren't working, you may want to check your IIS settings to make sure your site is configured as an application.

Postback problem for my custom control load wizard

I have some problem that happens when controls are loaded in init and it still doesn't help me to get proper postback event fired on time.
I am trying to create a rich wizard control that will enable switching, links with description, completely customized steps, integration of substeps - by using dynamic control load that is avoids standard asp.net wizard way of loading.
Idea is to have on left part navigation, on right part content, or substeps that are run from right part and that go over whole area.
Download source project
Ok, I re-read the question, and here is what you have to do. You have to re-load these controls on each postback, give them always the same "Id". This can be done in Page_Init or in Page_Load event. And of course, you have to re-attach event handlers on each post back.
Many thanks.. well i found the answer - id was the problem, in load control method. I was doing this wizard.. well most of things work now.
If someone is interested to see how does this works.. there are some updates:
public void LoadSplitViewControl(string path)
{
SwitchNavigationView(NavigationView.SplitView);
LastNavigationView = NavigationView.SplitView;
LoadControl(SplitControlLoader, path, "LoadedControlSplit");
}
public void LoadSingleViewControl(string path)
{
SwitchNavigationView(NavigationView.SingleView);
LastNavigationView = NavigationView.SingleView;
LoadControl(SingleControlLoader, path, "LoadedControlSingle");
}
public void LoadSingleViewControlAsClear(string path)
{
SwitchNavigationView(NavigationView.SingleView);
LastNavigationView = NavigationView.SingleView;
LoadControlAsClear(SingleControlLoader, path, "LoadedControlSingle");
}
private void LoadControl(PlaceHolder holder, string path, string ID)
{
UserControl ctrl = (UserControl)Page.LoadControl(path);
ctrl.ID = ID;
LastControlPath = path;
holder.Controls.Clear();
holder.Controls.Add(ctrl);
}
//as i am using steps loaded controls using splitview and substeps controls using single view sometimes viewstate will not be valid so error will be thrown but u can resolve this by using LoadSingleViewControlAsClear that will load below method.
private void LoadControlAsClear(PlaceHolder holder, string path, string ID)
{
UserControl ctrl = (UserControl)Page.LoadControl(path);
ctrl.ID = ID;
LastControlPath = path;
ctrl.EnableViewState = false;
holder.Controls.Add(ctrl);
}
/another cool idea i am using for such an wizard is that i am not using viewstate but rather session object for saving values collected over steps. My session object key is generated by authenticated username and pageguid - so u can have many loaded pages and each of them will handle different session object./
public Guid PageGuid
{
get
{
if (PageGuidField.Value == "")
{
var _pageGuid = Guid.NewGuid();
PageGuidField.Value = _pageGuid.ToString();
return _pageGuid;
}
return new Guid(PageGuidField.Value);
}
}

Resources