I'm creating a directory with spring boot, and I add the library Spring Ldap.
I created my odm of User and it works, but I don't know how to map attribute from another entry.
My users are in ou=people,dc=mycompany,dc=com
The user belongs to an unit in ou=units,dc=mycompany,dc=com
The user have 0 or 1 manager in `ou=people,dc=mycompany.' same as users
The user have 0 or more subsidiaries no attribute for that, I have to find it with the help of manager attribute.
Here my code:
#Entry( objectClasses = { "person", "top" }, base = "ou=People" )
public final class User{
#Id private Name dn;
private String fullname;
private String mail;
etc...
}
I would like to add private User manager and private String unit and private List<User> subsidiaries but I dont know how to map/link to another Entry.
There is currently no support for relations in Spring LDAP ODM. I'm not sure it would be quite worth the effort (since LDAP is not really a relational database anyway), but it could possibly be something we could take a shot at if the demand for it is high.
Facing the same issue, I've used a transient attribute in my User class :
#Attribute(name = "manager", syntax = "1.3.6.1.4.1.1466.115.121.1.12")
private Name managerName;
#Transient
private User manager;
Of course, you have to deal with the manager attribute yourself. Add the following code in your UserService class :
public User findUser(String uid) {
User user = getRepo().findByUid(uid);
if(null != user.getManagerName()) {
User manager = getRepo().findOne(user.getManagerName());
user.setManager(manager);
}
return user;
}
You have to write something similar when saving your user object, in order to fill the managerName attribute with its new value if required.
Related
I've recently tried to implement Spring Security into my web store project to distinguish between single users. Websites are working properly except there is one issue which I can't track to resolve. I have object called Customer within User class. Customer object has fields like id, balance, etc., and User has OneToOne relationship to Customer, so I can have single object for credentials and foreign key to specifics of user - his first name, last name, balance, owned products, etc.
I also have Product class which has ManyToOne relationship with Customer. It has its' own id, productCost, etc.
I'm using Spring MVC to take care of proper URL dispatching. When some action is taken, I'm using #AuthenticationPrincipal annotation to get currently logged Customer (through foreign key in User) and modify data regarding Customer linked with that foreign key.
When I modify Customer data through #AuthenticationPrincipal in controller, changes are immediate and they show up on website. But when I try to modify data through some DAO, for example by searching for Customer through id or try to get Customer that owns Product from Product getter (ManyToOne has reference to owning Customer), changes are not immediate. Database updates itself immediately and properly, like in first case, but collections in code and website state are not changed until I logout and login again - that's when data is updated. I suspect it may be due to fact that updating UserDetails updates data directly for currently logged user but then - how may I achieve same effect for Customer found by id?
Snippets of code:
Users.java:
#Entity
#Table(name="users")
public class Users {
#Id
#Column(name="username")
private String username;
#Column(name="password")
private String password;
#Column(name="enabled")
private boolean isActive;
#OneToMany(mappedBy="user")
private Set<Authorities> authorities;
#OneToOne
#JoinColumn(name="customer_id")
private Customer customer;
Product.java:
#Entity
#Table(name="product")
public class Product {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="name")
private String productName;
#Column(name="description")
private String productDescription;
#Column(name="category")
private String productCategory;
#Column(name="cost")
private int productCost;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name="owner_id")
private Customer productOwner;
Customer.java:
#Entity
#Table(name="customer")
public class Customer {
//Class fields
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="balance")
private int balance;
#Column(name="first_name")
private String firstName;
#Column(name="last_name")
private String lastName;
#Column(name="email")
private String email;
#OneToMany(mappedBy="productOwner", fetch=FetchType.EAGER)
private List<Product> ownedProducts;
Piece of controller code:
#Autowired
CustomerService customerService;
#Autowired
ProductService productService;
/*(...)*/
#GetMapping("/showOffer/{offerId}")
public String getOffer(#PathVariable int offerId, Model theModel, #AuthenticationPrincipal MyUserDetails user) {
Product retrievedProduct = productService.findById(offerId);
if (user.getCustomer().getBalance() >= retrievedProduct.getProductCost())
{
Customer retrievedProductOwner = retrievedProduct.getProductOwner();
/* This is where changes aren't applied immediately and I need to logout and login to process them. */
retrievedProductOwner.setBalance(1000);
/* This is where changes are immediately shown and Java collections are updated: */
user.getCustomer().setBalance(user.getCustomer().getBalance()-retrievedProduct.getProductCost());
/* Code below is an attempt to force immediate changes by updating collections directly from database - but that approach doesn't work */
productService.delete(retrievedProduct.getId());
retrievedProduct.getProductOwner().getOwnedProducts().clear();
retrievedProduct.getProductOwner().setOwnedProducts(productService.listOwnerProducts(retrievedProduct.getProductOwner()));
}
else {
System.out.println("Insufficient funds!");
}
return "redirect:/home";
TL:DR
I use UserDetails object in controller and I am also using DAO for Customer used as foreign key in UserDetails. Using UserDetails directly updates data and everything works fine, using DAO doesn't make changes until I logout and login.
as far as i understand your changes are only commited when you log out .
just try to synchronize and commit any modification at the right time and it would be safer that you manage sessions and transactions at the same time so you don't get any sort of incoherence when you do that. then tell me about the results .
Check whether CTRL+F5 in your browser (force cache clearance) updates your data similarly to logging out and back in. If so, it's a question of cached information. (this and (3) may occur at the same time)
Alternatively ... or perhaps complementarly ... your data fetch reqeust may be called before the database update/commit operation is completed. If so, it should become evident if you run distinct update and show routines. i.e. turn A into B, then into C, and you'd get something like B when you're expecting C... A instead of B... etc.
Lastly, depending on how you set up your back end, it is possible that you only populate whatever form you use for the front end exactly once, instead of dynamically querying the database whenever you access that form.
I am trying to implement a simple User-Roles relationship in a Spring application, for security. The basic entities (some fields and annotations trimmed):
User
#Table(name="usr")
public class User implements Serializable {
#Id
private UUID id;
#ManyToMany(fetch=FetchType.EAGER)
#JoinTable(name="user_roles", joinColumns=#JoinColumn(name="user_id", referencedColumnName="id"),
inverseJoinColumns=#JoinColumn(name="role_id", referencedColumnName="id"))
private Collection<Role> roles;
}
Role
public class Role implements Serializable {
#Id
private UUID id;
#ManyToMany(mappedBy="roles")
private Collection<User> users;
private String name;
}
So far, so good. However, I also have a class that defines a list of role-name values:
UserRoles
public class UserRole {
public static final String ADMIN = "admin";
public static final String USER = "user";
}
I want to constrain the values of the Role's name field to the values in UserRoles, effectively like an enum.
These role values will get used within Spring Security functions that require roles to be string values. As such, if I were to make UserRoles an enum, any database storage would be of ints – the ordinal definition position within UserRoles – which would force me to keep any potentially deprecated options, and also require a hacky conversion every time I need to convert the role to a string that can be passed around in a JWT, etc. (If I want to look at my database directly, it will also be far less informative.)
Is there some way to define Role's name field as limited to the static values in UserRoles? (Changing how or where these values are stored is entirely acceptable.)
You can define like this
public enum UserRoleEnum {
USER, ADMIN
}
And in entity
#Enumerated(EnumType.ORDINAL)
private UserRoleEnum role;
I am trying to figure it out how to do this proper way. Let's say I have entity Employee like that:
#Entity
public class EmployeeEntity{
#Id
private Long id;
private String username;
private String password;
private List<AddressEntity> addresses;
private DepartmentEntity department;
}
Now let's say some AddressEntity and DepartmentEntity are already created so I just want to point it. Controller would look like this:
#RestController
public class EmployeeController{
#Autowired
private EmployeeService;
#PostMapping
public EmployeeDto createEmployee(#RequestBody EmployeeDto employee){
return employeeService.createEmployee(employee);
}
}
And DTO:
public class EmployeeDto{
private Long id;
private String username;
private String password;
private List<AddressDto> addresses;
// private List<Long> addressesIds;
private DepartmentDto department;
// private Long departmentId;
}
So what bothers me is how properly transfer data from request to service layer and to response.
should DTO be object 1:1 same like entity?
or with additional values, like ids of others related objects?
or DTO is just concept and as well I can use custom request/response for every occasion? This would be handy but is it the way it should be done? There would be plenty of one-case-use classes.
Crating new entity is first problem but how about updating? If I would like to update just Employee username, I shouldn`t pass all rest of the objects so ids maybe? And it should be custom UpdateEmployeeRequest with only updatable fields or DTO with all data like password?
Sorry if I messed up a little. Too much new knowledge and I feel like I go round and round like a child in the fog...
should DTO be object 1:1 same like entity? or with additional values, like ids of others related objects?
Not necessary. DTOs are mostly to pass data to view layer. You can wrap data from multiple entities and send in one DTO to view.
or DTO is just concept and as well I can use custom request/response for every occasion? This would be handy but is it the way it should be done? There would be plenty of one-case-use classes.
Yes. It is like custom request/response for every occasion (data transfer to view and from view).
Crating new entity is first problem but how about updating? If I would like to update just Employee username, I shouldn`t pass all rest of the objects so ids maybe? And it should be custom UpdateEmployeeRequest with only updatable fields or DTO with all data like password?
Pass the required minimum fields and use same DTO on Create/Update (Id with field to update on update and other fields on Create).
Example dto for create:
username : "some user",
password : "some password",
... other fields
Example JSON for update username:
id: 1,
username : "some user",
I am using objectify. Say, I have a User kind with name and email properties. When implementing signup, I want to check if a user with same name or same email is already registered. Because signup can be called from many sources a race condition might occur.
To prevent race condition everything must be wrapped inside a transaction somehow. How can I eliminate the race condition?
The GAE documents explain how to create an entity if it doesn't exist but they assume the id is known. Since, I need to check two properties I can't specify an id.
Inspired by #konqi's answer I have came up with a similar solution.
The idea is to create User_Name and User_Email entities that will keep the name and emails of all the users created so far. There will be no parent relationship. For convenience we are going to keep name and email properties on user too; we are trading storage for less read/write.
#Entity
public class User {
#Id public Long id;
#Index public String name;
#Index public String email;
// other properties...
}
#Entity
public class User_Name {
private User_Name() {
}
public User_Name(String name) {
this.name = name;
}
#Id public String name;
}
#Entity
public class User_Email {
private User_Email() {
}
public User_Email(String email) {
this.email = email;
}
#Id public String email;
}
Now create user within a transaction by checking unique fields:
User user = ofy().transact(new Work<User>() {
#Override
public User run()
{
User_Name name = ofy().load().key(Key.create(User_Name.class, data.username)).now();
if (name != null)
return null;
User_Email email = ofy().load().key(Key.create(User_Email.class, data.email)).now();
if (email != null)
return null;
name = new User_Name(data.username);
email = new User_Email(data.email);
ofy().save().entity(name).now();
ofy().save().entity(email).now();
// only if email and name is unique create the user
User user = new User();
user.name = data.username;
user.email = data.email;
// fill other properties...
ofy().save().entity(user).now();
return user;
}
});
This will guarantee uniqueness of those properties (at least my tests empirically proved it :)). And by not using Ref<?>s we are keeping the data compact which will result in less queries.
If there was only one unique property it is better to make it #Id of the main entity.
It is also possible to set the #Id of the user as email or name, and decrease the number of new kinds by one. But I think creating a new entity kind for each unique property makes the intent (and code) more clear.
I can think of two possible solutions:
Using entity design:
Lets say you have a User #Entity which will use the email address as #Id. Then you create a Login #Entity which has the Name as #Id and a Ref<User> to the User. Now both can be queried for with key queries which can be used in transactions. This way it's impossible to have duplicates.
#Entity
public class User {
#Id
private String email;
}
#Entity
public class Login {
#Id
private String name;
private Ref<User> user;
}
Using indexed composite property:
You can define an indexed composite property that contains both values like this (Note: This just shows what i mean by indexed composite property, do not implement it like that):
#Entity
public class User {
#Id
private Long id;
private String email;
private String name;
#Index
private String composite;
#OnSave
private onSave(){
composite = email + name;
}
}
However, as stickfigure pointed out there is no guarantee for uniqueness if you use an indexed property in a transaction (as a matter of fact you can't query by indexed property in a transaction at all).
That is because in a transaction you can only query by key or ancestor. So what you need to to is outsource your composite key into a separate #Entity that uses the composite key as #Id.
#Entity
public class UserUX {
// for email OR name: email + name (concatenation of two values)
// for email AND name: email OR name
// (you would create two entities for each user, one with name and one with the email)
#Id
private String composite;
private Ref<User> user;
}
This entity is usable in a key query and therefor in a transaction.
Edit:
If, as commented on this answer you wish to 'restricts users with same email and name' you can use the UserUX entity as well. You would create one with the email and one with the name. I added code comments above.
This is from the python sdk but the concepts should translate to java
http://webapp-improved.appspot.com/_modules/webapp2_extras/appengine/auth/models.html#Unique
"""A model to store unique values.
The only purpose of this model is to "reserve" values that must be unique
within a given scope, as a workaround because datastore doesn't support
the concept of uniqueness for entity properties.
For example, suppose we have a model `User` with three properties that
must be unique across a given group: `username`, `auth_id` and `email`::
class User(model.Model):
username = model.StringProperty(required=True)
auth_id = model.StringProperty(required=True)
email = model.StringProperty(required=True)
To ensure property uniqueness when creating a new `User`, we first create
`Unique` records for those properties, and if everything goes well we can
save the new `User` record::
#classmethod
def create_user(cls, username, auth_id, email):
# Assemble the unique values for a given class and attribute scope.
uniques = [
'User.username.%s' % username,
'User.auth_id.%s' % auth_id,
'User.email.%s' % email,
]
# Create the unique username, auth_id and email.
success, existing = Unique.create_multi(uniques)
if success:
# The unique values were created, so we can save the user.
user = User(username=username, auth_id=auth_id, email=email)
user.put()
return user
else:
# At least one of the values is not unique.
# Make a list of the property names that failed.
props = [name.split('.', 2)[1] for name in uniques]
raise ValueError('Properties %r are not unique.' % props)
"""
im new in hibernate.
I would like to build a simple autentication / login system in java with hibernate.
So let's say, i have a class User
public class User {
private int id;
private String username;
private String passwordHash;
...
}
Now i have a DAO to store a new User, and to get all users (as a list). Now im wondering, if its possible to get a list of users without the passwordHash field (for security reason)?
It would be nice if it was a question of configuration.
An other idea would be to split the User class into
public class User {
private int id;
private String username;
...
}
public class UserWithPassword extends User {
private String passwordHash;
...
}
So i could use UserWithPassword to store a new user into the database and use
the User class to query the list of all users (without password).
Any other suggestion?
Your split class won't work because you have to link a class to Hibernate.
Your DAO doesn't have to return the class itself. You can write an HQL query such:
select username
from User
See?
Then your DAO would have a method like public Collection getUserNames()
you can use
java.util.List temp = hibernateTemplate.find("select u from user u ");
you can take all user from temp;
but if you want authenticate,you can use spring security,i suggest