I'm a newbie in Groovy and SpringBoot, but have to use these technologies in my work.
I have a User and Role classes, they don't have any relations inside classes. In Get-action in Controller I pass User and List of all possible roles to the form.
Controller class:
#GetMapping
public String edit(Model model){
model.addAttribute("user", new User());
model.addAttribute("roles", roleRepository.findAll());
return "edit";
}
My edit.tpl file:
...
form(..., method:'post'){
label(for:'username', 'Username: ')
input(type:'text', id:'username', name:'username', value:user.username)
label("Add roles: ")br()
roles.each {role ->
label(for:role.name, role.name)
input(type:'checkbox', id:role.name, name:role.name, value:role.name)
br()
}
input(type:'hidden', id:'_method', name:'_method', value:'put')
input(type:'submit', class:'btn btn-info', value:'Save')
}
I want to retrieve all the checked Roles.
Like that:
role_1 unchecked
role_2 checked
role_3 checked
List = [role_2, role_3];
I get the User like that:
#PutMapping("{id}")
public String update(#Valid User user, #PathVariable Long id){
user.setId(id);
userRepository.save(user);
return "redirect:/users";
}
And also in future I want to somehow check the current active Roles when passing them to the form.
Like that:
Current active roles are: role_2, role_3.
When I make a Get-request to show the form for editing, i have role_1 unchecked, and role_2 and role_3 checked.
My classes are as follows:
public class Role{
private String name;
//getters, setters, etc.
}
public class User{
#NotBlank(message="Please enter username")
private String username;
//getters, setters, etc.
}
Thanks for any help!! :)
I've found a way to get necessary information from the checkbox inputs:
use the #RequestParam annotation the following way:
Controller.java
public String update(#Valid User user, #RequestParam("roleIDs") List<Long> roles, #PathVariable Long id){
System.out.println(roles); //-the checked roles
return "redirect: /users";
}
Groovy .tpl file:
roles.each {role ->
label(for:role.name, role.name)
input(type:'checkbox', id:role.name, name:'roleIDs', value:role.id)
br()
}
Seems like I haven't googled the right question :)
Related
I have a simple sandbox project I'm using to get to better understand how .net Core Identity works and I've come across a bit of an inconsistency that I hope someone can explain. This project is using Entity Framework.
I used this awesome article to help me set up the project, https://medium.com/#goodealsnow/asp-net-core-identity-3-0-6018fc151b4#.2env44446 and my User class is as follows.
public class User : IdentityUser<int>
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string TempPassword { get; set; }
}
I seeded the db with three users and three roles, one user for each role, "Owner", "Admin", and "User". I added some policies for my actions,
auth.AddPolicy("Owner", policy =>
{
policy.RequireAuthenticatedUser();
policy.RequireRole("Owner");
});
auth.AddPolicy("Admin", policy =>
{
policy.RequireAuthenticatedUser();
policy.RequireRole("Admin", "Owner");
});
auth.AddPolicy("User", policy =>
{
policy.RequireAuthenticatedUser();
});
so my attributes like [Authorize("Admin")] work great. I even added some principal extensions as so
public static class PrincipalExtensions
{
public static bool IsOwner(this ClaimsPrincipal principal)
{
return principal.IsInRole("Owner");
}
public static bool IsAdmin(this ClaimsPrincipal principal)
{
return principal.IsInRole("Admin") || principal.IsInRole("Owner");
}
public static bool IsUser(this ClaimsPrincipal principal)
{
return principal.Identity.IsAuthenticated;
}
}
so I can do if(User.IsAdmin()) and this works perfectly as well.
Here is where it gets weird...
If I step through the following code I get confusing results.
var user = await _userManager.GetUserAsync(User);
var userRoles = await _userManager.GetRolesAsync(user);
await _userManager.AddToRoleAsync(user, "Owner");
The first line gets me a User object for the principal. On that object there is a collection of his Roles, user.Roles, but it will show empty (Count = 0) even though the user does have roles.
The second line gets the Roles for the user and it populates correctly.
The third line adds the "Owner" role to the user and it works correctly (the db is updated) but also, the local variable user suddenly now has that role in user.Roles! Note, none of the user's other roles will show up, just that one.
So I have basically two questions: 1. Why doesn't the user object have the user.Roles populated to begin with? 2. Why is it suddenly synced after I add a role?
Any help is appreciated.
Your Roles collection isn't populated after calling GetUserAsync() as the EntityFramework Identity UserStore doesn't request the information. It's doing the equivalent of you accessing the user data directly through your DbContext and without any calls to Include().
Right now EF Core does not support lazy loading, and therefore the user.Roles navigation property isn't automatically populated. And yes, this makes the behaviour somewhat disingenuous at the moment.
In your calls to GetRolesAsync() and AddToRoleAsync() the data is being explicitly populated for you, as you are operating on the roles directly.
Here is my entity class:
public class User {
#Id
UserIdentifier userIdentifier;
String name;
}
public class UserIdentifier {
String ssn;
String id;
}
Here is what I am trying to do:
public interface UserRepository extends MongoRepository<User, UserIdentifier>
{
User findBySsn(String ssn);
}
I get an exception message (runtime) saying:
No property ssn found on User!
How can I implement/declare such a query?
According to Spring Data Repositories reference:
Property expressions can refer only to a direct property of the managed entity, as shown in the preceding example. At query creation time you already make sure that the parsed property is a property of the managed domain class. However, you can also define constraints by traversing nested properties.
So, instead of
User findBySsn(String ssn);
the following worked (in my example):
User findByUserIdentifierSsn(String ssn);
How to authenticate and redirect a user to his own page i.e to www.mysite.com/"user's email".
I am using the following algo which is not working...
userDB in User class:
Map<String,String> userdata=new HashMap<String,String>();
First my login process form :
#Path("/login")
#POST
#Produces(MediaType.TEXT_HTML)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void login(
#FormParam("email") String emailc,
#FormParam("password") String pass,
#Context HttpServletResponse servletResponse
) throws IOException,RuntimeException {
User u1=new User();
pass=u1.getPassword();
emailc=u1.getEmailaddrs();
boolean checked=false;
boolean exists;
exists=u1.userdata.containsKey(emailc);
if(exists){
String mypass =u1.userdata.get(emailc);
if(mypass==pass){
checked=true;
}else{
checked=false;
}
}else{
checked=false;
}
if(!checked){
//User Doesn't exists
servletResponse.sendRedirect("http://localhost:8080/MySite/pages/Create_Profile.html");
}else{
servletResponse.sendRedirect("http://localhost:8080/MySite/{email}"); <<<< How to redirect using #FormParam("email")
}
}
createprofile
#POST
#Produces(MediaType.TEXT_HTML)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void newUser(
#FormParam("email") String email,
#FormParam("password") String password,
#Context HttpServletResponse servletResponse
) throws IOException {
User u = new User(email,password);
User.userdata.put(email,password);
}
Your usage of userdata [Map] looks wrong to me. Is it a part of user class, is it non static or static ?
If it is non static then every time you will do new User() .. that map will be initialized and it will have no data in it. Hence u1.userdata.containsKey(emailc); will be always false.
If you are using a hashmap as a temporary database for dev purposes then, make it static rather keep it in a different class like UserStore or some DB access layer. Exmaple below:
public class UserDAO(){
private static Map<String,User> userdata = new HashMap<String,User>();
public boolean hasUser(String email){
return userdata.contains(email);
}
public User saveUser(String email, String password ...){
//make user object save it in map and return the same
}
// more methods for delete and edit etc.
}
And use this in your REST layer classes like this
exists = userDao.hasUser(email);
Advantages :
Your problem will be solved.
Later on when you move to actual db implementation you will just have to change your UserDao code and rest application code will be just fine. -- Loose coupling :)
Also regarding forward using email
servletResponse.sendRedirect("http://localhost:8080/MySite/{email}"); <<<< How to redirect using #FormParam("email")
add the email parameter there in the url only, if thats what you want:
servletResponse.sendRedirect("http://localhost:8080/MySite/"+emailc);
UPDATE :
See the fundamental thing is that you get request parameters [email , password]. You check it whether it is present in map or not. Now what you are doing wrong here is you create a new user like this User u = new User(); and then get email and password from it emailc = u.getEmail();. This emailc will always be null and your userdata map will always return false for that. You have two choices :
Either set email and password in user object and then get the data from user object.
Use the email and password obtained from request parameters for your logic. Do not alter them
One good practice to follow while programming is that at all times think of your method parameters as final parameters.
UPDATE 2 :
if(mypass==pass){
checked=true;
}else{
checked=false;
}
Change == to equals method. String matching should be done by equals or equalsIgnoreCase method not ==.
You always create a new User without any parameters: User u1=new User();. All these User instances will have the same property values and probably exists is always false.
As far as I am aware, for the property to be saved in the database it cannot be ReadOnly.
IIdentity properties: AuthenticationType, IsAuthenticated and Name are all ReadOnly.
Is making the wrapper to the properties that need to be saved the only solution or there are better ones?
EDIT:
I might not have explained my question that well. Here is the sample code for one of the ReadOnly properties, I have added UserName property for the Entity Framework:
Public Property UserName As String
Get
Return _userName
End Get
Private Set(value As String)
userName = value
End Set
Public ReadOnly Property Name As String Implements System.Security.Principal.IIdentity.Name
Get
Return UserName
End Get
End Property
What I wanted to ask is if there is any better way of doing it.
IIdentity properties are read only but the implementation can have setters. If you are using EDMX for mapping you don't have to expose these setters as public.
Edit:
This is possible in C# so hopefully you can use similar approach with VB.NET (I can only read VB code, not write):
public interface ITest {
string Name { get; }
}
public class Test : ITest {
public string Name { get; set; }
}
The class offers setter even the interface didn't define it.
The EF persists objects, not interfaces. Your object can have whatever properties you would like it to have. You cannot add an interface to your entity model, but you can add an object type which implements that interface.
I was trying to edit a user page but i dont want to validate the password field. so i put a [Bind(Exclude="Password")] before my model. but it still causes the modelstate.Isvalid to return false. Can anybody know why is that so?
public ActionResult Edit([Bind(Exclude="Password")]User user, string selectedStatus, string password, string confirmPassword)
{
if(ModelState.Isvalid)<---- returns false
{
///logic
}
}
This attribute excludes a property from being bound, but not from being validated. So basically the model binder will never assign it a value but if you have validation attributes on it that require a value it won't work.
Obviously the solution to this is to use a view model (which by the way is the solution to about 70% of the questions I am answering on SO related to ASP.NET MVC).
So if you don't want a password field at all simply don't include it in this view model and if you want a password but don't want validation simply don't decorate it with a Required attribute.
So instead of:
public ActionResult Edit([Bind(Exclude="Password")]User user, string selectedStatus, string password, string confirmPassword)
use:
public ActionResult Edit(EditUserViewModel user)