New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[JENKINS-34545] - Support case insensitive user IDs in roles #43
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And it would be a breaking change... I tried to do something similar at some point, bu it appeared that there are Jenkins users who really rely on the current behavior.
I would be fine if the plugin becomes case-insensitive by default, but there should be an option to restore the original behavior
OK, I'll look into that. |
bldr.append(name); | ||
bldr.append(':'); | ||
bldr.append(pattern); | ||
return bldr.toString(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
with String.format() you can do it in one line
Maybe it makes sense to move the setting to the common Authorization Strategy configuration screen ( No strong opinion tho, it is a wider problem than this PR |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After some consideration, I believe that using standard data binding is required here. JCasC 1.0 has been released recently, and I do not want to introduce compatibility issues
@@ -78,8 +78,10 @@ | |||
.expireAfterWrite(Settings.USER_DETAILS_CACHE_EXPIRATION_TIME_SEC, TimeUnit.SECONDS) | |||
.build(); | |||
|
|||
private final transient boolean caseInsensitiveUser; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure what is the intention here. With the current logic the flag will be always false after the restart, and adding new roles from API won't behave as expected
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The real configuration variable is in RoleBasedAuthorizationStrategy. That one is currently marshed/unmarshed explicitly in that RoleBasedAuthorizationStrategy.ConverterImpl. I just followed what was already there. Does the ConvererImpl entirely need to be converted or do you want the new variable managed elsewhere?
This RoleMap.caseInsensitiveUser is a copy needed to create the TreeSet when RoleMap.addRole is called. I made it transient because I didn't want it stored in config.xml and final to show that it was not the config.xml variable. It loads correctly at startup from config.xml, but I need to recreate all the TreeSets when it's changed realtime.
config.xml
class="com.michelin.cio.hudson.plugins.rolestrategy.RoleBasedAuthorizationStrategy">
<caseInsensitiveUser>true</caseInsensitiveUser>
new variable placement on Configure Global Security page.
Why not integrate with case sensitivity of the chosen security realm? Or what am I missing? |
reload grantedRoles when caseinsensitive changes
[maven-release-plugin] copy for tag role-strategy-2.9.0
@oleg-nenashev Any plans to integrate this.. |
No short-term plans. If somebody wants to contribute, happy to share my
vision
…On Sat, Nov 10, 2018, 11:55 Karol Lassak ***@***.*** wrote:
@oleg-nenashev <https://github.com/oleg-nenashev> Any plans to integrate
this..
Its really hard to explain users to login using always the same case..
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#43 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AC3IoDHhx1Rp376vU08IyFaD5VKwVYOfks5utrCogaJpZM4V7CNT>
.
|
Just an update. My original fix was two lines lines of code. master...seanmceligot:case-insensitive-2.9.0 I started to add an configuration option turn it on and off as requested for legacy users, but my change wasn't compatible with configuration as code. I'd need to delve into the CASC framework if I wanted to continue. |
…e-strategy-plugin into caseinsensitive
…lugin into case-insensitive-2.11
@seanmceligot can we expect any progress on this issue in future. |
Now should be a good time since configuration as code was merged. Let me look at adding the configuration parameter again and make sure it's CASC compatible. |
@seanmceligot thanks that helps us a lot. |
Thanks @seanmceligot ! It would be really great to get it over the line |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not much to add other than there are several code formatting issues and what seems to be excessive logging. hasPermission
is called quite often and to prevent polluting logs it might make sense to log it at a finer level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There seems to be quite a big performance penalty of this change apart from formatting issues.
@@ -141,7 +158,7 @@ private boolean hasPermission(String sid, Permission permission, RoleType roleTy | |||
new RoleWalker() { | |||
public void perform(Role current) { | |||
if (current.hasAnyPermission(permissions)) { | |||
if (grantedRoles.get(current).contains(sid)) { | |||
if (hasSid( grantedRoles.get(current), userIdStrategy, sid)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
contains()
in a set is a relatively fast operation. Now, you have to traverse the entire set to see if the role actually contains the sid.
The benchmarks suggest the same:
The results were compared to the last successful build of master (4efef4f).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I can't use Set because it's optimized at creation time time but IDStrategy as a configuration can be changed anytime. I suppose I could use a case insensitive Set and then verify it using the correct IDStrategy at lookup time. Or, rebuild the Set every time the configuration changes, but that's a but trickier to get right.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rebuilding set would be a good approach IMO.
I would be fine if the entire RoleStrategy
object gets rebuilt on a configuration change
@@ -127,12 +141,15 @@ public RoleMap(@Nonnull SortedMap<Role,Set<String>> grantedRoles) { | |||
public static void invalidateDangerousPermissions() { | |||
PermissionHelper.DANGEROUS_PERMISSIONS.forEach(implyingPermissionCache::remove); | |||
} | |||
|
|||
private boolean hasSid(Set<String> roleAssignements, IdStrategy userIdStrategy, String sid) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo here: roleAssignements
→ roleAssignments
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed. Thank you. seanmceligot@3be3e3b
@@ -1,6 +1,7 @@ | |||
jenkins: | |||
authorizationStrategy: | |||
roleBased: | |||
userIdStrategyChoice: DEFAULT |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we have a test case where the userIdStrategyChoice
is not present?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should. Also, it is odd that all tests require "DEFAULT" and none really tests values set
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @seanmceligot ! It is getting closer. but ultimately we need a performance degradation issue solved. Also, would be nice to get new autotests, Javadoc and formatting fixes
agentRoles.invalidateCache(); | ||
itemRoles.invalidateCache(); | ||
} | ||
public void setUserIdStrategyChoiceStr(String userIdStrategyChoice) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest not making such public method.
public void setUserIdStrategyChoiceStr(String userIdStrategyChoice) { | |
@Restricted(NoExternalUse.class) | |
public void setUserIdStrategyChoiceStr(String userIdStrategyChoice) { |
} | ||
public void setUserIdStrategyChoiceStr(String userIdStrategyChoice) { | ||
LOGGER.info("setUserIdStrategyChoice"+ userIdStrategyChoice); | ||
setUserIdStrategyChoice( UserIdStrategyChoice.valueOf(userIdStrategyChoice) ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if a non-existent value is passed. It will fail here, right?
@@ -680,6 +757,7 @@ public void doRolesSubmit(StaplerRequest req, StaplerResponse rsp) throws Unsupp | |||
|
|||
req.setCharacterEncoding("UTF-8"); | |||
JSONObject json = req.getSubmittedForm(); | |||
LOGGER.info("doRolesSubmit: "+json.toString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated. If you need it, please use FINEST
level or lower
@@ -696,6 +774,7 @@ public void doAssignSubmit(StaplerRequest req, StaplerResponse rsp) throws Unsup | |||
|
|||
req.setCharacterEncoding("UTF-8"); | |||
JSONObject json = req.getSubmittedForm(); | |||
LOGGER.info("doAssignSubmit: "+json.toString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same
@@ -141,7 +158,7 @@ private boolean hasPermission(String sid, Permission permission, RoleType roleTy | |||
new RoleWalker() { | |||
public void perform(Role current) { | |||
if (current.hasAnyPermission(permissions)) { | |||
if (grantedRoles.get(current).contains(sid)) { | |||
if (hasSid( grantedRoles.get(current), userIdStrategy, sid)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rebuilding set would be a good approach IMO.
I would be fine if the entire RoleStrategy
object gets rebuilt on a configuration change
@@ -33,6 +36,8 @@ | |||
@Restricted({NoExternalUse.class}) | |||
public class RoleBasedAuthorizationStrategyConfigurator extends BaseConfigurator<RoleBasedAuthorizationStrategy> { | |||
|
|||
private static final Logger LOGGER = Logger.getLogger(RoleBasedAuthorizationStrategyConfigurator.class.getName()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tabs vs. spaces
private static final Logger LOGGER = Logger.getLogger(RoleBasedAuthorizationStrategyConfigurator.class.getName()); | |
private static final Logger LOGGER = Logger.getLogger(RoleBasedAuthorizationStrategyConfigurator.class.getName()); |
@@ -7,8 +7,7 @@ | |||
import hudson.model.FreeStyleProject; | |||
import hudson.model.Item; | |||
import hudson.model.User; | |||
import hudson.security.AuthorizationStrategy; | |||
import io.jenkins.plugins.casc.ConfigurationContext; | |||
import hudson.security.AuthorizationStrategy; import io.jenkins.plugins.casc.ConfigurationContext; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import hudson.security.AuthorizationStrategy; import io.jenkins.plugins.casc.ConfigurationContext; | |
import hudson.security.AuthorizationStrategy; | |
import io.jenkins.plugins.casc.ConfigurationContext; |
@@ -56,7 +56,7 @@ | |||
<td class="left-most">${title}</td> | |||
<j:forEach var="r" items="${it.strategy.getGrantedRoles(attrs.type)}"> | |||
<td width="*"> | |||
<f:checkbox name="[${r.key.name}]" checked="${r.value.contains(attrs.sid)}"/> | |||
<f:checkbox name="[${r.key.name}]" checked="${r.value.contains(attrs.sid)}"/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Revert it
<f:checkbox name="[${r.key.name}]" checked="${r.value.contains(attrs.sid)}"/> | |
<f:checkbox name="[${r.key.name}]" checked="${r.value.contains(attrs.sid)}"/> |
@@ -1,6 +1,7 @@ | |||
jenkins: | |||
authorizationStrategy: | |||
roleBased: | |||
userIdStrategyChoice: DEFAULT |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should. Also, it is odd that all tests require "DEFAULT" and none really tests values set
@@ -127,12 +141,15 @@ public RoleMap(@Nonnull SortedMap<Role,Set<String>> grantedRoles) { | |||
public static void invalidateDangerousPermissions() { | |||
PermissionHelper.DANGEROUS_PERMISSIONS.forEach(implyingPermissionCache::remove); | |||
} | |||
|
|||
private boolean hasSid(Set<String> roleAssignments, IdStrategy userIdStrategy, String sid) { | |||
return roleAssignments.stream().anyMatch( rolesid -> userIdStrategy.compare(rolesid, sid) == 0 ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was going through how Matrix Authorization plugin handles it. Looks to me that this check is incomplete. Since these sids can be group names too and there exists SecurityRealm#getGroupIdStrategy()
, you will need to take care of that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will look that at. Good catch.
Just check if there's any plan to release this feature? |
This should fix JENKINS-34545 and related issues. It allows case insensitive matching to user/group values for Global and Item roles in Assign Roles.