I am working on an intranet site with Windows Authentication for logins. However, I want to extend the IPrincipal to have other properties. For instance, I'd like to get the user's FirstName in #User.FirstName or User.AuthorizedActivity("Admin/Permissions/Edit") (would retrieve from db) using activities instead of roles to hide certain links, etc. I am really having a heck of a time figuring this out over the past 2 days and find much information doing this with Windows Authentication.
My CustomPrincipal and BaseViewPage setup:
namespace Intranet_v2.Helpers
{
public interface ICustomPrincipal : IPrincipal
{
Guid UserGuid { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
string FullName { get; set; }
}
public class CustomPrincipal : ICustomPrincipal
{
public IIdentity Identity { get; private set; }
public bool IsInRole(string role) { return false; }
public CustomPrincipal(string identity)
{
this.Identity = new GenericIdentity(identity);
}
public Guid UserGuid { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName { get; set; }
}
public class CustomPrincipalSerializeModel
{
public Guid UserGuid { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName { get; set; }
}
public class BaseController : Controller
{
protected virtual new CustomPrincipal User
{
get { return HttpContext.User as CustomPrincipal; }
}
}
public abstract class BaseViewPage : WebViewPage
{
public virtual new CustomPrincipal User
{
get { return base.User as CustomPrincipal; }
}
}
public abstract class BaseViewPage<TModel> : WebViewPage<TModel>
{
public virtual new CustomPrincipal User
{
get { return base.User as CustomPrincipal; }
}
}
}
Views Web.Config BaseViewPage:
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="Intranet_v2.Helpers.BaseViewPage">
<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="Intranet_v2" />
</namespaces>
</pages>
I think my main problem is I have no idea what to do in the protected void Application_PostAuthenticateRequest(object sender, EventArgs args) for my Global.asax.cs file. I have a poor attempt at setting it up here:
protected void Application_PostAuthenticateRequest(object sender, EventArgs args)
{
//var application = (HttpApplication)sender;
var context = application.Context;
if (context.User != null || !context.User.Identity.IsAuthenticated) return;
var formsIdentity = (FormsIdentity)context.User.Identity;
if (formsIdentity == null) return;
var ticket = formsIdentity.Ticket;
JavaScriptSerializer serializer = new JavaScriptSerializer();
CustomPrincipalSerializeModel serializeModel = serializer.Deserialize<CustomPrincipalSerializeModel>(ticket.UserData);
CustomPrincipal newUser = new CustomPrincipal(ticket.Name);
newUser.UserGuid = serializeModel.UserGuid;
newUser.FirstName = serializeModel.FirstName;
newUser.LastName = serializeModel.LastName;
newUser.FullName = serializeModel.FullName;
var values = ticket.UserData.Split('|');
var roles = values[1].Split(',');
context.User = new GenericPrincipal(new GenericIdentity(ticket.Name, "Forms"), roles);
}
Now I'm at the point where #User.Name is now null. I'm in way over my head on this. Any help is appreciated. My protected void Application_PostAuthenticateRequest(object sender, EventArgs args) is completely out of wack.
All I want to do is rely on Windows Authentication to do what it does normally and add a few extra properties to the HttpContext.Current.User. Any help is appreciated... I can't be the only one trying to do this.
What I normally do is just request the additional user information later. For instance, using an Extension method like:
public static class PrincipalExtensions
{
private static void Initialize(string userName)
{
var userRecord = //Get user information from DB;
var session = HttpContext.Current.Session;
if (session != null)
{
session.Add("UserID", userRecord.ID);
session.Add("UserEmail", userRecord.Email);
//And so on
}
}
public static long? GetUserID(this IPrincipal user)
{
var id = HttpContext.Current.Session["UserID"] as long?;
if (id == null)
Initialize();
return (long)HttpContext.Current.Session["UserID"];
}
}
This is roughly what I implement in some of my projects; rather than tapping into the login process and store it in the cookie, the system can lazy load the information and cache in session when the information is needed.
Related
I am trying to connect oracle with MVC as below
Config file
<connectionStrings>
<add name="OracleDbContext" providerName="Oracle.ManagedDataAccess.Client" connectionString="User Id=test;Password=123_test;Data Source=local:xxxx/liveprod" />
</connectionStrings>
User table model
public class sys_users
{
[Key]
public long us_id { get; set; }
public string us_name { get; set; }
public string us_pass { get; set; }
}
Db context
public class OracleDBContext : DbContext
{
public OracleDBContext()
: base("name=OracleDbContext")
{
}
public virtual DbSet<sys_users> sys_users { get; set; }
}
Controller
public ActionResult Login(string Name, string Password)
{
var u = db.sys_users.Where(d => d.us_name.Equals(Name) && d.us_pass.Trim().Equals(Password)).FirstOrDefault();
if (u != null)
{
Session["LoggedInAdminUserId"] = u.us_id.ToString();
Session["LoggedInAdminUsername"] = u.us_name.ToString();
return RedirectToAction("Login");
}
else
{
ViewBag.message = "Username or Password is invalid.";
}
return View();
}
But at the line
var u = db.sys_users.Where(d => d.us_name.Equals(Name) && d.us_pass.Trim().Equals(Password)).FirstOrDefault();
I am getting error
ORA-01918: user 'dbo' does not exist
Do I need to do anything else for using oracle tables as model in MVC??
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Configure default schema
modelBuilder.HasDefaultSchema("STORE");
}
Entity default schema user is based on Sql (dbo) change the default to anything else but in UPPERCASE
I have the following code to create entities and seed them with data for use in an asp.net mvc application. I am using code first and entity framework. I generate controllers and run the application but on the index.cshtml page my list is empty where the seed data should be.
public class MyContext : DbContext
{
public MyContext() : base("dataDb") {}
public DbSet<Owner> Owners { get; set; }
public DbSet<Pet> Pets { get; set; }
}
public class MyInitializer : DropCreateDatabaseAlways<MyContext>
{
protected override void Seed(MyContext context)
{
// seed database here
context.Owners.AddOrUpdate(
new Owner()
{
//name=,
//id=,
Pets = new List<Pet> { new Pet()
{
//id=,
//name=,
},
new Pet()
{
//id=,
//name=,
}}
},
new Owner()
{
//id=,
//name=,
Pets = new List<Pet> { new Pet()
{
//id=,
//name=,,
}
}
}
);
context.SaveChanges();
}
}
public class Owner
{
public int OwnerId { get; set; }
public string Name { get; set; }
public virtual List<Pet> Pets { get; set; }
}
public class Pet
{
public int PetId { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public virtual Owner Owner { get; set; }
}
}
I found a solution to this problem:
Database.SetInitializer(new MyInitializer()); //important for seed to work
add this line to the context constuctor:
public MyContext() : base("dataDb") {}
You can also add it to your web.config. When it comes to deployment, you can easily remove it from the web.config by using configuration transformer.
<entityFramework>
<contexts>
<context type="MyProject.MyContext, MyProject">
<databaseInitializer type="MyProject.MyInitializer, MyProject" />
</context>
</contexts>
...
</entityFramework>
<connectionStrings>
<add name="MyContext" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;Initial Catalog=OwnersPets; AttachDBFilename=|DataDirectory|\OwnersPets.mdf; Integrated Security=SSPI;" providerName="System.Data.SqlClient" />
</connectionString>
Example conn string to use with localDb
I try to implement a basic web site that uses EF in ASP.NET MVC. Simply put, I try to connect SQL database and list them. I have a Product model. However, nothing comes to the screen. Here is my Product class:
public class Product
{
public int ProductID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
}
A repository pattern has been used in the Project. So interface IProductRepository here:
public interface IProductRepository
{
IQueryable<Product> Products { get; }
}
Here is the Product Controller in the project. It is the only controller in entire solution:
public class ProductController : Controller
{
private IProductRepository repository;
public ProductController(IProductRepository productRepository)
{
this.repository = productRepository;
}
public ViewResult List()
{
//This is not the part of the original project. In order to make sure that the code interacts with
//the database.
if (repository.Products.Count() == 0)
{
NinjectControllerFactory nin = new NinjectControllerFactory();
IKernel kernel = nin.GetNinjectKernel();
this.repository = kernel.Get<IProductRepository>();
}
return View(repository.Products);
}
}
Default route has been set there. Here is the content of the List.cshtml
#model IEnumerable<Model.Product>
#{
ViewBag.Title = "List";
}
<h2>List</h2>
#foreach (var p in Model)
{
<div class="item">
<h3>#p.Name</h3>
#p.Description
<h4>#p.Price.ToString("c")</h4>
</div>
}
I connect to my local database, define a table, populate it. I inserted connection string to the web.config of the project:
add name="EFDbContext" connectionString="Data
Source=(localdb)\v11.0;Initial Catalog=efver1;Integrated
Security=True" providerName="System.Data.SqlClient"
Here is the EFProductRepository.cs
public class EFProductRepository : IProductRepository
{
private EFDbContext context = new EFDbContext();
public IQueryable<Product> Products
{
get { return context.Products; }
}
}
and here is the EFDbContext.cs:
public class EFDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
}
I am using Ninject as a dependency injector. Here it is :
public class NinjectControllerFactory : DefaultControllerFactory
{
private IKernel ninjectKernel;
public NinjectControllerFactory()
{
ninjectKernel = new StandardKernel();
AddBindings();
}
protected override IController GetControllerInstance(RequestContext
requestContext, Type controllerType)
{
return controllerType == null
? null
: (IController)ninjectKernel.Get(controllerType);
}
private void AddBindings()
{
ninjectKernel.Bind<IProductRepository>().To<EFProductRepository>();
}
public IKernel GetNinjectKernel()
{
return this.ninjectKernel;
}
}
I made necessary changes here:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
//Burası da önemli.
ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
}
However, only page I can see is a blank screen as follows :
What I am missing here? thanks in advance.
edit 1:
Here is the data inside local database:
add name="EFDbContext" connectionString="Data Source=(localdb)\v11.0;Initial Catalog=efver1;Integrated Security=True" providerName="System.Data.SqlClient"
You are not connecting to the right database. According to the screenshot, your data is in INT-0014\SQLEXPRESS, not (localdb)\v11.0.
I am using code first EF and new to this framework. I am trying to create a database using Database.SetInitializer but it looks like I need SQL Server Express. But I have to create database in SQL Server 2014. How to do this?
Can anybody explain this with the example from EF-dbcontext book which has following classes.
public class BreakAwayContext : DbContext
{
public DbSet<Destination> Destinations { get; set; }
public DbSet<Lodging> Lodgings { get; set; }
public DbSet<Trip> Trips { get; set; }
public DbSet<Person> People { get; set; }
public DbSet<Reservation> Reservations { get; set; }
public DbSet<Payment> Payments { get; set; }
public DbSet<Activity> Activities { get; set; }
}
class Program
{
static void Main(string[] args)
{
Database.SetInitializer(new InitializeBagaDatabaseWithSeedData());
try
{
using (var context = new BreakAwayContext())
{
foreach (var destination in context.Destinations)
Console.WriteLine(destination.Name);
}
}
catch(Exception ex){
Console.WriteLine(ex.ToString());
}
Console.Read();
}
}
public class InitializeBagaDatabaseWithSeedData : DropCreateDatabaseAlways<BreakAwayContext>
{
protected override void Seed(BreakAwayContext context)
{
context.Destinations.Add(new Destination
{
Name = "Hawaii",
Country = "USA",
Description = "Sunshine, beaches and fun."
});
context.Destinations.Add(new Destination
{
Name = "Wine Glass Bay",
Country = "Australia",
Description = "Picturesque sandy beaches."
});
}
Set your connection string in your constructor:
public class BreakAwayContext : DbContext
{
public BreakAwayContext()
: base("MyConnectionString", throwIfV1Schema: false)
{
}
...
Then set your connection string in web.config or app.config:
<connectionStrings>
<add name="MyConnectionString" connectionString="Data Source=servername;Initial Catalog=dbname;..." providerName="System.Data.SqlClient" />
</connectionStrings>
I have User table in my database where I keep user's role (master admin, admin, developer). I want to authorize some controllers
so only master admin can have access.
namespace TicketSystem.Controllers
{
public class UserCredentials : ClaimsPrincipal, IIdentity, IPrincipal
{
public IIdentity Identity { get; private set; }
public int UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string[] roles { get; set; }
public string email { get; set; }
override
public bool IsInRole(string role)
{
if (roles.Any(r => role.Contains(r)))
{
return true;
}
else
{
return false;
}
}
public UserCredentials() { }
public UserCredentials(ClaimsPrincipal principal)
: base(principal)
{
}
public UserCredentials(int userId, string email, string firstName, string lastName, string[] roles)
{
this.Identity = new GenericIdentity(email);
this.UserId = userId;
this.email = email;
this.FirstName = firstName;
this.LastName = lastName;
this.roles = roles;
}
override
public string ToString()
{
return UserId + "";
}
}
}
This is my login method
UserCredentials loggedUser = null;
User loginUser = db.tblUser.Where(x => x.email == model.UserName).FirstOrDefault();
loggedUser = new UserCredentials( loginUser.idUser,
loginUser.email, loginUser.firsName, loginUser.lastName, new string[] { loginUser.role });
if (loggedUser != null)
{
var identity = new ClaimsIdentity(new[] {
new Claim(ClaimTypes.Name, loggedUser.email),
new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", User.Identity.AuthenticationType),
new Claim(ClaimTypes.NameIdentifier, loggedUser.FirstName),
new Claim(ClaimTypes.Role, loggedUser.roles[0])
}, "ApplicationCookie");
var ctx = Request.GetOwinContext();
var authManager = ctx.Authentication;
authManager.SignIn(identity);
I try with this
public class CustomRoleProvider : RoleProvider
{
public override bool IsUserInRole(string username, string roleName)
{
using (var usersContext = new TicketSystemEntities())
{
var user = usersContext.tblUser.SingleOrDefault(u => u.email == username);
if (user == null)
return false;
return user.role != null && user.role==roleName;
}
}
}
but I don't know how to configure web.Config. Also I'm having errors such as
TicketSystem.Models.CustomRoleProvider' does not implement inherited abstract member 'System.Web.Security.RoleProvider.GetUsersInRole(string)
I was searching other examples but I didn't find any example where the author uses Claim
RoleProvider is an abstract class, you have to implement all abstract methods to compile your CustomRoleProvider.
In the Web.config you need to add section roleManager and add your custom provider. Something like this:
<roleManager enabled="true" defaultProvider="CustomRoleProvider">
<providers>
<clear/>
<add name="CustomRoleProvider"
type="TicketSystem.Models.CustomRoleProvider,
TicketSystem, Version=1.0.0.0, Culture=neutral"
connectionStringName="TicketSystemEntities"
enablePasswordRetrieval="false" enablePasswordReset="true"/>
</providers>
</roleManager>
For reference check RoleProvider docs https://msdn.microsoft.com/en-us/library/system.web.security.roleprovider(v=vs.140).aspx and roleManager docs https://msdn.microsoft.com/en-us/library/vstudio/ms164660%28v=vs.100%29.aspx