提交 e3a1a78f 编写于 作者: K Kohsuke Kawaguchi

ProjectAuthenticator -> QueueItemAuthenticator

As Jesse pointed out, contextual information is often useful in authenticating the build, which means we need to take Queue.Item (initially AbstractBuild was the parameter, which provided the context, and I failed to accommodate that in transition to AbstractProject.)

To still allow Queue.Tasks to provide a meaningful value fallback to Queue.Task.getDefaultAuthentication() as opposed to hard code it to ACL.SYSTEM. This allow plugins like remote-terminal-access to supply a meaningful secure value without forcing a configuration change to the user.
上级 ec91933f
......@@ -39,7 +39,6 @@ import hudson.Launcher;
import hudson.Util;
import hudson.cli.declarative.CLIMethod;
import hudson.cli.declarative.CLIResolver;
import hudson.matrix.MatrixConfiguration;
import hudson.model.Cause.LegacyCodeCause;
import hudson.model.Cause.RemoteCause;
import hudson.model.Cause.UserIdCause;
......@@ -90,9 +89,6 @@ import jenkins.model.lazy.AbstractLazyLoadRunMap.Direction;
import jenkins.scm.DefaultSCMCheckoutStrategyImpl;
import jenkins.scm.SCMCheckoutStrategy;
import jenkins.scm.SCMCheckoutStrategyDescriptor;
import jenkins.security.ProjectAuthenticator;
import jenkins.security.ProjectAuthenticatorConfiguration;
import jenkins.security.ProjectAuthenticatorConfiguration;
import jenkins.util.TimeDuration;
import net.sf.json.JSONObject;
import org.acegisecurity.Authentication;
......@@ -113,6 +109,7 @@ import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.interceptor.RequirePOST;
import javax.annotation.Nonnull;
import javax.servlet.ServletException;
import java.io.File;
import java.io.IOException;
......@@ -1178,13 +1175,10 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
return this;
}
/**
* Let the identity determined by {@link ProjectAuthenticator}.
*
* @since 1.520
*/
public Authentication getIdentity() {
return ProjectAuthenticatorConfiguration.get().authenticate(this);
@Nonnull
public Authentication getDefaultAuthentication() {
// backward compatible behaviour.
return ACL.SYSTEM;
}
/**
......
......@@ -239,7 +239,7 @@ public class Executor extends Thread implements ModelObject {
}
}
final SecurityContext savedContext = ACL.impersonate(Tasks.getIdentityOf(task));
final SecurityContext savedContext = ACL.impersonate(workUnit.context.item.authenticate());
try {
setName(threadName + " : executing " + executable.toString());
if (LOGGER.isLoggable(FINE))
......
......@@ -327,7 +327,7 @@ public abstract class Node extends AbstractModelObject implements Reconfigurable
if(l==null && getMode()== Mode.EXCLUSIVE)
return CauseOfBlockage.fromMessage(Messages._Node_BecauseNodeIsReserved(getNodeName())); // this node is reserved for tasks that are tied to it
Authentication identity = item.task.getIdentity();
Authentication identity = item.authenticate();
if (!getACL().hasPermission(identity,AbstractProject.BUILD)) {
// doesn't have a permission
// TODO: does it make more sense to define a separate permission?
......
......@@ -62,6 +62,7 @@ import hudson.model.queue.CauseOfBlockage.BecauseNodeIsOffline;
import hudson.model.queue.CauseOfBlockage.BecauseLabelIsOffline;
import hudson.model.queue.CauseOfBlockage.BecauseNodeIsBusy;
import hudson.model.queue.WorkUnitContext;
import hudson.security.ACL;
import hudson.triggers.SafeTimerTask;
import hudson.triggers.Trigger;
import hudson.util.OneShotEvent;
......@@ -99,10 +100,14 @@ import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nonnull;
import javax.servlet.ServletException;
import jenkins.model.Jenkins;
import jenkins.security.QueueItemAuthenticator;
import jenkins.security.QueueItemAuthenticatorConfiguration;
import org.acegisecurity.AccessDeniedException;
import org.acegisecurity.Authentication;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.HttpResponses;
import org.kohsuke.stapler.export.Exported;
......@@ -1282,6 +1287,28 @@ public class Queue extends ResourceController implements Saveable {
* @since 1.377
*/
Collection<? extends SubTask> getSubTasks();
/**
* This method allows the task to provide the default fallback authentication object to be used
* when {@link QueueItemAuthenticator} fails to authenticate the build.
*
* <p>
* When the task execution touches other objects inside Jenkins, the access control is performed
* based on whether this {@link Authentication} is allowed to use them. Implementers, if you are unsure,
* consider returning the identity of the user who created the task, or
* {@link ACL#SYSTEM} to bypass the access control and run as the super user, which has been
* the traditional behaviour.)
*
* <p>
* This method was added to an interface after it was created, so plugins built against
* older versions of Jenkins may not have this method implemented. Called {@link Tasks#_getDefaultAuthenticationOf(Task)}
* to avoid {@link AbstractMethodError}.
*
* @since 1.520
* @see QueueItemAuthenticator
* @see Tasks#getDefaultAuthenticationOf(Task)
*/
@Nonnull Authentication getDefaultAuthentication();
}
/**
......@@ -1512,6 +1539,27 @@ public class Queue extends ResourceController implements Saveable {
return HttpResponses.forwardToPreviousPage();
}
/**
* Returns the identity that this task carries when it runs, for the purpose of access control.
*
* When the task execution touches other objects inside Jenkins, the access control is performed
* based on whether this {@link Authentication} is allowed to use them. Implementers, if you are unsure,
* return the identity of the user who queued the task, or {@link ACL#SYSTEM} to bypass the access control
* and run as the super user.
*
* @since 1.520
*/
@Nonnull
public Authentication authenticate() {
for (QueueItemAuthenticator auth : QueueItemAuthenticatorConfiguration.get().getAuthenticators()) {
Authentication a = auth.authenticate(this);
if (a!=null)
return a;
}
return Tasks.getDefaultAuthenticationOf(task);
}
/**
* Participates in the cancellation logic to set the {@link #future} accordingly.
*/
......
......@@ -62,7 +62,7 @@ public abstract class AbstractQueueTask implements Queue.Task {
/**
* This default implementation is the historical behaviour, but this is no longer desirable. Please override.
* See {@link SubTask#getIdentity()} for the contract.
* See {@link Task#getIdentity()} for the contract.
*/
public Authentication getIdentity() {
return ACL.SYSTEM;
......
......@@ -26,8 +26,6 @@ package hudson.model.queue;
import hudson.model.Label;
import hudson.model.Node;
import hudson.model.ResourceList;
import hudson.security.ACL;
import org.acegisecurity.Authentication;
/**
* Partial default implementation of {@link SubTask} to avoid
......@@ -55,8 +53,4 @@ public abstract class AbstractSubTask implements SubTask {
public ResourceList getResourceList() {
return new ResourceList();
}
public Authentication getIdentity() {
return getOwnerTask().getIdentity();
}
}
......@@ -67,9 +67,6 @@ import static java.lang.Math.*;
* See {@link SubTask#getSameNodeConstraint()}
* <li>
* Label constraint. {@link SubTask}s can specify that it can be only run on nodes that has the label.
* <li>
* Permission constraint. {@link SubTask}s have {@linkplain SubTask#getIdentity() identities} that need to have
* permissions to build on the node.
* </ul>
*
* <p>
......@@ -137,10 +134,8 @@ public class MappingWorksheet {
if (c.assignedLabel!=null && !c.assignedLabel.contains(node))
return false; // label mismatch
for (SubTask task : c) {
if (!nodeAcl.hasPermission(task.getIdentity(), AbstractProject.BUILD))
return false; // tasks don't have a permission to run on this node
}
if (!nodeAcl.hasPermission(item.authenticate(), AbstractProject.BUILD))
return false; // tasks don't have a permission to run on this node
return true;
}
......
......@@ -30,7 +30,6 @@ import hudson.model.Queue;
import hudson.model.Queue.Executable;
import hudson.model.Queue.Task;
import hudson.model.ResourceList;
import org.acegisecurity.Authentication;
import java.io.IOException;
import java.util.Collection;
......@@ -119,8 +118,4 @@ public abstract class QueueTaskFilter implements Queue.Task {
public Object getSameNodeConstraint() {
return base.getSameNodeConstraint();
}
public Authentication getIdentity() {
return base.getIdentity();
}
}
......@@ -86,17 +86,4 @@ public interface SubTask extends ResourceActivity {
* colocation constraint.
*/
Object getSameNodeConstraint();
/**
* Returns the identity that this task carries when it runs, for the purpose of access control.
*
* When the task execution touches other objects inside Jenkins, the access control is performed
* based on whether this {@link Authentication} is allowed to use them. Implementers, if you are unsure,
* return the identity of the user who queued the task, or {@link ACL#SYSTEM} to bypass the access control
* and run as the super user.
*
* @since 1.520
* @see Tasks#getIdentityOf(SubTask)
*/
@Nonnull Authentication getIdentity();
}
......@@ -90,13 +90,13 @@ public class Tasks {
* A pointless function to work around what appears to be a HotSpot problem. See JENKINS-5756 and bug 6933067
* on BugParade for more details.
*/
private static Authentication _getIdentityOf(SubTask t) {
return t.getIdentity();
private static Authentication _getDefaultAuthenticationOf(Task t) {
return t.getDefaultAuthentication();
}
public static Authentication getIdentityOf(SubTask t) {
public static Authentication getDefaultAuthenticationOf(Task t) {
try {
return _getIdentityOf(t);
return _getDefaultAuthenticationOf(t);
} catch (AbstractMethodError e) {
return ACL.SYSTEM;
}
......
package jenkins.security;
import hudson.ExtensionPoint;
import hudson.model.AbstractBuild;
import hudson.model.AbstractDescribableImpl;
import hudson.model.AbstractProject;
import hudson.security.ACL;
import org.acegisecurity.Authentication;
import javax.annotation.CheckForNull;
/**
* Extension point to run {@link AbstractBuild}s under a specific identity for better access control.
*
* @author Kohsuke Kawaguchi
* @since 1.520
* @see ProjectAuthenticatorConfiguration
* @see AbstractProject#getIdentity()
*/
public abstract class ProjectAuthenticator extends AbstractDescribableImpl<ProjectAuthenticator> implements ExtensionPoint {
/**
* Determines the identity in which the build will run as.
*
* @param project
* The project to be built.
*
* @return
* returning non-null will determine the identity. If null is returned, the next
* configured {@link ProjectAuthenticator} will be given a chance to authenticate
* the executor. If everything fails, fall back to the historical behaviour of
* {@link ACL#SYSTEM}.
*/
public abstract @CheckForNull Authentication authenticate(AbstractProject<?,?> project);
@Override
public ProjectAuthenticatorDescriptor getDescriptor() {
return (ProjectAuthenticatorDescriptor)super.getDescriptor();
}
}
package jenkins.security;
import hudson.ExtensionPoint;
import hudson.model.AbstractDescribableImpl;
import hudson.model.AbstractProject;
import hudson.model.Action;
import hudson.model.CauseAction;
import hudson.model.Queue;
import hudson.model.Queue.Item;
import hudson.model.Queue.Task;
import hudson.security.ACL;
import org.acegisecurity.Authentication;
import javax.annotation.CheckForNull;
/**
* Extension point to run {@link Queue.Executable}s under a specific identity for better access control.
*
* @author Kohsuke Kawaguchi
* @since 1.520
* @see QueueItemAuthenticatorConfiguration
* @see Item#authenticate()
* @see Task#getDefaultAuthentication()
*/
public abstract class QueueItemAuthenticator extends AbstractDescribableImpl<QueueItemAuthenticator> implements ExtensionPoint {
/**
* Determines the identity in which the {@link Queue.Executable} will run as.
*
* @param item
* The contextual information to assist the authentication.
* The primary interest is likely {@link Queue.Item#task}, which is often {@link AbstractProject}.
* {@link Action}s associated with the item is also likely of interest, such as {@link CauseAction}.
*
* @return
* returning non-null will determine the identity. If null is returned, the next
* configured {@link QueueItemAuthenticator} will be given a chance to authenticate
* the executor. If everything fails, fall back to {@link Task#getDefaultAuthentication()}.
*/
public abstract @CheckForNull Authentication authenticate(Queue.Item item);
@Override
public QueueItemAuthenticatorDescriptor getDescriptor() {
return (QueueItemAuthenticatorDescriptor)super.getDescriptor();
}
}
......@@ -14,17 +14,17 @@ import org.kohsuke.stapler.StaplerRequest;
import java.io.IOException;
/**
* Show the {@link ProjectAuthenticator} configurations on the system config page.
* Show the {@link QueueItemAuthenticator} configurations on the system config page.
*
* @author Kohsuke Kawaguchi
* @since 1.520
*/
@Extension
public class ProjectAuthenticatorConfiguration extends GlobalConfiguration {
private final DescribableList<ProjectAuthenticator,ProjectAuthenticatorDescriptor> authenticators
= new DescribableList<ProjectAuthenticator, ProjectAuthenticatorDescriptor>(this);
public class QueueItemAuthenticatorConfiguration extends GlobalConfiguration {
private final DescribableList<QueueItemAuthenticator,QueueItemAuthenticatorDescriptor> authenticators
= new DescribableList<QueueItemAuthenticator, QueueItemAuthenticatorDescriptor>(this);
public ProjectAuthenticatorConfiguration() {
public QueueItemAuthenticatorConfiguration() {
load();
}
......@@ -33,30 +33,21 @@ public class ProjectAuthenticatorConfiguration extends GlobalConfiguration {
return GlobalConfigurationCategory.get(GlobalConfigurationCategory.Security.class);
}
public DescribableList<ProjectAuthenticator, ProjectAuthenticatorDescriptor> getAuthenticators() {
public DescribableList<QueueItemAuthenticator, QueueItemAuthenticatorDescriptor> getAuthenticators() {
return authenticators;
}
@Override
public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
try {
authenticators.rebuildHetero(req,json, ProjectAuthenticatorDescriptor.all(),"authenticators");
authenticators.rebuildHetero(req,json, QueueItemAuthenticatorDescriptor.all(),"authenticators");
return true;
} catch (IOException e) {
throw new FormException(e,"authenticators");
}
}
public Authentication authenticate(AbstractProject<?,?> project) {
for (ProjectAuthenticator auth : get().getAuthenticators()) {
Authentication a = auth.authenticate(project);
if (a!=null)
return a;
}
return ACL.SYSTEM;
}
public static ProjectAuthenticatorConfiguration get() {
return Jenkins.getInstance().getInjector().getInstance(ProjectAuthenticatorConfiguration.class);
public static QueueItemAuthenticatorConfiguration get() {
return Jenkins.getInstance().getInjector().getInstance(QueueItemAuthenticatorConfiguration.class);
}
}
......@@ -5,15 +5,15 @@ import hudson.model.Descriptor;
import jenkins.model.Jenkins;
/**
* {@link Descriptor} for {@link ProjectAuthenticator}.
* {@link Descriptor} for {@link QueueItemAuthenticator}.
*
* @author Kohsuke Kawaguchi
* @since 1.520
*/
public abstract class ProjectAuthenticatorDescriptor extends Descriptor<ProjectAuthenticator> {
public abstract class QueueItemAuthenticatorDescriptor extends Descriptor<QueueItemAuthenticator> {
// nothing defined here yet
public static DescriptorExtensionList<ProjectAuthenticator,ProjectAuthenticatorDescriptor> all() {
return Jenkins.getInstance().getDescriptorList(ProjectAuthenticator.class);
public static DescriptorExtensionList<QueueItemAuthenticator,QueueItemAuthenticatorDescriptor> all() {
return Jenkins.getInstance().getDescriptorList(QueueItemAuthenticator.class);
}
}
package jenkins.security.ProjectAuthenticator;
// the default is empty configuration
\ No newline at end of file
package jenkins.security.QueueItemAuthenticator;
// the default is empty configuration
\ No newline at end of file
......@@ -21,13 +21,13 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.security.ProjectAuthenticatorConfiguration
package jenkins.security.QueueItemAuthenticatorConfiguration
import jenkins.security.ProjectAuthenticatorDescriptor;
import jenkins.security.QueueItemAuthenticatorDescriptor;
f=namespace(lib.FormTagLib)
if (!ProjectAuthenticatorDescriptor.all().isEmpty()) {
if (!QueueItemAuthenticatorDescriptor.all().isEmpty()) {
f.section(title:_("Access Control for Builds")) {
f.block() {
f.repeatableHeteroProperty(field:"authenticators",hasHeader:true)
......
......@@ -48,8 +48,8 @@ import hudson.matrix.MatrixRun;
import hudson.slaves.DummyCloudImpl;
import hudson.slaves.NodeProvisioner;
import jenkins.model.Jenkins;
import jenkins.security.ProjectAuthenticator;
import jenkins.security.ProjectAuthenticatorConfiguration;
import jenkins.security.QueueItemAuthenticator;
import jenkins.security.QueueItemAuthenticatorConfiguration;
import org.acegisecurity.Authentication;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.acls.sid.PrincipalSid;
......@@ -369,14 +369,14 @@ public class QueueTest extends HudsonTestCase {
}
@Inject
ProjectAuthenticatorConfiguration pac;
QueueItemAuthenticatorConfiguration qac;
/**
* Make sure that the running build actually carries an credential.
*/
public void testAccessControl() throws Exception {
configureUserRealm();
pac.getAuthenticators().add(new ProjectAuthenticatorImpl());
qac.getAuthenticators().add(new QueueItemAuthenticatorImpl());
FreeStyleProject p = createFreeStyleProject();
p.getBuildersList().add(new TestBuilder() {
@Override
......@@ -389,10 +389,9 @@ public class QueueTest extends HudsonTestCase {
}
@TestExtension
public static class ProjectAuthenticatorImpl extends ProjectAuthenticator {
public static class QueueItemAuthenticatorImpl extends QueueItemAuthenticator {
@Override
public Authentication authenticate(AbstractProject<?, ?> project) {
public Authentication authenticate(Queue.Item item) {
return alice;
}
}
......@@ -412,7 +411,7 @@ public class QueueTest extends HudsonTestCase {
DumbSlave s2 = createSlave();
configureUserRealm();
pac.getAuthenticators().add(new ProjectAuthenticatorImpl());
qac.getAuthenticators().add(new QueueItemAuthenticatorImpl());
FreeStyleProject p = createFreeStyleProject();
p.getBuildersList().add(new TestBuilder() {
@Override
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册