Skip to content

Commit

Permalink
Merge pull request #32 from daniel-beck/JENKINS-34616
Browse files Browse the repository at this point in the history
[JENKINS-34616] Add symbols, getter, and constructor for pipeline
  • Loading branch information
daniel-beck committed Sep 20, 2017
2 parents 67dffd1 + 2d84b61 commit a1e6e21
Show file tree
Hide file tree
Showing 9 changed files with 206 additions and 2 deletions.
20 changes: 20 additions & 0 deletions pom.xml
Expand Up @@ -40,6 +40,26 @@
<version>2.1.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-cps</artifactId>
<version>2.30</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-multibranch</artifactId>
<version>2.10</version>
<scope>test</scope>
</dependency>
<dependency>
<!-- requireUpperBoundDeps -->
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>structs</artifactId>
<version>1.6</version>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<repository>
Expand Down
34 changes: 34 additions & 0 deletions src/main/java/hudson/security/AuthorizationMatrixProperty.java
Expand Up @@ -34,12 +34,18 @@
import hudson.util.FormValidation;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
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.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.CheckForNull;
Expand All @@ -48,6 +54,7 @@
import net.sf.json.JSONObject;
import org.acegisecurity.acls.sid.PrincipalSid;
import org.acegisecurity.acls.sid.Sid;
import org.jenkinsci.Symbol;
import org.jenkinsci.plugins.matrixauth.AbstractAuthorizationPropertyConverter;
import org.jenkinsci.plugins.matrixauth.AuthorizationPropertyDescriptor;
import org.jenkinsci.plugins.matrixauth.AuthorizationProperty;
Expand All @@ -57,6 +64,8 @@
import org.jenkinsci.plugins.matrixauth.inheritance.InheritParentStrategy;
import org.jenkinsci.plugins.matrixauth.inheritance.InheritanceStrategy;
import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.verb.GET;
Expand Down Expand Up @@ -99,6 +108,29 @@ public AuthorizationMatrixProperty(Map<Permission, Set<String>> grantedPermissio
this.grantedPermissions.put(e.getKey(), new HashSet<>(e.getValue()));
}

@DataBoundConstructor
public AuthorizationMatrixProperty(List<String> permissions) {
for (String str : permissions) {
if (str != null) {
this.add(str);
}
}
}

public List<String> getPermissions() {
List<String> permissions = new ArrayList<>();

SortedMap<Permission, Set<String>> map = new TreeMap<>(Comparator.comparing(Permission::getId));
map.putAll(this.grantedPermissions);
for (Map.Entry<Permission, Set<String>> entry : map.entrySet()) {
String permission = entry.getKey().getId();
for (String sid : new TreeSet<>(entry.getValue())) {
permissions.add(permission + ":" + sid);
}
}
return permissions;
}

public Set<String> getGroups() {
return new HashSet<>(sids);
}
Expand Down Expand Up @@ -126,6 +158,7 @@ public void add(Permission p, String sid) {
}

@Extension
@Symbol("authorizationMatrix")
public static class DescriptorImpl extends JobPropertyDescriptor implements AuthorizationPropertyDescriptor<AuthorizationMatrixProperty> {

@Override
Expand Down Expand Up @@ -171,6 +204,7 @@ public SidACL getACL() {
return acl;
}

@DataBoundSetter
public void setInheritanceStrategy(InheritanceStrategy inheritanceStrategy) {
this.inheritanceStrategy = inheritanceStrategy;
}
Expand Down
Expand Up @@ -23,6 +23,7 @@
*/
package org.jenkinsci.plugins.matrixauth;

import hudson.model.Descriptor;
import hudson.security.GlobalMatrixAuthorizationStrategy;
import hudson.security.Permission;
import hudson.security.SecurityRealm;
Expand All @@ -37,6 +38,8 @@
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;

@Restricted(NoExternalUse.class)
public interface AuthorizationContainer {
Expand All @@ -59,6 +62,9 @@ public int compare(String o1, String o2) {
void add(Permission permission, String sid);
Map<Permission, Set<String>> getGrantedPermissions();

@SuppressWarnings("rawtypes")
Descriptor getDescriptor();

/**
* Works like {@link #add(Permission, String)} but takes both parameters
* from a single string of the form <tt>PERMISSIONID:sid</tt>
Expand All @@ -69,6 +75,12 @@ default void add(String shortForm) {
Permission p = Permission.fromId(shortForm.substring(0, idx));
if (p==null)
throw new IllegalArgumentException("Failed to parse '"+shortForm+"' --- no such permission");
String sid = shortForm.substring(idx + 1);
if (!p.isContainedBy(((AuthorizationContainerDescriptor<?>) getDescriptor()).getPermissionScope())) {
Logger.getLogger(AuthorizationContainer.class.getName()).log(Level.WARNING,
"Tried to add inapplicable permission " + p + " for " + sid + " in " + this + ", skipping");
return;
}
add(p, shortForm.substring(idx + 1));
}

Expand Down
Expand Up @@ -28,6 +28,7 @@
import hudson.security.AccessControlled;
import hudson.security.ProjectMatrixAuthorizationStrategy;
import jenkins.model.Jenkins;
import org.jenkinsci.Symbol;
import org.kohsuke.stapler.DataBoundConstructor;

import javax.annotation.Nonnull;
Expand All @@ -47,6 +48,7 @@ public ACL getEffectiveACL(ACL acl, AccessControlled subject) {
return ProjectMatrixAuthorizationStrategy.inheritingACL(Jenkins.getInstance().getAuthorizationStrategy().getRootACL(), acl);
}

@Symbol("inheritingGlobal")
@Extension
public static class DescriptorImpl extends InheritanceStrategyDescriptor {

Expand Down
Expand Up @@ -30,6 +30,7 @@
import hudson.security.AccessControlled;
import hudson.security.ProjectMatrixAuthorizationStrategy;
import jenkins.model.Jenkins;
import org.jenkinsci.Symbol;
import org.kohsuke.stapler.DataBoundConstructor;

import javax.annotation.Nonnull;
Expand Down Expand Up @@ -63,6 +64,7 @@ public ACL getEffectiveACL(ACL acl, AccessControlled subject) {
}
}

@Symbol("inheriting")
@Extension(ordinal = 100)
public static class DescriptorImpl extends InheritanceStrategyDescriptor {

Expand Down
Expand Up @@ -27,7 +27,6 @@
import hudson.model.Descriptor;
import jenkins.model.Jenkins;

import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.List;

Expand Down
Expand Up @@ -29,6 +29,7 @@
import hudson.security.Permission;
import jenkins.model.Jenkins;
import org.acegisecurity.Authentication;
import org.jenkinsci.Symbol;
import org.kohsuke.stapler.DataBoundConstructor;

import javax.annotation.Nonnull;
Expand Down Expand Up @@ -69,7 +70,8 @@ private boolean isUltimatelyImpliedByAdminister(Permission permission) {
}
};
}


@Symbol("nonInheriting")
@Extension(ordinal = -100)
public static class DescriptorImpl extends InheritanceStrategyDescriptor {

Expand Down
Expand Up @@ -34,22 +34,27 @@
import hudson.security.ACLContext;
import hudson.security.HudsonPrivateSecurityRealm;
import hudson.security.ProjectMatrixAuthorizationStrategy;
import java.util.logging.Level;

import jenkins.model.Jenkins;
import org.acegisecurity.AccessDeniedException;
import static org.junit.Assert.*;

import org.jenkinsci.plugins.matrixauth.AuthorizationContainer;
import org.junit.Assert;
import org.jenkinsci.plugins.matrixauth.inheritance.InheritParentStrategy;
import org.jenkinsci.plugins.matrixauth.inheritance.NonInheritingStrategy;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.LoggerRule;

public class AuthorizationMatrixPropertyTest {

@Rule public JenkinsRule r = new JenkinsRule();

@Rule public LoggerRule l = new LoggerRule();

@Test
public void ensureCreatorHasPermissions() throws Exception {
HudsonPrivateSecurityRealm realm = new HudsonPrivateSecurityRealm(false, false, null);
Expand Down Expand Up @@ -147,4 +152,15 @@ public void ensureCreatorHasPermissions() throws Exception {
wc = r.createWebClient().login("alice");
wc.getPage(foo); // this should succeed
}

@Test
public void inapplicablePermissionIsSkipped() throws Exception {
AuthorizationMatrixProperty property = new AuthorizationMatrixProperty();
l.record(AuthorizationContainer.class, Level.WARNING).capture(1);
property.add("hudson.model.Hudson.Administer:alice");
assertTrue(property.getGrantedPermissions().isEmpty());
assertTrue(l.getMessages().get(0).contains("Tried to add inapplicable permission"));
assertTrue(l.getMessages().get(0).contains("Administer"));
assertTrue(l.getMessages().get(0).contains("alice"));
}
}
117 changes: 117 additions & 0 deletions src/test/java/hudson/security/AuthorizationMatrixPropertyTest.java
@@ -0,0 +1,117 @@
package hudson.security;

import hudson.model.Item;
import hudson.scm.SCM;
import jenkins.model.Jenkins;
import org.jenkinsci.plugins.matrixauth.AuthorizationContainer;
import org.jenkinsci.plugins.matrixauth.inheritance.NonInheritingStrategy;
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
import org.jenkinsci.plugins.workflow.cps.SnippetizerTester;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.multibranch.JobPropertyStep;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.LoggerRule;

import java.util.Collections;
import java.util.logging.Level;

public class AuthorizationMatrixPropertyTest {

@Rule
public JenkinsRule j = new JenkinsRule();

@Rule
public LoggerRule l = new LoggerRule();

@Test
public void testSnippetizer() throws Exception {
AuthorizationMatrixProperty property = new AuthorizationMatrixProperty(Collections.emptyMap());
property.add(Item.CONFIGURE, "alice");
property.add(Item.READ, "bob");
property.add(Item.READ, "alice");
property.add(SCM.TAG, "bob"); // use this to test for JENKINS-17200 robustness
property.setInheritanceStrategy(new NonInheritingStrategy());
SnippetizerTester tester = new SnippetizerTester(j);
tester.assertRoundTrip(new JobPropertyStep(Collections.singletonList(property)),
"properties([authorizationMatrix(inheritanceStrategy: nonInheriting(), " +
"permissions: ['hudson.model.Item.Configure:alice', 'hudson.model.Item.Read:alice', 'hudson.model.Item.Read:bob', 'hudson.scm.SCM.Tag:bob'])])");

}

@Test
@Issue("JENKINS-46944")
public void testSnippetizerInapplicablePermission() throws Exception {
AuthorizationMatrixProperty property = new AuthorizationMatrixProperty(Collections.emptyMap());
l.record(AuthorizationContainer.class, Level.WARNING).capture(1);
property.add("hudson.model.Item.Configure:alice");
property.add("hudson.model.Item.Read:bob");
property.add("hudson.model.Item.Read:alice");
property.add("hudson.scm.SCM.Tag:bob"); // use this to test for JENKINS-17200 robustness
property.add("hudson.model.Hudson.Read:carol"); // the important line for this test, inapplicable permission

property.setInheritanceStrategy(new NonInheritingStrategy());

SnippetizerTester tester = new SnippetizerTester(j);
tester.assertRoundTrip(new JobPropertyStep(Collections.singletonList(property)),
"properties([authorizationMatrix(inheritanceStrategy: nonInheriting(), " +
"permissions: ['hudson.model.Item.Configure:alice', 'hudson.model.Item.Read:alice', 'hudson.model.Item.Read:bob', 'hudson.scm.SCM.Tag:bob'])])");

Assert.assertTrue(l.getMessages().get(0).contains("Tried to add inapplicable permission"));
Assert.assertTrue(l.getMessages().get(0).contains("Hudson,Read"));
Assert.assertTrue(l.getMessages().get(0).contains("carol"));
}

@Test
public void testPipelineReconfiguration() throws Exception {

HudsonPrivateSecurityRealm realm = new HudsonPrivateSecurityRealm(true, false, null);
realm.createAccount("alice", "alice");
realm.createAccount("bob", "bob");
realm.createAccount("carol", "carol");
j.jenkins.setSecurityRealm(realm);

ProjectMatrixAuthorizationStrategy strategy = new ProjectMatrixAuthorizationStrategy();
strategy.add(Jenkins.ADMINISTER, "alice");
strategy.add(Jenkins.READ, "bob");
strategy.add(Jenkins.READ, "carol");
strategy.add(Item.READ, "carol");

j.jenkins.setAuthorizationStrategy(strategy);

WorkflowJob project = j.createProject(WorkflowJob.class);

// bob cannot see the project due to lack of Item.Read
j.createWebClient().login("bob").assertFails(project.getUrl(), 404);

// but bob can discover the project and get a 403
strategy.add(Item.DISCOVER, "bob");
j.createWebClient().login("bob").assertFails(project.getUrl(), 403);

// alice OTOH is admin and can see it
j.createWebClient().login("alice").goTo(project.getUrl()); // succeeds

// carol can also see the project, she has global Item.Read
j.createWebClient().login("carol").goTo(project.getUrl());

project.setDefinition(new CpsFlowDefinition("properties([authorizationMatrix(inheritanceStrategy: nonInheriting(), " +
"permissions: ['hudson.model.Item.Read:bob', 'hudson.model.Item.Configure:bob', 'hudson.scm.SCM.Tag:bob'])])", true));
j.buildAndAssertSuccess(project);

// let's look ast the property
AuthorizationMatrixProperty property = project.getProperty(AuthorizationMatrixProperty.class);
Assert.assertTrue(property.getInheritanceStrategy() instanceof NonInheritingStrategy);
Assert.assertEquals(3, property.getGrantedPermissions().size());
Assert.assertEquals("bob", property.getGroups().toArray()[0]);

// now bob has access, including configure
j.createWebClient().login("bob").goTo(project.getUrl());
j.createWebClient().login("bob").goTo(project.getUrl() + "configure");

// and carol no longer has access due to non-inheriting strategy
j.createWebClient().login("carol").assertFails(project.getUrl(), 404);
}
}

0 comments on commit a1e6e21

Please sign in to comment.