I have a service/manager class where I fetch users from data source. The methods are simulare in flow but has different retrieve methods.
public User GetByUserName(string userName)
{
user = dependencyResolver.UserRepository.GetByUserName(userName);
if (user == null) return null;
AddStuffToUser();
return user;
}
public User GetById(int id)
{
user = dependencyResolver.UserRepository.GetById(id);
if (user == null) return null;
AddStuffToUser();
return user;
}
public User GetByUserName(string userName, string encryptedPassword)
{
user = dependencyResolver.UserRepository.GetByUsernameAndPassword(userName, encryptedPassword);
if (user == null) return null;
AddStuffToUser();
return user;
}
I would like to remove the duplication of the flow logic, but I can't find a satisfying design. I could do it by using OOP but I think that adds to much complexity for such a simple task. I also tried passing the fetch methods as a Func<> parameter, but I didn't get it right since the fetch methods have different parameters.
Which are my options here?
replace:
if (user == null) return null;
AddStuffToUser();
return user;
with:
return doXYZ(user);
and add:
private User doXYZ(User user){
if (user != null){
AddStuffToUser();
return user;}
return null;
at this point u placed 6 Lines for 9 and its better code-style (OOP-standard is usefull in any task)
Related
I found this resource for creating an AuthorizationHandler and followed along to create one. My handler checks for given_name, a string value, and if given_name has a value of "Bob", authorization success.
The problem, I can't access the given_name claim and its value. I can see all the expected claims when I inspect context.User but context.User.HasClaim(c => c.Type == ClaimTypes.GivenName) always returns false.
How do I check for the presence of a claim and get its value?
Update - As a workaround, I can access all the claims by calling .ToList() on context.User.Claims and then using .Any() on the list. It works but I haven't seen this approach in any examples.
For Asp.NET Core 6, you can pass in the AuthorizationHandlerContext.
Here is a sample AuthorizationHandler that leverages that context for a ClaimsPrincipal.
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Filters;
namespace MyIdentityApp.Security
{
public class CanReadAffiliatedOwnerInfoHandler : AuthorizationHandler<ManageAffiliatedOwnerInfoRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ManageAffiliatedOwnerInfoRequirement requirement)
{
var authFilterContext = (HttpContext?)context.Resource;
if (authFilterContext == null)
{
return Task.CompletedTask;
}
else
{
//access user's claims here, via context.User
var loggedInUserAffiliatedOwners = context.User.Claims.FirstOrDefault(c => c.Value == OwnerIdAttemptingToAccess);
if (loggedInUserAffiliatedOwners != null)
{
context.Succeed(requirement);
}
}
return Task.CompletedTask;
}
}
}
What's the advantage or recommendation on using IActionResult as the return type of a WebApi controller instead of the actual type you want to return?
Most of the examples I've seen return IActionResult, but when I build my first site I exclusively use View Model classes as my return types.... now I feel like I did it all wrong!
The main advantage is that you can return error/status codes or redirects/resource urls.
For example:
public IActionResult Get(integer id)
{
var user = db.Users.Where(u => u.UserId = id).FirstOrDefault();
if(user == null)
{
// Returns HttpCode 404
return NotFound();
}
// returns HttpCode 200
return ObjectOk(user);
}
or
public IActionResult Create(User user)
{
if(!ModelState.IsValid)
{
// returns HttpCode 400
return BadRequest(ModelState);
}
db.Users.Add(user);
db.SaveChanges();
// returns HttpCode 201
return CreatedAtActionResult("User", "Get", new { id = user.Id} );
}
The main advantage is that you can easily test your code using a mocking framework.
And as you build your controllers, you can easily change your return object as well. IActionResult is a interface and has many implementations like JsonResult, ViewResult, FileResult and so on.
Currently I am looking for best practice in handling conditions inside the controller actions in asp.net mvc. For example -
public ActionResult Edit(int Id = 0)
{
var Item = _todoListItemsRepository.Find(Id);
**if (Item == null)
return View("NotFound");
if (!Item.IsAuthorized())
return View("NotValidOwner");**
return View("Edit", Item);
}
The above two conditions marked in bold is used in other actions inside the controller. So, in order not to repeat these conditions in all the actions. I have used the below approach.
[HttpGet]
[Authorize]
[ModelStatusActionFilter]
public ActionResult Edit(int Id = 0)
{
var Item = _todoListItemsRepository.Find(Id);
return View("Edit", Item);
}
public class ModelStatusActionFilterAttribute : ActionFilterAttribute
{
private readonly ITodoListItemsRepository _todoListItemsRepository;
public ModelStatusActionFilterAttribute()
: this(new TodoListItemsRepository())
{
}
public ModelStatusActionFilterAttribute(ITodoListItemsRepository todoListItemsRepository)
{
_todoListItemsRepository = todoListItemsRepository;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
try
{
var Id = Convert.ToInt32(filterContext.RouteData.Values["Id"]);
var Item = _todoListItemsRepository.Find(Id);
if (Item == null)
{
filterContext.Result = new ViewResult() { ViewName = "NotFound" };
}
else if (!Item.IsAuthorized())
{
filterContext.Result = new ViewResult() { ViewName = "NotValidOwner" };
}
}
catch
{
}
}
}
I am unsure if this is the best practice in handling such scenarios. So, could someone please advise ?
Regards,
Ram
usually you don't use action filter for so-called business logic of your web application - this is what the controllers are for. Action filter are rather for the whole stuff which is external to the actual logic - common case is logging, performance measurement, checking if user is authenticated / authorized (I don't think this is your case, although you call IsAuthorized method on the "Item").
Reducing code is generally good thing but in this case, I don't think putting the logic to action is a good way, because you;ve actually made it a bit unreadable, and unreadable code is in my opinon much worse than repeated code.
Also, specifically in your case, for all valid items you actually call the _todoListItemsRepository.Find() twice (for each valid item), which might be costly if this is some webservice call or db lookup.
If the code is just repeated throughout the actions, make a method out of it like:
private View ValidateItem(Item) {
if (Item == null)
return View("NotFound");
if (!Item.IsAuthorized())
return View("NotValidOwner");
return null; }
In MVC4, I created a custom membership provider that returns true if the user authentication passes. No biggie here - this portion works the way it should:
public override bool ValidateUser(string username, string password)
{
var crypto = new SimpleCrypto.PBKDF2(); // type of encryption
// TODO: using (var unitOfWork = new Website.Repository.UnitOfWork(_dbContext))
//var unitOfWork1 = new Website.Repository.UnitOfWork(_dbContext);
using (var db = new Website.DAL.WebsiteDbContext())
{
var user = db.Users
.Include("MembershipType")
.FirstOrDefault(u => u.UserName == username);
if (user != null && user.Password == crypto.Compute(password, user.PasswordSalt))
{
FormsAuthentication.SetAuthCookie(username, true);
return true;
}
}
return false;
}
In my Login Action:
[HttpPost]
[AllowAnonymous]
public ActionResult Login(Models.UserModel user)
{
if (ModelState.IsValid)
{
// custom membership provider
if (Membership.ValidateUser(user.UserName, user.Password))
{
// Cannot use this block as user needs to login twice
//if (User.IsInRole("WaitConfirmation")) // checks the custom role provider and caches based on web.config settings
//{
// //TempData["EmailAddress"] = thisUser.Email;
// // email address has not yet been confirmed
// return RedirectToAction("WaitConfirmation");
// //return View("Account", thisUser)
//}
//else
//{
// // get custom identity - user properties
// string userName = UserContext.Identity.Name;
// //CustomIdentity identity = (CustomIdentity)User.Identity;
// var identity = UserContext.Identity;
// int userId = identity.UserId;
// return RedirectToAction("Index", "Dashboard");
//}
if (User.Identity.IsAuthenticated && User.IsInRole("WaitConfirmation")) // checks the custom role provider and caches based on web.config settings
{
return RedirectToAction("WaitConfirmation");
}
else if (User.Identity.IsAuthenticated)
{
// get custom identity - user properties
string userName = UserContext.Identity.Name;
return RedirectToAction("Index", "Dashboard");
}
}
else
{
ModelState.AddModelError("", "Login data is incorrect.");
}
}
return View(user);
}
In stepping through the code, when a user first logs in, User.Identity.IsAuthenticated is false and the page is redirected back to the login page. At this point if I either:
manually navigate to the user page (Dashboard) the user is details are available
login again, this works
I believe the answer lies somewhere in why the User.Identity.IsAuthenticated is not immediately true but can't figure out this is false the first time around.
The first block of commented-out code fails with Unable to cast object of type 'System.Security.Principal.GenericIdentity' to type 'Website.AdminWebsite.Infrastructure.CustomIdentity' as there is no IsAuthenticated check.
Suggestions?
This post describes a problem with similar symptoms.
http://forums.asp.net/t/1177741.aspx
Please have a read and ensure the order of your events (i.e. Authenticate, LoggedIn)
After reading the article #mcsilvio's suggested, I added an RedirectToAction() as follows to initiate a new page life-cycle:
public ActionResult Login(Models.UserModel user)
{
if (ModelState.IsValid)
{
// custom membership provider
if (Membership.ValidateUser(user.UserName, user.Password))
{
return RedirectToAction("VerifyIdentity", user);
}
else
{
ModelState.AddModelError("", "Login data is incorrect.");
}
}
return View(user);
}
public ActionResult VerifyIdentity(Models.UserModel user)
{
if (User.Identity.IsAuthenticated && User.IsInRole("WaitConfirmation")) // checks the custom role provider and caches based on web.config settings
{
return RedirectToAction("WaitConfirmation");
}
else if (User.Identity.IsAuthenticated)
{
// get custom identity - user properties
string userName = UserContext.Identity.Name;
return RedirectToAction("Index", "Dashboard");
}
return View(User);
}
This did the trick, but I'm wondering if there is a better way or is it always done like this?
Somehow, in this controller, after the SaveChanges, the CurrentUserId becomes -1.
The data post works, and the CurrentUserId has it's logged in value (example 8888), but after the SQL insert, the WebSecurity.CurrentUserId becomes -1. Any clue? During debug I can't find where and why.
// POST: /Account/Edit
[HttpPost]
[ValidateInput(false)]
public ActionResult Edit(UserProfile model)
{
if (ModelState.IsValid)
{
using (var context = new dbContext())
{
var id = WebSecurity.CurrentUserId;
var account = context.UserProfiles.Find(id);
UpdateModel(account);
context.SaveChanges();
return RedirectToAction("Index", "Account");
}
}
else
{
return View(model);
}
}
that will always return -1, what you need is the below code
int currentuserid = WebSecurity.GetUserId(username);
You can then validate that the userid above, matches the userid in the model, in order to prevent users, changing other users code
as Additional. I use this in my Base Controller:
public int GetUserId()
{
var userid = "0";
if (Request.IsAuthenticated && User.Identity.Name != null)
{
var membershipUser = Membership.GetUser(User.Identity.Name);
if (membershipUser != null)
{
if (membershipUser.ProviderUserKey != null)
{
userid = membershipUser.ProviderUserKey.ToString();
}
}
}
return Convert.ToInt32(userid);
}