Skip to content

Commit

Permalink
[JENKINS-29815] Add disabling of permission inheritance for Folders
Browse files Browse the repository at this point in the history
This adds the same tick-box to disable inheritance of global permissions
to Folders as already exists for Projects. Consistency!

Support for the Folder plugin seems to originally have been done by
copy-pasting in an only slightly modified version of an ancient copy of
the AuthorizationMatrixProperty.java file. This commit is as such very
similar to the patch Kohsuke once applied to the Project's AMP:
6411fff

Now, these two files are close to identical, and should be refactored
into one class in my opinion:

    src/main/java/hudson/security/AuthorizationMatrixProperty.java
    src/main/java/com/cloudbees/hudson/plugins/folder/properties/AuthorizationMatrixProperty.java
  • Loading branch information
Mathias Nyman authored and mathias-nyman committed May 12, 2017
1 parent 2ab009d commit 63d6897
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 2 deletions.
Expand Up @@ -80,6 +80,8 @@ public class AuthorizationMatrixProperty extends AbstractFolderProperty<Abstract

private Set<String> sids = new HashSet<String>();

private boolean blocksInheritance = false;

protected AuthorizationMatrixProperty() {
}

Expand Down Expand Up @@ -141,6 +143,10 @@ public AbstractFolderProperty<?> newInstance(StaplerRequest req, JSONObject form
return null;

AuthorizationMatrixProperty amp = new AuthorizationMatrixProperty();

// Disable inheritance, if so configured
amp.setBlocksInheritance(!formData.getJSONObject("blocksInheritance").isNullObject());

for (Map.Entry<String, Object> r : (Set<Map.Entry<String, Object>>) formData.getJSONObject("data").entrySet()) {
String sid = r.getKey();
if (r.getValue() instanceof JSONObject) {
Expand Down Expand Up @@ -205,6 +211,25 @@ public SidACL getACL() {
return acl;
}

/**
* Sets the flag to block inheritance
*
* @param blocksInheritance
*/
public void setBlocksInheritance(boolean blocksInheritance) {
this.blocksInheritance = blocksInheritance;
}

/**
* Returns true if the authorization matrix is configured to block
* inheritance from the parent.
*
* @return
*/
public boolean isBlocksInheritance() {
return this.blocksInheritance;
}

/**
* Checks if the given SID has the given permission.
*/
Expand Down Expand Up @@ -250,6 +275,12 @@ public void marshal(Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
AuthorizationMatrixProperty amp = (AuthorizationMatrixProperty) source;

if (amp.isBlocksInheritance()) {
writer.startNode("blocksInheritance");
writer.setValue("true");
writer.endNode();
}

for (Entry<Permission, Set<String>> e : amp.grantedPermissions
.entrySet()) {
String p = e.getKey().getId();
Expand All @@ -265,6 +296,20 @@ public Object unmarshal(HierarchicalStreamReader reader,
final UnmarshallingContext context) {
AuthorizationMatrixProperty as = new AuthorizationMatrixProperty();

String prop = reader.peekNextChild();

if (prop!=null && prop.equals("useProjectSecurity")) {
reader.moveDown();
reader.getValue(); // we used to use this but not any more.
reader.moveUp();
prop = reader.peekNextChild(); // We check the next field
}
if ("blocksInheritance".equals(prop)) {
reader.moveDown();
as.setBlocksInheritance("true".equals(reader.getValue()));
reader.moveUp();
}

while (reader.hasMoreChildren()) {
reader.moveDown();
try {
Expand Down
Expand Up @@ -95,7 +95,14 @@ public ACL getACL(AbstractItem item) {
if (item instanceof AbstractFolder) {
com.cloudbees.hudson.plugins.folder.properties.AuthorizationMatrixProperty p = (com.cloudbees.hudson.plugins.folder.properties.AuthorizationMatrixProperty) ((AbstractFolder) item).getProperties().get(com.cloudbees.hudson.plugins.folder.properties.AuthorizationMatrixProperty.class);
if (p != null) {
return inheritingACL(getACL(item.getParent()), p.getACL());
SidACL folderAcl = p.getACL();

if (!p.isBlocksInheritance()) {
final ACL parentAcl = getACL(item.getParent());
return inheritingACL(parentAcl, folderAcl);
} else {
return folderAcl;
}
}
}
}
Expand Down
Expand Up @@ -24,6 +24,12 @@ THE SOFTWARE.
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<f:optionalBlock name="useProjectSecurity" title="${%Enable project-based security}" checked="${instance!=null}">
<st:include class="hudson.security.GlobalMatrixAuthorizationStrategy" page="config.jelly"/>
<f:nested>
<table style="width:100%">
<f:optionalBlock field="blocksInheritance"
title="${%Block inheritance of global authorization matrix}" />
<st:include class="hudson.security.GlobalMatrixAuthorizationStrategy" page="config.jelly"/>
</table>
</f:nested>
</f:optionalBlock>
</j:jelly>
@@ -0,0 +1,9 @@
<div>
If checked, the global configuration matrix will not be inherited.
This allows you to configure a Folder that has a more strict access control list than the rest of the global permission set.
<br />
<br />
<b>WARNING</b>: because the parent ACL will not be inherited, it is possible to revoke your own configuration access accidentally.
If you enable this setting, please also remember to grant yourself or your group configuration access so that you do not lock yourself out of the Folder.
Otherwise the only ways to get back in will be to disable project-based security in global configuration, or manually edit the permissions list in the project's XML configuration.
</div>
Expand Up @@ -55,6 +55,9 @@ public class AuthorizationMatrixPropertyTest {

Folder f = r.jenkins.createProject(Folder.class, "d");
AuthorizationMatrixProperty amp = new AuthorizationMatrixProperty();

assertEquals(amp.isBlocksInheritance(), false); // inherit permissions by default

amp.add(Item.READ,"alice");
amp.add(Item.BUILD,"alice");
f.getProperties().add(amp);
Expand Down Expand Up @@ -86,4 +89,34 @@ public Object call() throws Exception {
}
});
}

@Test public void disabling_permission_inheritance_removes_global_permissions() throws Exception {
HudsonPrivateSecurityRealm realm = new HudsonPrivateSecurityRealm(false);
realm.createAccount("alice","alice");
realm.createAccount("bob","bob");
r.jenkins.setSecurityRealm(realm);

ProjectMatrixAuthorizationStrategy as = new ProjectMatrixAuthorizationStrategy();
r.jenkins.setAuthorizationStrategy(as);
as.add(Hudson.READ,"authenticated");

Folder f = r.jenkins.createProject(Folder.class, "d");
AuthorizationMatrixProperty amp = new AuthorizationMatrixProperty();
amp.setBlocksInheritance(true);
amp.add(Item.READ,"alice");
f.getProperties().add(amp);

final FreeStyleProject foo = f.createProject(FreeStyleProject.class, "foo");

JenkinsRule.WebClient wc = r.createWebClient().login("bob");
try {
wc.getPage(foo);
fail();
} catch (FailingHttpStatusCodeException e) {
assertEquals(404, e.getStatusCode());
}

wc = r.createWebClient().login("alice");
wc.getPage(foo); // this should succeed
}
}

0 comments on commit 63d6897

Please sign in to comment.