Closed Andreas-PPI closed 1 year ago
Thanks for reporting this, @Andreas-PPI!
It looks as though the change in 63607ee213c74d84ef52167ac08a2f67eb0b2f89 changed the way attribute values are searched/returned and subsequently mapped, resulting in the possible NPE. Would you be interested in submitting a PR to fix the issue?
I believe the authorityMapper
would need to be changed to return null
in this case, and the for-loop below would skip null
return values.
@sjohnr can I work on this :) ?
Sure @dkodippily! Just checking, @Andreas-PPI that you're not already working on it?
Hi @sjohnr , hi @dkodippily , I checked out the source code but didn't change anything. I wanted to start with it this morning, but if you'd like, you can take it over.
Thanks @Andreas-PPI , have you done anything on this yet, if not I'll pick this.
Hi @dkodippily , no I've done nothing until now. I'm looking forward for your changes.
@Andreas-PPI @sjohnr , managed to run some code and reproduce the issue, but I'm facing some issues building the latest master, Execution failed for task ':checkstyleNohttp', companing about documents with http, trying to get my build right at the moment.
Thanks for reporting this, @Andreas-PPI!
It looks as though the change in 63607ee changed the way attribute values are searched/returned and subsequently mapped, resulting in the possible NPE. Would you be interested in submitting a PR to fix the issue?
I believe the
authorityMapper
would need to be changed to returnnull
in this case, and the for-loop below would skipnull
return values.
Returning null from the authorityMapper is still results in a NPE, how about filtering it from the method calling point as below
for (Map<String, List<String>> role : userRoles) { if(role.keySet().contains(this.groupRoleAttribute)) { authorities.add(this.authorityMapper.apply(role)); } }
Hi @dkodippily , I think this will work. It would also have the advantage that the solution would work for other similar implementations of authorityMapper.
Hi @dkodippily, @Andreas-PPI!
Returning null from the authorityMapper is still results in a NPE
Did you see the part of my comment that says "and the for-loop below would skip null
return values."? If so, I'm not sure what you're referring to that would cause an NPE?
how about filtering it from the method calling point as below
I believe this would be a breaking change, as you would be changing what gets passed into the mapper. Do you agree? If so, I believe allowing null
to be returned from the mapper would be the safer change and wouldn't surprise users who have provided a custom authorityMapper
.
It would also have the advantage that the solution would work for other similar implementations of
authorityMapper
.
See comment above. I think that would be up to the custom mapper to handle. Since we're providing a Map<String, List<String>>
, the calling code probably shouldn't try to predict what the mapper will do with that collection. Do you agree? I do see your point about possibly benefiting custom mappers though, and I agree it would be nice to prevent a null-pointer in custom code if it weren't a breaking change.
Hi @sjohnr I Agree. , @Andreas-PPI
Allowing null returns from the authorityMapper is a much safer way of doing it, for the two scenarios the group-role-attribute is defined without a value assignment or completely omitted i can check and return null from the authorityMapper as below.
However through the calling method Set<GrantedAuthority> getGroupMembershipRoles
the returning Set will allow a single null value, and I can remove it as shown below.
Hi @sjohnr , I agree with your points. Whether that meant a breaking change, I wasn't sure. But I can understand your argument.
Hi @dkodippily , I agree with your first change, so that authorityMapper
returns null
for the two scenarios "undefined or empty group-role-attribute".
I would omit the second change. This way the caller can decide for himself what to do with a GrantedAuthority = null
. In case of doubt it can be removed there itself.
Or is there a problem I do not see with the null
entry?
Hi @dkodippily
i can check and return null from the authorityMapper as below.
If you wouldn't mind, please share code samples instead of screenshots. I can add notes on a PR but I'd recommend calling record.get
only once and using CollectionUtils
and StringUtils
to check the values respectively. Something like:
this.authorityMapper = (record) -> {
List<String> roles = record.get(this.groupRoleAttribute);
if (CollectionUtils.isEmpty(roles)) {
return null;
}
String role = roles.get(0);
if (!StringUtils.hasLength(role)) {
return null;
}
if (this.convertToUpperCase) {
role = role.toUpperCase();
}
return new SimpleGrantedAuthority(this.rolePrefix + role);
};
I can remove it as shown below.
I think it would be better not to allow a null to be added at all.
Hi @Andreas-PPI
Or is there a problem I do not see with the
null
entry?
I believe this would be a breaking change as well, is that right?
Hi @sjohnr ,
I believe this would be a breaking change as well, is that right?
Compared to throwing an NPE, both behaviors are breaking changes. ;-)
I thought it is better to have a GrantedAuthority
entry for each entry in the LDAP as well. I had overlooked that these are in a Set
, same roles are mapped just like null
only as 1 entry, so that the number can or will deviate anyway. In this case it is probably better not to supply the entry null
, because probably nothing useful can be done with it anyway.
Hi @sjohnr Thanks for the feedback , @Andreas-PPI , yes a null would not make any sense, also it will again throw a NPE when mapping the authority list to a Set through AuthorityUtils.authorityListToSet
, this method only asserts if the input list is null or not , not the elements. As @sjohnr suggested better not to allow null at all.
for (Map<String, List<String>> role : userRoles) {
GrantedAuthority authority = this.authorityMapper.apply(role);
Assert.notNull(authority, "authority cannot be null");
authorities.add(authority);
}
@sjohnr for testing , is it better to add a separate ldif file with "undefined or empty group-role-attribute" and load it in a new test class rather adding new entries to the test-server.ldif ?
Hi @dkodippily , I hava a question to your code sample. authorityMapper
can return null
, see code sample from @sjohnr . In this case authority
is null
. Then Assert.notNull()
will throw an IlegalArgumentException
. This is not the desired behavior. Do you agree?
Why throws AuthorityUtils.authorityListToSet()
a NPE? This method return a HashSet
, which implementation allows null
entries - more exactly: one null
entry.
Regardless, we agreed that getGroupMembershipRoles()
should not contain null
entries in its returned Set
.
Hi @dkodippily. Sorry if I was unclear. When I said
I think it would be better not to allow a null to be added at all.
I meant add a null check and do not add the null
to the collection (e.g. skip the element). At this point, I think I'd prefer if you submitted a PR and we can review the code more easily there. What do you think?
Hi @Andreas-PPI , yes we don't need to call Assert.notNull()
, this is how I tested the code, simply can check for null before calling authorities.add(authority);
inside getGroupMembershipRoles()
.
True, since we agreed getGroupMembershipRoles()
should not contain null there will be no NPEs, but I was answering your previous question, If we were to let that single null to go through AuthorityUtils.authorityListToSet()
will break with a NPE due to below call, it is extracting the String role value from each GrantedAuthority
. I'll raise a PR, lets keep improving.
for (GrantedAuthority authority : userAuthorities) {
set.add(authority.getAuthority());
}
Also @dkodippily:
is it better to add a separate ldif file with "undefined or empty group-role-attribute" and load it in a new test class rather adding new entries to the test-server.ldif ?
Yes, feel free to add a new ldif file for this case. I think that would be the preference unless an existing test needs to be changed for some reason. I have not reviewed it to determine whether it does or not.
Hi @dkodippily. Sorry if I was unclear. When I said
I think it would be better not to allow a null to be added at all.
I meant add a null check and do not add the
null
to the collection (e.g. skip the element). At this point, I think I'd prefer if you submitted a PR and we can review the code more easily there. What do you think?
Hi @sjohnr , Yes its high time now :)
Hi @sjohnr , @Andreas-PPI PR is raised #12258
Hi @sjohnr, appreciate any feedback on this PR #12258
@dkodippily thanks for the PR! I will look into it at the first opportunity. Just a heads up, I'm currently working hard on content for the SpringOne virtual conference to be recorded soon, so reviews and responses may be delayed.
@dkodippily thanks for the PR! I will look into it at the first opportunity. Just a heads up, I'm currently working hard on content for the SpringOne virtual conference to be recorded soon, so reviews and responses may be delayed.
Thanks and good luck with the conference.
Describe the bug I want to evaluate the LDAP entries of a user to derive the role of the user. The attribute I have chosen is 'description' of the groups the user is a member of. The corresponding configuration in the security.xml is as follows:
If 'description' is not assigned for one of the entries, a NullPointerException occurs in the class DefaultLdapAuthoritiesPopulator. I think this happens in line 172:
String role = record.get(this.groupRoleAttribute).get(0);
In this caserecord.get(this.groupRoleAttribute)
returns null.I'm using spring-security-ldap:5.7.3 - the newest version which works with Java 8. Previously I used V3.1.1, no NullPointerException was thrown there. Instead such entries were skipped, which I think is the better behavior.
To Reproduce Add a group without description to your LDAP-groups or remove description from existing group.
Expected behavior Unspecified attribute values should be treated as empty strings. Alternatively, the affected entries can be omitted so that DefaultLdapAuthoritiesPopulator.getGrantedAuthorities() returns fewer entries than are present in the LDAP.
Stack trace