So I have implemented my own custom membership provider, currently only overwriting ValidateUser(). I also have signalr working, currently just a simple method that calls send message and then sends it out to all listeners. I would like to add some validation checking so that this command cannot be run on its own. So far I have found you can do this using: Context.User.Identity.IsAuthenticated
[HubName("messageController")]
public class MessageController : Hub
{
public void SendMessage(string message)
{
if (Context.User.Identity.IsAuthenticated) // this line not working
{
Clients.addMessage(message);
}
else
{
Clients.addMessage("not authenticated");
}
}
}
My problem though is because I am currently using a custom membership provider value is false. Is there something else I should be using here instead?
Currently when I Login I execute:
[AllowAnonymous]
[HttpPost]
public ActionResult Login(LoginModel model, string returnUrl)
{
if(Membership.ValidateUser(model.UserName, model.Password))
{
// Need something here to actually set the logged in user
}
return RedirectToAction("Index", "");
}
What am I missing here? Do I need to store my own code to handle the session, I tried using FormsAuthentication.SetAuthCookie(model.UserName, true); which worked but I'm pretty sure its wrong. When I tried to introduce Roles by changing it to Context.User.IsInRole("Admin") it returned false. even though I used the below User model (which when debugging never gets to this method):
public class User : IPrincipal
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
public long UserID;
public string Name;
public string ConnectionId;
public virtual IIdentity Identity
{
get;
set;
}
public virtual bool IsInRole(string role)
{
return true;
}
}
I'm pretty confident I am missing something with this, any ideas?
Using FormsAuthentication.SetAuthCookie() after you've validated the user is perfectly OK, the default AccountController created by the MVC3 template does exactly the same.
The reason why your call to Context.User.IsInRole() doesn't work is because that won't invoke your custom User class (the framework doesn't know about it), instead it will try to get the roles via a RoleProvider. You need to build a custom provider and hook it up in the Web.config like you did with the membership provider.
Related
I have a scenario where I need to update an object from information that was posted to the action. As long as the information is in the page this works fine. However, it requires that I put information into hidden fields if I don't want the modelstate to complain.
As an example, lets say I am using the class below as the model:
public class Client
{
[Required]
public string Name { get; set; }
public string Email { get; set; }
public int Id { get; set; }
}
If I don't want the user to edit the name, I need to include it in a hidden field so that it get bound to the model and the validation passes.
The problem I have is that is obviously not secure if used with more sensitive information. So I tried this:
public virtual ActionResult Save(Client model, int clientId)
{
var client = datasource.LoadEntity(adapter, clientId); // clientId is passed as a querystring to the action
if (!TryUpdateModel(client))
return new RedirectResult('The edit page URL');
}
The problem is that the modelstate still complains about the "Name" value not being available even though it was loaded to the client object from the database.
Obviously I am doing something wrong but I can't figure out what.
The view model is just for information coming from the client.
So you have to remove the Name property and get it from somewhere else.
If this is a view model also used by the administrator for example (who is able to enter/change the name) then the best would be a derived view model like this:
public class Client
{
public string Email { get; set; }
public int Id { get; set; }
}
public class ClientWithName : Client
{
[Required]
public string Name { get; set; }
}
You can use the overload TryUpdateModel(TModel, string\[\]); if my understanding is correct, this should allow to specify the property to include in the update, like this:
public virtual ActionResult Save(Client model, int clientId)
{
var client = datasource.LoadEntity(adapter, clientId);
if (!TryUpdateModel(client, new string[] { "Email" }))
return new RedirectResult('The edit page URL');
}
I never tried it though, can you let us know if it works as expected?
I've gone with the solution outlined here: Asp.net MVC 3 Validation exclude some field validation in TryUpdateModel
Essentially, it removes the validation from the Modelstate if those fields aren't present which works for me as those values are retrieved from the database.
In my ASP.NET MVC application I want to just update two variables in my Register method.
My register method in my controller looks like this(it hasn't got its own view, so im not sure what to return in this case):
[HttpPost]
public ActionResult Register(Container c)
{
String email = c.register.Email.ToString();
String password = c.register.Password.ToString();
return view();
}
My index view looks like this;
#model VoterMvcTest.Models.Container
#using(Html.BeginForm("Register", "Default1"))
{
#Html.LabelFor(o => o.register.Email)
#Html.EditorFor(o => o.register.Email)
#Html.LabelFor(o => o.register.Password)
#Html.EditorFor(o => o.register.Password)
<input class="button" type="submit" value="Register" />
}
And my model looks like this;
public class Container
{
public Login login { get; set; }
public Register register { get; set; }
}
public class Login
{
public string Email { get; set; }
public string Password { get; set; }
}
public class Register
{
public string Email { get; set; }
public string Password { get; set; }
}
When you click the button, I want the two variables email and password to be set, and available to use in another method. I'm not sure what I should return(The goal would be to stay in the same view). I've tried to redirect to the same view i'm currently at (index view), but it seems like that causes a postback and the variables are lost.
ASP.NET MVC does not have ViewState as you would have when using WebForms. So if you
perform a post and do not persist the data in either a database or a temporary memory
(session object for instance) you'll lose that data as soon as the request has finished.
If you do some kind of validation and determine that the user was well registered you
could redirect him to another view using:
return RedirectToAction("Index")
Try this one
return View("Index",c)
it will work
I have a "New user" form both for admins and for regular users. Both form use the RegisterModel
public class RegisterModel
{
[Required]
public string Name { get; set; }
public string Email { get; set; }
[Required]
public string Password { get; set; }
}
The difference is that on my front end "New user" page I want users to provide their own password. But in back end, I want the system to generate the password.
Since I use the same RegisterModel for both forms, I get a validateion error in the back end saying Password is required..
I thought, I could solve this by adding this to my controller:
[HttpPost]
public ActionResult New(RegisterModel model)
{
model.Password = Membership.GeneratePassword(6, 1);
if (TryValidateModel(model))
{
// Do stuff
}
return View(model);
}
But I still get the error message Password is required.. Why is this the issue when I do call TryValidate in my controller?
What would be best practice for this issue, create a separate RegisterModelBackEnd or are there any other solutions to this?
When updating model manually, you do not need to use it as parameter in Action. Also, use this overload that lets you specify only the properties on which binding will occur.
protected internal bool TryUpdateModel<TModel>(
TModel model,
string[] includeProperties
)
where TModel : class
So, the working code will be
[HttpPost]
public ActionResult New()
{
RegisterModel model = new RegisterModel();
model.Password = Membership.GeneratePassword(6, 1);
if (TryValidateModel(model, new string[] {"Name", "Email"}))
{
// Do stuff
}
return View(model);
}
You can make this even simpler, using BindAttribute
[HttpPost]
public ActionResult New([Bind(Exlude="Password")]RegisterModel model)
{
if(ModelState.IsValid)
{
model.Password = Membership.GeneratePassword(6, 1);
// Do Stuff
}
return View(model);
}
And finally simplest and the best way
Define separate view models
I have a layered application that send commands to the business layer (actually, the application is based on ncqrs framework, but I don't think it's important here).
A command looks like this :
public class RegisterUserCommand : CommandBase
{
public string UserName { get; set; }
public string Email{ get; set; }
public DateTime RegistrationDate { get; set; }
public string ApiKey {get; set;} // edit
}
There is no logic in this class, only data.
I want to have the users type their user name, email and I want the system to use the current date to build the command.
What is best between :
create a strongly typed view based on the RegisterUserCommand, then inject the date and the APi Key just before sending it to the business layer ?
create a RegisterUserViewModel class, create the view with this class and create the command object based on the view input ?
I wrote the following code (for the solution n°2) :
public class RegisterController : Controller
{
//
// GET: /Register/
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(RegisterUserViewModel registrationData)
{
var service = NcqrsEnvironment.Get<ICommandService>();
service.Execute(
new RegisterUserCommand
{
RegistrationDate = DateTime.UtcNow,
Email= registrationData.Email,
UserName= registrationData.Name,
ApiKey = "KeyFromConfigSpecificToCaller" // edit
}
);
return View();
}
public class RegisterUserViewModel
{
[Required]
[StringLength(16)]
public string Name { get; set; }
[Required]
[StringLength(64)]
public string Email{ get; set; }
}
}
This code is working... but I wonder if I choose the correct way...
thanks for advises
[Edit] As the Datetime seems to cause misunderstanding, I added another property "ApiKey", that should also be set server side, from the web layer (not from the command layer)
[Edit 2] try the Erik suggestion and implement the 1st solution I imagined :
[HttpPost]
public ActionResult Index(RegisterUserCommand registrationCommand)
{
var service = NcqrsEnvironment.Get<ICommandService>();
registrationCommand.RegistrationDate = DateTime.UtcNow;
registrationCommand.ApiKey = "KeyFromConfigSpecificToCaller";
service.Execute(
registrationCommand
);
return View();
}
... Is it acceptable ?
I think you would be better off with option #2, where you would have a separate ViewModel and a Command. While it may seem redundant (to an extent), your commands are really messages from your web server to your command handler. Those messages may not be formatted the same as your ViewModel, nor should they be. And if you're using NCQRS as is, you would then have to map your commands to your AR methods and constructors.
While it may save you a little bit of time, I think you pigeon-hole yourself in to modeling your domain after your ViewModels, and that should not be the case. Your ViewModels should be a reflection of what your user experiences and sees; your domain should be a reflection of your business rules and knowledge, and are not always reflected in your view.
It may seem like a bit more work now, but do yourself a favor and keep your commands separate from your view models. You'll thank yourself later.
I hope this helps. Good luck!
I would recommend putting this into the constructor of the RegisterUserCommand class. That way the default behavior is always to set it to DateTime.UtcNow, and if you need to set it to something explicitly you can just add it to the object initializer. This will also help in scenarios where you're using this class in other parts of your project, and you forget to set the RegistrationDate explicitly.
public class RegisterUserCommand : CommandBase
{
public string UserName { get; set; }
public string Email{ get; set; }
public DateTime RegistrationDate { get; set; }
public RegisterUserCommand()
{
RegistrationDate = DateTime.UtcNow;
}
}
And the Controller
public class RegisterController : Controller
{
//
// GET: /Register/
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(RegisterUserViewModel registrationData)
{
var service = NcqrsEnvironment.Get<ICommandService>();
service.Execute(
new RegisterUserCommand
{
Email= registrationData.Email,
OpenIdIdentifier = registrationData.OpenIdIdentifier
}
);
return View();
}
public class RegisterUserViewModel
{
[Required]
[StringLength(16)]
public string Name { get; set; }
[Required]
[StringLength(64)]
public string Email{ get; set; }
}
}
I would use number 1 and use the system.componentmodel.dataannotations.metadatatype for validation.
I created an example (answer) for another SO question Here.
This allows you to keep your model in another library, validate the fields and show the fields like you would internal/private classes with DataAnnotations. I'm not a big fan of creating a completely separate class for a view that has no additional value while having to ORM the data back to another class. (If you had additional values like dropdown list values, or default values then I think it would make sense).
Instead of
[HttpPost]
public ActionResult Index(RegisterUserViewModel registrationData)
{
var service = NcqrsEnvironment.Get<ICommandService>();
service.Execute(
new RegisterUserCommand
{
RegistrationDate = DateTime.UtcNow,
Email= registrationData.Email,
UserName= registrationData.Name,
ApiKey = "KeyFromConfigSpecificToCaller" // edit
}
);
return View();
}
You can have
[HttpPost]
public ActionResult Index(RegisterUserCommand registrationData)
{
var service = NcqrsEnvironment.Get<ICommandService>();
registrationData.ApiKey = "KeyFromConfigSpecificToCaller";
service.Execute(registrationData);
return View();
}
Once again I'm having trouble with Linq to Sql and the MVC Model Binder.
I have Linq to Sql generated classes, to illustrate them they look similar to this:
public class Client
{
public int ClientID { get; set; }
public string Name { get; set; }
}
public class Site
{
public int SiteID { get; set; }
public string Name { get; set; }
}
public class User
{
public int UserID { get; set; }
public string Name { get; set; }
public int? ClientID { get; set; }
public EntityRef<Client> Client { get; set; }
public int? SiteID { get; set; }
public EntityRef<Site> Site { get; set; }
}
The 'User' has a relationship with the 'Client' and 'Site
. The User class has nullable ClientIDs and SiteIDs because the admin users are not bound to a Client or Site.
Now I have a view where a user can edit a 'User' object, the view has fields for all the 'User' properties. When the form is submitted, the appropiate 'Save' action is called in my UserController:
public ActionResult Save(User user, FormCollection form)
{
//form['SiteID'] == 1
//user.SiteID == 1
//form['ClientID'] == 1
//user.ClientID == null
}
The problem here is that the ClientID is never set, it is always null, even though the value is in the FormCollection.
To figure out whats going wrong I set breakpoints for the ClientID and SiteID getters and setters in the Linq to Sql designer generated classes. I noticed the following:
SiteID is being set, then ClientID is being set, but then the Client EntityRef property is being set with a null value which in turn is setting the ClientID to null too! I don't know why and what is trying to set the Client property, because the Site property setter is never beeing called, only the Client setter is being called.
Manually setting the ClientID from the FormCollection like this:
user.ClientID = int.Parse(form["ClientID"].ToString());
throws a 'ForeignKeyReferenceAlreadyHasValueException', because it was already set to null before. The only workaround I have found is to extend the generated partial User class with a custom method:
Client = default(EntityRef<Client>)
but this is not a satisfying solution. I don't think it should work like this?
Please enlighten me someone. So far Linq to Sql is driving me crazy!
Best regards
I've just run in the same issue with MVC 3. I think this happens because model binder sets all public properties of the model class while creating it. So it sets user.ClientID to 1 and then user.Client to null (because it doesn't exist in form collection). And after that the value of user.ClientID becomes null too.
So you just need to exclude the Client property of the User class from binding like this:
public ActionResult Save([Bind(Exclude="Client")]User user, FormCollection form)
This worked for me.