Skip to content

Commit

Permalink
Merge pull request #9 from jenkinsci/case-sensitivity-support
Browse files Browse the repository at this point in the history
[JENKINS-23805] Add support for the security realm's provided case sensitivity
  • Loading branch information
stephenc committed Jun 27, 2017
2 parents ae9771e + e986879 commit 6ce42a1
Show file tree
Hide file tree
Showing 5 changed files with 297 additions and 35 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Expand Up @@ -37,7 +37,7 @@
<dependency> <!-- TODO JENKINS-33095 workaround -->
<groupId>org.jenkins-ci.plugins.icon-shim</groupId>
<artifactId>icon-shim</artifactId>
<version>2.0.3</version>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
Expand Down
97 changes: 73 additions & 24 deletions src/main/java/hudson/security/AuthorizationMatrixProperty.java
Expand Up @@ -23,45 +23,43 @@
*/
package hudson.security;

import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.Extension;
import hudson.diagnosis.OldDataMonitor;
import hudson.model.Item;
import hudson.model.Job;
import hudson.model.JobProperty;
import hudson.model.JobPropertyDescriptor;
import jenkins.model.Jenkins;
import hudson.Extension;
import hudson.util.FormValidation;
import hudson.util.RobustReflectionConverter;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Collections;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.io.IOException;

import javax.annotation.CheckForNull;
import javax.servlet.ServletException;
import jenkins.model.IdStrategy;
import jenkins.model.Jenkins;
import net.sf.json.JSONObject;

import org.acegisecurity.acls.sid.PrincipalSid;
import org.acegisecurity.acls.sid.Sid;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.AncestorInPath;

import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import javax.annotation.CheckForNull;

import javax.servlet.ServletException;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;

/**
* {@link JobProperty} to associate ACL for each project.
Expand Down Expand Up @@ -104,7 +102,7 @@ public Set<String> getGroups() {
* @return Always non-null.
*/
public List<String> getAllSIDs() {
Set<String> r = new HashSet<String>();
Set<String> r = new TreeSet<String>(new GlobalMatrixAuthorizationStrategy.IdStrategyComparator());
for (Set<String> set : grantedPermissions.values())
r.addAll(set);
r.remove("anonymous");
Expand Down Expand Up @@ -204,7 +202,7 @@ private final class AclImpl extends SidACL {
@SuppressFBWarnings(value = "NP_BOOLEAN_RETURN_NULL",
justification = "As designed, implements a third state for the ternary logic")
protected Boolean hasPermission(Sid sid, Permission p) {
if (AuthorizationMatrixProperty.this.hasPermission(toString(sid),p)) {
if (AuthorizationMatrixProperty.this.hasPermission(toString(sid),p,sid instanceof PrincipalSid)) {
return true;
}
return null;
Expand Down Expand Up @@ -238,20 +236,71 @@ public boolean isBlocksInheritance() {
* Checks if the given SID has the given permission.
*/
public boolean hasPermission(String sid, Permission p) {
final SecurityRealm securityRealm = Jenkins.getActiveInstance().getSecurityRealm();
final IdStrategy groupIdStrategy = securityRealm.getGroupIdStrategy();
final IdStrategy userIdStrategy = securityRealm.getUserIdStrategy();
for (; p != null; p = p.impliedBy) {
Set<String> set = grantedPermissions.get(p);
if (!p.getEnabled()) {
continue;
}
Set<String> set = grantedPermissions.get(p);
if (set != null && set.contains(sid))
return true;
if (set != null) {
for (String s: set) {
if (userIdStrategy.equals(s, sid) || groupIdStrategy.equals(s, sid)) {
return true;
}
}
}
}
return false;
}

/**
* Checks if the given SID has the given permission.
*/
public boolean hasPermission(String sid, Permission p, boolean principal) {
final SecurityRealm securityRealm = Jenkins.getActiveInstance().getSecurityRealm();
final IdStrategy strategy = principal ? securityRealm.getUserIdStrategy() : securityRealm.getGroupIdStrategy();
for (; p != null; p = p.impliedBy) {
if (!p.getEnabled()) {
continue;
}
Set<String> set = grantedPermissions.get(p);
if (set == null) {
continue;
}
if (set.contains(sid)) {
return true;
}
for (String s : set) {
if (strategy.equals(s, sid)) {
return true;
}
}
}
return false;
}

/**
* Checks if the permission is explicitly given, instead of implied through {@link Permission#impliedBy}.
*/
public boolean hasExplicitPermission(String sid, Permission p) {
Set<String> set = grantedPermissions.get(p);
return set != null && set.contains(sid);
if (set != null && p.getEnabled()) {
if (set.contains(sid))
return true;
final SecurityRealm securityRealm = Jenkins.getActiveInstance().getSecurityRealm();
final IdStrategy groupIdStrategy = securityRealm.getGroupIdStrategy();
final IdStrategy userIdStrategy = securityRealm.getUserIdStrategy();
for (String s: set) {
if (userIdStrategy.equals(s, sid) || groupIdStrategy.equals(s, sid)) {
return true;
}
}
}
return false;
}

/**
Expand Down
Expand Up @@ -32,6 +32,7 @@
import hudson.PluginManager;
import hudson.diagnosis.OldDataMonitor;
import hudson.model.Descriptor;
import jenkins.model.IdStrategy;
import jenkins.model.Jenkins;
import hudson.model.Item;
import hudson.util.FormValidation;
Expand All @@ -43,6 +44,7 @@
import hudson.model.User;
import net.sf.json.JSONObject;
import org.acegisecurity.AuthenticationException;
import org.acegisecurity.acls.sid.PrincipalSid;
import org.acegisecurity.userdetails.UsernameNotFoundException;
import org.acegisecurity.acls.sid.Sid;
import org.jenkinsci.plugins.matrixauth.Messages;
Expand All @@ -56,12 +58,14 @@
import javax.servlet.ServletException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.io.IOException;
Expand Down Expand Up @@ -135,6 +139,8 @@ public ACL getRootACL() {
}

public Set<String> getGroups() {
final TreeSet<String> sids = new TreeSet<String>(new IdStrategyComparator());
sids.addAll(this.sids);
return sids;
}

Expand Down Expand Up @@ -168,10 +174,50 @@ public boolean hasPermission(String sid, Permission p) {
if (!ENABLE_DANGEROUS_PERMISSIONS && DANGEROUS_PERMISSIONS.contains(p)) {
return hasPermission(sid, Jenkins.ADMINISTER);
}
for(; p!=null; p=p.impliedBy) {
final SecurityRealm securityRealm = Jenkins.getActiveInstance().getSecurityRealm();
final IdStrategy groupIdStrategy = securityRealm.getGroupIdStrategy();
final IdStrategy userIdStrategy = securityRealm.getUserIdStrategy();
for (; p != null; p = p.impliedBy) {
if (!p.getEnabled()) {
continue;
}
Set<String> set = grantedPermissions.get(p);
if (set == null) {
continue;
}
if (set.contains(sid)) {
return true;
}
for (String s : set) {
if (userIdStrategy.equals(s, sid) || groupIdStrategy.equals(s, sid)) {
return true;
}
}
}
return false;
}

/**
* Checks if the given SID has the given permission.
*/
public boolean hasPermission(String sid, Permission p, boolean principal) {
final SecurityRealm securityRealm = Jenkins.getActiveInstance().getSecurityRealm();
final IdStrategy strategy = principal ? securityRealm.getUserIdStrategy() : securityRealm.getGroupIdStrategy();
for (; p != null; p = p.impliedBy) {
if (!p.getEnabled()) {
continue;
}
Set<String> set = grantedPermissions.get(p);
if(set!=null && set.contains(sid) && p.getEnabled())
if (set != null && set.contains(sid)) {
return true;
}
if (set != null) {
for (String s : set) {
if (strategy.equals(s, sid)) {
return true;
}
}
}
}
return false;
}
Expand All @@ -181,7 +227,20 @@ public boolean hasPermission(String sid, Permission p) {
*/
public boolean hasExplicitPermission(String sid, Permission p) {
Set<String> set = grantedPermissions.get(p);
return set != null && set.contains(sid) && p.getEnabled();
if (set != null && p.getEnabled()) {
if (set.contains(sid)) {
return true;
}
final SecurityRealm securityRealm = Jenkins.getActiveInstance().getSecurityRealm();
final IdStrategy groupIdStrategy = securityRealm.getGroupIdStrategy();
final IdStrategy userIdStrategy = securityRealm.getUserIdStrategy();
for (String s : set) {
if (userIdStrategy.equals(s, sid) || groupIdStrategy.equals(s, sid)) {
return true;
}
}
}
return false;
}

boolean isAnyRelevantDangerousPermissionExplicitlyGranted() {
Expand Down Expand Up @@ -212,7 +271,7 @@ boolean isAnyRelevantDangerousPermissionExplicitlyGranted(String sid) {
* Always non-null.
*/
public List<String> getAllSIDs() {
Set<String> r = new HashSet<String>();
Set<String> r = new TreeSet<String>(new IdStrategyComparator());
for (Set<String> set : grantedPermissions.values())
r.addAll(set);
r.remove("anonymous");
Expand All @@ -222,12 +281,26 @@ public List<String> getAllSIDs() {
return Arrays.asList(data);
}

/*package*/ static class IdStrategyComparator implements Comparator<String> {
private final SecurityRealm securityRealm = Jenkins.getActiveInstance().getSecurityRealm();
private final IdStrategy groupIdStrategy = securityRealm.getGroupIdStrategy();
private final IdStrategy userIdStrategy = securityRealm.getUserIdStrategy();

public int compare(String o1, String o2) {
int r = userIdStrategy.compare(o1, o2);
if (r == 0) {
r = groupIdStrategy.compare(o1, o2);
}
return r;
}
}

private final class AclImpl extends SidACL {
@CheckForNull
@SuppressFBWarnings(value = "NP_BOOLEAN_RETURN_NULL",
justification = "As designed, implements a third state for the ternary logic")
protected Boolean hasPermission(Sid p, Permission permission) {
if(GlobalMatrixAuthorizationStrategy.this.hasPermission(toString(p),permission))
if(GlobalMatrixAuthorizationStrategy.this.hasPermission(toString(p),permission, p instanceof PrincipalSid))
return true;
return null;
}
Expand All @@ -246,15 +319,16 @@ public boolean canConvert(Class type) {
}

public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
final IdStrategyComparator comparator = new IdStrategyComparator();
GlobalMatrixAuthorizationStrategy strategy = (GlobalMatrixAuthorizationStrategy)source;

// Output in alphabetical order for readability.
SortedMap<Permission, Set<String>> sortedPermissions = new TreeMap<Permission, Set<String>>(Permission.ID_COMPARATOR);
sortedPermissions.putAll(strategy.grantedPermissions);
for (Entry<Permission, Set<String>> e : sortedPermissions.entrySet()) {
String p = e.getKey().getId();
List<String> sids = new ArrayList<String>(e.getValue());
Collections.sort(sids);
Set<String> sids = new TreeSet<String>(comparator);
sids.addAll(e.getValue());
for (String sid : sids) {
writer.startNode("permission");
writer.setValue(p+':'+sid);
Expand Down
Expand Up @@ -40,8 +40,8 @@
import org.acegisecurity.Authentication;
import org.jenkinsci.plugins.matrixauth.Messages;

import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;

/**
* {@link GlobalMatrixAuthorizationStrategy} plus per-project ACL.
Expand Down Expand Up @@ -111,7 +111,7 @@ public ACL getACL(AbstractItem item) {

@Override
public Set<String> getGroups() {
Set<String> r = new HashSet<String>();
Set<String> r = new TreeSet<String>(new IdStrategyComparator());
r.addAll(super.getGroups());
for (Job<?,?> j : Jenkins.getActiveInstance().getItems(Job.class)) {
AuthorizationMatrixProperty amp = j.getProperty(AuthorizationMatrixProperty.class);
Expand All @@ -138,7 +138,7 @@ public static class ConverterImpl extends GlobalMatrixAuthorizationStrategy.Conv
private RobustReflectionConverter ref;

public ConverterImpl(Mapper m) {
ref = new RobustReflectionConverter(m,new JVM().bestReflectionProvider());
ref = new RobustReflectionConverter(m,JVM.newReflectionProvider());
}

@Override
Expand Down

0 comments on commit 6ce42a1

Please sign in to comment.