Get UserId in MVC to use in View - asp.net

I have the following problem in my MVC 5 web app. I used database first approach and the entity framwork to create a table [accountlist] that has a foreign key relationship on [UserId] to the [Id] column from [AspNetUsers]. Right now the pertaining accountlist.cshtml contains a dropdown menu. I also tried #Html.HiddenFor(model => model.UserId, new { #Value = HttpContext.Current.User.Identity.Name}) but the problem is how that I need the correct userid and not the username (I don't want the [UserName] from [AspNetUsers] to be an primary key).
Unfortunately, HttpContext.Current.User.Identity.GetUserId() as suggested in this solution says that Identity contains no definition for GetUserId().
Does anyone has solution?
Thanks in advance!

#stephen.vakil is correct. You need to import the namespace. Specifically, you need to add:
#using Microsoft.AspNet.Identity
#model Namespace.To.Foo
Alternatively, if you want to do this more often or just don't want to include the namespace in the view, you can edit the Web.config in the Views folder of your project and add it there instead:
<?xml version="1.0"?>
<configuration>
...
<system.web.webPages.razor>
...
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Optimization" />
<add namespace="System.Web.Routing" />
<add namespace="Microsoft.AspNet.Identity" />
Any namespaces there are available to every view automatically.

this is the best Article for asp.net mvc authentication from ben foster. It helped me last time.
It works even if you are not going for entity framework
SigIn Like this
var identity = new ClaimsIdentity(new[] {
new Claim(ClaimTypes.Sid, userID)
},
"ApplicationCookie");
var ctx = Request.GetOwinContext();
var authManager = ctx.Authentication;
authManager.SignIn(identity);
Create New Class
public class AppUser : ClaimsPrincipal
{
public AppUser(ClaimsPrincipal principal)
: base(principal)
{
}
public int ID
{
get
{
return this.FindFirst(ClaimTypes.Sid).Value;
}
}
}
Let's create a custom base view page for our Razor views that provides access to our AppUser principal:
public abstract class AppViewPage<TModel> : WebViewPage<TModel>
{
protected AppUser CurrentUser
{
get
{
return new AppUser(this.User as ClaimsPrincipal);
}
}
}
public abstract class AppViewPage : AppViewPage<dynamic>
{
}
Open up /views/web.config and the set the pageBaseType:
<system.web.webPages.razor>
<pages pageBaseType="NakedIdentity.Mvc.AppViewPage">
Rebuild the project and update the Index.cshtml view:
<p>
User ID: #CurrentUser.ID?
</p>

If you are using SimpleMemberShip provider, then
int UserId = WebSecurity.CurrentUserId;
and add attribute [InitializeSimpleMembership] on the top of controller method.

add this line to your controller constructor:
user = System.Web.HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>().FindById(System.Web.HttpContext.Current.User.Identity.GetUserId());
then you will have user.Id in all you methods inside your controller. And for security purpose, never keep your user id inside view as hidden!.

Related

Programmatically test access to a given path with FormsAuthentication

Is it possible to programmatically test whether the current user is able to access a given path? This would be a path to a resource somewhere that I need to restrict access to, and it would be a resource which is being accessed via a handler rather than directly by its path.
For example, given these configuration settings:
<authentication mode="Forms">
<forms loginUrl="~/login/" defaultUrl="~/private/" protection="All" cookieless="UseCookies" slidingExpiration="true" path="/" />
</authentication>
...
<location path="private">
<system.web>
<authorization>
<deny users="?" />
<allow users="*" />
</authorization>
</system.web>
</location>
<location path="private/general">
<system.web>
<authorization>
<allow roles="general" />
<deny users="*" />
</authorization>
</system.web>
</location>
Could we do something like this?:
HttpContext.Current.User.Identity.IsAllowedToAccess("~/private/general/my-resource")
Which would return true for users within the 'general' role, and false for anyone else?
Note that the configuration settings are just an example - there could potentially be a lot more location definitions and roles etc., so testing using lots of myPath.StartsWith("/private/") statements isn't really a good solution.
I never quite managed to figure this out, so I had to change the approach slightly.
This is assuming that the resource we're trying to get at is one that wouldn't normally be served by IIS - in my case, this is web forms user controls (.ascx), which I want to get via Ajax, rendered to a string, which can then be injected by JavaScript somewhere in the HTML.
I got rid of the handler, and instead made it so that any requests to these resources were made directly to the resource, but with a slightly modified .ascx.asmx extension. For example, /private/general/my-resource.ascx.asmx.
I then added a HTTP module (integrated pipeline mode):
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="MyResourceHttpModule" type="MyApp.Controls.MyResourceHttpModule" />
</modules>
</system.webServer>
(or classic pipeline mode):
<system.web>
<httpModules>
<add name="MyResourceHttpModule" type="MyApp.Controls.MyResourceHttpModule" />
</httpModules>
</system.web>
And the related code:
using System;
using System.Web;
namespace MyApp.Controls
{
public class MyResourceHttpModule : IHttpModule
{
#region IHttpModule Members
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.AuthorizeRequest += new EventHandler(OnAuthorizeRequest);
}
#endregion
void OnAuthorizeRequest(object sender, EventArgs e)
{
var application = (HttpApplication)sender;
if (application.Context.Request.Url.AbsolutePath.EndsWith(".ascx.asmx"))
{
application.Context.RewritePath("~/Services/MyResource.asmx/GetResource");
}
}
}
}
This module rewrites the requested URL to a web service (MyResource.asmx), which then strips off the extra extension we originally added, and finally serves the file we're trying to get to (in this case, a web forms user control):
using System.Collections.Generic;
using System.Web;
using System.Web.Script.Services;
using System.Web.Services;
using System.Web.UI;
namespace MyApp.Services
{
[ScriptService]
public class MyResource : System.Web.Services.WebService
{
[WebMethod(EnableSession = true)]
public object GetResource()
{
var controlPath = HttpContext.Current.Request.RawUrl;
if (Paths.ApplicationPath != "" && controlPath.StartsWith(Paths.ApplicationPath)) controlPath = '~' + controlPath.Substring(Paths.ApplicationPath.Length);
if (controlPath.EndsWith(".asmx")) controlPath = controlPath.Substring(0, controlPath.Length - 5);
using (var myUserControl = (MyApp.Controls.MyUserControl)(new Page()).LoadControl(controlPath))
{
myUserControl.DataBind();
return myUserControl.RenderToString();
}
}
}
}
(This uses this RenderToString extension method):
public static string RenderToString(this System.Web.UI.Control control)
{
var stringBuilder = new StringBuilder();
using (var stringWriter = new StringWriter(stringBuilder))
{
using (var htmlTextWriter = new System.Web.UI.HtmlTextWriter(stringWriter))
{
control.RenderControl(htmlTextWriter);
return stringBuilder.ToString();
}
}
}
This all works because forms authentication runs through the authorization rules set in web.config before allowing the request, so we know that by the time it reaches our HTTP module it's OK to serve the requested file because the current user must have permission to access the path.

KendoUI Column Center Custom MVC Extension Method

I am looking for a way to create a custom extension method for a KendoUI column that will allow me to easily add a css class to the header or the cell itself using an extension method.
Right now i'm using the following to center the contents of both the header and the cell:
columns.Bound(x => x.Value).HeaderHtmlAttributes(new { #class = "cent" }).HtmlAttributes(new { #class = "cent" });
where the cent css class is:
.cent { text-align:center; }
To me this seems like alot of code to write for something this simple.
What i am looking for is a solution to create a custom column builder extension method where i can just do something like this:
columns.Bound(x => x.Value).CenterHeader().CenterCell();
Where the CenterHeader() and CenterCell() would be custom extension methods that would add this css class to the header and cell respectively. The code would look much cleaner if this were possible IMO.
Does anyone know a way to do this?
Why not just create these extensions?
using Kendo.Mvc.UI.Fluent;
namespace MyNamespace
{
public static class MyExtensions
{
public static GridTemplateColumnBuilder<T> CenterHeader<T>(this GridTemplateColumnBuilder<T> builder) where T : class
{
return builder.HeaderHtmlAttributes(new { #class = "cent" });
}
.....
}
}
And add this namespace (so that you shouldn't import it all the time) in Views/web.config:
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
......
<add namespace="Kendo.Mvc.UI"/>
<add namespace="MyNamespace"/>
......
</namespaces>
</pages>
</system.web.webPages.razor>

System.Web.Mvc.HtmlHelper<dynamic> exception on Html.PagedListPager

When im trying to use my aspx file
#using PagedList.Mvc;
...
#Html.PagedListPager(ViewBag.test, page => Url.Action("Test", new { page })).
I'll get this error message
'System.Web.Mvc.HtmlHelper<dynamic>' has no applicable method named 'PagedListPager' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax.
My .cs file the code look like this
using PagedList;
private List<Quality> Qualitys()
{
List<Quality> Qualitys = new List<Quality>()
{
new Quality(){ID=1,Name="All Qualitys"},
new Quality(){ID=2,Name="1"},
new Quality(){ID=3,Name="2"},
new Quality(){ID=4,Name="3"},
new Quality(){ID=5,Name="4"},
new Quality(){ID=6,Name="5"}
};
return Qualitys;
}
public ActionResult Test(int? page)
{
int pageSize = 3;
int pageNumber = page ?? 1;
ViewBag.test = Qualitys().ToPagedList(pageNumber, pageSize);
return View();
}
In my ~Views/Web.config file I'll have there.
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Optimization"/>
<add namespace="System.Web.Routing" />
<add namespace="Web" />
</namespaces>
I cant see why Im getting this error, please help.
You need to cast dynamic properties (in this case ViewBag.test) to the required type. it should be
#Html.PagedListPager((IPagedList)ViewBag.test, page => Url.Action("Test", new { page }))

MVC Easy util in view?

I am using asp.net MVC 4, I'd like to do something like this in my view
my link
I don't like it. Is there a way I can have the namespace implicit? Can I not pass in Viewbag and grab it somehow like global.request.current.viewbag?
If it helps I don't need TheFunctionIUseAlot to be static. I just need it to be easily callable in all my views and hopefully i'd like to not pass in viewbag. The reason I pass viewbag is because i need something in the controller which i stuck in viewbag as well. Maybe i can put it in the model but i don't want the same problem where i am passing model instead of viewbag
You can make the namespace implicit by adding #using NamespaceHere; at the top of your view, or by registering it as a namespace in the views web config. Something like this:
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
<add namespace="NamespaceHere"/>
</namespaces>
</pages>
</system.web.webPages.razor>
You can get access to the viewbag in your function if you use a custom page base type for your views. You can find instructions on doing that here: http://haacked.com/archive/2011/02/21/changing-base-type-of-a-razor-view.aspx.
In your situation, to get access to the ViewBag you will want to define your FunctionThatIUseALot in a non-static class that takes a WebViewPage as a constructor parameter (either as dynamic or as a ViewDataDictionary). Then, in your WebViewPage's InitHelpers override, you can create your helper class, passing in this. That will give you access to all of the standard properties of the WebViewPage.
Something like this:
public class Utils {
private WebViewPage page;
public Utils(WebViewPage page) { this.page = page; }
public FunctionThatIUseALot(string someParameter) {
string someViewBagValue = page.ViewBag.SomeViewBagValue;
}
}
public abstract class CustomWebViewPage : WebViewPage {
public Utils Utils { get; set; } // Utils is your helper class
public override void InitHelpers() {
base.InitHelpers();
Utils = new Utils(this);
}
}
The above is not everything you need to do to make it work, you will need to read the article for the rest. But the end result should be that you can just call #Utils.FunctionThatIUseALot("some value") directly in your views.
Note: you will need to do this twice - once for the standard WebViewPage base class, and one for the WebViewPage<TModel> base class.

Custom Virtual Path Provider in IIS

What is the correct configuration to implement a custom virtual path provider in IIS 7.5? The following code works as expected when run from Visual Studio using the ASP.NET Development Server but does not load the image when run from IIS.
.NET 4.0 Project File
CustomVirtualPathProvider.zip - SkyDrive file
Web.config
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
Default.aspx
<%# Page Title="Home Page" Language="C#" AutoEventWireup="true" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Virtual Path Provider</title>
</head>
<body>
<img src="Box.png" />
</body>
</html>
Global.asax
public class Global : System.Web.HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
System.Web.Hosting.HostingEnvironment.RegisterVirtualPathProvider(new WebApplication1.CustomVirtualPathProvider());
}
}
CustomVirtualFile.cs
public class CustomVirtualFile : System.Web.Hosting.VirtualFile
{
private string _VirtualPath;
public CustomVirtualFile(string virtualPath) : base(virtualPath)
{
_VirtualPath = virtualPath.Replace("/", string.Empty);
}
public override Stream Open()
{
string ImageFile =
System.IO.Path.Combine(HttpContext.Current.Request.PhysicalApplicationPath, #"Crazy\Image\Path", _VirtualPath);
return System.IO.File.Open(ImageFile, FileMode.Open, FileAccess.Read);
}
}
CustomVirtualPathProvider.cs
public class CustomVirtualPathProvider : System.Web.Hosting.VirtualPathProvider
{
Collection<string> ImageTypes;
public CustomVirtualPathProvider() : base()
{
ImageTypes = new Collection<string>();
ImageTypes.Add(".PNG");
ImageTypes.Add(".GIF");
}
public override bool FileExists(string virtualPath)
{
if (IsImage(virtualPath))
{
return true;
}
return base.FileExists(virtualPath);
}
public override System.Web.Hosting.VirtualFile GetFile(string virtualPath)
{
if (IsImage(virtualPath))
{
return new CustomVirtualFile(virtualPath);
}
return base.GetFile(virtualPath);
}
private bool IsImage(string file)
{
return ImageTypes.IndexOf(file.ToUpperInvariant().Substring(file.Length - 4, 4)) > -1;
}
}
Filesystem
\Crazy\Image\Path\Box.png
IIS Configuration
Default site with no configuration changes.
Here is what I found to "fix" my issue.
http://sunali.com/2008/01/09/virtualpathprovider-in-precompiled-web-sites/
In brief:
HostingEnviornment explicitly ignores Virtual Path Providers in precompiled sites. You can circumvent this limitation by using reflection to call an internal version that omits this check. Thus, instead of calling
HostingEnviornment.RegisterVirtualPathProvider(new EmbeddedViewVirtualPathProvider();
call this instead:
typeof(HostingEnvironment).GetMethod("RegisterVirtualPathProviderInternal",
BindingFlags.Static | BindingFlags.InvokeMethod | BindingFlags.NonPublic)
.Invoke(null, new object[] {new EmbeddedViewPathProvider()});
I had the same problem but Tom Clarkson led me on the right track, he's absolutely right in that you need additional configuration in order to make IIS serve the content provider by the virtual path provider. I found a solution here
Here is an example web.config-snippet that I think will work for you
<system.web>
<httpHandlers>
<add path="*.png" verb="*" type="System.Web.StaticFileHandler" validate="true" />
<add path="*.gif" verb="*" type="System.Web.StaticFileHandler" validate="true" />
</httpHandlers>
</system.web>
You could also register a "wildcard httphandler" under a special location (eg "/MyVirtualFiles"), which might be useful if your virtual path provider serves many different file types.
<location path="MyVirtualFiles">
<system.web>
<httpHandlers>
<add path="*" verb="*" type="System.Web.StaticFileHandler" validate="true" />
</httpHandlers>
</system.web>
</location>
When FileExists returns true, it is interpreted as "there is a file there, IIS can serve it without ASP.NET". To get the next step of actually downloading the file to go through your virtual path provider, you need to set IIS to use ASP.NET to serve all image files and add code in global.asax or an http handler that will make use of your virtual path provider.

Resources