提交 2a34cdaf 编写于 作者: D dty

[HUDSON-3677] First pass at providing read-only access to project configuration.

Defined a new permission, ExtendedRead, which can be enabled by specifying true
for the system property hudson.security.ExtendedReadPermission. Granting job
configuration permissions implies this permission.

	main/core/src/main/java/model/Item.java

Added plumbing for dynamic enabling and disabling of permissions at runtime.
This will be the basis for future UI based toggling of the availability of a
permission.

	main/core/src/main/java/hudson/security/Permission.java

Add the enabled state of a permission to the decision about whether a principal
has it or not. Only show a permission when it's enabled.

	main/core/src/main/java/hudson/security/GlobalMatrixAuthorizationStrategy.java

Allow access to config.xml when ExtendedRead is granted.

	main/core/src/main/java/hudson/model/Job.java

Don't show the save button if the user only has ExtendedRead.

	main/core/src/main/resources/hudson/model/Job/configure.jelly

Change the text of the Configure link in the side panel based on whether a user
has Configure or ExtendedRead permissions.

	main/core/src/main/resources/hudson/model/AbstractProject/sidepanel.jelly



git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@21626 71c3de6d-444a-0410-be80-ed276b4c234a
上级 75ec5aef
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Yahoo! Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
......@@ -196,6 +196,7 @@ public interface Item extends PersistenceRoot, SearchableModelObject, AccessCont
public static final Permission DELETE = new Permission(PERMISSIONS,"Delete", Permission.DELETE);
public static final Permission CONFIGURE = new Permission(PERMISSIONS,"Configure", Permission.CONFIGURE);
public static final Permission READ = new Permission(PERMISSIONS,"Read", Permission.READ);
public static final Permission EXTENDED_READ = new Permission(PERMISSIONS,"ExtendedRead", Messages._AbstractProject_ExtendedReadPermission_Description(), CONFIGURE, Boolean.getBoolean("hudson.security.ExtendedReadPermission"));
public static final Permission BUILD = new Permission(PERMISSIONS, "Build", Messages._AbstractProject_BuildPermission_Description(), Permission.UPDATE);
public static final Permission WORKSPACE = new Permission(PERMISSIONS, "Workspace", Messages._AbstractProject_WorkspacePermission_Description(), Permission.READ);
}
......@@ -977,7 +977,7 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
@WebMethod(name = "config.xml")
public void doConfigDotXml(StaplerRequest req, StaplerResponse rsp)
throws IOException {
checkPermission(CONFIGURE);
checkPermission(EXTENDED_READ);
if (req.getMethod().equals("GET")) {
// read
......
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Yahoo! Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
......@@ -144,7 +144,7 @@ public class GlobalMatrixAuthorizationStrategy extends AuthorizationStrategy {
public boolean hasPermission(String sid, Permission p) {
for(; p!=null; p=p.impliedBy) {
Set<String> set = grantedPermissions.get(p);
if(set!=null && set.contains(sid))
if(set!=null && set.contains(sid) && p.getEnabled())
return true;
}
return false;
......@@ -155,7 +155,7 @@ public class GlobalMatrixAuthorizationStrategy extends AuthorizationStrategy {
*/
public boolean hasExplicitPermission(String sid, Permission p) {
Set<String> set = grantedPermissions.get(p);
return set != null && set.contains(sid);
return set != null && set.contains(sid) && p.getEnabled();
}
/**
......@@ -266,7 +266,7 @@ public class GlobalMatrixAuthorizationStrategy extends AuthorizationStrategy {
}
public boolean showPermission(Permission p) {
return true;
return p.getEnabled();
}
public FormValidation doCheckName(@QueryParameter String value ) throws IOException, ServletException {
......
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Yahoo! Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
......@@ -82,6 +82,18 @@ public final class Permission {
*/
public final Permission impliedBy;
/**
* Whether this permission is available for use.
*
* <p>
* This allows us to dynamically enable or disable the visibility of
* permissions, so administrators can control the complexitity of their
* permission matrix.
*
* @since 1.325
*/
public boolean enabled;
/**
* Defines a new permission.
*
......@@ -106,7 +118,7 @@ public final class Permission {
* @param impliedBy
* See {@link #impliedBy}.
*/
public Permission(PermissionGroup group, String name, Localizable description, Permission impliedBy) {
public Permission(PermissionGroup group, String name, Localizable description, Permission impliedBy, boolean enable) {
if(!JSONUtils.isJavaIdentifier(name))
throw new IllegalArgumentException(name+" is not a Java identifier");
this.owner = group.owner;
......@@ -114,11 +126,16 @@ public final class Permission {
this.name = name;
this.description = description;
this.impliedBy = impliedBy;
this.enabled = enable;
group.add(this);
ALL.add(this);
}
public Permission(PermissionGroup group, String name, Localizable description, Permission impliedBy) {
this(group, name, description, impliedBy, true);
}
/**
* @deprecated since 1.257.
* Use {@link #Permission(PermissionGroup, String, Localizable, Permission)}
......@@ -171,6 +188,14 @@ public final class Permission {
return "Permission["+owner+','+name+']';
}
public void setEnabled(boolean enable) {
enabled = enable;
}
public boolean getEnabled() {
return enabled;
}
/**
* Returns all the {@link Permission}s available in the system.
* @return
......
......@@ -54,7 +54,14 @@ THE SOFTWARE.
</script>
</j:if>
<l:task icon="images/24x24/edit-delete.gif" href="${url}/delete" title="${%delete(it.pronoun)}" permission="${it.DELETE}" />
<l:task icon="images/24x24/setting.gif" href="${url}/configure" title="${%Configure}" permission="${it.CONFIGURE}" />
<j:choose>
<j:when test="${h.hasPermission(it,it.CONFIGURE)}">
<l:task icon="images/24x24/setting.gif" href="${url}/configure" title="${%Configure}" />
</j:when>
<j:when test="${h.hasPermission(it,it.EXTENDED_READ)}">
<l:task icon="images/24x24/setting.gif" href="${url}/configure" title="${%View Configuration}" />
</j:when>
</j:choose>
</j:if>
<st:include page="actions.jelly" />
</l:tasks>
......
......@@ -26,7 +26,7 @@ THE SOFTWARE.
Config page. derived class specific entries should go to configure-entries.jsp
-->
<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" xmlns:i="jelly:fmt">
<l:layout title="${it.displayName} Config" norefresh="true" permission="${it.CONFIGURE}">
<l:layout title="${it.displayName} Config" norefresh="true" permission="${it.EXTENDED_READ}">
<st:include page="sidepanel.jelly" />
<l:main-panel>
<f:form method="post" action="configSubmit" name="config">
......@@ -67,10 +67,12 @@ THE SOFTWARE.
<!-- additional entries from derived classes -->
<st:include page="configure-entries.jelly" />
<f:block>
<!--<input type="button" name="StructureTest" value="Test" onclick="buildFormTree(this.form)" />-->
<f:submit value="${%Save}" />
</f:block>
<j:if test="${h.hasPermission(it,it.CONFIGURE)}">
<f:block>
<!--<input type="button" name="StructureTest" value="Test" onclick="buildFormTree(this.form)" />-->
<f:submit value="${%Save}" />
</f:block>
</j:if>
</f:form>
</l:main-panel>
</l:layout>
......
......@@ -43,6 +43,10 @@ AbstractProject.WorkspacePermission.Description=\
This permission grants the ability to retrieve the contents of a workspace \
Hudson checked out for performing builds. If you don''t want an user to access \
the source code, you can do so by revoking this permission.
AbstractProject.ExtendedReadPermission.Description=\
This permission grants read-only access to project configurations. Please be \
aware that sensitive information in your builds, such as passwords, will be \
exposed to a wider audience by granting this permission.
Api.MultipleMatch=XPath "{0}" matched {1} nodes. \
Create XPath that only matches one, or use the "wrapper" query parameter to wrap them all under a root element.
......
package hudson.security;
import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import com.gargoylesoftware.htmlunit.html.HtmlButton;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import hudson.model.Item;
import org.jvnet.hudson.test.HudsonTestCase;
import org.jvnet.hudson.test.recipes.LocalData;
/**
*
* @author dty
*/
public class ExtendedReadPermissionTest extends HudsonTestCase {
/**
* alice: Job/Configure+Read
* bob: Job/Read
* charlie: Job/ExtendedRead+Read
*/
private void setPermissionEnabled(boolean enabled) throws Exception {
Item.EXTENDED_READ.setEnabled(enabled);
}
@LocalData
public void testReadOnlyConfigAccessWithPermissionEnabled() throws Exception {
setPermissionEnabled(true);
AuthorizationStrategy as = hudson.getAuthorizationStrategy();
assertTrue("Expecting GlobalMatrixAuthorizationStrategy", (as instanceof GlobalMatrixAuthorizationStrategy));
GlobalMatrixAuthorizationStrategy gas = (GlobalMatrixAuthorizationStrategy)as;
assertTrue("Charlie should have extended read for this test", gas.hasExplicitPermission("charlie",Item.EXTENDED_READ));
WebClient wc = new WebClient().login("charlie","charlie");
HtmlPage page = wc.goTo("job/a/configure");
HtmlForm form = page.getFormByName("config");
HtmlButton saveButton = getButtonByCaption(form,"Save");
assertNull(saveButton);
}
@LocalData
public void testReadOnlyConfigAccessWithPermissionDisabled() throws Exception {
setPermissionEnabled(false);
AuthorizationStrategy as = hudson.getAuthorizationStrategy();
assertTrue("Expecting GlobalMatrixAuthorizationStrategy", (as instanceof GlobalMatrixAuthorizationStrategy));
GlobalMatrixAuthorizationStrategy gas = (GlobalMatrixAuthorizationStrategy)as;
assertFalse("Charlie should not have extended read for this test", gas.hasExplicitPermission("charlie",Item.EXTENDED_READ));
WebClient wc = new WebClient().login("charlie","charlie");
try {
HtmlPage page = wc.goTo("job/a/configure");
}
catch (FailingHttpStatusCodeException e) {
assertEquals(403,e.getStatusCode());
return;
}
fail("Charlie should not have been able to access the configuration page");
}
@LocalData
public void testNoConfigAccessWithPermissionEnabled() throws Exception {
setPermissionEnabled(true);
AuthorizationStrategy as = hudson.getAuthorizationStrategy();
assertTrue("Expecting GlobalMatrixAuthorizationStrategy", (as instanceof GlobalMatrixAuthorizationStrategy));
GlobalMatrixAuthorizationStrategy gas = (GlobalMatrixAuthorizationStrategy)as;
assertFalse("Bob should not have extended read for this test", gas.hasExplicitPermission("bob",Item.EXTENDED_READ));
WebClient wc = new WebClient().login("bob","bob");
try {
HtmlPage page = wc.goTo("job/a/configure");
}
catch (FailingHttpStatusCodeException e) {
assertEquals(403,e.getStatusCode());
return;
}
fail("Bob should not have been able to access the configuration page");
}
/*
@LocalData
public void testConfigureLink() throws Exception {
}
@LocalData
public void testViewConfigurationLink() throws Exception {
}
@LocalData
public void testMatrixWithPermissionEnabled() throws Exception {
}
@LocalData
public void testMatrixWithPermissionDisabled() throws Exception {
}
*/
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册