Building CustomAuthorization in ASP.NET MVC - asp.net

In the DB i have Role and User entities with one to many relationship.
What i am trying to do is to build custom authorization filter. All the tutorials that i have seen are using default ASP.NET membership. All i know is that i need to inherit AuthorizationAttribute but do not know which methods do i need to override and how to implement them.
public class UserAuth : AuthorizeAttribute
{
}
In the DB:
Role
public class Role
{
[Key]
public int RoleID { get; set; }
[Required]
public int RolenameValue { get; set; }
[MaxLength(100)]
public string Description { get; set; }
// // // // //
public Rolename Rolename
{
get { return (ProjectName.Domain.Enums.Rolename)RolenameValue; }
set { RolenameValue = (int)value; }
}
public virtual ICollection<User> Users { get; set; }
}
User
public class User
{
[Key]
public int UserID { get; set; }
[Required]
[MaxLength(30)]
public string Username { get; set; }
[Required]
[MinLength(5)]
public string Password { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[MaxLength(30)]
public string FirstName { get; set; }
[MaxLength(50)]
public string LastName { get; set; }
[DataType(DataType.Date)]
public DateTime Birthdate { get; set; }
public int GenderValue { get; set; }
// // // // // // //
public Gender Gender
{
get { return (ProjectName.Domain.Enums.Gender)GenderValue; }
set { GenderValue = (int)value; }
}
public int RoleID { get; set; }
[ForeignKey("RoleID")]
public Role Role { get; set; }

You don't need to create a custom attribute. You can use existing AuthoriseAttribute but what you should do is implement custom Principal class that will use your own roles from DB. In your Principal class you will implement IsInRole method:
public bool IsInRole(string role)
{
if(this.Roles == null)
this.Roles = DependencyResolver.Current
.GetService<ISecurityService>()
.GetUserPermissions(this.Identity.Name);
return this.Roles.Any(p => p.Name == role);
}
You should set your custom Principal in Global.asax
void OnPostAuthenticateRequest(object sender, EventArgs e)
{
// Get a reference to the current User
IPrincipal user = HttpContext.Current.User;
// If we are dealing with an authenticated forms authentication request
if (user.Identity.IsAuthenticated && user.Identity.AuthenticationType == "Forms")
{
// Create custom Principal
var principal = new MyCustomPrincipal(user.Identity);
// Attach the Principal to HttpContext.User and Thread.CurrentPrincipal
HttpContext.Current.User = principal;
System.Threading.Thread.CurrentPrincipal = principal;
}
}

Related

ASP.NET how do I authorize user to delete his own post but not other's?

Im currently working on an ASP.NET project but I have a problem. I know a little about policies. but dont really know how I can grant acces to a user to delete his own data(posts, comments, etc..) but denying to delete or modify the uther user's data. Can you guys help me with that? Im currently authenticating with a JWT token.
My token:
public static class JWTToken
{
public static string CreateToken(User user)
{
List lista = new List();
lista.Add(new Claim("id", user.Id.ToString()));
lista.Add(new Claim("username", user.Username));
lista.Add(new Claim("email", user.Email));
lista.Add(new Claim("password", user.Password));
lista.Add(new Claim("role", user.Role.Name));
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Secure.Key));
;
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
claims: lista,
expires: DateTime.UtcNow.AddDays(5),
signingCredentials: creds);
var jwt = new JwtSecurityTokenHandler().WriteToken(token);
return jwt;
}
public static JwtSecurityToken DecodeToken(string stream) {
var handler = new JwtSecurityTokenHandler();
return handler.ReadJwtToken(stream);
}
//Már nem jó mert át lettek nevezve a jwt token adatok
public static string GetDataFromToken(HttpContext context, string type) {
ClaimsIdentity identity = context.User.Identity as ClaimsIdentity;
IEnumerable<Claim> claim = identity.Claims;
var data = claim.Where(x => x.Type == type).FirstOrDefault().ToString().Split(':')[1].Trim();
return data;
}
}
Post model:
[Table("Post")]
public class Post
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Data { get; set; }
[NotMapped]
[JsonIgnore]
public virtual User User { get; set; }
[ForeignKey(nameof(User))]
public int Post_UserId { get; set; }
}
User model:
[Table("User")]
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[MaxLength(15)]
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string Username { get; set; }
[MaxLength(30)]
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string Email { get; set; }
[MaxLength(30)]
[Required]
//[JsonIgnore]
public string Password { get; set; }
public int? Money { get; set; }
public bool Validated { get; set; }
public string ProfilePictureRoot { get; set; }
[NotMapped]
[JsonIgnore]
public virtual IList<NWEvent> Events { get; set; }
[NotMapped]
[JsonIgnore]
public virtual Role Role { get; set; }
[ForeignKey(nameof(Role))]
public string Rolename { get; set; }
[NotMapped]
[JsonIgnore]//nem volt itt
public virtual IList<Post> Posts { get; set; }
[NotMapped]
[JsonIgnore]
public virtual IList<Event_User_Connect> Event_User_Conns { get; set; }
/*
public string ApiString()
{
return $"{Id}~{Username}~{Email}~{Password}";
}
*/
}
How can I manage this?

Best way to create a map between two entities with a third one from another context

Hi I'd like to create a map between two entities (source: User, target: UserInfosDto) while one member of the target DTO (UserItemPreference) needs info from a third entity inside another context.
public class UserInfosDto
{
//public int UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public UserItemPreferencesDto UserItemPreferences { get; set; }
}
public class UserItemPreferencesDto
{
public bool SeeActuality { get; set; }
public bool IsInEditorMode { get; set; }
}
public class User
{
public string IdentityId { get; set; }
//...
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
public class UserIdentity
{
public string IdentityId { get; set; }
//...
public bool SeeActuality { get; set; }
public bool IsInEditorMode { get; set; }
}
User and UserIdentity come from different databases but have a common property IdentityId. I thought about using ITypeConverter in which I would inject the UserIdentity dbContext. Problem is that I can't find a way to use ITypeConverter on one member only.
Use an IValueResolver instead, which allows to resolve separate members instead of full types.
For your case above it will look like
public class UserItemPreferencesResolver
: IValueResolver<User, UserInfosDto, UserItemPreferencesDto>
{
private readonly UserEntityDbContext _dbContext;
public UserItemPreferencesResolver(UserEntityDbContext dbContext)
{
_dbContext = dbContext;
}
public UserItemPreferencesDto Resolve(
User source,
UserInfosDto destination,
UserItemPreferencesDto destinationMember,
ResolutionContext context
)
{
UserItemPreferencesDto preferences = /* resolve from _dbContext (and transform) */
return preferences;
}
}
Your create the mapping via
CreateMap<User, UserInfosDto>()
.ForMember(
dest => dest.UserItemPreferences,
opt => opt.MapFrom<UserItemPreferencesResolver>()
);

The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.ConsoleUserInfoes_dbo.ConsolesCheckBoxes_consoleId"

I'm getting this error:
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.ConsoleUserInfoes_dbo.ConsolesCheckBoxes_consoleId". The conflict occurred in database "aspnet-ForePlay-20180525122039", table "dbo.ConsolesCheckBoxes", column 'ConsoleId'.
I'm using Entity Framework and ASP.NET MVC 5 and IdentityUser and try to insert data form checkListBox to table into my database.
This is happening on the register view, when user need to register and fill the form.
public class ConsoleUserInfo
{
[Key]
public int identity { get; set; }
[Required]
[StringLength(255)]
[ForeignKey("User")]
public string userid { get; set; }
[Required]
[ForeignKey("consolesCheckBox")]
public int consoleId { get; set; }
public virtual ApplicationUser User { get; set; }
public virtual ConsolesCheckBox consolesCheckBox { get; set; }
}
This is the table that need to get a user id (form applictionUser) and consoleId
(form ConsolesCheckBox )
This is the ApplicationUserUser model class:
public class ApplicationUser : IdentityUser
{
[Required]
[StringLength(255)]
override
public string UserName { get; set; }
[Required]
[StringLength(50)]
public string Phone { get; set; }
public byte[] UserPhoto { get; set; }
public virtual UserAddress Address { get; set; }
public virtual ICollection<ConsolesCheckBox> consoleCheckBox { get; set; }
}
and this is the checkBoxList table:
public class ConsolesCheckBox
{
[Key]
public int ConsoleId { get; set; }
public string ConsoleName { get; set; }
public bool IsChecked { get; set; }
public virtual ICollection<ApplicationUser> ApplicationUser { get; set; }
}
This is my account controller, all in the register get and post
// GET: /Account/Register
[AllowAnonymous]
public ActionResult Register()
{
//using database
using (ApplicationDbContext dbo = new ApplicationDbContext())
{
//data will save list of the consoleCheckBoxItem
var data = dbo.consolesCheckBox.ToList();
// because the view is request a common model, we will create new one
CommenModel a = new CommenModel();
a.ConsolesCheckBoxList = data;
// we will need to return common model, that way we will return a
return View(a);
}
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register([Bind(Exclude = "UserPhoto")]CommenModel model)
{
if (ModelState.IsValid)
{
// To convert the user uploaded Photo as Byte Array before save to DB
byte[] imageData = null;
if (Request.Files.Count > 0)
{
HttpPostedFileBase poImgFile = Request.Files["UserPhoto"];
using (var binary = new BinaryReader(poImgFile.InputStream))
{
imageData = binary.ReadBytes(poImgFile.ContentLength);
}
}
var user = new ApplicationUser
{
UserName = model.registerViewModel.Email,
Email = model.registerViewModel.Email,
Phone = model.registerViewModel.Phone
};
user.UserPhoto = imageData;
var result = await UserManager.CreateAsync(user, model.registerViewModel.Password);
//after the user create, we will use the id and add the id to the userAddress table include
// Address, longitude and latitude.
using (ApplicationDbContext dbo = new ApplicationDbContext())
{
var currentUserId = user.Id;
var pasinfo = dbo.userAddress.FirstOrDefault(d => d.Userid == currentUserId);
if (pasinfo == null)
{
pasinfo = dbo.userAddress.Create();
pasinfo.Userid = currentUserId;
dbo.userAddress.Add(pasinfo);
}
pasinfo.Address = model.useraddress.Address;
pasinfo.latitude = model.useraddress.latitude;
pasinfo.longitude = model.useraddress.longitude;
dbo.SaveChanges();
foreach (var item in model.ConsolesCheckBoxList.Where(x => x.IsChecked).Select(x => x.ConsoleId))
{
var consoleUserInfo = new ConsoleUserInfo
{
userid = currentUserId,
consoleId = item
};
dbo.consoleUserInfo.Add(consoleUserInfo);
}
dbo.SaveChanges();
}
}
}
In the register GET I have a common model, because I used 3 models in the view
this is the common model:
public class CommonModel
{
public UserAddress useraddress { get; set; }
public RegisterViewModel registerViewModel { get; set; }
public List<ConsolesCheckBox> ConsolesCheckBoxList { get; set; }
}
I need your help here, I've been trying to fix this all day.

Many to many with extra foreign key?

I want to generate a junction table between user and post and I want to have the userId in the post table.
I have this code
public class Post
{
public Post()
{
this.ApplicationUser = new HashSet<ApplicationUser>();
}
public int PostId { get; set; }
public string Message { get; set; }
public DateTime MessageDate { get; set; }
public virtual ApplicationUser User { get; set; } //this is the problem
public virtual ICollection<ApplicationUser> ApplicationUser { get; set; }
}
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
public ApplicationUser()
{
this.Posts = new HashSet<Post>();
}
public virtual ICollection<Post> Posts { get; set; }
}
I get the extra junction table and many-to-many relation between user and post. But this is wrong.
public virtual ApplicationUser User { get; set; }
This generates two UserId in the post table (applicationUser_id and User_id) and Post_PostId in the User table. I just want one extra field in the Post table, FK UserId.
I want three tables like this
Post
PostId
Message
Date
UserId FK
User
UserId
And the rest of the fields in asp.net identity user
UserPosts
UserId
PostId
User table
public partial class User
{
public User()
{
this.Posts = new HashSet<Post>();
this.UserPosts = new HashSet<UserPost>();
}
public int UserId { get; set; }
public string Username { get; set; }
public virtual ICollection<Post> Posts { get; set; }
public virtual ICollection<UserPost> UserPosts { get; set; }
}
Post table
public partial class Post
{
public Post()
{
this.UserPosts = new HashSet<UserPost>();
}
public int PostId { get; set; }
public string Message { get; set; }
public Nullable<System.DateTime> Date { get; set; }
public Nullable<int> UserId { get; set; }
public virtual User User { get; set; }
public virtual ICollection<UserPost> UserPosts { get; set; }
}
and your mapping table, like this
your column 1) Id (pk), 2) UserId (fk) 3) PostId (fk)
using entity framework table have one primary key necessary.
UserPost table
public partial class UserPost
{
public int Id { get; set; }
public Nullable<int> UserId { get; set; }
public Nullable<int> PostId { get; set; }
public virtual Post Post { get; set; }
public virtual User User { get; set; }
}
Updated Code
modelBuilder.Entity<Post>().ToTable("userid table name");
this line add in below method of this class ApplicationDbContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}

asp.net mvc - how to save a model with enum fields?

My model is defined as follows:
namespace Project.Models
{
public enum LogType
{
Login = 0,
Login_Fail = 1
}
[Table("UserLog")]
public class UserLog
{
public long Id { get; set; }
public int UserId { get; set; }
public DateTime Date { get; set; }
public string Des { get; set; }
public LogType Type { get; set; }
public virtual User User { get; set; }
}
}
Base type of Type field in the UserLog table is tinyint.
Login controller code as follows:
[HttpPost]
public virtual JsonResult Login(UserViewModel model)
{
if (userRepository.CheckUserLogin(model.UserName, model.Password))
{
UserLog log = new UserLog();
log.Date = DateTime.Now;
log.Des = "";
log.Type = LogType.Login;
userRepository.AddUserLog(model.UserName, log);
userRepository.Save();
Session["LoginUser"] = model.UserName;
}
}
And Login Repository code as follows:
public void AddUserLog(string username, UserLog log)
{
User user = GetUserByUserName(username);
if (user != null)
user.UserLogs.Add(log);
}
The problem is that information is properly stored in UserLog table, but the Type field remains Null!
I've used this solution:
public int Type { get; set; }
[NotMapped]
public LogType UserLogType
{
get { return (LogType)Type; }
set { Type = (int)value; }
}

Resources