How would I mimic User.IsInRole() - asp.net-mvc

I have a website thats build with VS 2012 Internet Application ( Simple membership) EF Code First
Updates
I would like to know how to extend HttpContext.User.IsInRole(role) 's functionality for a custom table -> User.IsInClient(client).

Here is the way I'd suggest to solve your issue:
Create your own interface which implements System.Security.Principal, where you could place any methods you need:
public interface ICustomPrincipal : IPrincipal
{
bool IsInClient(string client);
}
Implement this interface:
public class CustomPrincipal : ICustomPrincipal
{
private readonly IPrincipal _principal;
public CustomPrincipal(IPrincipal principal) { _principal = principal; }
public IIdentity Identity { get { return _principal.Identity; } }
public bool IsInRole(string role) { return _principal.IsInRole(role); }
public bool IsInClient(string client)
{
return _principal.Identity.IsAuthenticated
&& GetClientsForUser(_principal.Identity.Name).Contains(client);
}
private IEnumerable<string> GetClientsForUser(string username)
{
using (var db = new YourContext())
{
var user = db.Users.SingleOrDefault(x => x.Name == username);
return user != null
? user.Clients.Select(x => x.Name).ToArray()
: new string[0];
}
}
}
In the Global.asax.cs assign your custom principal to the request user context (and optionally to the executing thread if you plan to use it later). I suggest to use Application_PostAuthenticateRequest event not Application_AuthenticateRequest for this assignment, otherwise your principal will be overridden (at least by ASP.NET MVC 4):
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
Context.User = Thread.CurrentPrincipal = new CustomPrincipal(User);
/*
* BTW: Here you could deserialize information you've stored earlier in the
* cookie of authenticated user. It would be helpful if you'd like to avoid
* redundant database queries, for some user-constant information, like roles
* or (in your case) user related clients. Just sample code:
*
* var authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
* var authTicket = FormsAuthentication.Decrypt(authCookie.Value);
* var cookieData = serializer.Deserialize<CookieData>(authCookie.UserData);
*
* Next, pass some deserialized data to your principal:
*
* Context.User = new CustomPrincipal(User, cookieData.clients);
*
* Obviously such data have to be available in the cookie. It should be stored
* there after you've successfully authenticated, e.g. in your logon action:
*
* if (Membership.ValidateUser(user, password))
* {
* var cookieData = new CookieData{...};
* var userData = serializer.Serialize(cookieData);
*
* var authTicket = new FormsAuthenticationTicket(
* 1,
* email,
* DateTime.Now,
* DateTime.Now.AddMinutes(15),
* false,
* userData);
*
* var authTicket = FormsAuthentication.Encrypt(authTicket);
* var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName,
authTicket);
* Response.Cookies.Add(authCookie);
* return RedirectToAction("Index", "Home");
* }
*/
}
Next, to be able to use the property User from HttpContext in the controller without casting it to ICustomPrincipal each time, define base controller where you override the default User property:
public class BaseController : Controller
{
protected virtual new ICustomPrincipal User
{
get { return (ICustomPrincipal)base.User; }
}
}
Now, let other controllers inherit from it:
public class HomeController : BaseController
{
public ActionResult Index()
{
var x = User.IsInClient(name);
If you use Razor View Engine, and you'd like to be able to use your method in the very similar way on the views:
#User.IsInClient(name)
you need to redefine WebViewPage type:
public abstract class BaseViewPage : WebViewPage
{
public virtual new ICustomPrincipal User
{
get { return (ICustomPrincipal)base.User; }
}
}
public abstract class BaseViewPage<TModel> : WebViewPage<TModel>
{
public virtual new ICustomPrincipal User
{
get { return (ICustomPrincipal)base.User; }
}
}
and tell Razor to reflect you changes, by modifying appropriate section of the Views\Web.config file:
<system.web.webPages.razor>
...
<pages pageBaseType="YourNamespace.BaseViewPage">

Use Linq:
var Users = Membership.GetAllUsers();
//**Kinda Like Users.InCLients(userName).
var users = from x in Users
join y in db.Clinets on x.ProviderUserKey equals y.UserID
select x
//**Kinda Like Clients.InUsers(userName)
var clients = from x in db.Clinets
join y in Users on x.UserID equals y.ProviderUserKey
select x

try this way
List<Clinets> AllClinets =entityObject.Clinets .ToList();
Foreach( var check in AllClinets)
{
if(check.UserTable.RoleTable.RoleName=="Rolename1")
{
//This users are Rolename1
}
else
{
//other.
}
}

Stored procedure would be better in this case.

Related

.NET Core Custom Authorize Filter

I'm developing a new application in MVC .NET Core 2 but I have to use the existing Authentication and Authorization that's used in another application developed in MVC5. It calls a webservice with the current users details to get a list of the groups and roles the user is in, this is done in the GetUserRoles method. From googling it looks like this is done differently now compared to MVC 5. Could anyone help me implement this so I can use it like so:
[BasicAuthorisation(Roles = "SpecificUser")]
Here is a snippet of the filter that I'm trying to implement in .NET Core:
[AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)]
public class BasicAuthorisation : AuthorizeAttribute, IAuthorizationFilter
{
MemoryCache mc = MemoryCache.Default;
private ClaimsPrincipal _principal;
private string _applicationName = "";
public override void OnAuthorization(AuthorizationContext filterContext)
{
bool skipAuthorisation = filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true)
|| filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true);
_principal = HttpContext.Current.User as ClaimsPrincipal;
if (_principal.Identity == null)
{
_principal = Thread.CurrentPrincipal as ClaimsPrincipal;
}
_applicationName = System.Configuration.ConfigurationManager.AppSettings["ApplicationName"];
if (_principal.Identity.IsAuthenticated)
{
if (!skipAuthorisation)
{
// set userRole cache name
string userRoleCache = _principal.Identity.Name.Replace(DoubleSlash, string.Empty) + CacheSuffix;
List<string> userRoles = GetUserRoles(_principal, userRoleCache);
//get system defined Role groups
List<string> restrictedRoleGroups = Roles.Split(_doubleBar, StringSplitOptions.RemoveEmptyEntries).Select(r => r.Trim()).ToList();
// iterate groups for authorisation
for (int i = 0; i < restrictedRoleGroups.Count(); i++)
{
List<string> restrictedRoles = restrictedRoleGroups[i].Split(',').Select(r => r.Trim()).ToList();
List<string> matchingRoles = (from ur in userRoles
where (restrictedRoles.Any(mr => ur == mr))
select ur).ToList();
if (matchingRoles.Count().Equals(restrictedRoles.Count()))
{
_outcome = true;
}
}
}
else { _outcome = true; }
}
if (_outcome == false)
{
filterContext.Result = new RedirectResult("~/Message/Index");
}
}

Trouble retaining CustomPrincipal type in HttpContext

I have an MVC app which I'm having trouble ex[posing a custom principal to my views. It has the following classes that help me manage auth cookies.
public class AuthenticationManager
{
public void SetAuthCookie(UserViewModel user)
{
var serializeModel = new CustomPrincipalSerializeModel
{
Id = user.UserId,
Email = user.Email,
Name = user.Name
};
var serializer = new JavaScriptSerializer();
var customPrincipal = customPrincipalMapper.Convert(serializeModel);
var httpContext = ContextHelper.GetHttpContextBase();
httpContext.User = customPrincipal;
var userData = serializer.Serialize(serializeModel);
var authTicket = new FormsAuthenticationTicket(1, serializeModel.Email, DateTime.Now, DateTime.Now.AddYears(5), false, userData);
var encTicket = FormsAuthentication.Encrypt(authTicket);
var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
httpContext.Response.Cookies.Add(authCookie);
}
}
public static class ContextHelper
{
public static HttpContextBase GetHttpContextBase()
{
return new HttpContextWrapper(HttpContext.Current);
}
}
I also have the following BaseViewPage classes which allow me to expose the current user to my views:
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; }
}
}
FWIW, this requires <pages pageBaseType="Giftster.Web.Views.BaseViewPage"> to be in my View's Web.config file.
Immediately, after the httpContext.User = customPrincipal; line runs in AuthenticationManager.SetAuthCookie(), the type of object which ContextHelper.GetHttpContextBase().User returns is a CustomPrincipal. If I refresh the page, however, and put a break point in BaseViewPage, base.User as CustomPrincipal (and ContextHelper.GetHttpContextBase().User as CustomPrincipal for that matter) equals null. base.User is not null, though: It is of type GenericPrincipal, so there is either a casting issue or a problem with storing/retrieving the correct type.
Why is base.User in my BaseViewPage not of type CustomPrincipal?
Thanks in advance.
You need to create your CustomPrincipal from the cookie in each request and add it to the current context. Add the following to the Global.asax.cs file
protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
// Get the authentication cookie
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
// If the cookie can't be found, don't issue the ticket
if (authCookie == null)
{
return;
}
// Extract the forms authentication cookie
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
// Deserialise user data
JavaScriptSerializer serializer = new JavaScriptSerializer();
CustomPrincipalSerializeModel data = serializer.Deserialize<CustomPrincipalSerializeModel>(authTicket.UserData);
// Create principal
CustomPrincipal principal = new CustomPrincipal(authTicket.Name);
// Set the properties of CustomPrincipal
principal.Email = data.Email;
.... // etc
// Assign to current context
HttpContext.Current.User = principal;
}
Note also the following line is not required in you SetAuthCookie() method
httpContext.User = customPrincipal;
You might also consider adding the following to a BaseController (from which all other controllers derive) so the CustomPrincipal properties can be accessed easily in each controller method
public new CustomPrincipalSerializeModel User
{
get { return (CustomPrincipal)HttpContext.User; }
}

ASP.NET MVC: Unit testing session variables does't change

I'm trying to unit test a Logout action of my controller. My controller recive an interface wich deals with session variables:
public HomeController(IUnitOfWork unitOfWork, ISecurityService security = null)
{
_unitOfWork = unitOfWork;
if (security != null)
{
_securityService = security;
}
else
{
_userService = new UserService(_unitOfWork)
_securityService = new SecurityService(_userService);
}
}
Using Moq, I create a HttpSessionStateBase object
Which I use to create a SecurityService object in my unit test class:
var mockSession = new Mock<HttpSessionStateBase>();
mockSession.SetupGet(s => s["UserID"]).Returns("1");
HttpSessionStateBase session = mockSession.Object;
ISecurityService _securityService = new SecurityService(_userService, session);
My SecurityService
public class SecurityService : ISecurityService
{
private readonly IUsuarioService _userService ;
private readonly HttpSessionStateBase _session;
public int UserID
{
get { return Convert.ToInt32(_session["UserID"]); }
set { _session["UserID"] = value; }
}
public SecurityService(IUserService service, HttpSessionStateBase session = null)
{
_userService = service;
_session = session ?? new HttpSessionStateWrapper(HttpContext.Current.Session);
}
public void Logout()
{
// I'm trying to delete this variable, but I can't
_session["UserID"] = 0;
_session.Clear();
_session.Abandon();
}
}
Finally, my test method
[TestMethod]
public void Should_delete_session_variable_from_the_user()
{
controller.Logout();
// It's allways 1
Assert.IsTrue(Convert.ToInt32(session["UserID"]) == 0);
}
When I debug this code, I can see that the session variable does not chage its value. But, if I open a "Quick Watch" window and execute _session["UserID"] = 0;, its value change.
I can't understand why. This is the first time that I see that a variable does not chage its value in debugging.
Finally I found, the solution is to add an SetupSet method.
This is the final code:
int sessionValue = 0;
var mockSession = new Mock<HttpSessionStateBase>();
mockSession.SetupSet(s => s["UserId"] = It.IsAny<int>())
.Callback((string name, object val) => sessionValue = (int) val);
mockSession.SetupGet(s => s["UserId"]).Returns(() => sessionValue);

Moq Roles.AddUserToRole test

I am writing unit tests for a project in ASP.NET MVC 1.0 using Moq and MvcContrib TestHelper classes. I have run into a problem.
When I come to Roles.AddUserToRole in my AccountController, I get a System.NotSupportedException. The Roles class is static and Moq cannot mock a static class.
What can I do?
You could use a pattern like DI (Dependency Injection). In your case, I would pass a RoleProvider to the AccountController, which would be the default RoleProvider by default, and a mock object in your tests. Something like:
public class AccountController
{
private MembershipProvider _provider;
private RoleProvider roleProvider;
public AccountController()
: this(null, null)
{
}
public AccountController(MembershipProvider provider, RoleProvider roleProvider)
{
_provider = provider ?? Membership.Provider;
this.roleProvider = roleProvider ?? System.Web.Security.Roles.Provider;
}
}
The MVC runtime will call the default constructor, which in turn will initialize the AccountController with the default role provider. In your unit test, you can directly call the overloaded constructor, and pass a MockRoleProvider (or use Moq to create it for you):
[Test]
public void AccountControllerTest()
{
AccountController controller = new AccountController(new MockMembershipProvider(), new MockRoleProvider());
}
EDIT: And here's how I mocked the entire HttpContext, including the principal user.
To get a Moq version of the HttpContext:
public static HttpContextBase GetHttpContext(IPrincipal principal)
{
var httpContext = new Mock<HttpContextBase>();
var request = new Mock<HttpRequestBase>();
var response = new Mock<HttpResponseBase>();
var session = new Mock<HttpSessionStateBase>();
var server = new Mock<HttpServerUtilityBase>();
var user = principal;
httpContext.Setup(ctx => ctx.Request).Returns(request.Object);
httpContext.Setup(ctx => ctx.Response).Returns(response.Object);
httpContext.Setup(ctx => ctx.Session).Returns(session.Object);
httpContext.Setup(ctx => ctx.Server).Returns(server.Object);
httpContext.Setup(ctx => ctx.User).Returns(user);
return httpContext.Object;
}
A mock implementation of Principal:
public class MockPrincipal : IPrincipal
{
private IIdentity _identity;
private readonly string[] _roles;
public MockPrincipal(IIdentity identity, string[] roles)
{
_identity = identity;
_roles = roles;
}
public IIdentity Identity
{
get { return _identity; }
set { this._identity = value; }
}
public bool IsInRole(string role)
{
if (_roles == null)
return false;
return _roles.Contains(role);
}
}
A MockIdentity:
public class MockIdentity : IIdentity
{
private readonly string _name;
public MockIdentity(string userName) {
_name = userName;
}
public override string AuthenticationType
{
get { throw new System.NotImplementedException(); }
}
public override bool IsAuthenticated
{
get { return !String.IsNullOrEmpty(_name); }
}
public override string Name
{
get { return _name; }
}
}
And the magic call:
MockIdentity identity = new MockIdentity("JohnDoe");
var httpContext = MoqHelpers.GetHttpContext(new MockPrincipal(identity, null));
Note that I edited the code above to leave out some custom stuff, but I'm quite sure this should still work.
Now I run into another problem when I try to test the ChangePassword() method in ASP.NET MVC.
try
{
if (MembershipService.ChangePassword(User.Identity.Name, currentPassword, newPassword))
{
if (!TempData.ContainsKey("ChangePassword_success"))
{
TempData.Add("ChangePassword_success", true);
}
return PartialView("ChangePassword");
}
Now I get that User is null, when I reach this line. In my testclass I have:
mockMembershipService.Setup(cp => cp.ChangePassword("johndoe", currentPassword, newPassword)).Returns(true);
I thought that this would work, but it doesn't care for that I send "johndoe". And If I were to mock IPrincipal, the User property is readonly.
TypeMock Isolator does mocking of statics etc. But I second (and +1'd) Razzie's answer.
I have done what you coded, but I still get that User is null when it reaches:
mockMembershipService.Setup(cp => cp.ChangePassword("johndoe", currentPassword, newPassword)).Returns(true);
In my Testclass I have:
//Arrange (Set up a scenario)
var mockMembershipService = new Mock<IMembershipService>();
MockIdentity identity = new MockIdentity("JohnDoe");
var httpContext = MoqHelpers.GetHttpContext(new MockPrincipal(identity, null));
var controller = new AccountController(null, mockMembershipService.Object, null, null, null);
string currentPassword = "qwerty";
string newPassword = "123456";
string confirmPassword = "123456";
// Expectations
mockMembershipService.Setup(pw => pw.MinPasswordLength).Returns(6);
mockMembershipService.Setup(cp => cp.ChangePassword("johndoe", currentPassword, newPassword)).Returns(true);
Do I call my cp.ChangePassword with wrong parameters? And should MVCContrib Testhelpers classes be able to mock Http context and so on? I just can't find info for how to setup User.Identity.Name with MVCContrib.
I have used this from a tutorial to test something (mock) session:
var builder = new TestControllerBuilder();
var controller = new AccountController(mockFormsAuthentication.Object, mockMembershipService.Object, mockUserRepository.Object, null, mockBandRepository.Object);
builder.InitializeController(controller);
EDIT: I have come a little further:
MockIdentity identity = new MockIdentity("JohnDoe");
var httpContext = MoqHelpers.GetHttpContext(new MockPrincipal(identity, null));
var controller = new AccountController(null, mockMembershipService.Object, null, null, null);
controller.ControllerContext = new ControllerContext(httpContext, new RouteData(), controller);
but my now I can't get my cp.ChangePassword in the expectation to return true:
mockMembershipService.Setup(cp => cp.ChangePassword("johndoe", currentPassword, newPassword)).Returns(true);
I am sending "johndoe" string, because, it requires a string as a parameter for User.Identity.Name, but it doesn't return true.

How do I restrict access to certain pages in ASP.NET MVC?

I wish to lock out access to a user's EDIT page (eg. /user/pure.krome/edit) if
a) Identity.IsAuthenticated = false
or they are authenticated but
b) Idenitity.Name != user name of the user page they are trying to edit
c) Identity.UserType() != UserType.Administrator // This is like a Role, without using RoleProviders.
I'm assuming u can decorate a controller or a controller's action method with something(s), but i'm just not sure what?
Look at the AuthorizeAttribute.
ASP.Net MVC: Can the AuthorizeAttribute be overriden?
A custom attribute derived from AuthorizeAttribute is what I use to do this. Override the OnAuthorize method and implement your own logic.
public class OnlyUserAuthorizedAttribute : AuthorizeAttribute
{
public override void OnAuthorize( AuthorizationContext filterContext )
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new HttpUnauthorizeResult();
}
...
}
}
I implemented the following ActionFilterAttribute and it works to handle both authentication and roles. I am storing roles in my own DB tables like this:
User
UserRole (contains UserID and RoleID foreign keys)
Role
public class CheckRoleAttribute : ActionFilterAttribute
{
public string[] AllowedRoles { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string userName = filterContext.HttpContext.User.Identity.Name;
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
if (AllowedRoles.Count() > 0)
{
IUserRepository userRepository = new UserRepository();
User user = userRepository.GetUser(userName);
bool userAuthorized = false;
foreach (Role userRole in user.Roles)
{
userAuthorized = false;
foreach (string allowedRole in AllowedRoles)
{
if (userRole.Name == allowedRole)
{
userAuthorized = true;
break;
}
}
}
if (userAuthorized == false)
{
filterContext.HttpContext.Response.Redirect("/Account/AccessViolation", true);
}
}
else
{
filterContext.HttpContext.Response.Redirect("/Account/AccessViolation", true);
}
}
else
{
filterContext.HttpContext.Response.Redirect(FormsAuthentication.LoginUrl + String.Format("?ReturnUrl={0}", filterContext.HttpContext.Request.Url.AbsolutePath), true);
}
}
I call this like this...
[CheckRole(AllowedRoles = new string[] { "admin" })]
public ActionResult Delete(int id)
{
//delete logic here
}

Resources