Closed paul-ovchinnikov closed 5 years ago
We run into this issue at work, it's problematic for us and it seems quite simple to fix, is there any reason it was never addressed ? Would a pull request fixing it be accepted ?
Thanks !
Same issue here. I went around it by extending the JdbcUserDetailsManager. However then I run into issues with private validation functions: private void validateUserDetails(UserDetails user) and private void validateAuthorities(Collection<? extends GrantedAuthority> authorities). I would rather see them protected.
Thanks for the ping @123raoul123 If someone wants to send a PR (the changes must be passive) for this, we'd gladly accept it. If anyone wants to work on it, please claim it via the comments
Hello @rwinch, I would like to take this one
Thank you for volunteering @pvliss! The issue is all yours. Please let us know if you have any questions!
Hello @rwinch,
Thanks!!! I have already actually created a first draft commit at my fork although I do have some questions that I need your help with before I submit the PR:
enableLockingAttributes
) to JdbcUserDetailsManager
similar to the flags used by JdbcDaoImpl
. Is that ok with you or do you think we should take another approach (e.g. add a subclass)? Also no changes have been made to the default DB schema as I guess that client code is expected to do that manually?JdbcUserDetailsManagerConfigurer
and a new attribute to the XSD. Are these ok? My main concern is that I had to generate a new XSD.SecuirtyNamespaceHandler.matchesVersionInternal()
I made a change to allow for usage of 5.x version. Also the InMemoryXmlApplicationContext
is fixed to version 4.2, is that by purpose?Thanks for all your hard work!!!
Regards,
Thanks for putting this together @pvliss!
I think it might be easier if we specify the contract as if the column count is larger than 3, then it will get the accountNonExpired, credentialsNonExpired, accountNonLocked attributes. Then in the RowMapper
we can check the column count and if it is bigger than three use the additional properties.
Hello @rwinch,
Thanks for taking a look.
While your suggestion/solution requires no additional configuration I think that it is not safe as there might be cases where it fails at run-time as an app might already have more columns on the user table and at the same time not fully customize JdbcUserDetailsManager
. At least I did have some extra columns at that table at my last application (e.g. user prefs stored as JSON text). So in case we rely on checking the schema instead of using a flag then I think it would be best to check for actual columns instead of a simple column count. What do you think?
What about other users? @paul-ovchinnikov, @avaud , @123raoul123 any comments
Regards,
I don't understand why you would retrieve the user prefs in that context since it won't be mapped anyway. For the sake of simplicity I would have gone with @rwinch solution, but I'm not really an expert in Spring so I might miss a point here.
Hello @avaud
You misunderstood me or maybe I was not clear enough.
I did not say I would retrieve the user prefs in that context, its obviously not needed. What I meant was that I had more columns added in the users
DB table for other custom logic of my app related to the user entity and I did not want to have an extra table. Hence, in such a scenario, doing a simple column count on the table would assume that the extra columns needed by Spring Security would be available while in reality they would not be there and result in an error at runtime.
This could of-course be caught in your test run but that would be yet another assumption.
I think that checking the schema with the help of DatabaseMetaData#getColumns() would be a safer solution. Ofcourse will be checking just the existence of a single column for simplicity and once at startup to avoid possible performance issues.
Will create a new POC soon
Regards,
Hello,
I get that but if you use ResultSetMetaData: ` ResultSetMetaData rsmd = rs.getMetaData();
int columnsNumber = rsmd.getColumnCount(); ` Then you would get the number of columns selected in your query, not the number in the table. It could fail if someone has a "select * " as a query but I think it's acceptable since it's not really a good practice to do that.
Hey, thanks for helping out.
Yes, you are right but in order to use ResultSetMetaData
I would need to execute the proper select query first. So if there's no flag then I would have to use a select * from users
in order to find out the column count. Using a query with predefined columns e.g. select username,password,enabled, acc_locked, acc_expired, creds_expired from users where username = ?
would cause an error and never reach the mapping phase anyways.
Am I missing something obvious here?
I don't get why it would cause an error since it's written by a dev who should know the columns in the DB. But since @rwinch seemed to have an idea on how to do that, I suggest you wait for his advice. He will probably know better than me.
So I am guessing you are talking about the case where someone also overrides the default queries. In that case, yes it would not be such a problem.
I guess you are right, I will wait for @rwinch's advice.
Nevertheless, I have taken another shot at it using DatabaseMetaData
: https://github.com/pvliss/spring-security/commit/3f23fb54a1c90d0eab7fd28b52d3748bfbc096ea
Well I did not know there was default queries... That explains the confusion.
I'm not sure I understand the concern. Spring Security does not have select *
by default. It specifically lists out the columns it wants in the order it wants them.
The JdbcUserDetailsManager
and JdbcDaoImpl
both already have a requirement on the order the columns are returned, so users who customize it with select *
are already asking for trouble. Optionally processing additional columns being returned in the query seems to fit in with how it works already quite nicely.
Hello Rob. Thanks for your advice. So if I understood correctly we should not care about the default query and only have the user define custom queries in order to enable these attributes in a pre-defined order. Is that right?
Please see my latest commit using that approach and let me know if that is what you had in mind.
@pvliss Thanks for the fast turnaround. Sorry I wasn't clear about leaving the default query the same. We need to do this for passivity. Only user's needing the new functionality would leverage this by changing the query.
Yes this is what I had in mind. A few pieces of feedback:
@rwinch No problem. Glad we are on the same page now. Should have asked for more clarifications before going on to implementing things.
Now to answer your points:
addAccLockingColumns
to setUpAccLockingColumns
then I have added this method in my second commit and is shared by both createUserInsertsCorrectDataWithLocking
and updateUserChangesDataCorrectlyAndClearsCacheWithLocking
which are also new test methods added by me. Maybe when I squash the commits this will become easier to see.update users set password = ?, enabled = ?, acc_locked=?, acc_expired=?, creds_expired=? where username = ?
so I am forced to set username as the 6th parameter. Again , I am missing something here?@rwinch So I squashed the commits, hopefully the changes are more easier to spot now.
Regarding your last point
In general, I expect the only changes in the main classes is to have a check for the column count.
Does that mean I should also revert my change from anonymous classes to lambdas as well or are you fine with them?
So I squashed the commits, hopefully the changes are more easier to spot now.
Thanks!
Does that mean I should also revert my change from anonymous classes to lambdas as well or are you fine with them?
I think they are ok changes, but not as a part of this issue. We want to keep changes isolated to the ticket in the commit so that we can track the changes. By keeping changes isolated we make thinks like backporting and tracking down issues a lot easier.
Do you mind submitting this as a PR so it is easier to provide feedback? Commenting on the commit's isn't ideal since the feedback is lost if your branch is removed, forced push occurs, etc.
We want to keep changes isolated to the ticket in the commit so that we can track the changes. By keeping changes isolated we make thinks like backporting and tracking down issues a lot easier. Do you mind submitting this as a PR so it is easier to provide feedback?
Yes, I understand. I have reverted all lambda changes, squashed again and submitted the PR.
I think they are ok changes, but not as a part of this issue.
Should I create an issue just for these JDBC related classes or a more generic one for the whole project? IDEA reports 54 such cases and 90 incl. tests.
Yes, I understand. I have reverted all lambda changes, squashed again and submitted the PR.
Thank you.
Should I create an issue just for these JDBC related classes or a more generic one for the whole project? IDEA reports 54 such cases and 90 incl. tests.
Thank you. Yes that would be good. It might help (so we don't need to rebase) to wait till these changes get merged first.
Thank you. Yes that would be good. It might help (so we don't need to rebase) to wait till these changes get merged first.
Just to avoid any misunderstandings I will ask again.
A generic one(i.e. convert anonymous classes to lambdas throughout the whole project) or a JDBC code specific one?
Summary
JdbcUserDetailsManager
has excellent native implementation CRUD and utility methods to handle User state (groups, authorities, roles and so on)org.springframework.security.core.userdetails.User
andorg.springframework.security.core.userdetails.UserDetails
also determines additional states:Actual Behavior
At the moment
org.springframework.security.provisioning.JdbcUserDetailsManager
andorg.springframework.security.core.userdetails.jdbc.JdbcDaoImpl
only can handle enabled state of theUser
On the other hand oauth2 Spring implementation is able to handle this states already. But security framework returns default hardcoded values for accountNonExpired, accountNonLocked, credentialsNonExpired (true, true, true)
Expected Behavior
Please extend
org.springframework.security.provisioning.JdbcUserDetailsManager
andorg.springframework.security.core.userdetails.jdbc.JdbcDaoImpl
to support allUserDetails
andUser
states. And make private methods to be public:Version
Spring Framework: 4.3.8.RELEASE Spring Security Framework: 4.2.3.RELEASE