diff --git a/Jenkinsfile b/Jenkinsfile
index f42069f6688b..7d28d339c98c 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -43,7 +43,7 @@ for(j = 0; j < jdks.size(); j++) {
"MAVEN_OPTS=-Xmx1536m -Xms512m"], buildType, jdk) {
// Actually run Maven!
// -Dmaven.repo.local=… tells Maven to create a subdir in the temporary directory for the local Maven repository
- def mvnCmd = "mvn -Pdebug -U -Dset.changelist help:evaluate -Dexpression=changelist -Doutput=$changelistF clean install ${runTests ? '-Dmaven.test.failure.ignore' : '-DskipTests'} -V -B -ntp -Dmaven.repo.local=$m2repo -e"
+ def mvnCmd = "mvn -Pdebug -Pjapicmp -U -Dset.changelist help:evaluate -Dexpression=changelist -Doutput=$changelistF clean install ${runTests ? '-Dmaven.test.failure.ignore' : '-DskipTests'} -V -B -ntp -Dmaven.repo.local=$m2repo -e"
if(isUnix()) {
sh mvnCmd
@@ -69,6 +69,7 @@ for(j = 0; j < jdks.size(); j++) {
allowEmptyArchive: true, // in case we forgot to reincrementalify
fingerprint: true
}
+ publishHTML([allowMissing: true, alwaysLinkToLastBuild: false, includes: 'japicmp.html', keepAll: false, reportDir: 'core/target/japicmp', reportFiles: 'japicmp.html', reportName: 'API compatibility', reportTitles: 'japicmp report'])
}
}
}
diff --git a/bom/pom.xml b/bom/pom.xml
index fd67d9d3f3f1..8f2fb973dd42 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -42,12 +42,18 @@ THE SOFTWARE.
11.0.11.7.301.261
- 2.5.6.SEC032.4.12
+
+ org.springframework.security
+ spring-security-bom
+ 5.4.1
+ pom
+ import
+ com.github.spotbugsspotbugs-annotations
@@ -418,29 +424,6 @@ THE SOFTWARE.
1.1-beta-11
-
-
- org.springframework
- spring-webmvc
- ${spring.version}
-
-
- org.springframework
- spring-core
- ${spring.version}
-
-
- org.springframework
- spring-aop
- ${spring.version}
-
-
- org.acegisecurity
- acegi-security
- 1.0.7
-
-
-
org.jenkins-ci.modules
diff --git a/core/pom.xml b/core/pom.xml
index 6a16873c1dc6..561ff058a7ac 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -387,16 +387,12 @@ THE SOFTWARE.
commons-jexl
- org.acegisecurity
- acegi-security
+ org.springframework.security
+ spring-security-weborg.springframework
- spring-remoting
-
-
- org.springframework
- spring-support
+ spring-jcl
@@ -414,22 +410,6 @@ THE SOFTWARE.
org.fusesource.jansijansi
-
-
- org.springframework
- spring-webmvc
-
-
- org.springframework
- spring-core
-
-
- org.springframework
- spring-aop
- org.junit.jupiterjunit-jupiter-api
@@ -869,5 +849,41 @@ THE SOFTWARE.
true
+
+ japicmp
+
+
+
+ com.github.siom79.japicmp
+ japicmp-maven-plugin
+ 0.14.4-20200728.214757-1
+
+
+
+ \d+[.]\d+
+
+ true
+
+
+
+ javax.servlet
+ javax.servlet-api
+ 3.1.0
+ provided
+
+
+
+
+
+ verify
+
+ cmp
+
+
+
+
+
+
+
diff --git a/core/src/main/java/hudson/DependencyRunner.java b/core/src/main/java/hudson/DependencyRunner.java
index 6fd8889caa3c..68e7257bb265 100644
--- a/core/src/main/java/hudson/DependencyRunner.java
+++ b/core/src/main/java/hudson/DependencyRunner.java
@@ -52,7 +52,7 @@ public DependencyRunner(ProjectRunnable runnable) {
}
public void run() {
- try (ACLContext ctx = ACL.as(ACL.SYSTEM)) {
+ try (ACLContext ctx = ACL.as2(ACL.SYSTEM2)) {
Set topLevelProjects = new HashSet<>();
// Get all top-level projects
LOGGER.fine("assembling top level projects");
diff --git a/core/src/main/java/hudson/ExpressionFactory2.java b/core/src/main/java/hudson/ExpressionFactory2.java
index c8704fccfdb8..e3722182493b 100644
--- a/core/src/main/java/hudson/ExpressionFactory2.java
+++ b/core/src/main/java/hudson/ExpressionFactory2.java
@@ -1,6 +1,5 @@
package hudson;
-import org.acegisecurity.AcegiSecurityException;
import org.apache.commons.jelly.JellyContext;
import org.apache.commons.jelly.JellyException;
import org.apache.commons.jelly.expression.Expression;
@@ -15,6 +14,7 @@
import java.util.logging.Logger;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
+import org.springframework.security.access.AccessDeniedException;
/**
* {@link ExpressionFactory} so that security exception aborts the page rendering.
@@ -72,7 +72,7 @@ public Object evaluate(JellyContext context) {
CURRENT_CONTEXT.set(context);
JexlContext jexlContext = new JellyJexlContext( context );
return expression.evaluate(jexlContext);
- } catch (AcegiSecurityException e) {
+ } catch (AccessDeniedException e) {
// let the security exception pass through
throw e;
} catch (Exception e) {
diff --git a/core/src/main/java/hudson/ExtensionFinder.java b/core/src/main/java/hudson/ExtensionFinder.java
index ed4e3581b203..4ac88b9d8dee 100644
--- a/core/src/main/java/hudson/ExtensionFinder.java
+++ b/core/src/main/java/hudson/ExtensionFinder.java
@@ -578,7 +578,7 @@ public void onProvision(ProvisionInvocation provision) {
// so that we invoke them before derived class one. This isn't specified in JSR-250 but implemented
// this way in Spring and what most developers would expect to happen.
- final Set interfaces = ClassUtils.getAllInterfacesAsSet(instance);
+ final Set> interfaces = ClassUtils.getAllInterfacesAsSet(instance);
while (c != Object.class) {
Arrays.stream(c.getDeclaredMethods())
@@ -607,7 +607,7 @@ public void onProvision(ProvisionInvocation provision) {
* This allows to introspect metadata for a method which is both declared in parent class and in implemented
* interface(s). {@code interfaces} typically is obtained by {@link ClassUtils#getAllInterfacesAsSet}
*/
- Collection getMethodAndInterfaceDeclarations(Method method, Collection interfaces) {
+ Collection getMethodAndInterfaceDeclarations(Method method, Collection> interfaces) {
final List methods = new ArrayList<>();
methods.add(method);
diff --git a/core/src/main/java/hudson/Functions.java b/core/src/main/java/hudson/Functions.java
index 3ce3e038833d..926d4cee4d5f 100644
--- a/core/src/main/java/hudson/Functions.java
+++ b/core/src/main/java/hudson/Functions.java
@@ -145,7 +145,6 @@
import jenkins.model.ModelObjectWithChildren;
import jenkins.model.ModelObjectWithContextMenu;
-import org.acegisecurity.AccessDeniedException;
import org.apache.commons.jelly.JellyContext;
import org.apache.commons.jelly.JellyTagException;
import org.apache.commons.jelly.Script;
@@ -174,6 +173,7 @@
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.accmod.restrictions.DoNotUse;
+import org.springframework.security.access.AccessDeniedException;
/**
* Utility functions used in views.
@@ -1770,7 +1770,7 @@ public static String toCCStatus(Item i) {
* Checks if the current user is anonymous.
*/
public static boolean isAnonymous() {
- return ACL.isAnonymous(Jenkins.getAuthentication());
+ return ACL.isAnonymous2(Jenkins.getAuthentication2());
}
/**
diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java
index 150754805283..0614c5339e34 100644
--- a/core/src/main/java/hudson/PluginManager.java
+++ b/core/src/main/java/hudson/PluginManager.java
@@ -69,7 +69,6 @@
import jenkins.util.xml.RestrictiveEntityResolver;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
-import org.acegisecurity.Authentication;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
@@ -156,6 +155,7 @@
import static hudson.init.InitMilestone.*;
import static java.util.logging.Level.*;
+import org.springframework.security.core.Authentication;
/**
* Manages {@link PluginWrapper}s.
@@ -884,7 +884,7 @@ public void dynamicLoad(File arc) throws IOException, InterruptedException, Rest
*/
@Restricted(NoExternalUse.class)
public void dynamicLoad(File arc, boolean removeExisting, @CheckForNull List batch) throws IOException, InterruptedException, RestartRequiredException {
- try (ACLContext context = ACL.as(ACL.SYSTEM)) {
+ try (ACLContext context = ACL.as2(ACL.SYSTEM2)) {
LOGGER.log(FINE, "Attempting to dynamic load {0}", arc);
PluginWrapper p = null;
String sn;
@@ -951,7 +951,7 @@ public void dynamicLoad(File arc, boolean removeExisting, @CheckForNull List plugins) throws Exception {
- try (ACLContext context = ACL.as(ACL.SYSTEM)) {
+ try (ACLContext context = ACL.as2(ACL.SYSTEM2)) {
Map pluginsByName = plugins.stream().collect(Collectors.toMap(p -> p.getShortName(), p -> p));
// recalculate dependencies of plugins optionally depending the newly deployed ones.
@@ -1548,7 +1548,7 @@ private List> install(@NonNull Collection> f = item != null ? (QueueTaskFuture)item.getFuture() : null;
if (wait || sync || follow) {
diff --git a/core/src/main/java/hudson/cli/CLIAction.java b/core/src/main/java/hudson/cli/CLIAction.java
index efca422c4437..9564fa675f80 100644
--- a/core/src/main/java/hudson/cli/CLIAction.java
+++ b/core/src/main/java/hudson/cli/CLIAction.java
@@ -61,9 +61,9 @@
import jenkins.util.FullDuplexHttpService;
import jenkins.websocket.WebSocketSession;
import jenkins.websocket.WebSockets;
-import org.acegisecurity.Authentication;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.HttpResponses;
+import org.springframework.security.core.Authentication;
/**
* Shows usage of CLI and commands.
@@ -118,7 +118,7 @@ public HttpResponse doWs() {
if (!WebSockets.isSupported()) {
return HttpResponses.notFound();
}
- Authentication authentication = Jenkins.getAuthentication();
+ Authentication authentication = Jenkins.getAuthentication2();
return WebSockets.upgrade(new WebSocketSession() {
ServerSideImpl connection;
class OutputImpl implements PlainCLIProtocol.Output {
@@ -269,7 +269,7 @@ void run() throws IOException, InterruptedException {
sendExit(2);
return;
}
- command.setTransportAuth(authentication);
+ command.setTransportAuth2(authentication);
command.setClientCharset(encoding);
CLICommand orig = CLICommand.setCurrent(command);
try {
@@ -303,7 +303,7 @@ protected FullDuplexHttpService createService(StaplerRequest req, UUID uuid) thr
return new FullDuplexHttpService(uuid) {
@Override
protected void run(InputStream upload, OutputStream download) throws IOException, InterruptedException {
- try (ServerSideImpl connection = new ServerSideImpl(new PlainCLIProtocol.FramedOutput(download), Jenkins.getAuthentication())) {
+ try (ServerSideImpl connection = new ServerSideImpl(new PlainCLIProtocol.FramedOutput(download), Jenkins.getAuthentication2())) {
new PlainCLIProtocol.FramedReader(connection, upload).start();
connection.run();
}
diff --git a/core/src/main/java/hudson/cli/CLICommand.java b/core/src/main/java/hudson/cli/CLICommand.java
index a1f11658cddb..77440d3e898e 100644
--- a/core/src/main/java/hudson/cli/CLICommand.java
+++ b/core/src/main/java/hudson/cli/CLICommand.java
@@ -23,35 +23,18 @@
*/
package hudson.cli;
+import edu.umd.cs.findbugs.annotations.CheckForNull;
+import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.AbortException;
import hudson.Extension;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
-import hudson.cli.declarative.CLIMethod;
import hudson.ExtensionPoint.LegacyInstancesAreScopedToHudson;
import hudson.Functions;
+import hudson.cli.declarative.CLIMethod;
import hudson.cli.declarative.OptionHandlerExtension;
-import jenkins.model.Jenkins;
import hudson.remoting.Channel;
import hudson.security.SecurityRealm;
-import org.acegisecurity.AccessDeniedException;
-import org.acegisecurity.Authentication;
-import org.acegisecurity.BadCredentialsException;
-import org.acegisecurity.context.SecurityContext;
-import org.acegisecurity.context.SecurityContextHolder;
-import org.apache.commons.discovery.ResourceClassIterator;
-import org.apache.commons.discovery.ResourceNameIterator;
-import org.apache.commons.discovery.resource.ClassLoaders;
-import org.apache.commons.discovery.resource.classes.DiscoverClasses;
-import org.apache.commons.discovery.resource.names.DiscoverServiceNames;
-import org.jvnet.hudson.annotation_indexer.Index;
-import org.jvnet.tiger_types.Types;
-import org.kohsuke.accmod.Restricted;
-import org.kohsuke.accmod.restrictions.NoExternalUse;
-import org.kohsuke.args4j.CmdLineException;
-import org.kohsuke.args4j.CmdLineParser;
-import org.kohsuke.args4j.spi.OptionHandler;
-
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -64,8 +47,24 @@
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
-import edu.umd.cs.findbugs.annotations.CheckForNull;
-import edu.umd.cs.findbugs.annotations.NonNull;
+import jenkins.model.Jenkins;
+import org.apache.commons.discovery.ResourceClassIterator;
+import org.apache.commons.discovery.ResourceNameIterator;
+import org.apache.commons.discovery.resource.ClassLoaders;
+import org.apache.commons.discovery.resource.classes.DiscoverClasses;
+import org.apache.commons.discovery.resource.names.DiscoverServiceNames;
+import org.jvnet.hudson.annotation_indexer.Index;
+import org.jvnet.tiger_types.Types;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
+import org.kohsuke.args4j.CmdLineException;
+import org.kohsuke.args4j.CmdLineParser;
+import org.kohsuke.args4j.spi.OptionHandler;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
/**
* Base class for Hudson CLI.
@@ -189,7 +188,7 @@ public String getName() {
* The default implementation uses args4j to parse command line arguments and call {@link #run()},
* but if that processing is undesirable, subtypes can directly override this method and leave {@link #run()}
* to an empty method.
- * You would however then have to consider {@link #getTransportAuthentication},
+ * You would however then have to consider {@link #getTransportAuthentication2},
* so this is not really recommended.
*
* @param args
@@ -236,10 +235,11 @@ public int main(List args, Locale locale, InputStream stdin, PrintStream
Authentication old = null;
Authentication auth;
try {
+ // TODO as in CLIRegisterer this may be doing too much work
sc = SecurityContextHolder.getContext();
old = sc.getAuthentication();
- sc.setAuthentication(auth = getTransportAuthentication());
+ sc.setAuthentication(auth = getTransportAuthentication2());
if (!(this instanceof HelpCommand || this instanceof WhoAmICommand))
Jenkins.get().checkPermission(Jenkins.READ);
@@ -285,7 +285,7 @@ public int main(List args, Locale locale, InputStream stdin, PrintStream
}
private void logFailedCommandAndPrintExceptionErrorMessage(List args, Throwable e) {
- Authentication auth = getTransportAuthentication();
+ Authentication auth = getTransportAuthentication2();
String logMessage = String.format("Failed call to CLI command %s, with %d arguments, as user %s.",
getName(), args.size(), auth != null ? auth.getName() : "");
@@ -316,28 +316,6 @@ public Channel checkChannel() throws AbortException {
throw new AbortException("This command is requesting the -remoting mode which is no longer supported. See https://jenkins.io/redirect/cli-command-requires-channel");
}
- /**
- * Determines if the user authentication is attempted through CLI before running this command.
- *
- *
- * If your command doesn't require any authentication whatsoever, and if you don't even want to let the user
- * authenticate, then override this method to always return false — doing so will result in all the commands
- * running as anonymous user credential.
- *
- *
- * Note that even if this method returns true, the user can still skip aut
- *
- * @param auth
- * Always non-null.
- * If the underlying transport had already performed authentication, this object is something other than
- * {@link jenkins.model.Jenkins#ANONYMOUS}.
- * @deprecated Unused.
- */
- @Deprecated
- protected boolean shouldPerformAuthentication(Authentication auth) {
- return auth== Jenkins.ANONYMOUS;
- }
-
/**
* Returns the identity of the client as determined at the CLI transport level.
*
@@ -352,18 +330,38 @@ protected boolean shouldPerformAuthentication(Authentication auth) {
* then this method can return a valid identity of the client.
*
*
- * If the transport doesn't do authentication, this method returns {@link jenkins.model.Jenkins#ANONYMOUS}.
+ * If the transport doesn't do authentication, this method returns {@link jenkins.model.Jenkins#ANONYMOUS2}.
+ * @since TODO
*/
- public Authentication getTransportAuthentication() {
+ public Authentication getTransportAuthentication2() {
Authentication a = transportAuth;
- if (a==null) a = Jenkins.ANONYMOUS;
+ if (a==null) a = Jenkins.ANONYMOUS2;
return a;
}
- public void setTransportAuth(Authentication transportAuth) {
+ /**
+ * @deprecated use {@link #getTransportAuthentication2}
+ */
+ @Deprecated
+ public org.acegisecurity.Authentication getTransportAuthentication() {
+ return org.acegisecurity.Authentication.fromSpring(getTransportAuthentication2());
+ }
+
+ /**
+ * @since TODO
+ */
+ public void setTransportAuth2(Authentication transportAuth) {
this.transportAuth = transportAuth;
}
+ /**
+ * @deprecated use {@link #setTransportAuth2}
+ */
+ @Deprecated
+ public void setTransportAuth(org.acegisecurity.Authentication transportAuth) {
+ setTransportAuth2(transportAuth.toSpring());
+ }
+
/**
* Executes the command, and return the exit code.
*
diff --git a/core/src/main/java/hudson/cli/HelpCommand.java b/core/src/main/java/hudson/cli/HelpCommand.java
index a4623f5b8f8b..fa4c73756f6c 100644
--- a/core/src/main/java/hudson/cli/HelpCommand.java
+++ b/core/src/main/java/hudson/cli/HelpCommand.java
@@ -30,8 +30,8 @@
import java.util.Map;
import java.util.TreeMap;
-import org.acegisecurity.AccessDeniedException;
import org.kohsuke.args4j.Argument;
+import org.springframework.security.access.AccessDeniedException;
/**
* Show the list of all commands.
diff --git a/core/src/main/java/hudson/cli/WhoAmICommand.java b/core/src/main/java/hudson/cli/WhoAmICommand.java
index 6ad618d12217..9ffc35861ace 100644
--- a/core/src/main/java/hudson/cli/WhoAmICommand.java
+++ b/core/src/main/java/hudson/cli/WhoAmICommand.java
@@ -25,8 +25,8 @@
import hudson.Extension;
import jenkins.model.Jenkins;
-import org.acegisecurity.Authentication;
-import org.acegisecurity.GrantedAuthority;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
/**
* Report the current granted authorities
@@ -41,7 +41,7 @@ public String getShortDescription() {
}
protected int run() {
- Authentication a = Jenkins.getAuthentication();
+ Authentication a = Jenkins.getAuthentication2();
stdout.println("Authenticated as: "+a.getName());
stdout.println("Authorities:");
for (GrantedAuthority ga : a.getAuthorities()) {
diff --git a/core/src/main/java/hudson/cli/declarative/CLIRegisterer.java b/core/src/main/java/hudson/cli/declarative/CLIRegisterer.java
index 8f7f01b6490b..168aa49f7bb5 100644
--- a/core/src/main/java/hudson/cli/declarative/CLIRegisterer.java
+++ b/core/src/main/java/hudson/cli/declarative/CLIRegisterer.java
@@ -23,6 +23,7 @@
*/
package hudson.cli.declarative;
+import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.AbortException;
import hudson.Extension;
import hudson.ExtensionComponent;
@@ -32,20 +33,6 @@
import hudson.cli.CLICommand;
import hudson.cli.CloneableCLICommand;
import hudson.model.Hudson;
-import jenkins.ExtensionComponentSet;
-import jenkins.ExtensionRefreshException;
-import jenkins.model.Jenkins;
-import org.acegisecurity.AccessDeniedException;
-import org.acegisecurity.Authentication;
-import org.acegisecurity.BadCredentialsException;
-import org.acegisecurity.context.SecurityContext;
-import org.acegisecurity.context.SecurityContextHolder;
-import org.jvnet.hudson.annotation_indexer.Index;
-import org.jvnet.localizer.ResourceBundleHolder;
-import org.kohsuke.args4j.CmdLineParser;
-import org.kohsuke.args4j.CmdLineException;
-
-import edu.umd.cs.findbugs.annotations.NonNull;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
@@ -59,11 +46,22 @@
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.Stack;
-import static java.util.logging.Level.SEVERE;
-
import java.util.UUID;
import java.util.logging.Level;
+import static java.util.logging.Level.SEVERE;
import java.util.logging.Logger;
+import jenkins.ExtensionComponentSet;
+import jenkins.ExtensionRefreshException;
+import jenkins.model.Jenkins;
+import org.jvnet.hudson.annotation_indexer.Index;
+import org.jvnet.localizer.ResourceBundleHolder;
+import org.kohsuke.args4j.CmdLineException;
+import org.kohsuke.args4j.CmdLineParser;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
/**
* Discover {@link CLIMethod}s and register them as {@link CLICommand} implementations.
@@ -202,13 +200,14 @@ public int main(List args, Locale locale, InputStream stdin, PrintStream
CmdLineParser parser = bindMethod(binders);
try {
+ // TODO this could probably use ACL.as; why is it calling SecurityContext.setAuthentication rather than SecurityContextHolder.setContext?
SecurityContext sc = SecurityContextHolder.getContext();
Authentication old = sc.getAuthentication();
try {
// fill up all the binders
parser.parseArgument(args);
- Authentication auth = getTransportAuthentication();
+ Authentication auth = getTransportAuthentication2();
sc.setAuthentication(auth); // run the CLI with the right credential
jenkins.checkPermission(Jenkins.READ);
diff --git a/core/src/main/java/hudson/cli/handlers/GenericItemOptionHandler.java b/core/src/main/java/hudson/cli/handlers/GenericItemOptionHandler.java
index 5bd9834b485b..99bae388ca68 100644
--- a/core/src/main/java/hudson/cli/handlers/GenericItemOptionHandler.java
+++ b/core/src/main/java/hudson/cli/handlers/GenericItemOptionHandler.java
@@ -31,13 +31,13 @@
import java.util.logging.Level;
import java.util.logging.Logger;
import jenkins.model.Jenkins;
-import org.acegisecurity.Authentication;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.OptionDef;
import org.kohsuke.args4j.spi.OptionHandler;
import org.kohsuke.args4j.spi.Parameters;
import org.kohsuke.args4j.spi.Setter;
+import org.springframework.security.core.Authentication;
/**
* Refers to an {@link Item} by its name.
@@ -61,8 +61,8 @@ protected GenericItemOptionHandler(CmdLineParser parser, OptionDef option, Sette
final String src = params.getParameter(0);
T s = j.getItemByFullName(src, type());
if (s == null) {
- final Authentication who = Jenkins.getAuthentication();
- try (ACLContext acl = ACL.as(ACL.SYSTEM)) {
+ final Authentication who = Jenkins.getAuthentication2();
+ try (ACLContext acl = ACL.as2(ACL.SYSTEM2)) {
Item actual = j.getItemByFullName(src);
if (actual == null) {
LOGGER.log(Level.FINE, "really no item exists named {0}", src);
diff --git a/core/src/main/java/hudson/cli/handlers/ViewOptionHandler.java b/core/src/main/java/hudson/cli/handlers/ViewOptionHandler.java
index 3c8a52d07e86..33f787e998b1 100644
--- a/core/src/main/java/hudson/cli/handlers/ViewOptionHandler.java
+++ b/core/src/main/java/hudson/cli/handlers/ViewOptionHandler.java
@@ -30,7 +30,6 @@
import jenkins.model.Jenkins;
-import org.acegisecurity.AccessDeniedException;
import org.kohsuke.MetaInfServices;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
@@ -40,6 +39,7 @@
import org.kohsuke.args4j.spi.Setter;
import edu.umd.cs.findbugs.annotations.CheckForNull;
+import org.springframework.security.access.AccessDeniedException;
/**
* Refers to {@link View} by its name.
diff --git a/core/src/main/java/hudson/diagnosis/OldDataMonitor.java b/core/src/main/java/hudson/diagnosis/OldDataMonitor.java
index 82dbffcf085a..3cc2520c11c0 100644
--- a/core/src/main/java/hudson/diagnosis/OldDataMonitor.java
+++ b/core/src/main/java/hudson/diagnosis/OldDataMonitor.java
@@ -118,7 +118,7 @@ public Map getData() {
private static void remove(Saveable obj, boolean isDelete) {
Jenkins j = Jenkins.get();
OldDataMonitor odm = get(j);
- try (ACLContext ctx = ACL.as(ACL.SYSTEM)) {
+ try (ACLContext ctx = ACL.as2(ACL.SYSTEM2)) {
odm.data.remove(referTo(obj));
if (isDelete && obj instanceof Job, ?>) {
for (Run r : ((Job, ?>) obj).getBuilds()) {
diff --git a/core/src/main/java/hudson/model/AbstractItem.java b/core/src/main/java/hudson/model/AbstractItem.java
index 7e636b2dda8f..6af15f3ad15d 100644
--- a/core/src/main/java/hudson/model/AbstractItem.java
+++ b/core/src/main/java/hudson/model/AbstractItem.java
@@ -75,7 +75,6 @@
import java.util.regex.Pattern;
import edu.umd.cs.findbugs.annotations.NonNull;
-import org.acegisecurity.AccessDeniedException;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
@@ -102,6 +101,7 @@
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.Ancestor;
+import org.springframework.security.access.AccessDeniedException;
/**
* Partial default implementation of {@link Item}.
@@ -313,13 +313,13 @@ private void checkIfNameIsUsed(@NonNull String newName) throws Failure {
if (item != null) {
throw new Failure(Messages.AbstractItem_NewNameInUse(newName));
}
- try (ACLContext ctx = ACL.as(ACL.SYSTEM)) {
+ try (ACLContext ctx = ACL.as2(ACL.SYSTEM2)) {
item = getParent().getItem(newName);
if (item != null) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "Unable to rename the job {0}: name {1} is already in use. " +
"User {2} has no {3} permission for existing job with the same name",
- new Object[] {this.getFullName(), newName, ctx.getPreviousContext().getAuthentication().getName(), Item.DISCOVER.name} );
+ new Object[] {this.getFullName(), newName, ctx.getPreviousContext2().getAuthentication().getName(), Item.DISCOVER.name} );
}
// Don't explicitly mention that there is another item with the same name.
throw new Failure(Messages.Jenkins_NotAllowedName(newName));
diff --git a/core/src/main/java/hudson/model/AsyncAperiodicWork.java b/core/src/main/java/hudson/model/AsyncAperiodicWork.java
index cd9f46b830cf..b9b705d8bc0b 100644
--- a/core/src/main/java/hudson/model/AsyncAperiodicWork.java
+++ b/core/src/main/java/hudson/model/AsyncAperiodicWork.java
@@ -114,7 +114,7 @@ public final void doAperiodicRun() {
long stopTime;
StreamTaskListener l = createListener();
- try (ACLContext ctx = ACL.as(ACL.SYSTEM)) {
+ try (ACLContext ctx = ACL.as2(ACL.SYSTEM2)) {
l.getLogger().printf("Started at %tc%n", new Date(startTime));
execute(l);
} catch (IOException e) {
diff --git a/core/src/main/java/hudson/model/AsyncPeriodicWork.java b/core/src/main/java/hudson/model/AsyncPeriodicWork.java
index 80031750b764..c0ae6eb5c18f 100644
--- a/core/src/main/java/hudson/model/AsyncPeriodicWork.java
+++ b/core/src/main/java/hudson/model/AsyncPeriodicWork.java
@@ -96,7 +96,7 @@ public final void doRun() {
StreamTaskListener l = createListener();
try {
l.getLogger().printf("Started at %tc%n", new Date(startTime));
- try (ACLContext ctx = ACL.as(ACL.SYSTEM)) {
+ try (ACLContext ctx = ACL.as2(ACL.SYSTEM2)) {
execute(l);
}
} catch (IOException e) {
diff --git a/core/src/main/java/hudson/model/BuildAuthorizationToken.java b/core/src/main/java/hudson/model/BuildAuthorizationToken.java
index dd54ffb30756..5c31e1fadabf 100644
--- a/core/src/main/java/hudson/model/BuildAuthorizationToken.java
+++ b/core/src/main/java/hudson/model/BuildAuthorizationToken.java
@@ -33,8 +33,8 @@
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import jenkins.security.ApiTokenProperty;
-import org.acegisecurity.AccessDeniedException;
import org.kohsuke.stapler.HttpResponses;
+import org.springframework.security.access.AccessDeniedException;
/**
* Authorization token to allow projects to trigger themselves under the secured environment.
diff --git a/core/src/main/java/hudson/model/Cause.java b/core/src/main/java/hudson/model/Cause.java
index b5e481f98203..23bc9c8e9042 100644
--- a/core/src/main/java/hudson/model/Cause.java
+++ b/core/src/main/java/hudson/model/Cause.java
@@ -362,7 +362,7 @@ public static class DeeplyNestedUpstreamCause extends Cause {
public static class UserCause extends Cause {
private String authenticationName;
public UserCause() {
- this.authenticationName = Jenkins.getAuthentication().getName();
+ this.authenticationName = Jenkins.getAuthentication2().getName();
}
/**
diff --git a/core/src/main/java/hudson/model/Computer.java b/core/src/main/java/hudson/model/Computer.java
index ad10061530d0..cbf495d3cea3 100644
--- a/core/src/main/java/hudson/model/Computer.java
+++ b/core/src/main/java/hudson/model/Computer.java
@@ -1366,7 +1366,7 @@ public String call() throws IOException {
Executors.newCachedThreadPool(
new ExceptionCatchingThreadFactory(
new NamingThreadFactory(
- new DaemonThreadFactory(), "Computer.threadPoolForRemoting"))), ACL.SYSTEM));
+ new DaemonThreadFactory(), "Computer.threadPoolForRemoting"))), ACL.SYSTEM2));
//
//
diff --git a/core/src/main/java/hudson/model/DependencyGraph.java b/core/src/main/java/hudson/model/DependencyGraph.java
index f52fb4e1eaca..b5dd097acf06 100644
--- a/core/src/main/java/hudson/model/DependencyGraph.java
+++ b/core/src/main/java/hudson/model/DependencyGraph.java
@@ -86,7 +86,7 @@ public DependencyGraph() {
public void build() {
// Set full privileges while computing to avoid missing any projects the current user cannot see.
- try (ACLContext ctx = ACL.as(ACL.SYSTEM)){
+ try (ACLContext ctx = ACL.as2(ACL.SYSTEM2)){
this.computationalData = new HashMap<>();
for( AbstractProject p : Jenkins.get().allItems(AbstractProject.class) )
p.buildDependencyGraph(this);
@@ -401,7 +401,7 @@ public AbstractProject getDownstreamProject() {
* Decide whether build should be triggered and provide any Actions for the build.
* Default implementation always returns true (for backward compatibility), and
* adds no Actions. Subclasses may override to control how/if the build is triggered.
- *
The authentication in effect ({@link Jenkins#getAuthentication}) will be that of the upstream build.
+ *
The authentication in effect ({@link Jenkins#getAuthentication2}) will be that of the upstream build.
* An implementation is expected to perform any relevant access control checks:
* that an upstream project can both see and build a downstream project,
* or that a downstream project can see an upstream project.
diff --git a/core/src/main/java/hudson/model/Executor.java b/core/src/main/java/hudson/model/Executor.java
index dfcb32a9ee6f..10d33101a71e 100644
--- a/core/src/main/java/hudson/model/Executor.java
+++ b/core/src/main/java/hudson/model/Executor.java
@@ -36,8 +36,6 @@
import jenkins.model.CauseOfInterruption.UserInterruption;
import jenkins.model.InterruptedBuildAction;
import jenkins.model.Jenkins;
-import org.acegisecurity.AccessDeniedException;
-import org.acegisecurity.Authentication;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.HttpResponses;
import org.kohsuke.stapler.QueryParameter;
@@ -76,6 +74,8 @@
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.DoNotUse;
import org.kohsuke.accmod.restrictions.NoExternalUse;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.core.Authentication;
/**
@@ -196,8 +196,8 @@ public void interrupt(Result result) {
}
private void interrupt(Result result, boolean forShutdown) {
- Authentication a = Jenkins.getAuthentication();
- if (a == ACL.SYSTEM)
+ Authentication a = Jenkins.getAuthentication2();
+ if (a.equals(ACL.SYSTEM2))
interrupt(result, forShutdown, new CauseOfInterruption[0]);
else {
// worth recording who did it
@@ -340,7 +340,7 @@ public void run() {
lock.writeLock().unlock();
}
- try (ACLContext ctx = ACL.as(ACL.SYSTEM)) {
+ try (ACLContext ctx = ACL.as2(ACL.SYSTEM2)) {
SubTask task;
// transition from idle to building.
// perform this state change as an atomic operation wrt other queue operations
@@ -414,9 +414,9 @@ public SubTask call() throws Exception {
}
setName(getName() + " : executing " + executable.toString());
- Authentication auth = workUnit.context.item.authenticate();
+ Authentication auth = workUnit.context.item.authenticate2();
LOGGER.log(FINE, "{0} is now executing {1} as {2}", new Object[] {getName(), executable, auth});
- if (LOGGER.isLoggable(FINE) && auth.equals(ACL.SYSTEM)) { // i.e., unspecified
+ if (LOGGER.isLoggable(FINE) && auth.equals(ACL.SYSTEM2)) { // i.e., unspecified
if (QueueItemAuthenticatorDescriptor.all().isEmpty()) {
LOGGER.fine("no QueueItemAuthenticator implementations installed");
} else if (QueueItemAuthenticatorConfiguration.get().getAuthenticators().isEmpty()) {
@@ -425,7 +425,7 @@ public SubTask call() throws Exception {
LOGGER.log(FINE, "some QueueItemAuthenticator implementations configured but neglected to authenticate {0}", executable);
}
}
- try (ACLContext context = ACL.as(auth)) {
+ try (ACLContext context = ACL.as2(auth)) {
queue.execute(executable, task);
}
} catch (AsynchronousExecution x) {
diff --git a/core/src/main/java/hudson/model/Fingerprint.java b/core/src/main/java/hudson/model/Fingerprint.java
index 577a50373e80..b3480b70296a 100644
--- a/core/src/main/java/hudson/model/Fingerprint.java
+++ b/core/src/main/java/hudson/model/Fingerprint.java
@@ -71,8 +71,8 @@
import java.util.logging.Logger;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
-import org.acegisecurity.AccessDeniedException;
-import org.acegisecurity.Authentication;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.core.Authentication;
/**
* A file being tracked by Jenkins.
@@ -125,7 +125,7 @@ public String getName() {
*/
private boolean hasPermissionToDiscoverBuild() {
// We expose the data to Jenkins administrators in order to
- // let them manage the data for deleted jobs (also works for SYSTEM)
+ // let them manage the data for deleted jobs (also works for SYSTEM2)
final Jenkins instance = Jenkins.get();
if (instance.hasPermission(Jenkins.ADMINISTER)) {
return true;
@@ -831,7 +831,7 @@ public Object unmarshal(HierarchicalStreamReader reader, final UnmarshallingCont
public static final class ProjectRenameListener extends ItemListener {
@Override
public void onLocationChanged(final Item item, final String oldName, final String newName) {
- try (ACLContext acl = ACL.as(ACL.SYSTEM)) {
+ try (ACLContext acl = ACL.as2(ACL.SYSTEM2)) {
locationChanged(item, oldName, newName);
}
}
@@ -1452,9 +1452,9 @@ private static boolean canDiscoverItem(@NonNull final String fullName) {
}
// Probably it failed due to the missing Item.DISCOVER
- // We try to retrieve the job using SYSTEM user and to check permissions manually.
- final Authentication userAuth = Jenkins.getAuthentication();
- try (ACLContext acl = ACL.as(ACL.SYSTEM)) {
+ // We try to retrieve the job using SYSTEM2 user and to check permissions manually.
+ final Authentication userAuth = Jenkins.getAuthentication2();
+ try (ACLContext acl = ACL.as2(ACL.SYSTEM2)) {
final Item itemBySystemUser = jenkins.getItemByFullName(fullName);
if (itemBySystemUser == null) {
return false;
@@ -1462,14 +1462,14 @@ private static boolean canDiscoverItem(@NonNull final String fullName) {
// To get the item existence fact, a user needs Item.DISCOVER for the item
// and Item.READ for all container folders.
- boolean canDiscoverTheItem = itemBySystemUser.hasPermission(userAuth, Item.DISCOVER);
+ boolean canDiscoverTheItem = itemBySystemUser.hasPermission2(userAuth, Item.DISCOVER);
if (canDiscoverTheItem) {
ItemGroup> current = itemBySystemUser.getParent();
do {
if (current instanceof Item) {
final Item i = (Item) current;
current = i.getParent();
- if (!i.hasPermission(userAuth, Item.READ)) {
+ if (!i.hasPermission2(userAuth, Item.READ)) {
canDiscoverTheItem = false;
}
} else {
diff --git a/core/src/main/java/hudson/model/ItemGroup.java b/core/src/main/java/hudson/model/ItemGroup.java
index ad56acbc0780..b55609f910dc 100644
--- a/core/src/main/java/hudson/model/ItemGroup.java
+++ b/core/src/main/java/hudson/model/ItemGroup.java
@@ -32,7 +32,7 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
import edu.umd.cs.findbugs.annotations.CheckForNull;
-import org.acegisecurity.AccessDeniedException;
+import org.springframework.security.access.AccessDeniedException;
/**
* Represents a grouping inherent to a kind of {@link Item}s.
diff --git a/core/src/main/java/hudson/model/ItemGroupMixIn.java b/core/src/main/java/hudson/model/ItemGroupMixIn.java
index 25b2505f15b3..f34f89eb4df6 100644
--- a/core/src/main/java/hudson/model/ItemGroupMixIn.java
+++ b/core/src/main/java/hudson/model/ItemGroupMixIn.java
@@ -51,7 +51,7 @@
import java.util.logging.Logger;
import java.util.regex.Matcher;
import jenkins.security.NotReallyRoleSensitiveCallable;
-import org.acegisecurity.AccessDeniedException;
+import org.springframework.security.access.AccessDeniedException;
import org.xml.sax.SAXException;
/**
@@ -229,7 +229,7 @@ public synchronized T copy(T src, String name) throws I
while (matcher.find()) {
if (Secret.decrypt(matcher.group(1)) != null) {
// AccessDeniedException2 does not permit a custom message, and anyway redirecting the user to the login screen is obviously pointless.
- throw new AccessDeniedException(Messages.ItemGroupMixIn_may_not_copy_as_it_contains_secrets_and_(src.getFullName(), Jenkins.getAuthentication().getName(), Item.PERMISSIONS.title, Item.EXTENDED_READ.name, Item.CONFIGURE.name));
+ throw new AccessDeniedException(Messages.ItemGroupMixIn_may_not_copy_as_it_contains_secrets_and_(src.getFullName(), Jenkins.getAuthentication2().getName(), Item.PERMISSIONS.title, Item.EXTENDED_READ.name, Item.CONFIGURE.name));
}
}
}
@@ -282,7 +282,7 @@ public synchronized TopLevelItem createProjectFromXML(String name, InputStream x
}
});
- success = acl.getACL().hasCreatePermission(Jenkins.getAuthentication(), parent, result.getDescriptor())
+ success = acl.getACL().hasCreatePermission2(Jenkins.getAuthentication2(), parent, result.getDescriptor())
&& result.getDescriptor().isApplicableIn(parent);
add(result);
diff --git a/core/src/main/java/hudson/model/Items.java b/core/src/main/java/hudson/model/Items.java
index 298d30235845..e21efe6d5b05 100644
--- a/core/src/main/java/hudson/model/Items.java
+++ b/core/src/main/java/hudson/model/Items.java
@@ -52,9 +52,9 @@
import edu.umd.cs.findbugs.annotations.NonNull;
import jenkins.model.DirectlyModifiableTopLevelItemGroup;
import jenkins.model.Jenkins;
-import org.acegisecurity.Authentication;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
+import org.springframework.security.core.Authentication;
/**
* Convenience methods related to {@link Item}.
@@ -163,16 +163,16 @@ public static DescriptorExtensionList all()
* @since 1.607
*/
public static List all(ItemGroup c) {
- return all(Jenkins.getAuthentication(), c);
+ return all2(Jenkins.getAuthentication2(), c);
}
/**
* Returns all the registered {@link TopLevelItemDescriptor}s that the specified security principal is allowed to
* create within the specified item group.
*
- * @since 1.607
+ * @since TODO
*/
- public static List all(Authentication a, ItemGroup c) {
+ public static List all2(Authentication a, ItemGroup c) {
List result = new ArrayList<>();
ACL acl;
if (c instanceof AccessControlled) {
@@ -182,13 +182,22 @@ public static List all(Authentication a, ItemGroup c) {
acl = Jenkins.get().getACL();
}
for (TopLevelItemDescriptor d: all()) {
- if (acl.hasCreatePermission(a, c, d) && d.isApplicableIn(c)) {
+ if (acl.hasCreatePermission2(a, c, d) && d.isApplicableIn(c)) {
result.add(d);
}
}
return result;
}
+ /**
+ * @deprecated use {@link #all2(Authentication, ItemGroup)}
+ * @since 1.607
+ */
+ @Deprecated
+ public static List all(org.acegisecurity.Authentication a, ItemGroup c) {
+ return all2(a.toSpring(), c);
+ }
+
/**
* @deprecated Underspecified what the parameter is. {@link Descriptor#getId}? A {@link Describable} class name?
*/
@@ -438,7 +447,7 @@ private static void getAllItems(final ItemGroup root, Class
/**
* Gets a read-only view of all the {@link Item}s recursively in the {@link ItemGroup} tree visible to
- * {@link Jenkins#getAuthentication()} without concern for the order in which items are returned. Each iteration
+ * {@link Jenkins#getAuthentication2()} without concern for the order in which items are returned. Each iteration
* of the view will be "live" reflecting the items available between the time the iteration was started and the
* time the iteration was completed, however if items are moved during an iteration - depending on the move - it
* may be possible for such items to escape the entire iteration.
@@ -450,13 +459,13 @@ private static void getAllItems(final ItemGroup root, Class
* @since 2.37
*/
public static Iterable allItems(ItemGroup root, Class type) {
- return allItems(Jenkins.getAuthentication(), root, type);
+ return allItems2(Jenkins.getAuthentication2(), root, type);
}
/**
* Gets a read-only view of all the {@link Item}s recursively matching type and predicate
* in the {@link ItemGroup} tree visible to
- * {@link Jenkins#getAuthentication()} without concern for the order in which items are returned. Each iteration
+ * {@link Jenkins#getAuthentication2()} without concern for the order in which items are returned. Each iteration
* of the view will be "live" reflecting the items available between the time the iteration was started and the
* time the iteration was completed, however if items are moved during an iteration - depending on the move - it
* may be possible for such items to escape the entire iteration.
@@ -469,7 +478,7 @@ public static Iterable allItems(ItemGroup root, Class typ
* @since 2.221
*/
public static Iterable allItems(ItemGroup root, Class type, Predicate pred) {
- return allItems(Jenkins.getAuthentication(), root, type, pred);
+ return allItems2(Jenkins.getAuthentication2(), root, type, pred);
}
/**
@@ -483,10 +492,19 @@ public static Iterable allItems(ItemGroup root, Class typ
* @param type the type.
* @param the type.
* @return An {@link Iterable} for all items.
+ * @since TODO
+ */
+ public static Iterable allItems2(Authentication authentication, ItemGroup root, Class type) {
+ return allItems2(authentication, root, type, t -> true);
+ }
+
+ /**
+ * @deprecated use {@link #allItems2(Authentication, ItemGroup, Class)}
* @since 2.37
*/
- public static Iterable allItems(Authentication authentication, ItemGroup root, Class type) {
- return allItems(authentication, root, type, t -> true);
+ @Deprecated
+ public static Iterable allItems(org.acegisecurity.Authentication authentication, ItemGroup root, Class type) {
+ return allItems2(authentication.toSpring(), root, type);
}
/**
@@ -502,12 +520,21 @@ public static Iterable allItems(Authentication authenticatio
* @param the type.
* @param pred the predicate.
* @return An {@link Iterable} for all items.
- * @since 2.221
+ * @since TODO
*/
- public static Iterable allItems(Authentication authentication, ItemGroup root, Class type, Predicate pred) {
+ public static Iterable allItems2(Authentication authentication, ItemGroup root, Class type, Predicate pred) {
return new AllItemsIterable<>(root, authentication, type, pred);
}
+ /**
+ * @deprecated use {@link #allItems2(Authentication, ItemGroup, Class, Predicate)}
+ * @since 2.221
+ */
+ @Deprecated
+ public static Iterable allItems(org.acegisecurity.Authentication authentication, ItemGroup root, Class type, Predicate pred) {
+ return allItems2(authentication.toSpring(), root, type, pred);
+ }
+
/**
* Finds an item whose name (when referenced from the specified context) is closest to the given name.
* @param the type of item being considered
@@ -630,13 +657,13 @@ public boolean hasNext() {
}
ItemGroup group = stack.pop();
// group.getItems() is responsible for performing the permission check so we will not repeat it
- if (Jenkins.getAuthentication() == authentication) {
+ if (Jenkins.getAuthentication2().equals(authentication)) {
delegate = group.getItems(search).iterator();
} else {
// slower path because the caller has switched authentication
// we need to keep the original authentication so that allItems() can be used
// like getAllItems() without the cost of building the entire list up front
- try (ACLContext ctx = ACL.as(authentication)) {
+ try (ACLContext ctx = ACL.as2(authentication)) {
delegate = group.getItems(search).iterator();
}
}
@@ -679,7 +706,7 @@ public T next() {
*/
static void verifyItemDoesNotAlreadyExist(@NonNull ItemGroup> parent, @NonNull String newName, @CheckForNull Item variant) throws IllegalArgumentException, Failure {
Item existing;
- try (ACLContext ctxt = ACL.as(ACL.SYSTEM)) {
+ try (ACLContext ctxt = ACL.as2(ACL.SYSTEM2)) {
existing = parent.getItem(newName);
}
if (existing != null && existing != variant) {
diff --git a/core/src/main/java/hudson/model/Label.java b/core/src/main/java/hudson/model/Label.java
index dfb01c973114..d2865ef9dcf6 100644
--- a/core/src/main/java/hudson/model/Label.java
+++ b/core/src/main/java/hudson/model/Label.java
@@ -410,7 +410,7 @@ public int getTiedJobCount() {
// denormalize for performance
// we don't need to respect security as much when returning a simple count
- try (ACLContext ctx = ACL.as(ACL.SYSTEM)) {
+ try (ACLContext ctx = ACL.as2(ACL.SYSTEM2)) {
int result = 0;
for (AbstractProject ignored : Jenkins.get().allItems(AbstractProject.class, p -> matches(p.getAssignedLabelString()))) {
++result;
diff --git a/core/src/main/java/hudson/model/ListView.java b/core/src/main/java/hudson/model/ListView.java
index 5aff6afbb7ae..b40df81887ac 100644
--- a/core/src/main/java/hudson/model/ListView.java
+++ b/core/src/main/java/hudson/model/ListView.java
@@ -51,10 +51,8 @@
import net.jcip.annotations.GuardedBy;
import javax.servlet.ServletException;
import jenkins.model.Jenkins;
-import jenkins.model.ParameterizedJobMixIn;
import net.sf.json.JSONObject;
-import org.acegisecurity.AccessDeniedException;
import org.jenkinsci.Symbol;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
@@ -67,6 +65,7 @@
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.interceptor.RequirePOST;
import org.kohsuke.stapler.verb.POST;
+import org.springframework.security.access.AccessDeniedException;
/**
* Displays {@link Job}s in a flat list view.
@@ -532,7 +531,7 @@ public static List getDefaultColumns() {
public static final class Listener extends ItemListener {
@Override
public void onLocationChanged(final Item item, final String oldFullName, final String newFullName) {
- try (ACLContext acl = ACL.as(ACL.SYSTEM)) {
+ try (ACLContext acl = ACL.as2(ACL.SYSTEM2)) {
locationChanged(oldFullName, newFullName);
}
}
@@ -577,7 +576,7 @@ private void renameViewItem(String oldFullName, String newFullName, ViewGroup vg
@Override
public void onDeleted(final Item item) {
- try (ACLContext acl = ACL.as(ACL.SYSTEM)) {
+ try (ACLContext acl = ACL.as2(ACL.SYSTEM2)) {
deleted(item);
}
}
diff --git a/core/src/main/java/hudson/model/Node.java b/core/src/main/java/hudson/model/Node.java
index d8417f9c8ce3..a0eb7a07499d 100644
--- a/core/src/main/java/hudson/model/Node.java
+++ b/core/src/main/java/hudson/model/Node.java
@@ -65,7 +65,6 @@
import jenkins.util.SystemProperties;
import jenkins.util.io.OnMaster;
import net.sf.json.JSONObject;
-import org.acegisecurity.Authentication;
import org.jvnet.localizer.Localizable;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.ProtectedExternally;
@@ -74,6 +73,7 @@
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
+import org.springframework.security.core.Authentication;
/**
* Base type of Jenkins agents (although in practice, you probably extend {@link Slave} to define a new agent type).
@@ -398,8 +398,8 @@ public CauseOfBlockage canTake(Queue.BuildableItem item) {
}
}
- Authentication identity = item.authenticate();
- if (!(SKIP_BUILD_CHECK_ON_FLYWEIGHTS && item.task instanceof Queue.FlyweightTask) && !hasPermission(identity, Computer.BUILD)) {
+ Authentication identity = item.authenticate2();
+ if (!(SKIP_BUILD_CHECK_ON_FLYWEIGHTS && item.task instanceof Queue.FlyweightTask) && !hasPermission2(identity, Computer.BUILD)) {
// doesn't have a permission
return CauseOfBlockage.fromMessage(Messages._Node_LackingBuildPermission(identity.getName(), getDisplayName()));
}
diff --git a/core/src/main/java/hudson/model/Queue.java b/core/src/main/java/hudson/model/Queue.java
index 317c5829ac1f..afdb68a5f11e 100644
--- a/core/src/main/java/hudson/model/Queue.java
+++ b/core/src/main/java/hudson/model/Queue.java
@@ -116,8 +116,6 @@
import jenkins.model.Jenkins;
import jenkins.security.QueueItemAuthenticator;
import jenkins.util.AtmostOneTaskExecutor;
-import org.acegisecurity.AccessDeniedException;
-import org.acegisecurity.Authentication;
import org.jenkinsci.bytecode.AdaptField;
import org.jenkinsci.remoting.RoleChecker;
import org.kohsuke.accmod.restrictions.NoExternalUse;
@@ -139,6 +137,8 @@
import jenkins.model.queue.CompositeCauseOfBlockage;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.interceptor.RequirePOST;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.core.Authentication;
/**
* Build queue.
@@ -2000,13 +2000,26 @@ default Collection extends SubTask> getSubTasks() {
* 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.
*
- * @return by default, {@link ACL#SYSTEM}
- * @since 1.520
+ * @return by default, {@link ACL#SYSTEM2}
+ * @since TODO
* @see QueueItemAuthenticator
* @see Tasks#getDefaultAuthenticationOf(Queue.Task)
*/
- default @NonNull Authentication getDefaultAuthentication() {
- return ACL.SYSTEM;
+ default @NonNull Authentication getDefaultAuthentication2() {
+ if (Util.isOverridden(Queue.Task.class, getClass(), "getDefaultAuthentication")) {
+ return getDefaultAuthentication().toSpring();
+ } else {
+ return ACL.SYSTEM2;
+ }
+ }
+
+ /**
+ * @deprecated use {@link #getDefaultAuthentication2()}
+ * @since 1.520
+ */
+ @Deprecated
+ default @NonNull org.acegisecurity.Authentication getDefaultAuthentication() {
+ return org.acegisecurity.Authentication.fromSpring(getDefaultAuthentication2());
}
/**
@@ -2022,13 +2035,27 @@ default Collection extends SubTask> getSubTasks() {
* older versions of Jenkins may not have this method implemented. Called private method _getDefaultAuthenticationOf(Task) on {@link Tasks}
* to avoid {@link AbstractMethodError}.
*
- * @since 1.592
+ * @since TODO
* @see QueueItemAuthenticator
* @see Tasks#getDefaultAuthenticationOf(Queue.Task, Queue.Item)
*/
- default @NonNull Authentication getDefaultAuthentication(Queue.Item item) {
- return getDefaultAuthentication();
+ default @NonNull Authentication getDefaultAuthentication2(Queue.Item item) {
+ if (Util.isOverridden(Queue.Task.class, getClass(), "getDefaultAuthentication", Queue.Item.class)) {
+ return getDefaultAuthentication(item).toSpring();
+ } else {
+ return getDefaultAuthentication2();
+ }
+ }
+
+ /**
+ * @deprecated use {@link #getDefaultAuthentication2(Queue.Item)}
+ * @since 1.592
+ */
+ @Deprecated
+ default @NonNull org.acegisecurity.Authentication getDefaultAuthentication(Queue.Item item) {
+ return org.acegisecurity.Authentication.fromSpring(getDefaultAuthentication2(item));
}
+
}
/**
@@ -2316,19 +2343,28 @@ public HttpResponse doCancelQueue() {
*
* 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
+ * return the identity of the user who queued the task, or {@link ACL#SYSTEM2} to bypass the access control
* and run as the super user.
*
- * @since 1.520
+ * @since TODO
*/
@NonNull
- public Authentication authenticate() {
+ public Authentication authenticate2() {
for (QueueItemAuthenticator auth : QueueItemAuthenticatorProvider.authenticators()) {
- Authentication a = auth.authenticate(this);
+ Authentication a = auth.authenticate2(this);
if (a!=null)
return a;
}
- return task.getDefaultAuthentication(this);
+ return task.getDefaultAuthentication2(this);
+ }
+
+ /**
+ * @deprecated use {@link #authenticate2}
+ * @since 1.520
+ */
+ @Deprecated
+ public org.acegisecurity.Authentication authenticate() {
+ return org.acegisecurity.Authentication.fromSpring(authenticate2());
}
@Restricted(DoNotUse.class) // only for Stapler export
diff --git a/core/src/main/java/hudson/model/Run.java b/core/src/main/java/hudson/model/Run.java
index d8ee74992094..825640440098 100644
--- a/core/src/main/java/hudson/model/Run.java
+++ b/core/src/main/java/hudson/model/Run.java
@@ -119,8 +119,6 @@
import jenkins.util.VirtualFile;
import jenkins.util.io.OnMaster;
import net.sf.json.JSONObject;
-import org.acegisecurity.AccessDeniedException;
-import org.acegisecurity.Authentication;
import org.apache.commons.io.IOUtils;
import org.apache.commons.jelly.XMLOutput;
import org.apache.commons.lang.ArrayUtils;
@@ -136,6 +134,8 @@
import org.kohsuke.stapler.export.ExportedBean;
import org.kohsuke.stapler.interceptor.RequirePOST;
import org.kohsuke.stapler.verb.POST;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.core.Authentication;
/**
* A particular execution of {@link Job}.
@@ -1888,12 +1888,12 @@ protected final void execute(@NonNull RunExecution job) {
listener = createBuildListener(job, logger, charset);
listener.started(getCauses());
- Authentication auth = Jenkins.getAuthentication();
- if (auth.equals(ACL.SYSTEM)) {
+ Authentication auth = Jenkins.getAuthentication2();
+ if (auth.equals(ACL.SYSTEM2)) {
listener.getLogger().println(Messages.Run_running_as_SYSTEM());
} else {
String id = auth.getName();
- if (!auth.equals(Jenkins.ANONYMOUS)) {
+ if (!auth.equals(Jenkins.ANONYMOUS2)) {
final User usr = User.getById(id, false);
if (usr != null) { // Encode user hyperlink for existing users
id = ModelHyperlinkNote.encodeTo(usr);
@@ -2476,8 +2476,9 @@ public EnvVars getEnvironment() throws IOException, InterruptedException {
* @param id as produced by {@link #getExternalizableId}
* @return the same run, or null if the job or run was not found
* @throws IllegalArgumentException if the ID is malformed
+ * @throws AccessDeniedException as per {@link ItemGroup#getItem}
*/
- public @CheckForNull static Run,?> fromExternalizableId(String id) throws IllegalArgumentException {
+ public @CheckForNull static Run,?> fromExternalizableId(String id) throws IllegalArgumentException, AccessDeniedException {
int hash = id.lastIndexOf('#');
if (hash <= 0) {
throw new IllegalArgumentException("Invalid id");
diff --git a/core/src/main/java/hudson/model/TaskAction.java b/core/src/main/java/hudson/model/TaskAction.java
index d9c4a7a748da..76b045845cd4 100644
--- a/core/src/main/java/hudson/model/TaskAction.java
+++ b/core/src/main/java/hudson/model/TaskAction.java
@@ -62,7 +62,7 @@ public abstract class TaskAction extends AbstractModelObject implements Action {
/**
* Gets the permission object that represents the permission (against {@link #getACL}) to perform this task.
- * Generally your implementation of {@link #getIconFileName} should return null if {@code !getACL().hasPermission(getPermission())}.
+ * Generally your implementation of {@link #getIconFileName} should return null if {@code !getACL().hasPermission2(getPermission())}.
*/
protected abstract Permission getPermission();
diff --git a/core/src/main/java/hudson/model/TopLevelItemDescriptor.java b/core/src/main/java/hudson/model/TopLevelItemDescriptor.java
index b7942df57991..9dac84b79505 100644
--- a/core/src/main/java/hudson/model/TopLevelItemDescriptor.java
+++ b/core/src/main/java/hudson/model/TopLevelItemDescriptor.java
@@ -26,7 +26,6 @@
import hudson.ExtensionList;
import jenkins.model.Jenkins;
import jenkins.model.item_category.ItemCategory;
-import org.acegisecurity.AccessDeniedException;
import org.apache.commons.jelly.Script;
import org.apache.commons.jelly.XMLOutput;
import org.apache.commons.lang.StringUtils;
@@ -45,6 +44,7 @@
import java.io.StringWriter;
import java.util.logging.Level;
import java.util.logging.Logger;
+import org.springframework.security.access.AccessDeniedException;
/**
* {@link Descriptor} for {@link TopLevelItem}s.
diff --git a/core/src/main/java/hudson/model/UpdateCenter.java b/core/src/main/java/hudson/model/UpdateCenter.java
index d217f1decd14..a296ae91575c 100644
--- a/core/src/main/java/hudson/model/UpdateCenter.java
+++ b/core/src/main/java/hudson/model/UpdateCenter.java
@@ -67,7 +67,6 @@
import jenkins.util.io.OnMaster;
import net.sf.json.JSONObject;
-import org.acegisecurity.Authentication;
import org.apache.commons.io.input.CountingInputStream;
import org.apache.commons.io.output.NullOutputStream;
import org.jenkinsci.Symbol;
@@ -125,6 +124,7 @@
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
import org.kohsuke.stapler.interceptor.RequirePOST;
+import org.springframework.security.core.Authentication;
/**
@@ -702,7 +702,7 @@ private boolean checkMinVersion(@CheckForNull Plugin p, @CheckForNull VersionNum
@RequirePOST
public void doUpgrade(StaplerResponse rsp) throws IOException, ServletException {
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
- HudsonUpgradeJob job = new HudsonUpgradeJob(getCoreSource(), Jenkins.getAuthentication());
+ HudsonUpgradeJob job = new HudsonUpgradeJob(getCoreSource(), Jenkins.getAuthentication2());
if(!Lifecycle.get().canRewriteHudsonWar()) {
sendError("Jenkins upgrade not supported in this running mode");
return;
@@ -813,7 +813,7 @@ public void doDowngrade(StaplerResponse rsp) throws IOException, ServletExceptio
return;
}
- HudsonDowngradeJob job = new HudsonDowngradeJob(getCoreSource(), Jenkins.getAuthentication());
+ HudsonDowngradeJob job = new HudsonDowngradeJob(getCoreSource(), Jenkins.getAuthentication2());
LOGGER.info("Scheduling the core downgrade");
addJob(job);
rsp.sendRedirect2(".");
@@ -825,7 +825,7 @@ public void doDowngrade(StaplerResponse rsp) throws IOException, ServletExceptio
@RequirePOST
public void doRestart(StaplerResponse rsp) throws IOException, ServletException {
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
- HudsonDowngradeJob job = new HudsonDowngradeJob(getCoreSource(), Jenkins.getAuthentication());
+ HudsonDowngradeJob job = new HudsonDowngradeJob(getCoreSource(), Jenkins.getAuthentication2());
LOGGER.info("Scheduling the core downgrade");
addJob(job);
@@ -1534,7 +1534,7 @@ public synchronized boolean cancel() {
public RestartJenkinsJob(UpdateSite site) {
super(site);
- this.authentication = Jenkins.getAuthentication().getName();
+ this.authentication = Jenkins.getAuthentication2().getName();
}
public synchronized void run() {
@@ -2155,7 +2155,7 @@ public void _run() throws IOException, InstallationStatus {
// if this is a bundled plugin, make sure it won't get overwritten
PluginWrapper pw = plugin.getInstalled();
if (pw!=null && pw.isBundled()) {
- try (ACLContext ctx = ACL.as(ACL.SYSTEM)) {
+ try (ACLContext ctx = ACL.as2(ACL.SYSTEM2)) {
pw.doPin();
}
}
diff --git a/core/src/main/java/hudson/model/UpdateSite.java b/core/src/main/java/hudson/model/UpdateSite.java
index 4ba581611be2..3eeaa7867453 100644
--- a/core/src/main/java/hudson/model/UpdateSite.java
+++ b/core/src/main/java/hudson/model/UpdateSite.java
@@ -262,7 +262,7 @@ public FormValidation doVerifySignature() throws IOException {
* @since 2.9
*/
protected UpdateCenter.InstallationJob createInstallationJob(Plugin plugin, UpdateCenter uc, boolean dynamicLoad) {
- return uc.new InstallationJob(plugin, this, Jenkins.getAuthentication(), dynamicLoad);
+ return uc.new InstallationJob(plugin, this, Jenkins.getAuthentication2(), dynamicLoad);
}
/**
@@ -1598,7 +1598,7 @@ public Future deploy(boolean dynamicLoad, @CheckForNull UUID co
public Future deployBackup() {
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
UpdateCenter uc = Jenkins.get().getUpdateCenter();
- return uc.addJob(uc.new PluginDowngradeJob(this, UpdateSite.this, Jenkins.getAuthentication()));
+ return uc.addJob(uc.new PluginDowngradeJob(this, UpdateSite.this, Jenkins.getAuthentication2()));
}
/**
* Making the installation web bound.
diff --git a/core/src/main/java/hudson/model/User.java b/core/src/main/java/hudson/model/User.java
index f9b5d576b447..d50e79c1dca1 100644
--- a/core/src/main/java/hudson/model/User.java
+++ b/core/src/main/java/hudson/model/User.java
@@ -26,6 +26,9 @@
import com.google.common.base.Predicate;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
+import edu.umd.cs.findbugs.annotations.CheckForNull;
+import edu.umd.cs.findbugs.annotations.NonNull;
+import edu.umd.cs.findbugs.annotations.Nullable;
import hudson.BulkChange;
import hudson.CopyOnWrite;
import hudson.Extension;
@@ -40,12 +43,11 @@
import hudson.security.ACL;
import hudson.security.AccessControlled;
import hudson.security.SecurityRealm;
-import hudson.security.UserMayOrMayNotExistException;
+import hudson.security.UserMayOrMayNotExistException2;
import hudson.util.FormApply;
import hudson.util.FormValidation;
import hudson.util.RunList;
import hudson.util.XStream2;
-
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -62,26 +64,16 @@
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
-import edu.umd.cs.findbugs.annotations.CheckForNull;
-import edu.umd.cs.findbugs.annotations.NonNull;
-import edu.umd.cs.findbugs.annotations.Nullable;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
-
import jenkins.model.IdStrategy;
import jenkins.model.Jenkins;
import jenkins.model.ModelObjectWithContextMenu;
-import jenkins.security.ImpersonatingUserDetailsService;
+import jenkins.security.ImpersonatingUserDetailsService2;
import jenkins.security.LastGrantedAuthoritiesProperty;
import jenkins.security.UserDetailsCache;
import jenkins.util.SystemProperties;
import net.sf.json.JSONObject;
-import org.acegisecurity.Authentication;
-import org.acegisecurity.GrantedAuthority;
-import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
-import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken;
-import org.acegisecurity.userdetails.UserDetails;
-import org.acegisecurity.userdetails.UsernameNotFoundException;
import org.apache.commons.lang.StringUtils;
import org.jenkinsci.Symbol;
import org.kohsuke.accmod.Restricted;
@@ -93,7 +85,13 @@
import org.kohsuke.stapler.export.ExportedBean;
import org.kohsuke.stapler.interceptor.RequirePOST;
import org.kohsuke.stapler.verb.POST;
-import org.springframework.dao.DataAccessException;
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
/**
* Represents a user.
@@ -363,10 +361,23 @@ public T getProperty(Class clazz) {
* logged in.
*
* @throws UsernameNotFoundException If this user is not a valid user in the backend {@link SecurityRealm}.
+ * @since TODO
+ */
+ public @NonNull Authentication impersonate2() throws UsernameNotFoundException {
+ return this.impersonate(this.getUserDetailsForImpersonation2());
+ }
+
+ /**
+ * @deprecated use {@link #impersonate2}
* @since 1.419
*/
- public @NonNull Authentication impersonate() throws UsernameNotFoundException {
- return this.impersonate(this.getUserDetailsForImpersonation());
+ @Deprecated
+ public @NonNull org.acegisecurity.Authentication impersonate() throws org.acegisecurity.userdetails.UsernameNotFoundException {
+ try {
+ return org.acegisecurity.Authentication.fromSpring(impersonate2());
+ } catch (AuthenticationException x) {
+ throw org.acegisecurity.AuthenticationException.fromSpring(x);
+ }
}
/**
@@ -377,17 +388,18 @@ public T getProperty(Class clazz) {
*
* @return userDetails for the user, in case he's not found but seems legitimate, we provide a userDetails with minimum access
* @throws UsernameNotFoundException If this user is not a valid user in the backend {@link SecurityRealm}.
+ * @since TODO
*/
- public @NonNull UserDetails getUserDetailsForImpersonation() throws UsernameNotFoundException {
- ImpersonatingUserDetailsService userDetailsService = new ImpersonatingUserDetailsService(
- Jenkins.get().getSecurityRealm().getSecurityComponents().userDetails
+ public @NonNull UserDetails getUserDetailsForImpersonation2() throws UsernameNotFoundException {
+ ImpersonatingUserDetailsService2 userDetailsService = new ImpersonatingUserDetailsService2(
+ Jenkins.get().getSecurityRealm().getSecurityComponents().userDetails2
);
try {
UserDetails userDetails = userDetailsService.loadUserByUsername(id);
LOGGER.log(Level.FINE, "Impersonation of the user {0} was a success", id);
return userDetails;
- } catch (UserMayOrMayNotExistException e) {
+ } catch (UserMayOrMayNotExistException2 e) {
LOGGER.log(Level.FINE, "The user {0} may or may not exist in the SecurityRealm, so we provide minimum access", id);
} catch (UsernameNotFoundException e) {
if (ALLOW_NON_EXISTENT_USER_TO_LOGIN) {
@@ -396,23 +408,32 @@ public T getProperty(Class clazz) {
LOGGER.log(Level.FINE, "The user {0} was not found in the SecurityRealm", id);
throw e;
}
- } catch (DataAccessException e) {
- // seems like it's in the same boat as UserMayOrMayNotExistException
- LOGGER.log(Level.FINE, "The user {0} retrieval just threw a DataAccess exception with msg = {1}, so we provide minimum access", new Object[]{id, e.getMessage()});
}
return new LegitimateButUnknownUserDetails(id);
}
+ /**
+ * @deprecated use {@link #getUserDetailsForImpersonation2}
+ */
+ @Deprecated
+ public @NonNull org.acegisecurity.userdetails.UserDetails getUserDetailsForImpersonation() throws org.acegisecurity.userdetails.UsernameNotFoundException {
+ try {
+ return org.acegisecurity.userdetails.UserDetails.fromSpring(getUserDetailsForImpersonation2());
+ } catch (AuthenticationException x) {
+ throw org.acegisecurity.AuthenticationException.fromSpring(x);
+ }
+ }
+
/**
* Only used for a legitimate user we have no idea about. We give it only minimum access
*/
- private static class LegitimateButUnknownUserDetails extends org.acegisecurity.userdetails.User {
+ private static class LegitimateButUnknownUserDetails extends org.springframework.security.core.userdetails.User {
private LegitimateButUnknownUserDetails(String username) throws IllegalArgumentException {
super(
username, "",
true, true, true, true,
- new GrantedAuthority[]{SecurityRealm.AUTHENTICATED_AUTHORITY}
+ Collections.singleton(SecurityRealm.AUTHENTICATED_AUTHORITY2)
);
}
}
@@ -420,8 +441,8 @@ private LegitimateButUnknownUserDetails(String username) throws IllegalArgumentE
/**
* Creates an {@link Authentication} object that represents this user using the given userDetails
*
- * @param userDetails Provided by {@link #getUserDetailsForImpersonation()}.
- * @see #getUserDetailsForImpersonation()
+ * @param userDetails Provided by {@link #getUserDetailsForImpersonation2()}.
+ * @see #getUserDetailsForImpersonation2()
*/
@Restricted(NoExternalUse.class)
public @NonNull Authentication impersonate(@NonNull UserDetails userDetails) {
@@ -566,7 +587,7 @@ public void doSubmitDescription(StaplerRequest req, StaplerResponse rsp) throws
* @since 1.172
*/
public static @CheckForNull User current() {
- return get(Jenkins.getAuthentication());
+ return get2(Jenkins.getAuthentication2());
}
/**
@@ -575,9 +596,9 @@ public void doSubmitDescription(StaplerRequest req, StaplerResponse rsp) throws
*
* @param a the supplied {@link Authentication} .
* @return a {@link User} object for the supplied {@link Authentication} or {@code null}
- * @since 1.609
+ * @since TODO
*/
- public static @CheckForNull User get(@CheckForNull Authentication a) {
+ public static @CheckForNull User get2(@CheckForNull Authentication a) {
if (a == null || a instanceof AnonymousAuthenticationToken)
return null;
@@ -585,6 +606,15 @@ public void doSubmitDescription(StaplerRequest req, StaplerResponse rsp) throws
return getById(a.getName(), true);
}
+ /**
+ * @deprecated use {@link #get2(Authentication)}
+ * @since 1.609
+ */
+ @Deprecated
+ public static @CheckForNull User get(@CheckForNull org.acegisecurity.Authentication a) {
+ return get2(a != null ? a.toSpring() : null);
+ }
+
/**
* Gets the {@link User} object by its {@code id}
*
@@ -859,7 +889,7 @@ public void doConfigSubmit(StaplerRequest req, StaplerResponse rsp) throws IOExc
@RequirePOST
public void doDoDelete(StaplerRequest req, StaplerResponse rsp) throws IOException {
checkPermission(Jenkins.ADMINISTER);
- if (idStrategy().equals(id, Jenkins.getAuthentication().getName())) {
+ if (idStrategy().equals(id, Jenkins.getAuthentication2().getName())) {
rsp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Cannot delete self");
return;
}
@@ -898,8 +928,8 @@ public void doRssLatest(StaplerRequest req, StaplerResponse rsp) throws IOExcept
public ACL getACL() {
ACL base = Jenkins.get().getAuthorizationStrategy().getACL(this);
// always allow a non-anonymous user full control of himself.
- return ACL.lambda((a, permission) -> (idStrategy().equals(a.getName(), id) && !(a instanceof AnonymousAuthenticationToken))
- || base.hasPermission(a, permission));
+ return ACL.lambda2((a, permission) -> (idStrategy().equals(a.getName(), id) && !(a instanceof AnonymousAuthenticationToken))
+ || base.hasPermission2(a, permission));
}
/**
@@ -907,14 +937,14 @@ public ACL getACL() {
*/
public boolean canDelete() {
final IdStrategy strategy = idStrategy();
- return hasPermission(Jenkins.ADMINISTER) && !strategy.equals(id, Jenkins.getAuthentication().getName())
+ return hasPermission(Jenkins.ADMINISTER) && !strategy.equals(id, Jenkins.getAuthentication2().getName())
&& UserIdMapper.getInstance().isMapped(id);
}
/**
* Checks for authorities (groups) associated with this user.
* If the caller lacks {@link Jenkins#ADMINISTER}, or any problems arise, returns an empty list.
- * {@link SecurityRealm#AUTHENTICATED_AUTHORITY} and the username, if present, are omitted.
+ * {@link SecurityRealm#AUTHENTICATED_AUTHORITY2} and the username, if present, are omitted.
*
* @return a possibly empty list
* @since 1.498
@@ -926,13 +956,13 @@ public boolean canDelete() {
List r = new ArrayList<>();
Authentication authentication;
try {
- authentication = impersonate();
+ authentication = impersonate2();
} catch (UsernameNotFoundException x) {
LOGGER.log(Level.FINE, "cannot look up authorities for " + id, x);
return Collections.emptyList();
}
for (GrantedAuthority a : authentication.getAuthorities()) {
- if (a.equals(SecurityRealm.AUTHENTICATED_AUTHORITY)) {
+ if (a.equals(SecurityRealm.AUTHENTICATED_AUTHORITY2)) {
continue;
}
String n = a.getAuthority();
@@ -1212,7 +1242,7 @@ public String resolveCanonicalId(String idOrFullName, Map context) {
return userDetails.getUsername();
} catch (UsernameNotFoundException x) {
LOGGER.log(Level.FINE, "not sure whether " + idOrFullName + " is a valid username or not", x);
- } catch (DataAccessException | ExecutionException x) {
+ } catch (ExecutionException x) {
LOGGER.log(Level.FINE, "could not look up " + idOrFullName, x);
} finally {
resolving.set(false);
diff --git a/core/src/main/java/hudson/model/View.java b/core/src/main/java/hudson/model/View.java
index fde5d094c465..5b82acc1725a 100644
--- a/core/src/main/java/hudson/model/View.java
+++ b/core/src/main/java/hudson/model/View.java
@@ -1110,7 +1110,7 @@ public Categories doItemCategories(StaplerRequest req, StaplerResponse rsp, @Que
} else {
ctx = null;
}
- for (TopLevelItemDescriptor descriptor : DescriptorVisibilityFilter.apply(getOwner().getItemGroup(), Items.all(Jenkins.getAuthentication(), getOwner().getItemGroup()))) {
+ for (TopLevelItemDescriptor descriptor : DescriptorVisibilityFilter.apply(getOwner().getItemGroup(), Items.all2(Jenkins.getAuthentication2(), getOwner().getItemGroup()))) {
ItemCategory ic = ItemCategory.getCategory(descriptor);
Map metadata = new HashMap<>();
@@ -1291,7 +1291,7 @@ public static List allInstantiable() {
}
for (ViewDescriptor d : DescriptorVisibilityFilter.apply(owner, all())) {
if (d.isApplicableIn(owner) && d.isInstantiable()
- && owner.getACL().hasCreatePermission(Jenkins.getAuthentication(), owner, d)) {
+ && owner.getACL().hasCreatePermission2(Jenkins.getAuthentication2(), owner, d)) {
r.add(d);
}
}
diff --git a/core/src/main/java/hudson/model/listeners/ItemListener.java b/core/src/main/java/hudson/model/listeners/ItemListener.java
index b87db51150dd..9f6a336cd7de 100644
--- a/core/src/main/java/hudson/model/listeners/ItemListener.java
+++ b/core/src/main/java/hudson/model/listeners/ItemListener.java
@@ -272,7 +272,7 @@ public static void fireLocationChange(final Item rootItem, final String oldFullN
return null;
});
if (rootItem instanceof ItemGroup) {
- for (final Item child : Items.allItems(ACL.SYSTEM, (ItemGroup)rootItem, Item.class)) {
+ for (final Item child : Items.allItems2(ACL.SYSTEM2, (ItemGroup)rootItem, Item.class)) {
final String childNew = child.getFullName();
assert childNew.startsWith(newFullName);
assert childNew.charAt(newFullName.length()) == '/';
diff --git a/core/src/main/java/hudson/model/queue/MappingWorksheet.java b/core/src/main/java/hudson/model/queue/MappingWorksheet.java
index d953b48fd1b7..00c42e3bae38 100644
--- a/core/src/main/java/hudson/model/queue/MappingWorksheet.java
+++ b/core/src/main/java/hudson/model/queue/MappingWorksheet.java
@@ -134,7 +134,7 @@ public boolean canAccept(WorkChunk c) {
if (c.assignedLabel!=null && !c.assignedLabel.contains(node))
return false; // label mismatch
- if (!(Node.SKIP_BUILD_CHECK_ON_FLYWEIGHTS && item.task instanceof Queue.FlyweightTask) && !nodeAcl.hasPermission(item.authenticate(), Computer.BUILD))
+ if (!(Node.SKIP_BUILD_CHECK_ON_FLYWEIGHTS && item.task instanceof Queue.FlyweightTask) && !nodeAcl.hasPermission2(item.authenticate2(), Computer.BUILD))
return false; // tasks don't have a permission to run on this node
return true;
diff --git a/core/src/main/java/hudson/model/queue/Tasks.java b/core/src/main/java/hudson/model/queue/Tasks.java
index c591fe494075..470cfb11d222 100644
--- a/core/src/main/java/hudson/model/queue/Tasks.java
+++ b/core/src/main/java/hudson/model/queue/Tasks.java
@@ -27,12 +27,11 @@
import hudson.model.Queue.Item;
import hudson.model.Queue.Task;
import edu.umd.cs.findbugs.annotations.CheckForNull;
-import org.acegisecurity.Authentication;
-
import java.util.Collection;
import edu.umd.cs.findbugs.annotations.NonNull;
import jenkins.security.QueueItemAuthenticator;
import jenkins.security.QueueItemAuthenticatorProvider;
+import org.springframework.security.core.Authentication;
/**
* Convenience methods around {@link Task} and {@link SubTask}.
@@ -80,36 +79,46 @@ public static hudson.model.Item getItemOf(@NonNull SubTask t) {
return p instanceof hudson.model.Item ? (hudson.model.Item)p : null;
}
- /** @deprecated call {@link Task#getDefaultAuthentication()} directly */
+ /** @deprecated call {@link Queue.Task#getDefaultAuthentication()} directly */
@Deprecated
@NonNull
- public static Authentication getDefaultAuthenticationOf(Task t) {
+ public static org.acegisecurity.Authentication getDefaultAuthenticationOf(Task t) {
return t.getDefaultAuthentication();
}
- /** @deprecated call {@link Task#getDefaultAuthentication(Item)} directly */
+ /** @deprecated call {@link Queue.Task#getDefaultAuthentication(Item)} directly */
@Deprecated
@NonNull
- public static Authentication getDefaultAuthenticationOf(Task t, Item item) {
+ public static org.acegisecurity.Authentication getDefaultAuthenticationOf(Task t, Item item) {
return t.getDefaultAuthentication(item);
}
/**
* Finds what authentication a task is likely to be run under when scheduled.
- * The actual authentication after scheduling ({@link hudson.model.Queue.Item#authenticate}) might differ,
- * in case some {@link QueueItemAuthenticator#authenticate(hudson.model.Queue.Item)} takes (for example) actions into consideration.
+ * The actual authentication after scheduling ({@link hudson.model.Queue.Item#authenticate2}) might differ,
+ * in case some {@link QueueItemAuthenticator#authenticate2(hudson.model.Queue.Item)} takes (for example) actions into consideration.
* @param t a task
- * @return an authentication as specified by some {@link QueueItemAuthenticator#authenticate(hudson.model.Queue.Task)}; else {@link Task#getDefaultAuthentication()}
- * @since 1.560
+ * @return an authentication as specified by some {@link QueueItemAuthenticator#authenticate2(hudson.model.Queue.Task)}; else {@link Task#getDefaultAuthentication2()}
+ * @since TODO
*/
- public static @NonNull Authentication getAuthenticationOf(@NonNull Task t) {
+ public static @NonNull Authentication getAuthenticationOf2(@NonNull Task t) {
for (QueueItemAuthenticator qia : QueueItemAuthenticatorProvider.authenticators()) {
- Authentication a = qia.authenticate(t);
+ Authentication a = qia.authenticate2(t);
if (a != null) {
return a;
}
}
- return t.getDefaultAuthentication();
+ return t.getDefaultAuthentication2();
+ }
+
+ /**
+ * @deprecated use {@link #getAuthenticationOf2}
+ * @since 1.560
+ */
+ @Deprecated
+ public static @NonNull org.acegisecurity.Authentication getAuthenticationOf(@NonNull Task t) {
+ return org.acegisecurity.Authentication.fromSpring(getAuthenticationOf2(t));
}
+
}
diff --git a/core/src/main/java/hudson/security/ACL.java b/core/src/main/java/hudson/security/ACL.java
index 963fd398b7ee..e1412e5809f6 100644
--- a/core/src/main/java/hudson/security/ACL.java
+++ b/core/src/main/java/hudson/security/ACL.java
@@ -23,34 +23,34 @@
*/
package hudson.security;
+import edu.umd.cs.findbugs.annotations.CheckForNull;
+import edu.umd.cs.findbugs.annotations.NonNull;
+import hudson.Util;
+import hudson.model.Item;
+import hudson.model.ItemGroup;
+import hudson.model.TopLevelItemDescriptor;
import hudson.model.User;
import hudson.model.View;
import hudson.model.ViewDescriptor;
import hudson.model.ViewGroup;
+import hudson.remoting.Callable;
import java.util.LinkedHashSet;
import java.util.Set;
-import java.util.stream.Collectors;
-import edu.umd.cs.findbugs.annotations.CheckForNull;
-import edu.umd.cs.findbugs.annotations.NonNull;
-
-import hudson.model.Item;
-import hudson.remoting.Callable;
-import hudson.model.ItemGroup;
-import hudson.model.TopLevelItemDescriptor;
import java.util.function.BiFunction;
-import jenkins.security.NonSerializableSecurityContext;
+import java.util.stream.Collectors;
import jenkins.model.Jenkins;
+import jenkins.security.NonSerializableSecurityContext;
import jenkins.security.NotReallyRoleSensitiveCallable;
-import org.acegisecurity.AccessDeniedException;
-import org.acegisecurity.Authentication;
-import org.acegisecurity.context.SecurityContext;
-import org.acegisecurity.context.SecurityContextHolder;
-import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.acegisecurity.acls.sid.PrincipalSid;
import org.acegisecurity.acls.sid.Sid;
-import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
/**
* Gate-keeper that controls access to Hudson's model objects.
@@ -68,15 +68,15 @@ public abstract class ACL {
* if the user doesn't have the permission.
*/
public final void checkPermission(@NonNull Permission p) {
- Authentication a = Jenkins.getAuthentication();
- if (a == SYSTEM) {
+ Authentication a = Jenkins.getAuthentication2();
+ if (a.equals(SYSTEM2)) {
return;
}
- if (!hasPermission(a,p)) {
+ if (!hasPermission2(a,p)) {
while (!p.enabled && p.impliedBy != null) {
p = p.impliedBy;
}
- throw new AccessDeniedException2(a,p);
+ throw new AccessDeniedException3(a,p);
}
}
@@ -99,7 +99,7 @@ public final void checkAnyPermission(@NonNull Permission... permissions) {
boolean failed = !hasAnyPermission(permissions);
- Authentication authentication = Jenkins.getAuthentication();
+ Authentication authentication = Jenkins.getAuthentication2();
if (failed) { // we know that none of the permissions are granted
Set enabledPermissions = new LinkedHashSet<>();
for (Permission p : permissions) {
@@ -130,11 +130,11 @@ public final void checkAnyPermission(@NonNull Permission... permissions) {
* if the user doesn't have the permission.
*/
public final boolean hasPermission(@NonNull Permission p) {
- Authentication a = Jenkins.getAuthentication();
- if (a == SYSTEM) {
+ Authentication a = Jenkins.getAuthentication2();
+ if (a.equals(SYSTEM2)) {
return true;
}
- return hasPermission(a, p);
+ return hasPermission2(a, p);
}
/**
@@ -151,8 +151,8 @@ public final boolean hasAnyPermission(@NonNull Permission... permissions) {
throw new IllegalArgumentException("At least one permission must be provided");
}
- Authentication a = Jenkins.getAuthentication();
- if (a == SYSTEM) {
+ Authentication a = Jenkins.getAuthentication2();
+ if (a.equals(SYSTEM2)) {
return true;
}
@@ -168,21 +168,50 @@ public final boolean hasAnyPermission(@NonNull Permission... permissions) {
* Checks if the given principle has the given permission.
*
*
- * Note that {@link #SYSTEM} can be passed in as the authentication parameter,
+ * Note that {@link #SYSTEM2} can be passed in as the authentication parameter,
* in which case you should probably just assume it has every permission.
+ * @since TODO
*/
- public abstract boolean hasPermission(@NonNull Authentication a, @NonNull Permission permission);
+ public boolean hasPermission2(@NonNull Authentication a, @NonNull Permission permission) {
+ if (Util.isOverridden(ACL.class, getClass(), "hasPermission", org.acegisecurity.Authentication.class, Permission.class)) {
+ return hasPermission(org.acegisecurity.Authentication.fromSpring(a), permission);
+ } else {
+ throw new AbstractMethodError("implement hasPermission2");
+ }
+ }
+
+ /**
+ * @deprecated use {@link #hasPermission2}
+ */
+ @Deprecated
+ public boolean hasPermission(@NonNull org.acegisecurity.Authentication a, @NonNull Permission permission) {
+ return hasPermission2(a.toSpring(), permission);
+ }
/**
* Creates a simple {@link ACL} implementation based on a “single-abstract-method” easily implemented via lambda syntax.
- * @param impl the implementation of {@link ACL#hasPermission(Authentication, Permission)}
+ * @param impl the implementation of {@link ACL#hasPermission2(Authentication, Permission)}
* @return an adapter to that lambda
+ * @since TODO
+ */
+ public static ACL lambda2(final BiFunction impl) {
+ return new ACL() {
+ @Override
+ public boolean hasPermission2(Authentication a, Permission permission) {
+ return impl.apply(a, permission);
+ }
+ };
+ }
+
+ /**
+ * @deprecated use {@link #lambda2}
* @since 2.105
*/
- public static ACL lambda(final BiFunction impl) {
+ @Deprecated
+ public static ACL lambda(final BiFunction impl) {
return new ACL() {
@Override
- public boolean hasPermission(Authentication a, Permission permission) {
+ public boolean hasPermission(org.acegisecurity.Authentication a, Permission permission) {
return impl.apply(a, permission);
}
};
@@ -201,11 +230,11 @@ public boolean hasPermission(Authentication a, Permission permission) {
*/
public final void checkCreatePermission(@NonNull ItemGroup c,
@NonNull TopLevelItemDescriptor d) {
- Authentication a = Jenkins.getAuthentication();
- if (a == SYSTEM) {
+ Authentication a = Jenkins.getAuthentication2();
+ if (a.equals(SYSTEM2)) {
return;
}
- if (!hasCreatePermission(a, c, d)) {
+ if (!hasCreatePermission2(a, c, d)) {
throw new AccessDeniedException(Messages.AccessDeniedException2_MissingPermission(a.getName(),
Item.CREATE.group.title+"/"+Item.CREATE.name + Item.CREATE + "/" + d.getDisplayName()));
}
@@ -213,18 +242,32 @@ public final void checkCreatePermission(@NonNull ItemGroup c,
/**
* Checks if the given principal has the permission to create top level items within the specified item group.
*
- * Note that {@link #SYSTEM} can be passed in as the authentication parameter,
+ * Note that {@link #SYSTEM2} can be passed in as the authentication parameter,
* in which case you should probably just assume it can create anything anywhere.
* @param a the principal.
* @param c the container of the item.
* @param d the descriptor of the item to be created.
* @return false
* if the user doesn't have the permission.
+ * @since TODO
+ */
+ public boolean hasCreatePermission2(@NonNull Authentication a, @NonNull ItemGroup c,
+ @NonNull TopLevelItemDescriptor d) {
+ if (Util.isOverridden(ACL.class, getClass(), "hasCreatePermission", org.acegisecurity.Authentication.class, ItemGroup.class, TopLevelItemDescriptor.class)) {
+ return hasCreatePermission(org.acegisecurity.Authentication.fromSpring(a), c, d);
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * @deprecated use {@link #hasCreatePermission2(Authentication, ItemGroup, TopLevelItemDescriptor)}
* @since 1.607
*/
- public boolean hasCreatePermission(@NonNull Authentication a, @NonNull ItemGroup c,
+ @Deprecated
+ public boolean hasCreatePermission(@NonNull org.acegisecurity.Authentication a, @NonNull ItemGroup c,
@NonNull TopLevelItemDescriptor d) {
- return true;
+ return hasCreatePermission2(a.toSpring(), c, d);
}
/**
@@ -239,11 +282,11 @@ public boolean hasCreatePermission(@NonNull Authentication a, @NonNull ItemGroup
*/
public final void checkCreatePermission(@NonNull ViewGroup c,
@NonNull ViewDescriptor d) {
- Authentication a = Jenkins.getAuthentication();
- if (a == SYSTEM) {
+ Authentication a = Jenkins.getAuthentication2();
+ if (a.equals(SYSTEM2)) {
return;
}
- if (!hasCreatePermission(a, c, d)) {
+ if (!hasCreatePermission2(a, c, d)) {
throw new AccessDeniedException(Messages.AccessDeniedException2_MissingPermission(a.getName(),
View.CREATE.group.title + "/" + View.CREATE.name + View.CREATE + "/" + d.getDisplayName()));
}
@@ -252,18 +295,32 @@ public final void checkCreatePermission(@NonNull ViewGroup c,
/**
* Checks if the given principal has the permission to create views within the specified view group.
*
- * Note that {@link #SYSTEM} can be passed in as the authentication parameter,
+ * Note that {@link #SYSTEM2} can be passed in as the authentication parameter,
* in which case you should probably just assume it can create anything anywhere.
* @param a the principal.
* @param c the container of the view.
* @param d the descriptor of the view to be created.
* @return false
* if the user doesn't have the permission.
+ * @since TODO
+ */
+ public boolean hasCreatePermission2(@NonNull Authentication a, @NonNull ViewGroup c,
+ @NonNull ViewDescriptor d) {
+ if (Util.isOverridden(ACL.class, getClass(), "hasCreatePermission", org.acegisecurity.Authentication.class, ViewGroup.class, ViewDescriptor.class)) {
+ return hasCreatePermission(org.acegisecurity.Authentication.fromSpring(a), c, d);
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * @deprecated use {@link #hasCreatePermission2(Authentication, ItemGroup, TopLevelItemDescriptor)}
* @since 2.37
*/
- public boolean hasCreatePermission(@NonNull Authentication a, @NonNull ViewGroup c,
+ @Deprecated
+ public boolean hasCreatePermission(@NonNull org.acegisecurity.Authentication a, @NonNull ViewGroup c,
@NonNull ViewDescriptor d) {
- return true;
+ return hasCreatePermission2(a.toSpring(), c, d);
}
//
@@ -311,8 +368,15 @@ public String toString() {
*
* This is used when Hudson is performing computation for itself, instead
* of acting on behalf of an user, such as doing builds.
+ * @since TODO
*/
- public static final Authentication SYSTEM = new UsernamePasswordAuthenticationToken(SYSTEM_USERNAME,"SYSTEM");
+ public static final Authentication SYSTEM2 = new UsernamePasswordAuthenticationToken(SYSTEM_USERNAME,"SYSTEM");
+
+ /**
+ * @deprecated use {@link #SYSTEM2}
+ */
+ @Deprecated
+ public static final org.acegisecurity.Authentication SYSTEM = new org.acegisecurity.providers.UsernamePasswordAuthenticationToken((UsernamePasswordAuthenticationToken) SYSTEM2);
/**
* Changes the {@link Authentication} associated with the current thread
@@ -321,31 +385,40 @@ public String toString() {
*
* When the impersonation is over, be sure to restore the previous authentication
* via {@code SecurityContextHolder.setContext(returnValueFromThisMethod)};
- * or just use {@link #impersonate(Authentication,Runnable)}.
+ * or just use {@link #impersonate2(Authentication, Runnable)}.
*
*
* We need to create a new {@link SecurityContext} instead of {@link SecurityContext#setAuthentication(Authentication)}
* because the same {@link SecurityContext} object is reused for all the concurrent requests from the same session.
- * @since 1.462
- * @deprecated use try with resources and {@link #as(Authentication)}
+ * @since TODO
+ * @deprecated use try with resources and {@link #as2(Authentication)}
*/
@Deprecated
- public static @NonNull SecurityContext impersonate(@NonNull Authentication auth) {
+ public static @NonNull SecurityContext impersonate2(@NonNull Authentication auth) {
SecurityContext old = SecurityContextHolder.getContext();
SecurityContextHolder.setContext(new NonSerializableSecurityContext(auth));
return old;
}
/**
- * Safer variant of {@link #impersonate(Authentication)} that does not require a finally-block.
- * @param auth authentication, such as {@link #SYSTEM}
+ * @deprecated use {@link #impersonate2(Authentication)}
+ * @since 1.462
+ */
+ @Deprecated
+ public static @NonNull org.acegisecurity.context.SecurityContext impersonate(@NonNull org.acegisecurity.Authentication auth) {
+ return org.acegisecurity.context.SecurityContext.fromSpring(impersonate2(auth.toSpring()));
+ }
+
+ /**
+ * Safer variant of {@link #impersonate2(Authentication)} that does not require a finally-block.
+ * @param auth authentication, such as {@link #SYSTEM2}
* @param body an action to run with this alternate authentication in effect
- * @since 1.509
- * @deprecated use try with resources and {@link #as(Authentication)}
+ * @since TODO
+ * @deprecated use try with resources and {@link #as2(Authentication)}
*/
@Deprecated
- public static void impersonate(@NonNull Authentication auth, @NonNull Runnable body) {
- SecurityContext old = impersonate(auth);
+ public static void impersonate2(@NonNull Authentication auth, @NonNull Runnable body) {
+ SecurityContext old = impersonate2(auth);
try {
body.run();
} finally {
@@ -354,15 +427,24 @@ public static void impersonate(@NonNull Authentication auth, @NonNull Runnable b
}
/**
- * Safer variant of {@link #impersonate(Authentication)} that does not require a finally-block.
- * @param auth authentication, such as {@link #SYSTEM}
+ * @deprecated use {@link #impersonate2(Authentication, Runnable)}
+ * @since 1.509
+ */
+ @Deprecated
+ public static void impersonate(@NonNull org.acegisecurity.Authentication auth, @NonNull Runnable body) {
+ impersonate2(auth.toSpring(), body);
+ }
+
+ /**
+ * Safer variant of {@link #impersonate2(Authentication)} that does not require a finally-block.
+ * @param auth authentication, such as {@link #SYSTEM2}
* @param body an action to run with this alternate authentication in effect (try {@link NotReallyRoleSensitiveCallable})
- * @since 1.587
- * @deprecated use try with resources and {@link #as(Authentication)}
+ * @since TODO
+ * @deprecated use try with resources and {@link #as2(Authentication)}
*/
@Deprecated
- public static V impersonate(Authentication auth, Callable body) throws T {
- SecurityContext old = impersonate(auth);
+ public static V impersonate2(Authentication auth, Callable body) throws T {
+ SecurityContext old = impersonate2(auth);
try {
return body.call();
} finally {
@@ -370,6 +452,15 @@ public static V impersonate(Authentication auth, Callabl
}
}
+ /**
+ * @deprecated use {@link #impersonate2(Authentication, Callable)}
+ * @since 1.587
+ */
+ @Deprecated
+ public static V impersonate(org.acegisecurity.Authentication auth, Callable body) throws T {
+ return impersonate2(auth.toSpring(), body);
+ }
+
/**
* Changes the {@link Authentication} associated with the current thread to the specified one and returns an
* {@link AutoCloseable} that restores the previous security context.
@@ -377,21 +468,31 @@ public static V impersonate(Authentication auth, Callabl
*
* This makes impersonation much easier within code as it can now be used using the try with resources construct:
*
@@ -410,16 +511,26 @@ public static ACLContext as(@NonNull Authentication auth) {
*/
@NonNull
public static ACLContext as(@CheckForNull User user) {
- return as(user == null ? Jenkins.ANONYMOUS : user.impersonate());
+ return as2(user == null ? Jenkins.ANONYMOUS2 : user.impersonate2());
}
/**
* Checks if the given authentication is anonymous by checking its class.
- * @see Jenkins#ANONYMOUS
+ * @see Jenkins#ANONYMOUS2
* @see AnonymousAuthenticationToken
+ * @since TODO
*/
- public static boolean isAnonymous(@NonNull Authentication authentication) {
+ public static boolean isAnonymous2(@NonNull Authentication authentication) {
//TODO use AuthenticationTrustResolver instead to be consistent through the application
return authentication instanceof AnonymousAuthenticationToken;
}
+
+ /**
+ * @deprecated use {@link #isAnonymous2}
+ */
+ @Deprecated
+ public static boolean isAnonymous(@NonNull org.acegisecurity.Authentication authentication) {
+ return isAnonymous2(authentication.toSpring());
+ }
+
}
diff --git a/core/src/main/java/hudson/security/ACLContext.java b/core/src/main/java/hudson/security/ACLContext.java
index 2f1b4e207d6b..f68bde46104f 100644
--- a/core/src/main/java/hudson/security/ACLContext.java
+++ b/core/src/main/java/hudson/security/ACLContext.java
@@ -24,9 +24,9 @@
package hudson.security;
import edu.umd.cs.findbugs.annotations.NonNull;
-import org.acegisecurity.Authentication;
-import org.acegisecurity.context.SecurityContext;
-import org.acegisecurity.context.SecurityContextHolder;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
/**
* A {@link AutoCloseable} that captures the previous {@link SecurityContext} and restores it on {@link #close()}
@@ -52,12 +52,21 @@ public class ACLContext implements AutoCloseable {
/**
* Accessor for the previous context.
* @return the previous context.
+ * @since TODO
*/
@NonNull
- public SecurityContext getPreviousContext() {
+ public SecurityContext getPreviousContext2() {
return previousContext;
}
+ /**
+ * @deprecated use {@link #getPreviousContext2}
+ */
+ @Deprecated
+ public org.acegisecurity.context.SecurityContext getPreviousContext() {
+ return org.acegisecurity.context.SecurityContext.fromSpring(getPreviousContext2());
+ }
+
@Override
public void close() {
SecurityContextHolder.setContext(previousContext);
diff --git a/core/src/main/java/hudson/security/AbstractPasswordBasedSecurityRealm.java b/core/src/main/java/hudson/security/AbstractPasswordBasedSecurityRealm.java
index fa727202729a..13049d8424ae 100644
--- a/core/src/main/java/hudson/security/AbstractPasswordBasedSecurityRealm.java
+++ b/core/src/main/java/hudson/security/AbstractPasswordBasedSecurityRealm.java
@@ -1,25 +1,24 @@
package hudson.security;
-import groovy.lang.Binding;
-import hudson.util.spring.BeanBuilder;
+import hudson.Util;
import jenkins.model.Jenkins;
-import jenkins.security.ImpersonatingUserDetailsService;
+import jenkins.security.ImpersonatingUserDetailsService2;
import jenkins.security.SecurityListener;
-import org.acegisecurity.AuthenticationException;
-import org.acegisecurity.AuthenticationManager;
-import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
-import org.acegisecurity.providers.dao.AbstractUserDetailsAuthenticationProvider;
-import org.acegisecurity.userdetails.UserDetails;
-import org.acegisecurity.userdetails.UserDetailsService;
-import org.acegisecurity.userdetails.UsernameNotFoundException;
-import org.springframework.dao.DataAccessException;
-import org.springframework.web.context.WebApplicationContext;
+import org.springframework.security.authentication.AnonymousAuthenticationProvider;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.ProviderManager;
+import org.springframework.security.authentication.RememberMeAuthenticationProvider;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
/**
* Partial implementation of {@link SecurityRealm} for username/password based authentication.
* This is a convenience base class if all you are trying to do is to check the given username
* and password with the information stored in somewhere else, and you don't want to do anything
- * with Acegi.
+ * with Spring Security.
*
*
* This {@link SecurityRealm} uses the standard login form (and a few other optional mechanisms like BASIC auth)
@@ -28,18 +27,21 @@
* @author Kohsuke Kawaguchi
* @since 1.317
*/
-public abstract class AbstractPasswordBasedSecurityRealm extends SecurityRealm implements UserDetailsService {
+public abstract class AbstractPasswordBasedSecurityRealm extends SecurityRealm {
@Override
public SecurityComponents createSecurityComponents() {
- Binding binding = new Binding();
- binding.setVariable("authenticator", new Authenticator());
-
- BeanBuilder builder = new BeanBuilder();
- builder.parse(Jenkins.get().servletContext.getResourceAsStream("/WEB-INF/security/AbstractPasswordBasedSecurityRealm.groovy"),binding);
- WebApplicationContext context = builder.createApplicationContext();
+ // this does all the hard work.
+ Authenticator authenticator = new Authenticator();
+ // these providers apply everywhere
+ RememberMeAuthenticationProvider rmap = new RememberMeAuthenticationProvider(Jenkins.get().getSecretKey());
+ // this doesn't mean we allow anonymous access.
+ // we just authenticate2 anonymous users as such,
+ // so that later authorization can reject them if so configured
+ AnonymousAuthenticationProvider aap = new AnonymousAuthenticationProvider("anonymous");
+ AuthenticationManager authenticationManager = new ProviderManager(authenticator, rmap, aap);
return new SecurityComponents(
- findBean(AuthenticationManager.class, context),
- new ImpersonatingUserDetailsService(this));
+ authenticationManager,
+ new ImpersonatingUserDetailsService2(this::loadUserByUsername2));
}
/**
@@ -48,7 +50,7 @@ public SecurityComponents createSecurityComponents() {
*
*
* If the user name and the password pair matches, retrieve the information about this user and
- * return it as a {@link UserDetails} object. {@link org.acegisecurity.userdetails.User} is a convenient
+ * return it as a {@link UserDetails} object. {@link org.springframework.security.core.userdetails.User} is a convenient
* implementation to use, but if your backend offers additional data, you may want to use your own subtype
* so that the rest of Hudson can use those additional information (such as e-mail address --- see
* MailAddressResolver.)
@@ -64,13 +66,36 @@ public SecurityComponents createSecurityComponents() {
*
* If the user name and the password pair doesn't match, throw {@link AuthenticationException} to reject the login
* attempt.
+ * @since TODO
*/
- protected abstract UserDetails authenticate(String username, String password) throws AuthenticationException;
+ protected UserDetails authenticate2(String username, String password) throws AuthenticationException {
+ if (Util.isOverridden(AbstractPasswordBasedSecurityRealm.class, getClass(), "authenticate", String.class, String.class)) {
+ try {
+ return authenticate(username, password).toSpring();
+ } catch (org.acegisecurity.AcegiSecurityException x) {
+ throw x.toSpring();
+ }
+ } else {
+ throw new AbstractMethodError("Implement authenticate2");
+ }
+ }
+
+ /**
+ * @deprecated use {@link #authenticate2}
+ */
+ @Deprecated
+ protected org.acegisecurity.userdetails.UserDetails authenticate(String username, String password) throws org.acegisecurity.AuthenticationException {
+ try {
+ return org.acegisecurity.userdetails.UserDetails.fromSpring(authenticate2(username, password));
+ } catch (AuthenticationException x) {
+ throw org.acegisecurity.AuthenticationException.fromSpring(x);
+ }
+ }
private UserDetails doAuthenticate(String username, String password) throws AuthenticationException {
try {
- UserDetails user = authenticate(username, password);
- SecurityListener.fireAuthenticated(user);
+ UserDetails user = authenticate2(username, password);
+ SecurityListener.fireAuthenticated2(user);
return user;
} catch (AuthenticationException x) {
SecurityListener.fireFailedToAuthenticate(username);
@@ -87,15 +112,65 @@ private UserDetails doAuthenticate(String username, String password) throws Auth
* a query like this, just always throw {@link UsernameNotFoundException}.
*/
@Override
- public abstract UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException;
+ public UserDetails loadUserByUsername2(String username) throws UsernameNotFoundException {
+ if (Util.isOverridden(AbstractPasswordBasedSecurityRealm.class, getClass(), "loadUserByUsername", String.class)) {
+ try {
+ return loadUserByUsername(username).toSpring();
+ } catch (org.acegisecurity.AcegiSecurityException x) {
+ throw x.toSpring();
+ } catch (org.springframework.dao.DataAccessException x) {
+ throw x.toSpring();
+ }
+ } else {
+ throw new AbstractMethodError("Implement loadUserByUsername2");
+ }
+ }
+
+ /**
+ * @deprecated use {@link #loadUserByUsername2}
+ */
+ @Deprecated
+ @Override
+ public org.acegisecurity.userdetails.UserDetails loadUserByUsername(String username) throws org.acegisecurity.userdetails.UsernameNotFoundException, org.springframework.dao.DataAccessException {
+ try {
+ return org.acegisecurity.userdetails.UserDetails.fromSpring(loadUserByUsername2(username));
+ } catch (AuthenticationException x) {
+ throw org.acegisecurity.AuthenticationException.fromSpring(x);
+ }
+ }
/**
* Retrieves information about a group by its name.
*
- * This method is the group version of the {@link #loadUserByUsername(String)}.
+ * This method is the group version of the {@link #loadUserByUsername2(String)}.
*/
@Override
- public abstract GroupDetails loadGroupByGroupname(String groupname) throws UsernameNotFoundException, DataAccessException;
+ public GroupDetails loadGroupByGroupname2(String groupname, boolean fetchMembers) throws UsernameNotFoundException {
+ if (Util.isOverridden(AbstractPasswordBasedSecurityRealm.class, getClass(), "loadGroupByGroupname", String.class)) {
+ try {
+ return loadGroupByGroupname(groupname);
+ } catch (org.acegisecurity.AcegiSecurityException x) {
+ throw x.toSpring();
+ } catch (org.springframework.dao.DataAccessException x) {
+ throw x.toSpring();
+ }
+ } else {
+ throw new AbstractMethodError("Implement loadGroupByGroupname2");
+ }
+ }
+
+ /**
+ * @deprecated use {@link #loadGroupByGroupname2}
+ */
+ @Deprecated
+ @Override
+ public GroupDetails loadGroupByGroupname(String groupname) throws org.acegisecurity.userdetails.UsernameNotFoundException, org.springframework.dao.DataAccessException {
+ try {
+ return loadGroupByGroupname2(groupname, false);
+ } catch (AuthenticationException x) {
+ throw org.acegisecurity.AuthenticationException.fromSpring(x);
+ }
+ }
class Authenticator extends AbstractUserDetailsAuthenticationProvider {
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
diff --git a/core/src/main/java/hudson/security/AccessControlled.java b/core/src/main/java/hudson/security/AccessControlled.java
index 5e12debb3c3a..694744996be2 100644
--- a/core/src/main/java/hudson/security/AccessControlled.java
+++ b/core/src/main/java/hudson/security/AccessControlled.java
@@ -24,8 +24,8 @@
package hudson.security;
import edu.umd.cs.findbugs.annotations.NonNull;
-import org.acegisecurity.AccessDeniedException;
-import org.acegisecurity.Authentication;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.core.Authentication;
/**
* Object that has an {@link ACL}
@@ -75,14 +75,23 @@ default boolean hasAnyPermission(@NonNull Permission... permission) {
}
/**
- * Convenient short-cut for {@code getACL().hasPermission(a, permission)}
- * @since 2.92
+ * Convenient short-cut for {@code getACL().hasPermission2(a, permission)}
+ * @since TODO
*/
- default boolean hasPermission(@NonNull Authentication a, @NonNull Permission permission) {
- if (a == ACL.SYSTEM) {
+ default boolean hasPermission2(@NonNull Authentication a, @NonNull Permission permission) {
+ if (a.equals(ACL.SYSTEM2)) {
return true;
}
- return getACL().hasPermission(a, permission);
+ return getACL().hasPermission2(a, permission);
+ }
+
+ /**
+ * @deprecated use {@link #hasPermission2}
+ * @since 2.92
+ */
+ @Deprecated
+ default boolean hasPermission(@NonNull org.acegisecurity.Authentication a, @NonNull Permission permission) {
+ return hasPermission2(a.toSpring(), permission);
}
}
diff --git a/core/src/main/java/hudson/security/AccessDeniedException2.java b/core/src/main/java/hudson/security/AccessDeniedException2.java
index 7b39396bf667..ed6e5f49769e 100644
--- a/core/src/main/java/hudson/security/AccessDeniedException2.java
+++ b/core/src/main/java/hudson/security/AccessDeniedException2.java
@@ -2,7 +2,6 @@
import org.acegisecurity.AccessDeniedException;
import org.acegisecurity.Authentication;
-import org.acegisecurity.GrantedAuthority;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
@@ -11,7 +10,9 @@
/**
* {@link AccessDeniedException} with more information.
* @author Kohsuke Kawaguchi
+ * @deprecated use {@link AccessDeniedException3}
*/
+@Deprecated
public class AccessDeniedException2 extends AccessDeniedException {
/** If true, report {@code X-You-Are-In-Group} headers. Disabled due to JENKINS-39402; use {@code /whoAmI} etc. to diagnose permission issues. */
@@ -42,18 +43,7 @@ public AccessDeniedException2(Throwable t, Authentication authentication, Permis
* Reports the details of the access failure in HTTP headers to assist diagnosis.
*/
public void reportAsHeaders(HttpServletResponse rsp) {
- rsp.addHeader("X-You-Are-Authenticated-As",authentication.getName());
- if (REPORT_GROUP_HEADERS) {
- for (GrantedAuthority auth : authentication.getAuthorities()) {
- rsp.addHeader("X-You-Are-In-Group",auth.getAuthority());
- }
- } else {
- rsp.addHeader("X-You-Are-In-Group-Disabled", "JENKINS-39402: use -Dhudson.security.AccessDeniedException2.REPORT_GROUP_HEADERS=true or use /whoAmI to diagnose");
- }
- rsp.addHeader("X-Required-Permission", permission.getId());
- for (Permission p=permission.impliedBy; p!=null; p=p.impliedBy) {
- rsp.addHeader("X-Permission-Implied-By", p.getId());
- }
+ toSpring().reportAsHeaders(rsp);
}
/**
@@ -62,15 +52,12 @@ public void reportAsHeaders(HttpServletResponse rsp) {
* but instead of using HTTP headers, this version is meant to go inside the payload.
*/
public void report(PrintWriter w) {
- w.println("You are authenticated as: "+authentication.getName());
- w.println("Groups that you are in:");
- for (GrantedAuthority auth : authentication.getAuthorities()) {
- w.println(" "+auth.getAuthority());
- }
+ toSpring().report(w);
+ }
- w.println("Permission you need to have (but didn't): "+permission.getId());
- for (Permission p=permission.impliedBy; p!=null; p=p.impliedBy) {
- w.println(" ... which is implied by: "+p.getId());
- }
+ @Override
+ public AccessDeniedException3 toSpring() {
+ Throwable t = getCause();
+ return t != null ? new AccessDeniedException3(t, authentication.toSpring(), permission) : new AccessDeniedException3(authentication.toSpring(), permission);
}
}
diff --git a/core/src/main/java/hudson/security/AccessDeniedException3.java b/core/src/main/java/hudson/security/AccessDeniedException3.java
new file mode 100644
index 000000000000..64323bf5bc1e
--- /dev/null
+++ b/core/src/main/java/hudson/security/AccessDeniedException3.java
@@ -0,0 +1,77 @@
+package hudson.security;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.PrintWriter;
+import jenkins.util.SystemProperties;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+
+/**
+ * {@link AccessDeniedException} with more information.
+ * @author Kohsuke Kawaguchi
+ * @since TODO
+ */
+public class AccessDeniedException3 extends AccessDeniedException {
+
+ /** If true, report {@code X-You-Are-In-Group} headers. Disabled due to JENKINS-39402; use {@code /whoAmI} etc. to diagnose permission issues. */
+ @SuppressWarnings("deprecation")
+ private static /* not final */ boolean REPORT_GROUP_HEADERS = SystemProperties.getBoolean(AccessDeniedException2.class.getName() + ".REPORT_GROUP_HEADERS");
+
+ /**
+ * This object represents the user being authenticated.
+ */
+ public final Authentication authentication;
+
+ /**
+ * This object represents the permission that the user needed.
+ */
+ public final Permission permission;
+
+ public AccessDeniedException3(Authentication authentication, Permission permission) {
+ this(null,authentication,permission);
+ }
+
+ public AccessDeniedException3(Throwable t, Authentication authentication, Permission permission) {
+ super(Messages.AccessDeniedException2_MissingPermission(authentication.getName(),
+ permission.group.title+"/"+permission.name), t);
+ this.authentication = authentication;
+ this.permission = permission;
+ }
+
+ /**
+ * Reports the details of the access failure in HTTP headers to assist diagnosis.
+ */
+ public void reportAsHeaders(HttpServletResponse rsp) {
+ rsp.addHeader("X-You-Are-Authenticated-As",authentication.getName());
+ if (REPORT_GROUP_HEADERS) {
+ for (GrantedAuthority auth : authentication.getAuthorities()) {
+ rsp.addHeader("X-You-Are-In-Group",auth.getAuthority());
+ }
+ } else {
+ rsp.addHeader("X-You-Are-In-Group-Disabled", "JENKINS-39402: use -Dhudson.security.AccessDeniedException2.REPORT_GROUP_HEADERS=true or use /whoAmI to diagnose");
+ }
+ rsp.addHeader("X-Required-Permission", permission.getId());
+ for (Permission p=permission.impliedBy; p!=null; p=p.impliedBy) {
+ rsp.addHeader("X-Permission-Implied-By", p.getId());
+ }
+ }
+
+ /**
+ * Reports the details of the access failure.
+ * This method is similar to {@link #reportAsHeaders(HttpServletResponse)} for the intention
+ * but instead of using HTTP headers, this version is meant to go inside the payload.
+ */
+ public void report(PrintWriter w) {
+ w.println("You are authenticated as: "+authentication.getName());
+ w.println("Groups that you are in:");
+ for (GrantedAuthority auth : authentication.getAuthorities()) {
+ w.println(" "+auth.getAuthority());
+ }
+
+ w.println("Permission you need to have (but didn't): "+permission.getId());
+ for (Permission p=permission.impliedBy; p!=null; p=p.impliedBy) {
+ w.println(" ... which is implied by: "+p.getId());
+ }
+ }
+}
diff --git a/core/src/main/java/hudson/security/AccessDeniedHandlerImpl.java b/core/src/main/java/hudson/security/AccessDeniedHandlerImpl.java
index b784a3d37d01..92c45b0f9fe9 100644
--- a/core/src/main/java/hudson/security/AccessDeniedHandlerImpl.java
+++ b/core/src/main/java/hudson/security/AccessDeniedHandlerImpl.java
@@ -23,17 +23,16 @@
*/
package hudson.security;
-import jenkins.model.Jenkins;
-import org.acegisecurity.AccessDeniedException;
-import org.acegisecurity.ui.AccessDeniedHandler;
-import org.kohsuke.stapler.WebApp;
-
+import java.io.IOException;
import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
+import jenkins.model.Jenkins;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
+import org.kohsuke.stapler.WebApp;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.web.access.AccessDeniedHandler;
/**
* Handles {@link AccessDeniedException} happened during request processing.
@@ -41,16 +40,16 @@
*
* @author Kohsuke Kawaguchi
*/
+@Restricted(NoExternalUse.class)
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
- public void handle(ServletRequest request, ServletResponse response, AccessDeniedException cause) throws IOException, ServletException {
- HttpServletRequest req = (HttpServletRequest) request;
- HttpServletResponse rsp = (HttpServletResponse) response;
+ @Override
+ public void handle(HttpServletRequest req, HttpServletResponse rsp, AccessDeniedException cause) throws IOException, ServletException {
rsp.setStatus(HttpServletResponse.SC_FORBIDDEN);
req.setAttribute("exception",cause);
- if (cause instanceof AccessDeniedException2) {
- ((AccessDeniedException2)cause).reportAsHeaders(rsp);
+ if (cause instanceof AccessDeniedException3) {
+ ((AccessDeniedException3)cause).reportAsHeaders(rsp);
}
WebApp.get(Jenkins.get().servletContext).getSomeStapler()
diff --git a/core/src/main/java/hudson/security/AuthenticationManagerProxy.java b/core/src/main/java/hudson/security/AuthenticationManagerProxy.java
index 717f07dc77c2..2b5b4de7c83d 100644
--- a/core/src/main/java/hudson/security/AuthenticationManagerProxy.java
+++ b/core/src/main/java/hudson/security/AuthenticationManagerProxy.java
@@ -23,10 +23,12 @@
*/
package hudson.security;
-import org.acegisecurity.AuthenticationManager;
-import org.acegisecurity.Authentication;
-import org.acegisecurity.AuthenticationException;
-import org.acegisecurity.DisabledException;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.DisabledException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
/**
* {@link AuthenticationManager} proxy that delegates to another instance.
@@ -38,6 +40,7 @@
*
* @author Kohsuke Kawaguchi
*/
+@Restricted(NoExternalUse.class)
public class AuthenticationManagerProxy implements AuthenticationManager {
private volatile AuthenticationManager delegate;
diff --git a/core/src/main/java/hudson/security/AuthenticationProcessingFilter2.java b/core/src/main/java/hudson/security/AuthenticationProcessingFilter2.java
index e1a281b48214..71be533d7750 100644
--- a/core/src/main/java/hudson/security/AuthenticationProcessingFilter2.java
+++ b/core/src/main/java/hudson/security/AuthenticationProcessingFilter2.java
@@ -23,33 +23,46 @@
*/
package hudson.security;
-import java.util.Properties;
-import java.util.logging.Logger;
-import java.util.logging.Level;
+import hudson.model.User;
import java.io.IOException;
-
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
-
-import hudson.Util;
-import hudson.model.User;
import jenkins.security.SecurityListener;
import jenkins.security.seed.UserSeedProperty;
-import org.acegisecurity.Authentication;
-import org.acegisecurity.AuthenticationException;
-import org.acegisecurity.ui.webapp.AuthenticationProcessingFilter;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
/**
- * {@link AuthenticationProcessingFilter} with a change for Jenkins so that
+ * Login filter with a change for Jenkins so that
* we can pick up the hidden "from" form field defined in {@code login.jelly}
* to send the user back to where he came from, after a successful authentication.
*
* @author Kohsuke Kawaguchi
*/
-public class AuthenticationProcessingFilter2 extends AuthenticationProcessingFilter {
+@Restricted(NoExternalUse.class)
+public final class AuthenticationProcessingFilter2 extends UsernamePasswordAuthenticationFilter {
+
+ public AuthenticationProcessingFilter2(String authenticationGatewayUrl) {
+ setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/" + authenticationGatewayUrl, "POST"));
+ // Jenkins/login.jelly & SetupWizard/authenticate-security-token.jelly
+ setUsernameParameter("j_username");
+ setPasswordParameter("j_password");
+ }
+
+ /* TODO none of this compiles against Spring Security; rewrite (try InteractiveAuthenticationSuccessEvent & SimpleUrlAuthenticationFailureHandler):
+
@Override
protected String determineTargetUrl(HttpServletRequest request) {
+ AbstractAuthenticationProcessingFilter f = new UsernamePasswordAuthenticationFilter();
String targetUrl = request.getParameter("from");
request.getSession().setAttribute("from", targetUrl);
@@ -70,8 +83,8 @@ protected String determineTargetUrl(HttpServletRequest request) {
}
/**
- * @see org.acegisecurity.ui.AbstractProcessingFilter#determineFailureUrl(javax.servlet.http.HttpServletRequest, org.acegisecurity.AuthenticationException)
- */
+ * @see AbstractProcessingFilter#determineFailureUrl(HttpServletRequest, AuthenticationException)
+ * /
@Override
protected String determineFailureUrl(HttpServletRequest request, AuthenticationException failed) {
Properties excMap = getExceptionMappings();
@@ -80,16 +93,19 @@ protected String determineFailureUrl(HttpServletRequest request, AuthenticationE
request.getSession().setAttribute("from", whereFrom);
return excMap.getProperty(failedClassName, getAuthenticationFailureUrl());
}
+ */
@Override
- protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) throws IOException {
- super.onSuccessfulAuthentication(request,response,authResult);
+ protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
+ super.successfulAuthentication(request, response, chain, authResult);
// make sure we have a session to store this successful authentication, given that we no longer
// let HttpSessionContextIntegrationFilter2 to create sessions.
- // HttpSessionContextIntegrationFilter stores the updated SecurityContext object into this session later
+ // SecurityContextPersistenceFilter stores the updated SecurityContext object into this session later
// (either when a redirect is issued, via its HttpResponseWrapper, or when the execution returns to its
// doFilter method.
+ /* TODO causes an ISE on the next line:
request.getSession().invalidate();
+ */
HttpSession newSession = request.getSession();
if (!UserSeedProperty.DISABLE_USER_SEED) {
@@ -100,7 +116,7 @@ protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServle
newSession.setAttribute(UserSeedProperty.USER_SESSION_SEED, sessionSeed);
}
- // as the request comes from Acegi redirect, that's not a Stapler one
+ // as the request comes from Spring Security redirect, that's not a Stapler one
// thus it's not possible to retrieve it in the SecurityListener in that case
// for that reason we need to keep the above code that apply quite the same logic as UserSeedSecurityListener
SecurityListener.fireLoggedIn(authResult.getName());
@@ -108,18 +124,17 @@ protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServle
/**
* Leave the information about login failure.
- *
- *
- * Otherwise it seems like Acegi doesn't really leave the detail of the failure anywhere.
*/
@Override
- protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException {
- super.onUnsuccessfulAuthentication(request, response, failed);
+ protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
+ super.unsuccessfulAuthentication(request, response, failed);
LOGGER.log(Level.FINE, "Login attempt failed", failed);
+ /* TODO this information appears to have been deliberately removed from Spring Security:
Authentication auth = failed.getAuthentication();
if (auth != null) {
SecurityListener.fireFailedToLogIn(auth.getName());
}
+ */
}
private static final Logger LOGGER = Logger.getLogger(AuthenticationProcessingFilter2.class.getName());
diff --git a/core/src/main/java/hudson/security/AuthorizationStrategy.java b/core/src/main/java/hudson/security/AuthorizationStrategy.java
index 3d63272d0f07..66cab7d0b716 100644
--- a/core/src/main/java/hudson/security/AuthorizationStrategy.java
+++ b/core/src/main/java/hudson/security/AuthorizationStrategy.java
@@ -94,12 +94,12 @@ public abstract class AuthorizationStrategy extends AbstractDescribableImpl {
+ return ACL.lambda2((a, permission) -> {
ACL base = item.getOwner().getACL();
- boolean hasPermission = base.hasPermission(a, permission);
+ boolean hasPermission = base.hasPermission2(a, permission);
if (!hasPermission && permission == View.READ) {
- return base.hasPermission(a,View.CONFIGURE) || !item.getItems().isEmpty();
+ return base.hasPermission2(a,View.CONFIGURE) || !item.getItems().isEmpty();
}
return hasPermission;
@@ -221,7 +221,7 @@ private Object readResolve() {
return Collections.emptySet();
}
- private static final ACL UNSECURED_ACL = ACL.lambda((a, p) -> true);
+ private static final ACL UNSECURED_ACL = ACL.lambda2((a, p) -> true);
@Extension @Symbol("unsecured")
public static final class DescriptorImpl extends Descriptor {
diff --git a/core/src/main/java/hudson/security/BasicAuthenticationFilter.java b/core/src/main/java/hudson/security/BasicAuthenticationFilter.java
index 2c1c379d7d6f..c7eee8cd5db3 100644
--- a/core/src/main/java/hudson/security/BasicAuthenticationFilter.java
+++ b/core/src/main/java/hudson/security/BasicAuthenticationFilter.java
@@ -25,16 +25,9 @@
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.model.User;
-import jenkins.model.Jenkins;
import hudson.util.Scrambler;
-import jenkins.security.SecurityListener;
-import org.acegisecurity.Authentication;
-import jenkins.security.BasicApiTokenHelper;
-import org.acegisecurity.context.SecurityContextHolder;
-import org.acegisecurity.userdetails.UserDetails;
-import org.kohsuke.stapler.StaplerRequest;
-import org.kohsuke.stapler.StaplerResponse;
-
+import java.io.IOException;
+import java.net.URLEncoder;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
@@ -45,8 +38,14 @@
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.net.URLEncoder;
+import jenkins.model.Jenkins;
+import jenkins.security.BasicApiTokenHelper;
+import jenkins.security.SecurityListener;
+import org.kohsuke.stapler.StaplerRequest;
+import org.kohsuke.stapler.StaplerResponse;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
/**
* Implements the dual authentication mechanism.
@@ -109,7 +108,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
// normal requests, or security not enabled
if(req.getUserPrincipal()!=null) {
// before we route this request, integrate the container authentication
- // to Acegi. For anonymous users that doesn't have user principal,
+ // to Spring Security. For anonymous users that doesn't have user principal,
// AnonymousProcessingFilter that follows this should create
// an Authentication object.
SecurityContextHolder.getContext().setAuthentication(new ContainerAuthentication(req));
@@ -141,10 +140,10 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
{
User u = BasicApiTokenHelper.isConnectingUsingApiToken(username, password);
if(u != null){
- UserDetails userDetails = u.getUserDetailsForImpersonation();
+ UserDetails userDetails = u.getUserDetailsForImpersonation2();
Authentication auth = u.impersonate(userDetails);
- SecurityListener.fireAuthenticated(userDetails);
+ SecurityListener.fireAuthenticated2(userDetails);
SecurityContextHolder.getContext().setAuthentication(auth);
try {
diff --git a/core/src/main/java/hudson/security/ChainedServletFilter.java b/core/src/main/java/hudson/security/ChainedServletFilter.java
index a9a0e129074f..4c8805f9c83e 100644
--- a/core/src/main/java/hudson/security/ChainedServletFilter.java
+++ b/core/src/main/java/hudson/security/ChainedServletFilter.java
@@ -28,6 +28,7 @@
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;
+import java.util.regex.Pattern;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
@@ -35,6 +36,8 @@
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
/**
* Servlet {@link Filter} that chains multiple {@link Filter}s.
@@ -70,24 +73,41 @@ public void init(FilterConfig filterConfig) throws ServletException {
f.init(filterConfig);
}
+ private static final Pattern UNINTERESTING_URIS = Pattern.compile("/(images|jsbundles|css|scripts|adjuncts)/|/favicon[.]ico|/ajax");
+ @Override
public void doFilter(ServletRequest request, ServletResponse response, final FilterChain chain) throws IOException, ServletException {
- LOGGER.entering(ChainedServletFilter.class.getName(), "doFilter");
+ String uri = request instanceof HttpServletRequest ? ((HttpServletRequest) request).getRequestURI() : "?";
+ Level level = UNINTERESTING_URIS.matcher(uri).find() ? Level.FINER : Level.FINE;
+ LOGGER.log(level, () -> "starting filter on " + uri);
new FilterChain() {
private int position=0;
// capture the array for thread-safety
private final Filter[] filters = ChainedServletFilter.this.filters;
+ @Override
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
if(position==filters.length) {
- // reached to the end
+ LOGGER.log(level, () -> uri + " end: " + status());
chain.doFilter(request,response);
} else {
- // call next
- filters[position++].doFilter(request,response,this);
+ Filter next = filters[position++];
+ try {
+ LOGGER.log(level, () -> uri + " @" + position + " " + next + " »");
+ next.doFilter(request,response,this);
+ LOGGER.log(level, () -> uri + " @" + position + " " + next + " « success: " + status());
+ } catch (IOException | ServletException | RuntimeException x) {
+ LOGGER.log(level, () -> uri + " @" + position + " " + next + " « " + x + ": " + status());
+ throw x;
+ }
}
}
+
+ private int status() {
+ return response instanceof HttpServletResponse ? ((HttpServletResponse) response).getStatus() : 0;
+ }
}.doFilter(request,response);
+
}
public void destroy() {
diff --git a/core/src/main/java/hudson/security/ContainerAuthentication.java b/core/src/main/java/hudson/security/ContainerAuthentication.java
index ea6f404bdeb4..16b6952b61a6 100644
--- a/core/src/main/java/hudson/security/ContainerAuthentication.java
+++ b/core/src/main/java/hudson/security/ContainerAuthentication.java
@@ -24,29 +24,33 @@
package hudson.security;
import jenkins.model.Jenkins;
-import org.acegisecurity.Authentication;
-import org.acegisecurity.GrantedAuthority;
-import org.acegisecurity.GrantedAuthorityImpl;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.security.Principal;
import java.util.List;
import java.util.ArrayList;
+import java.util.Collection;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
/**
* {@link Authentication} implementation for {@link Principal}
* given through {@link HttpServletRequest}.
*
*
- * This is used to plug the container authentication to Acegi,
+ * This is used to plug the container authentication to Spring Security,
* for backward compatibility with Hudson < 1.160.
*
* @author Kohsuke Kawaguchi
*/
+@Restricted(NoExternalUse.class)
public final class ContainerAuthentication implements Authentication {
private final Principal principal;
- private GrantedAuthority[] authorities;
+ private Collection extends GrantedAuthority> authorities;
/**
* Servlet container can tie a {@link ServletRequest} to the request handling thread,
@@ -63,13 +67,13 @@ public ContainerAuthentication(HttpServletRequest request) {
List l = new ArrayList<>();
for( String g : Jenkins.get().getAuthorizationStrategy().getGroups()) {
if(request.isUserInRole(g))
- l.add(new GrantedAuthorityImpl(g));
+ l.add(new SimpleGrantedAuthority(g));
}
- l.add(SecurityRealm.AUTHENTICATED_AUTHORITY);
- authorities = l.toArray(new GrantedAuthority[0]);
+ l.add(SecurityRealm.AUTHENTICATED_AUTHORITY2);
+ authorities = l;
}
- public GrantedAuthority[] getAuthorities() {
+ public Collection extends GrantedAuthority> getAuthorities() {
return authorities;
}
diff --git a/core/src/main/java/hudson/security/DeferredCreationLdapAuthoritiesPopulator.java b/core/src/main/java/hudson/security/DeferredCreationLdapAuthoritiesPopulator.java
deleted file mode 100644
index ac3a228b57fd..000000000000
--- a/core/src/main/java/hudson/security/DeferredCreationLdapAuthoritiesPopulator.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * The MIT License
- *
- * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-package hudson.security;
-
-import org.acegisecurity.GrantedAuthority;
-import org.acegisecurity.ldap.InitialDirContextFactory;
-import org.acegisecurity.ldap.LdapDataAccessException;
-import org.acegisecurity.providers.ldap.LdapAuthoritiesPopulator;
-import org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator;
-import org.acegisecurity.userdetails.ldap.LdapUserDetails;
-import hudson.security.SecurityRealm.SecurityComponents;
-
-/**
- * Implementation of {@link LdapAuthoritiesPopulator} that defers creation of a
- * {@link DefaultLdapAuthoritiesPopulator} until one is needed. This is done to
- * ensure that the groupSearchBase property can be set.
- *
- * @author justinedelson
- * @deprecated as of 1.280
- * {@link SecurityComponents} are now created after {@link SecurityRealm} is created, so
- * the initialization order issue that this code was trying to address no longer exists.
- */
-@Deprecated
-public class DeferredCreationLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator {
-
- /**
- * A default role which will be assigned to all authenticated users if set.
- */
- private String defaultRole = null;
-
- /**
- * An initial context factory is only required if searching for groups is
- * required.
- */
- private InitialDirContextFactory initialDirContextFactory = null;
-
- /**
- * Controls used to determine whether group searches should be performed
- * over the full sub-tree from the base DN.
- */
- private boolean searchSubtree = false;
-
- /**
- * The ID of the attribute which contains the role name for a group
- */
- private String groupRoleAttribute = "cn";
-
- /**
- * The base DN from which the search for group membership should be
- * performed
- */
- private String groupSearchBase = null;
-
- /**
- * The pattern to be used for the user search. {0} is the user's DN
- */
- private String groupSearchFilter = "(| (member={0}) (uniqueMember={0}) (memberUid={0}))";
-
- private String rolePrefix = "ROLE_";
-
- private boolean convertToUpperCase = true;
-
- /**
- * Constructor.
- *
- * @param initialDirContextFactory
- * supplies the contexts used to search for user roles.
- * @param groupSearchBase
- * if this is an empty string the search will be performed from
- * the root DN of the context factory.
- */
- public DeferredCreationLdapAuthoritiesPopulator(
- InitialDirContextFactory initialDirContextFactory, String groupSearchBase) {
- this.setInitialDirContextFactory(initialDirContextFactory);
- this.setGroupSearchBase(groupSearchBase);
- }
-
- public GrantedAuthority[] getGrantedAuthorities(LdapUserDetails userDetails)
- throws LdapDataAccessException {
- return create().getGrantedAuthorities(userDetails);
- }
-
- public void setConvertToUpperCase(boolean convertToUpperCase) {
- this.convertToUpperCase = convertToUpperCase;
- }
-
- public void setDefaultRole(String defaultRole) {
- this.defaultRole = defaultRole;
- }
-
- public void setGroupRoleAttribute(String groupRoleAttribute) {
- this.groupRoleAttribute = groupRoleAttribute;
- }
-
- public void setGroupSearchBase(String groupSearchBase) {
- this.groupSearchBase = groupSearchBase;
- }
-
- public void setGroupSearchFilter(String groupSearchFilter) {
- this.groupSearchFilter = groupSearchFilter;
- }
-
- public void setInitialDirContextFactory(InitialDirContextFactory initialDirContextFactory) {
- this.initialDirContextFactory = initialDirContextFactory;
- }
-
- public void setRolePrefix(String rolePrefix) {
- this.rolePrefix = rolePrefix;
- }
-
- public void setSearchSubtree(boolean searchSubtree) {
- this.searchSubtree = searchSubtree;
- }
-
- /**
- * Create a new DefaultLdapAuthoritiesPopulator object.
- *
- * @return a DefaultLdapAuthoritiesPopulator.
- */
- private DefaultLdapAuthoritiesPopulator create() {
- DefaultLdapAuthoritiesPopulator populator = new DefaultLdapAuthoritiesPopulator(
- initialDirContextFactory, groupSearchBase);
- populator.setConvertToUpperCase(convertToUpperCase);
- if (defaultRole != null) {
- populator.setDefaultRole(defaultRole);
- }
- populator.setGroupRoleAttribute(groupRoleAttribute);
- populator.setGroupSearchFilter(groupSearchFilter);
- populator.setRolePrefix(rolePrefix);
- populator.setSearchSubtree(searchSubtree);
- return populator;
- }
-
-}
diff --git a/core/src/main/java/hudson/security/FederatedLoginService.java b/core/src/main/java/hudson/security/FederatedLoginService.java
index e87b30af68ed..0d7567668a5c 100644
--- a/core/src/main/java/hudson/security/FederatedLoginService.java
+++ b/core/src/main/java/hudson/security/FederatedLoginService.java
@@ -23,23 +23,22 @@
*/
package hudson.security;
+import edu.umd.cs.findbugs.annotations.CheckForNull;
+import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
-import jenkins.model.Jenkins;
import hudson.model.User;
import hudson.model.UserProperty;
-import org.acegisecurity.context.SecurityContextHolder;
-import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
-import org.acegisecurity.userdetails.UserDetails;
+import java.io.IOException;
+import java.io.Serializable;
+import javax.servlet.ServletException;
+import jenkins.model.Jenkins;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
-
-import edu.umd.cs.findbugs.annotations.CheckForNull;
-import edu.umd.cs.findbugs.annotations.NonNull;
-import javax.servlet.ServletException;
-import java.io.IOException;
-import java.io.Serializable;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
/**
* Abstraction for a login mechanism through external authenticator/identity provider
@@ -189,7 +188,7 @@ public User signin() throws UnclaimedIdentityException {
User u = locateUser();
if (u!=null) {
// login as this user
- UserDetails d = Jenkins.get().getSecurityRealm().loadUserByUsername(u.getId());
+ UserDetails d = Jenkins.get().getSecurityRealm().loadUserByUsername2(u.getId());
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(d,"",d.getAuthorities());
token.setDetails(d);
diff --git a/core/src/main/java/hudson/security/GroupDetails.java b/core/src/main/java/hudson/security/GroupDetails.java
index e83aaa531dc5..543074ef0e3a 100644
--- a/core/src/main/java/hudson/security/GroupDetails.java
+++ b/core/src/main/java/hudson/security/GroupDetails.java
@@ -23,9 +23,8 @@
*/
package hudson.security;
-import org.acegisecurity.userdetails.UserDetails;
-
import java.util.Set;
+import org.springframework.security.core.userdetails.UserDetails;
/**
* Represents the details of a group.
diff --git a/core/src/main/java/hudson/security/HttpSessionContextIntegrationFilter2.java b/core/src/main/java/hudson/security/HttpSessionContextIntegrationFilter2.java
index aafc93af3bfa..35baa6659f03 100644
--- a/core/src/main/java/hudson/security/HttpSessionContextIntegrationFilter2.java
+++ b/core/src/main/java/hudson/security/HttpSessionContextIntegrationFilter2.java
@@ -24,13 +24,7 @@
package hudson.security;
import hudson.model.User;
-import jenkins.security.NonSerializableSecurityContext;
import jenkins.security.seed.UserSeedProperty;
-import org.acegisecurity.context.HttpSessionContextIntegrationFilter;
-import org.acegisecurity.context.SecurityContext;
-import org.acegisecurity.Authentication;
-import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken;
-
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
@@ -38,27 +32,28 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
+import org.springframework.security.web.context.SecurityContextPersistenceFilter;
+import org.springframework.security.web.context.SecurityContextRepository;
-/**
- * Erases the {@link SecurityContext} persisted in {@link HttpSession}
- * if {@link InvalidatableUserDetails#isInvalid()} returns true.
- *
- * @see InvalidatableUserDetails
- */
-public class HttpSessionContextIntegrationFilter2 extends HttpSessionContextIntegrationFilter {
- public HttpSessionContextIntegrationFilter2() throws ServletException {
- setContext(NonSerializableSecurityContext.class);
+public class HttpSessionContextIntegrationFilter2 extends SecurityContextPersistenceFilter {
+ public HttpSessionContextIntegrationFilter2(SecurityContextRepository securityContextRepository) {
+ super(securityContextRepository);
}
+ @Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpSession session = ((HttpServletRequest) req).getSession(false);
if (session != null) {
- SecurityContext o = (SecurityContext) session.getAttribute(ACEGI_SECURITY_CONTEXT_KEY);
+ SecurityContext o = (SecurityContext) session.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
if (o != null) {
Authentication a = o.getAuthentication();
if (a != null) {
- if (isAuthInvalidated(a) || hasInvalidSessionSeed(a, session)) {
- session.setAttribute(ACEGI_SECURITY_CONTEXT_KEY, null);
+ if (hasInvalidSessionSeed(a, session)) {
+ session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, null);
}
}
}
@@ -67,18 +62,6 @@ public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
super.doFilter(req, res, chain);
}
- private boolean isAuthInvalidated(Authentication authentication) {
- if (authentication.getPrincipal() instanceof InvalidatableUserDetails) {
- InvalidatableUserDetails ud = (InvalidatableUserDetails) authentication.getPrincipal();
- if (ud.isInvalid()) {
- // don't let Acegi see invalid security context
- return true;
- }
- }
-
- return false;
- }
-
private boolean hasInvalidSessionSeed(Authentication authentication, HttpSession session) {
if (UserSeedProperty.DISABLE_USER_SEED || authentication instanceof AnonymousAuthenticationToken) {
return false;
diff --git a/core/src/main/java/hudson/security/HudsonAuthenticationEntryPoint.java b/core/src/main/java/hudson/security/HudsonAuthenticationEntryPoint.java
index 43798635ad74..c31be3b62fec 100644
--- a/core/src/main/java/hudson/security/HudsonAuthenticationEntryPoint.java
+++ b/core/src/main/java/hudson/security/HudsonAuthenticationEntryPoint.java
@@ -23,26 +23,23 @@
*/
package hudson.security;
+import com.google.common.base.Strings;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.Functions;
-
-import com.google.common.base.Strings;
-import org.acegisecurity.AuthenticationException;
-import org.acegisecurity.InsufficientAuthenticationException;
-import org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint;
-
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
-
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.URLEncoder;
import java.text.MessageFormat;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
+import org.springframework.security.authentication.InsufficientAuthenticationException;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
/**
* For anonymous requests to pages that require authentication,
@@ -59,12 +56,17 @@
*
* @author Kohsuke Kawaguchi
*/
-public class HudsonAuthenticationEntryPoint extends AuthenticationProcessingFilterEntryPoint {
- @Override
- public void commence(ServletRequest request, ServletResponse response, AuthenticationException reason) throws IOException, ServletException {
- HttpServletRequest req = (HttpServletRequest) request;
- HttpServletResponse rsp = (HttpServletResponse) response;
+@Restricted(NoExternalUse.class)
+public class HudsonAuthenticationEntryPoint implements AuthenticationEntryPoint {
+ private final String loginFormUrl;
+
+ public HudsonAuthenticationEntryPoint(String loginFormUrl) {
+ this.loginFormUrl = loginFormUrl;
+ }
+
+ @Override
+ public void commence(HttpServletRequest req, HttpServletResponse rsp, AuthenticationException reason) throws IOException, ServletException {
String requestedWith = req.getHeader("X-Requested-With");
if("XMLHttpRequest".equals(requestedWith)) {
// container authentication normally relies on session attribute to
@@ -77,7 +79,7 @@ public void commence(ServletRequest request, ServletResponse response, Authentic
// give the opportunity to include the target URL
String uriFrom = req.getRequestURI();
if(!Strings.isNullOrEmpty(req.getQueryString())) uriFrom += "?" + req.getQueryString();
- String loginForm = req.getContextPath()+getLoginFormUrl();
+ String loginForm = req.getContextPath() + loginFormUrl;
loginForm = MessageFormat.format(loginForm, URLEncoder.encode(uriFrom,"UTF-8"));
req.setAttribute("loginForm", loginForm);
@@ -86,11 +88,11 @@ public void commence(ServletRequest request, ServletResponse response, Authentic
Functions.advertiseHeaders(rsp);
- AccessDeniedException2 cause = null;
+ AccessDeniedException3 cause = null;
// report the diagnosis information if possible
if (reason instanceof InsufficientAuthenticationException) {
- if (reason.getCause() instanceof AccessDeniedException2) {
- cause = (AccessDeniedException2) reason.getCause();
+ if (reason.getCause() instanceof AccessDeniedException3) {
+ cause = (AccessDeniedException3) reason.getCause();
cause.reportAsHeaders(rsp);
}
}
diff --git a/core/src/main/java/hudson/security/HudsonFilter.java b/core/src/main/java/hudson/security/HudsonFilter.java
index b37b2ea4ea4e..a291a795e870 100644
--- a/core/src/main/java/hudson/security/HudsonFilter.java
+++ b/core/src/main/java/hudson/security/HudsonFilter.java
@@ -23,12 +23,9 @@
*/
package hudson.security;
-import jenkins.model.Jenkins;
-
import java.io.IOException;
-import java.util.logging.Logger;
import static java.util.logging.Level.SEVERE;
-
+import java.util.logging.Logger;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
@@ -37,10 +34,10 @@
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
-
-import org.acegisecurity.AuthenticationManager;
-import org.acegisecurity.ui.rememberme.RememberMeServices;
-import org.acegisecurity.userdetails.UserDetailsService;
+import jenkins.model.Jenkins;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.web.authentication.RememberMeServices;
/**
* {@link Filter} that Jenkins uses to implement security support.
@@ -67,7 +64,7 @@ public class HudsonFilter implements Filter {
private FilterConfig filterConfig;
/**
- * {@link AuthenticationManager} proxy so that the acegi filter chain can stay the same
+ * {@link AuthenticationManager} proxy so that the Spring Security filter chain can stay the same
* even when security setting is reconfigured.
*
* @deprecated in 1.271.
@@ -78,7 +75,7 @@ public class HudsonFilter implements Filter {
public static final AuthenticationManagerProxy AUTHENTICATION_MANAGER = new AuthenticationManagerProxy();
/**
- * {@link UserDetailsService} proxy so that the acegi filter chain can stay the same
+ * {@link UserDetailsService} proxy so that the Spring Security filter chain can stay the same
* even when security setting is reconfigured.
*
* @deprecated in 1.271.
@@ -89,7 +86,7 @@ public class HudsonFilter implements Filter {
public static final UserDetailsServiceProxy USER_DETAILS_SERVICE_PROXY = new UserDetailsServiceProxy();
/**
- * {@link RememberMeServices} proxy so that the acegi filter chain can stay the same
+ * {@link RememberMeServices} proxy so that the Spring Security filter chain can stay the same
* even when security setting is reconfigured.
*
* @deprecated in 1.271.
@@ -136,9 +133,9 @@ public static HudsonFilter get(ServletContext context) {
public void reset(SecurityRealm securityRealm) throws ServletException {
if (securityRealm != null) {
SecurityRealm.SecurityComponents sc = securityRealm.getSecurityComponents();
- AUTHENTICATION_MANAGER.setDelegate(sc.manager);
- USER_DETAILS_SERVICE_PROXY.setDelegate(sc.userDetails);
- REMEMBER_ME_SERVICES_PROXY.setDelegate(sc.rememberMe);
+ AUTHENTICATION_MANAGER.setDelegate(sc.manager2);
+ USER_DETAILS_SERVICE_PROXY.setDelegate(sc.userDetails2);
+ REMEMBER_ME_SERVICES_PROXY.setDelegate(sc.rememberMe2);
// make sure this.filter is always a valid filter.
Filter oldf = this.filter;
Filter newf = securityRealm.createFilter(this.filterConfig);
diff --git a/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java b/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java
index fcdfcf03cddf..463e20f051ac 100644
--- a/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java
+++ b/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java
@@ -47,16 +47,6 @@
import jenkins.util.SystemProperties;
import jenkins.security.seed.UserSeedProperty;
import net.sf.json.JSONObject;
-import org.acegisecurity.Authentication;
-import org.acegisecurity.AuthenticationException;
-import org.acegisecurity.BadCredentialsException;
-import org.acegisecurity.GrantedAuthority;
-import org.acegisecurity.context.SecurityContextHolder;
-import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
-import org.acegisecurity.providers.encoding.PasswordEncoder;
-import org.acegisecurity.providers.encoding.ShaPasswordEncoder;
-import org.acegisecurity.userdetails.UserDetails;
-import org.acegisecurity.userdetails.UsernameNotFoundException;
import org.jenkinsci.Symbol;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.ForwardToView;
@@ -68,7 +58,6 @@
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.interceptor.RequirePOST;
import org.mindrot.jbcrypt.BCrypt;
-import org.springframework.dao.DataAccessException;
import edu.umd.cs.findbugs.annotations.NonNull;
import javax.servlet.Filter;
@@ -86,7 +75,6 @@
import java.lang.reflect.Constructor;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
-import java.security.SecureRandom;
import java.util.*;
import java.util.logging.Logger;
import java.util.regex.Matcher;
@@ -94,6 +82,15 @@
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.crypto.password.PasswordEncoder;
/**
* {@link SecurityRealm} that performs authentication by looking up {@link User}.
@@ -183,12 +180,17 @@ private static boolean hasSomeUser() {
* This implementation doesn't support groups.
*/
@Override
- public GroupDetails loadGroupByGroupname(String groupname) throws UsernameNotFoundException, DataAccessException {
+ public GroupDetails loadGroupByGroupname2(String groupname, boolean fetchMembers) throws UsernameNotFoundException {
throw new UsernameNotFoundException(groupname);
}
@Override
- public Details loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
+ public UserDetails loadUserByUsername2(String username) throws UsernameNotFoundException {
+ return load(username).asUserDetails();
+ }
+
+ @Restricted(NoExternalUse.class)
+ public Details load(String username) throws UsernameNotFoundException {
User u = User.getById(username, false);
Details p = u!=null ? u.getProperty(Details.class) : null;
if(p==null)
@@ -199,18 +201,12 @@ public Details loadUserByUsername(String username) throws UsernameNotFoundExcept
}
@Override
- protected Details authenticate(String username, String password) throws AuthenticationException {
- Details u = loadUserByUsername(username);
+ protected UserDetails authenticate2(String username, String password) throws AuthenticationException {
+ Details u = load(username);
if (!u.isPasswordCorrect(password)) {
- String message;
- try {
- message = ResourceBundle.getBundle("org.acegisecurity.messages").getString("AbstractUserDetailsAuthenticationProvider.badCredentials");
- } catch (MissingResourceException x) {
- message = "Bad credentials";
- }
- throw new BadCredentialsException(message);
+ throw new BadCredentialsException("Bad credentials");
}
- return u;
+ return u.asUserDetails();
}
/**
@@ -281,7 +277,7 @@ private void loginAndTakeBack(StaplerRequest req, StaplerResponse rsp, User u) t
// ... and let him login
Authentication a = new UsernamePasswordAuthenticationToken(u.getId(),req.getParameter("password1"));
- a = this.getSecurityComponents().manager.authenticate(a);
+ a = this.getSecurityComponents().manager2.authenticate(a);
SecurityContextHolder.getContext().setAuthentication(a);
SecurityListener.fireLoggedIn(u.getId());
@@ -575,7 +571,7 @@ public User getUser(String id) {
}
// TODO
- private static final GrantedAuthority[] TEST_AUTHORITY = {AUTHENTICATED_AUTHORITY};
+ private static final Collection extends GrantedAuthority> TEST_AUTHORITY = Collections.singleton(AUTHENTICATED_AUTHORITY2);
public static final class SignupInfo {
public String username,password1,password2,fullname,email,captcha;
@@ -619,7 +615,7 @@ public SignupInfo(FederatedIdentity i) {
* is sent to the hidden input field by using {@link Protector}, so that
* the same password can be retained but without leaking information to the browser.
*/
- public static final class Details extends UserProperty implements InvalidatableUserDetails {
+ public static final class Details extends UserProperty {
/**
* Hashed password.
*/
@@ -642,20 +638,31 @@ static Details fromHashedPassword(String hashed) {
}
static Details fromPlainPassword(String rawPassword) {
- return new Details(PASSWORD_ENCODER.encodePassword(rawPassword,null));
+ return new Details(PASSWORD_ENCODER.encode(rawPassword));
}
- public GrantedAuthority[] getAuthorities() {
+ /**
+ * @since TODO
+ */
+ public Collection extends GrantedAuthority> getAuthorities2() {
// TODO
return TEST_AUTHORITY;
}
+ /**
+ * @deprecated use {@link #getAuthorities2}
+ */
+ @Deprecated
+ public org.acegisecurity.GrantedAuthority[] getAuthorities() {
+ return org.acegisecurity.GrantedAuthority.fromSpring(getAuthorities2());
+ }
+
public String getPassword() {
return passwordHash;
}
public boolean isPasswordCorrect(String candidate) {
- return PASSWORD_ENCODER.isPasswordValid(getPassword(),candidate,null);
+ return PASSWORD_ENCODER.matches(candidate, getPassword());
}
public String getProtectedPassword() {
@@ -687,8 +694,57 @@ public boolean isEnabled() {
return true;
}
- public boolean isInvalid() {
- return user==null;
+ UserDetails asUserDetails() {
+ return new UserDetailsImpl();
+ }
+
+ private final class UserDetailsImpl implements UserDetails {
+
+ @Override
+ public Collection extends GrantedAuthority> getAuthorities() {
+ return Details.this.getAuthorities2();
+ }
+
+ @Override
+ public String getPassword() {
+ return Details.this.getPassword();
+ }
+
+ @Override
+ public String getUsername() {
+ return Details.this.getUsername();
+ }
+
+ @Override
+ public boolean isAccountNonExpired() {
+ return Details.this.isAccountNonExpired();
+ }
+
+ @Override
+ public boolean isAccountNonLocked() {
+ return Details.this.isAccountNonLocked();
+ }
+
+ @Override
+ public boolean isCredentialsNonExpired() {
+ return Details.this.isCredentialsNonExpired();
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return Details.this.isEnabled();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof UserDetailsImpl && ((UserDetailsImpl) o).getUsername().equals(getUsername());
+ }
+
+ @Override
+ public int hashCode() {
+ return getUsername().hashCode();
+ }
+
}
public static class ConverterImpl extends XStream2.PassthruConverter {
@@ -696,7 +752,7 @@ public static class ConverterImpl extends XStream2.PassthruConverter {
@Override protected void callback(Details d, UnmarshallingContext context) {
// Convert to hashed password and report to monitor if we load old data
if (d.password!=null && d.passwordHash==null) {
- d.passwordHash = PASSWORD_ENCODER.encodePassword(Scrambler.descramble(d.password),null);
+ d.passwordHash = PASSWORD_ENCODER.encode(Scrambler.descramble(d.password));
OldDataMonitor.report(context, "1.283");
}
}
@@ -804,56 +860,7 @@ public Category getCategory() {
}
}
- /**
- * {@link PasswordEncoder} based on SHA-256 and random salt generation.
- *
- *
- * The salt is prepended to the hashed password and returned. So the encoded password is of the form
- * {@code SALT ':' hash(PASSWORD,SALT)}.
- *
- *
- * This abbreviates the need to store the salt separately, which in turn allows us to hide the salt handling
- * in this little class. The rest of the Acegi thinks that we are not using salt.
- */
- /*package*/ static final PasswordEncoder CLASSIC = new PasswordEncoder() {
- private final PasswordEncoder passwordEncoder = new ShaPasswordEncoder(256);
-
- public String encodePassword(String rawPass, Object obj) throws DataAccessException {
- return hash(rawPass);
- }
-
- public boolean isPasswordValid(String encPass, String rawPass, Object obj) throws DataAccessException {
- // pull out the sale from the encoded password
- int i = encPass.indexOf(':');
- if(i<0) return false;
- String salt = encPass.substring(0,i);
- return encPass.substring(i+1).equals(passwordEncoder.encodePassword(rawPass,salt));
- }
-
- /**
- * Creates a hashed password by generating a random salt.
- */
- private String hash(String password) {
- String salt = generateSalt();
- return salt+':'+passwordEncoder.encodePassword(password,salt);
- }
-
- /**
- * Generates random salt.
- */
- private String generateSalt() {
- StringBuilder buf = new StringBuilder();
- SecureRandom sr = new SecureRandom();
- for( int i=0; i<6; i++ ) {// log2(52^6)=34.20... so, this is about 32bit strong.
- boolean upper = sr.nextBoolean();
- char ch = (char)(sr.nextInt(26) + 'a');
- if(upper) ch=Character.toUpperCase(ch);
- buf.append(ch);
- }
- return buf.toString();
- }
- };
-
+ // TODO can we instead use BCryptPasswordEncoder from Spring Security, which has its own copy of BCrypt so we could drop the special library?
/**
* {@link PasswordEncoder} that uses jBCrypt.
*/
@@ -866,12 +873,14 @@ private static class JBCryptEncoder implements PasswordEncoder {
private static final Pattern BCRYPT_PATTERN = Pattern.compile("^\\$2a\\$([0-9]{2})\\$.{53}$");
- public String encodePassword(String rawPass, Object obj) throws DataAccessException {
- return BCrypt.hashpw(rawPass,BCrypt.gensalt());
+ @Override
+ public String encode(CharSequence rawPassword) {
+ return BCrypt.hashpw(rawPassword.toString(), BCrypt.gensalt());
}
- public boolean isPasswordValid(String encPass, String rawPass, Object obj) throws DataAccessException {
- return BCrypt.checkpw(rawPass,encPass);
+ @Override
+ public boolean matches(CharSequence rawPassword, String encodedPassword) {
+ return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
}
/**
@@ -895,9 +904,10 @@ public boolean isHashValid(String hash) {
/* package */ static final JBCryptEncoder JBCRYPT_ENCODER = new JBCryptEncoder();
+ // TODO check if DelegatingPasswordEncoder can be used
/**
- * Combines {@link #JBCRYPT_ENCODER} and {@link #CLASSIC} into one so that we can continue
- * to accept {@link #CLASSIC} format but new encoding will always done via {@link #JBCRYPT_ENCODER}.
+ * Wraps {@link #JBCRYPT_ENCODER}.
+ * There used to be a SHA-256-based encoder but this is long deprecated, and insecure anyway.
*/
/* package */ static class MultiPasswordEncoder implements PasswordEncoder {
/**
@@ -911,15 +921,17 @@ public boolean isHashValid(String hash) {
'#' is neither in base64 nor hex, which makes it a good choice.
*/
- public String encodePassword(String rawPass, Object salt) throws DataAccessException {
- return JBCRYPT_HEADER+JBCRYPT_ENCODER.encodePassword(rawPass,salt);
+ @Override
+ public String encode(CharSequence rawPassword) {
+ return JBCRYPT_HEADER+JBCRYPT_ENCODER.encode(rawPassword);
}
- public boolean isPasswordValid(String encPass, String rawPass, Object salt) throws DataAccessException {
+ @Override
+ public boolean matches(CharSequence rawPassword, String encPass) {
if (isPasswordHashed(encPass)) {
- return JBCRYPT_ENCODER.isPasswordValid(encPass.substring(JBCRYPT_HEADER.length()), rawPass, salt);
+ return JBCRYPT_ENCODER.matches(rawPassword, encPass.substring(JBCRYPT_HEADER.length()));
} else {
- return CLASSIC.isPasswordValid(encPass, rawPass, salt);
+ return false;
}
}
diff --git a/core/src/main/java/hudson/security/InvalidatableUserDetails.java b/core/src/main/java/hudson/security/InvalidatableUserDetails.java
deleted file mode 100644
index 8f59170c01bf..000000000000
--- a/core/src/main/java/hudson/security/InvalidatableUserDetails.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * The MIT License
- *
- * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-package hudson.security;
-
-import org.acegisecurity.Authentication;
-import org.acegisecurity.context.SecurityContext;
-import org.acegisecurity.userdetails.UserDetails;
-
-import javax.servlet.http.HttpSession;
-import jenkins.security.NonSerializableSecurityContext;
-
-/**
- * {@link UserDetails} that can mark {@link Authentication} invalid.
- *
- *
- * Tomcat persists sessions by using Java serialization (and
- * that includes the security token created by Acegi, which includes this object)
- * and when that happens, the next time the server comes back
- * it will try to deserialize {@link SecurityContext} that Acegi
- * puts into {@link HttpSession} (which transitively includes {@link UserDetails}
- * that can be implemented by Hudson.
- *
- *
- * Such {@link UserDetails} implementation can override the {@link #isInvalid()}
- * method and return false, so that such {@link SecurityContext} will be
- * dropped before the rest of Acegi sees it.
- *
- *
- * See JENKINS-1482
- *
- * @author Kohsuke Kawaguchi
- * @deprecated
- * Starting 1.285, Hudson stops persisting {@link Authentication} altogether
- * (see {@link NonSerializableSecurityContext}), so there's no need to use this mechanism.
- */
-@Deprecated
-public interface InvalidatableUserDetails extends UserDetails {
- boolean isInvalid();
-}
diff --git a/core/src/main/java/hudson/security/LegacySecurityRealm.java b/core/src/main/java/hudson/security/LegacySecurityRealm.java
index 3db5a4b92248..807e2f324af4 100644
--- a/core/src/main/java/hudson/security/LegacySecurityRealm.java
+++ b/core/src/main/java/hudson/security/LegacySecurityRealm.java
@@ -23,21 +23,19 @@
*/
package hudson.security;
-import org.acegisecurity.AuthenticationManager;
-import org.acegisecurity.Authentication;
-import org.acegisecurity.AuthenticationException;
+import hudson.Extension;
+import hudson.model.Descriptor;
+import java.util.ArrayList;
+import java.util.List;
+import javax.servlet.Filter;
+import javax.servlet.FilterConfig;
import org.jenkinsci.Symbol;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.DataBoundConstructor;
-import org.springframework.web.context.WebApplicationContext;
-import groovy.lang.Binding;
-import hudson.model.Descriptor;
-import hudson.util.spring.BeanBuilder;
-import hudson.Extension;
-
-import javax.servlet.Filter;
-import javax.servlet.FilterConfig;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
/**
* {@link SecurityRealm} that accepts {@link ContainerAuthentication} object
@@ -76,22 +74,17 @@ public String getLoginUrl() {
return "loginEntry";
}
- /**
- * Filter to run for the LegacySecurityRealm is the
- * ChainServletFilter legacy from /WEB-INF/security/SecurityFilters.groovy.
- */
@Override
public Filter createFilter(FilterConfig filterConfig) {
- Binding binding = new Binding();
- SecurityComponents sc = this.createSecurityComponents();
- binding.setVariable("securityComponents", sc);
- binding.setVariable("securityRealm",this);
- BeanBuilder builder = new BeanBuilder();
- builder.parse(filterConfig.getServletContext().getResourceAsStream("/WEB-INF/security/SecurityFilters.groovy"),binding);
-
- WebApplicationContext context = builder.createApplicationContext();
-
- return (Filter) context.getBean("legacy");
+ // this filter set up is used to emulate the legacy Hudson behavior
+ // of container authentication before 1.160
+ List filters = new ArrayList<>();
+ filters.add(new BasicAuthenticationFilter());
+ filters.addAll(commonFilters());
+ // when using container-authentication we can't hit /login directly.
+ // we first have to hit protected /loginEntry, then let the container
+ // trap that into /login.
+ return new ChainedServletFilter(filters);
}
/**
diff --git a/core/src/main/java/hudson/security/RememberMeServicesProxy.java b/core/src/main/java/hudson/security/RememberMeServicesProxy.java
index 6b4ba001a0b7..065d2fc8b242 100644
--- a/core/src/main/java/hudson/security/RememberMeServicesProxy.java
+++ b/core/src/main/java/hudson/security/RememberMeServicesProxy.java
@@ -23,13 +23,14 @@
*/
package hudson.security;
-import jenkins.model.Jenkins;
-import jenkins.security.ConfidentialStore;
-import org.acegisecurity.ui.rememberme.RememberMeServices;
-import org.acegisecurity.Authentication;
-
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import jenkins.model.Jenkins;
+import jenkins.security.ConfidentialStore;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.authentication.RememberMeServices;
/**
* {@link RememberMeServices} proxy.
@@ -45,6 +46,7 @@
*
* @author Kohsuke Kawaguchi
*/
+@Restricted(NoExternalUse.class)
public class RememberMeServicesProxy implements RememberMeServices {
private volatile RememberMeServices delegate;
diff --git a/core/src/main/java/hudson/security/SecurityMode.java b/core/src/main/java/hudson/security/SecurityMode.java
index a2a1922ad375..6119cd04e3ef 100644
--- a/core/src/main/java/hudson/security/SecurityMode.java
+++ b/core/src/main/java/hudson/security/SecurityMode.java
@@ -45,7 +45,7 @@ public enum SecurityMode {
*/
LEGACY,
/**
- * Security-enabled mode implemented through Acegi.
+ * Security-enabled mode implemented through Spring Security.
*/
SECURED
}
diff --git a/core/src/main/java/hudson/security/SecurityRealm.java b/core/src/main/java/hudson/security/SecurityRealm.java
index 4ee1202b920a..949f8cb6c2ea 100644
--- a/core/src/main/java/hudson/security/SecurityRealm.java
+++ b/core/src/main/java/hudson/security/SecurityRealm.java
@@ -23,55 +23,61 @@
*/
package hudson.security;
-import groovy.lang.Binding;
-import hudson.ExtensionPoint;
import hudson.DescriptorExtensionList;
import hudson.Extension;
+import hudson.ExtensionPoint;
+import hudson.Util;
import hudson.cli.CLICommand;
import hudson.model.AbstractDescribableImpl;
import hudson.model.Descriptor;
-import jenkins.model.IdStrategy;
-import jenkins.model.Jenkins;
import hudson.security.FederatedLoginService.FederatedIdentity;
import hudson.security.captcha.CaptchaSupport;
import hudson.util.DescriptorList;
import hudson.util.PluginServletFilter;
-import hudson.util.spring.BeanBuilder;
-import org.acegisecurity.Authentication;
-import org.acegisecurity.AuthenticationManager;
-import org.acegisecurity.GrantedAuthorityImpl;
-import org.acegisecurity.GrantedAuthority;
-import org.acegisecurity.context.SecurityContext;
-import org.acegisecurity.context.SecurityContextHolder;
-import org.acegisecurity.ui.rememberme.RememberMeServices;
-import static org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices.ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY;
-import org.acegisecurity.userdetails.UserDetailsService;
-import org.acegisecurity.userdetails.UserDetails;
-import org.acegisecurity.userdetails.UsernameNotFoundException;
-import org.apache.commons.lang.StringUtils;
-import org.kohsuke.accmod.Restricted;
-import org.kohsuke.accmod.restrictions.DoNotUse;
-import org.kohsuke.stapler.HttpResponse;
-import org.kohsuke.stapler.Stapler;
-import org.kohsuke.stapler.StaplerRequest;
-import org.kohsuke.stapler.StaplerResponse;
-import org.springframework.context.ApplicationContext;
-import org.springframework.web.context.WebApplicationContext;
-import org.springframework.dao.DataAccessException;
-
-import javax.servlet.Filter;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpSession;
-import javax.servlet.http.Cookie;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
-import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
+import javax.servlet.Filter;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpSession;
+import jenkins.model.IdStrategy;
+import jenkins.model.Jenkins;
+import jenkins.security.AcegiSecurityExceptionFilter;
+import jenkins.security.BasicHeaderProcessor;
import net.sf.json.JSONObject;
+import org.apache.commons.lang.StringUtils;
import org.jenkinsci.Symbol;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.DoNotUse;
+import org.kohsuke.stapler.HttpResponse;
+import org.kohsuke.stapler.Stapler;
+import org.kohsuke.stapler.StaplerRequest;
+import org.kohsuke.stapler.StaplerResponse;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.web.access.ExceptionTranslationFilter;
+import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
+import org.springframework.security.web.authentication.RememberMeServices;
+import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
+import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter;
+import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
+import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
+import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
/**
* Pluggable security realm that connects external user database to Hudson.
@@ -87,7 +93,7 @@
* For compatibility reasons, there are two somewhat different ways to implement a custom SecurityRealm.
*
*
- * One is to override the {@link #createSecurityComponents()} and create key Acegi components
+ * One is to override the {@link #createSecurityComponents()} and create key Spring Security components
* that control the authentication process.
* The default {@link SecurityRealm#createFilter(FilterConfig)} implementation then assembles them
* into a chain of {@link Filter}s. All the incoming requests to Hudson go through this filter chain,
@@ -106,7 +112,7 @@
* The other way of doing this is to ignore {@link #createSecurityComponents()} completely (by returning
* {@link SecurityComponents} created by the default constructor) and just concentrate on {@link #createFilter(FilterConfig)}.
* As long as the resulting filter chain properly sets up {@link Authentication} object at the end of the processing,
- * Hudson doesn't really need you to fit the standard Acegi models like {@link AuthenticationManager} and
+ * Jenkins doesn't really need you to fit the standard Spring Security models like {@link AuthenticationManager} and
* {@link UserDetailsService}.
*
*
@@ -157,12 +163,12 @@ public abstract class SecurityRealm extends AbstractDescribableImpl getDescriptor() {
/**
* Returns the URL to submit a form for the authentication.
* There's no need to override this, except for {@link LegacySecurityRealm}.
+ * @see AuthenticationProcessingFilter2
*/
public String getAuthenticationGatewayUrl() {
- return "j_acegi_security_check";
+ // Default as of Spring Security 3: https://stackoverflow.com/a/62552368/12916
+ // Cannot use the 4+ default of /login since that would clash with Jenkins/login.jelly which would be activated even for GET requests,
+ // and which cannot trivially be renamed since it is a fairly well-known URL sometimes used e.g. for K8s liveness checks.
+ return "j_spring_security_check";
}
/**
@@ -258,12 +268,30 @@ public boolean canLogOut() {
* This parameter allows you to redirect people to different pages depending on who they are.
* @return
* never null.
- * @since 1.314
+ * @since TODO
* @see #doLogout(StaplerRequest, StaplerResponse)
*/
- protected String getPostLogOutUrl(StaplerRequest req, Authentication auth) {
+ protected String getPostLogOutUrl2(StaplerRequest req, Authentication auth) {
+ if (Util.isOverridden(SecurityRealm.class, getClass(), "getPostLogOutUrl", StaplerRequest.class, org.acegisecurity.Authentication.class) && !insideGetPostLogOutUrl.get()) {
+ insideGetPostLogOutUrl.set(true);
+ try {
+ return getPostLogOutUrl(req, org.acegisecurity.Authentication.fromSpring(auth));
+ } finally {
+ insideGetPostLogOutUrl.set(false);
+ }
+ }
return req.getContextPath()+"/";
}
+ private static final ThreadLocal insideGetPostLogOutUrl = ThreadLocal.withInitial(() -> false);
+
+ /**
+ * @deprecated use {@link #getPostLogOutUrl2}
+ * @since 1.314
+ */
+ @Deprecated
+ protected String getPostLogOutUrl(StaplerRequest req, org.acegisecurity.Authentication auth) {
+ return getPostLogOutUrl2(req, auth.toSpring());
+ }
public CaptchaSupport getCaptchaSupport() {
return captchaSupport;
@@ -282,7 +310,7 @@ public List> getCaptchaSupportDescriptors() {
*
*
* The default implementation erases the session and do a few other clean up, then
- * redirect the user to the URL specified by {@link #getPostLogOutUrl(StaplerRequest, Authentication)}.
+ * redirect the user to the URL specified by {@link #getPostLogOutUrl2(StaplerRequest, Authentication)}.
*
* @since 1.314
*/
@@ -297,11 +325,11 @@ public void doLogout(StaplerRequest req, StaplerResponse rsp) throws IOException
resetRememberMeCookie(req, rsp, contextPath);
clearStaleSessionCookies(req, rsp, contextPath);
- rsp.sendRedirect2(getPostLogOutUrl(req,auth));
+ rsp.sendRedirect2(getPostLogOutUrl2(req,auth));
}
private void resetRememberMeCookie(StaplerRequest req, StaplerResponse rsp, String contextPath) {
- Cookie cookie = new Cookie(ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY, "");
+ Cookie cookie = new Cookie(TokenBasedRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, "");
cookie.setMaxAge(0);
cookie.setSecure(req.isSecure());
cookie.setHttpOnly(true);
@@ -363,25 +391,36 @@ public boolean allowsSignup() {
/**
* Shortcut for {@link UserDetailsService#loadUserByUsername(String)}.
*
- * @throws UserMayOrMayNotExistException
+ * @throws UserMayOrMayNotExistException2
* If the security realm cannot even tell if the user exists or not.
* @return
* never null.
+ * @since TODO
*/
- public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
- return getSecurityComponents().userDetails.loadUserByUsername(username);
+ public UserDetails loadUserByUsername2(String username) throws UsernameNotFoundException {
+ if (Util.isOverridden(SecurityRealm.class, getClass(), "loadUserByUsername", String.class)) {
+ try {
+ return loadUserByUsername(username).toSpring();
+ } catch (org.acegisecurity.AcegiSecurityException x) {
+ throw x.toSpring();
+ } catch (org.springframework.dao.DataAccessException x) {
+ throw x.toSpring();
+ }
+ } else {
+ return getSecurityComponents().userDetails2.loadUserByUsername(username);
+ }
}
/**
- * If this {@link SecurityRealm} supports a look up of {@link GroupDetails} by their names, override this method
- * to provide the look up.
- *
- *
- * This information, when available, can be used by {@link AuthorizationStrategy}s to improve the UI and
- * error diagnostics for the user.
+ * @deprecated use {@link #loadUserByUsername2}
*/
- public GroupDetails loadGroupByGroupname(String groupname) throws UsernameNotFoundException, DataAccessException {
- throw new UserMayOrMayNotExistException(groupname);
+ @Deprecated
+ public org.acegisecurity.userdetails.UserDetails loadUserByUsername(String username) throws org.acegisecurity.userdetails.UsernameNotFoundException, org.springframework.dao.DataAccessException {
+ try {
+ return org.acegisecurity.userdetails.UserDetails.fromSpring(loadUserByUsername2(username));
+ } catch (AuthenticationException x) {
+ throw org.acegisecurity.AuthenticationException.fromSpring(x);
+ }
}
/**
@@ -395,14 +434,56 @@ public GroupDetails loadGroupByGroupname(String groupname) throws UsernameNotFou
* @param fetchMembers if {@code true} then try and fetch the members of the group if it exists. Trying does not
* imply that the members will be fetched and {@link hudson.security.GroupDetails#getMembers()}
* may still return {@code null}
- * @throws UserMayOrMayNotExistException if no conclusive result could be determined regarding the group existence.
+ * @throws UserMayOrMayNotExistException2 if no conclusive result could be determined regarding the group existence.
* @throws UsernameNotFoundException if the group does not exist.
- * @throws DataAccessException if the backing security realm could not be connected to.
+ * @since TODO
+ */
+ public GroupDetails loadGroupByGroupname2(String groupname, boolean fetchMembers)
+ throws UsernameNotFoundException {
+ if (Util.isOverridden(SecurityRealm.class, getClass(), "loadGroupByGroupname", String.class)) {
+ try {
+ return loadGroupByGroupname(groupname);
+ } catch (org.acegisecurity.AcegiSecurityException x) {
+ throw x.toSpring();
+ } catch (org.springframework.dao.DataAccessException x) {
+ throw x.toSpring();
+ }
+ } else if (Util.isOverridden(SecurityRealm.class, getClass(), "loadGroupByGroupname", String.class, boolean.class)) {
+ try {
+ return loadGroupByGroupname(groupname, fetchMembers);
+ } catch (org.acegisecurity.AcegiSecurityException x) {
+ throw x.toSpring();
+ } catch (org.springframework.dao.DataAccessException x) {
+ throw x.toSpring();
+ }
+ } else {
+ throw new UserMayOrMayNotExistException2(groupname);
+ }
+ }
+
+ /**
+ * @deprecated use {@link #loadGroupByGroupname2}
+ */
+ @Deprecated
+ public GroupDetails loadGroupByGroupname(String groupname) throws org.acegisecurity.userdetails.UsernameNotFoundException, org.springframework.dao.DataAccessException {
+ try {
+ return loadGroupByGroupname2(groupname, false);
+ } catch (AuthenticationException x) {
+ throw org.acegisecurity.AuthenticationException.fromSpring(x);
+ }
+ }
+
+ /**
+ * @deprecated use {@link #loadGroupByGroupname2}
* @since 1.549
*/
- public GroupDetails loadGroupByGroupname(String groupname, boolean fetchMembers)
- throws UsernameNotFoundException, DataAccessException {
- return loadGroupByGroupname(groupname);
+ @Deprecated
+ public GroupDetails loadGroupByGroupname(String groupname, boolean fetchMembers) throws org.acegisecurity.userdetails.UsernameNotFoundException, org.springframework.dao.DataAccessException {
+ try {
+ return loadGroupByGroupname2(groupname, fetchMembers);
+ } catch (AuthenticationException x) {
+ throw org.acegisecurity.AuthenticationException.fromSpring(x);
+ }
}
/**
@@ -455,26 +536,6 @@ protected final boolean validateCaptcha(String text) {
return true;
}
- /**
- * Picks up the instance of the given type from the spring context.
- * If there are multiple beans of the same type or if there are none,
- * this method treats that as an {@link IllegalArgumentException}.
- *
- * This method is intended to be used to pick up a Acegi object from
- * spring once the bean definition file is parsed.
- */
- public static T findBean(Class type, ApplicationContext context) {
- Map m = context.getBeansOfType(type);
- switch(m.size()) {
- case 0:
- throw new IllegalArgumentException("No beans of "+type+" are defined");
- case 1:
- return type.cast(m.values().iterator().next());
- default:
- throw new IllegalArgumentException("Multiple beans of "+type+" are defined: "+m);
- }
- }
-
/**
* Holder for the SecurityComponents.
*/
@@ -497,7 +558,7 @@ public synchronized SecurityComponents getSecurityComponents() {
*
*
* The default implementation uses {@link #getSecurityComponents()} and builds
- * a standard filter chain from /WEB-INF/security/SecurityFilters.groovy.
+ * a standard filter chain.
* But subclasses can override this to completely change the filter sequence.
*
*
@@ -509,14 +570,46 @@ public synchronized SecurityComponents getSecurityComponents() {
public Filter createFilter(FilterConfig filterConfig) {
LOGGER.entering(SecurityRealm.class.getName(), "createFilter");
- Binding binding = new Binding();
SecurityComponents sc = getSecurityComponents();
- binding.setVariable("securityComponents", sc);
- binding.setVariable("securityRealm",this);
- BeanBuilder builder = new BeanBuilder();
- builder.parse(filterConfig.getServletContext().getResourceAsStream("/WEB-INF/security/SecurityFilters.groovy"),binding);
- WebApplicationContext context = builder.createApplicationContext();
- return (Filter) context.getBean("filter");
+ List filters = new ArrayList<>();
+ {
+ HttpSessionSecurityContextRepository httpSessionSecurityContextRepository = new HttpSessionSecurityContextRepository();
+ httpSessionSecurityContextRepository.setAllowSessionCreation(false);
+ filters.add(new HttpSessionContextIntegrationFilter2(httpSessionSecurityContextRepository));
+ }
+ { // if any "Authorization: Basic xxx:yyy" is sent this is the filter that processes it
+ BasicHeaderProcessor bhp = new BasicHeaderProcessor();
+ // if basic authentication fails (which only happens incorrect basic auth credential is sent),
+ // respond with 401 with basic auth request, instead of redirecting the user to the login page,
+ // since users of basic auth tends to be a program and won't see the redirection to the form
+ // page as a failure
+ BasicAuthenticationEntryPoint basicAuthenticationEntryPoint = new BasicAuthenticationEntryPoint();
+ basicAuthenticationEntryPoint.setRealmName("Jenkins");
+ bhp.setAuthenticationEntryPoint(basicAuthenticationEntryPoint);
+ bhp.setRememberMeServices(sc.rememberMe2);
+ filters.add(bhp);
+ }
+ {
+ AuthenticationProcessingFilter2 apf = new AuthenticationProcessingFilter2(getAuthenticationGatewayUrl());
+ apf.setAuthenticationManager(sc.manager2);
+ apf.setRememberMeServices(sc.rememberMe2);
+ apf.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler("/loginError"));
+ // TODO apf.defaultTargetUrl = "/" try SavedRequestAwareAuthenticationSuccessHandler
+ filters.add(apf);
+ }
+ filters.add(new RememberMeAuthenticationFilter(sc.manager2, sc.rememberMe2));
+ filters.addAll(commonFilters());
+ return new ChainedServletFilter(filters);
+ }
+
+ protected final List commonFilters() {
+ // like Jenkins.ANONYMOUS:
+ AnonymousAuthenticationFilter apf = new AnonymousAuthenticationFilter("anonymous", "anonymous", Collections.singletonList(new SimpleGrantedAuthority("anonymous")));
+ ExceptionTranslationFilter etf = new ExceptionTranslationFilter(new HudsonAuthenticationEntryPoint("/" + getLoginUrl() + "?from={0}"));
+ etf.setAccessDeniedHandler(new AccessDeniedHandlerImpl());
+ UnwrapSecurityExceptionFilter usef = new UnwrapSecurityExceptionFilter();
+ AcegiSecurityExceptionFilter asef = new AcegiSecurityExceptionFilter();
+ return Arrays.asList(apf, etf, usef, asef);
}
/**
@@ -578,7 +671,7 @@ public Authentication authenticate(Authentication authentication) {
return authentication;
}
}, new UserDetailsService() {
- public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
+ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
throw new UsernameNotFoundException(username);
}
});
@@ -588,7 +681,7 @@ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundEx
* There's no group.
*/
@Override
- public GroupDetails loadGroupByGroupname(String groupname) throws UsernameNotFoundException, DataAccessException {
+ public GroupDetails loadGroupByGroupname2(String groupname, boolean fetchMembers) throws UsernameNotFoundException {
throw new UsernameNotFoundException(groupname);
}
@@ -633,9 +726,33 @@ public SecurityRealm newInstance(StaplerRequest req, JSONObject formData) throws
* @see SecurityRealm#createSecurityComponents()
*/
public static final class SecurityComponents {
- public final AuthenticationManager manager;
- public final UserDetailsService userDetails;
- public final RememberMeServices rememberMe;
+ /**
+ * @since TODO
+ */
+ public final AuthenticationManager manager2;
+ /**
+ * @deprecated use {@link #manager2}
+ */
+ @Deprecated
+ public final org.acegisecurity.AuthenticationManager manager;
+ /**
+ * @since TODO
+ */
+ public final UserDetailsService userDetails2;
+ /**
+ * @deprecated use {@link #userDetails2}
+ */
+ @Deprecated
+ public final org.acegisecurity.userdetails.UserDetailsService userDetails;
+ /**
+ * @since TODO
+ */
+ public final RememberMeServices rememberMe2;
+ /**
+ * @deprecated use {@link #rememberMe2}
+ */
+ @Deprecated
+ public final org.acegisecurity.ui.rememberme.RememberMeServices rememberMe;
public SecurityComponents() {
// we use AuthenticationManagerProxy here just as an implementation that fails all the time,
@@ -643,39 +760,62 @@ public SecurityComponents() {
this(new AuthenticationManagerProxy());
}
+ /**
+ * @since TODO
+ */
public SecurityComponents(AuthenticationManager manager) {
// we use UserDetailsServiceProxy here just as an implementation that fails all the time,
// not as a proxy. No one is supposed to use this as a proxy.
this(manager,new UserDetailsServiceProxy());
}
+ /**
+ * @deprecated use {@link #SecurityComponents(AuthenticationManager)}
+ */
+ @Deprecated
+ public SecurityComponents(org.acegisecurity.AuthenticationManager manager) {
+ this(manager.toSpring());
+ }
+
+ /**
+ * @since TODO
+ */
public SecurityComponents(AuthenticationManager manager, UserDetailsService userDetails) {
this(manager,userDetails,createRememberMeService(userDetails));
}
+ /**
+ * @deprecated use {@link #SecurityComponents(AuthenticationManager, UserDetailsService)}
+ */
+ @Deprecated
+ public SecurityComponents(org.acegisecurity.AuthenticationManager manager, org.acegisecurity.userdetails.UserDetailsService userDetails) {
+ this(manager.toSpring(), userDetails.toSpring());
+ }
+
+ /**
+ * @since TODO
+ */
public SecurityComponents(AuthenticationManager manager, UserDetailsService userDetails, RememberMeServices rememberMe) {
assert manager!=null && userDetails!=null && rememberMe!=null;
- this.manager = manager;
- this.userDetails = userDetails;
- this.rememberMe = rememberMe;
+ this.manager2 = manager;
+ this.userDetails2 = userDetails;
+ this.rememberMe2 = rememberMe;
+ this.manager = org.acegisecurity.AuthenticationManager.fromSpring(manager);
+ this.userDetails = org.acegisecurity.userdetails.UserDetailsService.fromSpring(userDetails);
+ this.rememberMe = org.acegisecurity.ui.rememberme.RememberMeServices.fromSpring(rememberMe);
+ }
+
+ /**
+ * @deprecated use {@link #SecurityComponents(AuthenticationManager, UserDetailsService, RememberMeServices)}
+ */
+ @Deprecated
+ public SecurityComponents(org.acegisecurity.AuthenticationManager manager, org.acegisecurity.userdetails.UserDetailsService userDetails, org.acegisecurity.ui.rememberme.RememberMeServices rememberMe) {
+ this(manager.toSpring(), userDetails.toSpring(), rememberMe.toSpring());
}
- @SuppressWarnings("deprecation")
private static RememberMeServices createRememberMeService(UserDetailsService uds) {
// create our default TokenBasedRememberMeServices, which depends on the availability of the secret key
- TokenBasedRememberMeServices2 rms = new TokenBasedRememberMeServices2();
- rms.setUserDetailsService(uds);
- /*
- TokenBasedRememberMeServices needs to be used in conjunction with RememberMeAuthenticationProvider,
- and both needs to use the same key (this is a reflection of a poor design in AcegiSecurity, if you ask me)
- and various security plugins have its own groovy script that configures them.
-
- So if we change this, it creates a painful situation for those plugins by forcing them to choose
- to work with earlier version of Jenkins or newer version of Jenkins, and not both.
-
- So we keep this here.
- */
- rms.setKey(Jenkins.get().getSecretKey());
+ TokenBasedRememberMeServices2 rms = new TokenBasedRememberMeServices2(uds);
rms.setParameter("remember_me"); // this is the form field name in login.jelly
return rms;
}
@@ -703,6 +843,14 @@ public static DescriptorExtensionList> a
/**
* {@link GrantedAuthority} that represents the built-in "authenticated" role, which is granted to
* anyone non-anonymous.
+ * @since TODO
*/
- public static final GrantedAuthority AUTHENTICATED_AUTHORITY = new GrantedAuthorityImpl("authenticated");
+ public static final GrantedAuthority AUTHENTICATED_AUTHORITY2 = new SimpleGrantedAuthority("authenticated");
+
+ /**
+ * @deprecated use {@link #AUTHENTICATED_AUTHORITY2}
+ */
+ @Deprecated
+ public static final org.acegisecurity.GrantedAuthority AUTHENTICATED_AUTHORITY = new org.acegisecurity.GrantedAuthorityImpl("authenticated");
+
}
diff --git a/core/src/main/java/hudson/security/SidACL.java b/core/src/main/java/hudson/security/SidACL.java
index 1f9ca23610d0..3bdeb3ee4fe2 100644
--- a/core/src/main/java/hudson/security/SidACL.java
+++ b/core/src/main/java/hudson/security/SidACL.java
@@ -23,16 +23,15 @@
*/
package hudson.security;
-import org.acegisecurity.Authentication;
-import org.acegisecurity.GrantedAuthority;
-import org.acegisecurity.acls.sid.PrincipalSid;
-import org.acegisecurity.acls.sid.GrantedAuthoritySid;
-import org.acegisecurity.acls.sid.Sid;
-
import edu.umd.cs.findbugs.annotations.NonNull;
-import java.util.logging.Logger;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.FINER;
+import java.util.logging.Logger;
+import org.acegisecurity.acls.sid.GrantedAuthoritySid;
+import org.acegisecurity.acls.sid.PrincipalSid;
+import org.acegisecurity.acls.sid.Sid;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
/**
* {@link ACL} that checks permissions based on {@link GrantedAuthority}
@@ -43,8 +42,8 @@
public abstract class SidACL extends ACL {
@Override
- public boolean hasPermission(@NonNull Authentication a, Permission permission) {
- if(a==SYSTEM) {
+ public boolean hasPermission2(@NonNull Authentication a, Permission permission) {
+ if(a.equals(SYSTEM2)) {
if(LOGGER.isLoggable(FINE))
LOGGER.fine("hasPermission("+a+","+permission+")=>SYSTEM user has full access");
return true;
@@ -59,7 +58,7 @@ public boolean hasPermission(@NonNull Authentication a, Permission permission) {
}
/**
- * Implementation that backs up {@link #hasPermission(Authentication, Permission)}.
+ * Implementation that backs up {@link #hasPermission2(Authentication, Permission)}.
*
* @return
* true or false if {@link #hasPermission(Sid, Permission)} returns it.
@@ -100,7 +99,7 @@ protected Boolean _hasPermission(@NonNull Authentication a, Permission permissio
* Checks if the given {@link Sid} has the given {@link Permission}.
*
*
- * {@link #hasPermission(Authentication, Permission)} is implemented
+ * {@link #hasPermission2(Authentication, Permission)} is implemented
* by checking authentication's {@link GrantedAuthority} by using
* this method.
*
diff --git a/core/src/main/java/hudson/security/SparseACL.java b/core/src/main/java/hudson/security/SparseACL.java
index d9c67a05ecdd..0dd801f40501 100644
--- a/core/src/main/java/hudson/security/SparseACL.java
+++ b/core/src/main/java/hudson/security/SparseACL.java
@@ -23,13 +23,12 @@
*/
package hudson.security;
-import org.acegisecurity.Authentication;
-import org.acegisecurity.acls.sid.Sid;
-
import java.util.ArrayList;
import java.util.List;
-import java.util.logging.Logger;
import static java.util.logging.Level.FINE;
+import java.util.logging.Logger;
+import org.acegisecurity.acls.sid.Sid;
+import org.springframework.security.core.Authentication;
/**
* Access control list.
@@ -69,15 +68,15 @@ public void add(Sid sid, Permission permission, boolean allowed) {
}
@Override
- public boolean hasPermission(Authentication a, Permission permission) {
- if(a==SYSTEM) return true;
+ public boolean hasPermission2(Authentication a, Permission permission) {
+ if(a.equals(SYSTEM2)) return true;
Boolean b = _hasPermission(a,permission);
if(b!=null) return b;
if(parent!=null) {
if(LOGGER.isLoggable(FINE))
LOGGER.fine("hasPermission("+a+","+permission+") is delegating to parent ACL: "+parent);
- return parent.hasPermission(a,permission);
+ return parent.hasPermission2(a,permission);
}
// the ultimate default is to reject everything
diff --git a/core/src/main/java/hudson/security/TokenBasedRememberMeServices2.java b/core/src/main/java/hudson/security/TokenBasedRememberMeServices2.java
index 6d2957cba9a0..065d8753c29e 100644
--- a/core/src/main/java/hudson/security/TokenBasedRememberMeServices2.java
+++ b/core/src/main/java/hudson/security/TokenBasedRememberMeServices2.java
@@ -23,38 +23,29 @@
*/
package hudson.security;
-import hudson.Functions;
+import com.google.common.annotations.VisibleForTesting;
import hudson.model.User;
+import java.util.Date;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
import jenkins.model.Jenkins;
import jenkins.security.HMACConfidentialKey;
-import jenkins.security.ImpersonatingUserDetailsService;
+import jenkins.security.ImpersonatingUserDetailsService2;
import jenkins.security.LastGrantedAuthoritiesProperty;
import jenkins.security.seed.UserSeedProperty;
import jenkins.util.SystemProperties;
-import org.acegisecurity.Authentication;
-import org.acegisecurity.providers.rememberme.RememberMeAuthenticationToken;
-import org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices;
-import org.acegisecurity.userdetails.UserDetails;
-import org.acegisecurity.userdetails.UserDetailsService;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
-import org.springframework.util.Assert;
-import org.springframework.util.StringUtils;
-
-import edu.umd.cs.findbugs.annotations.CheckForNull;
-import edu.umd.cs.findbugs.annotations.NonNull;
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.nio.charset.StandardCharsets;
-import java.security.MessageDigest;
-import java.util.Base64;
-import java.util.Date;
-import java.util.concurrent.TimeUnit;
-import java.util.logging.Level;
-import java.util.logging.Logger;
+import org.springframework.security.authentication.RememberMeAuthenticationProvider;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.web.authentication.rememberme.InvalidCookieException;
+import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
/**
* {@link TokenBasedRememberMeServices} with modification so as not to rely
@@ -66,6 +57,7 @@
*
* @author Kohsuke Kawaguchi
*/
+@Restricted(NoExternalUse.class)
public class TokenBasedRememberMeServices2 extends TokenBasedRememberMeServices {
private static final Logger LOGGER = Logger.getLogger(TokenBasedRememberMeServices2.class.getName());
@@ -73,35 +65,35 @@ public class TokenBasedRememberMeServices2 extends TokenBasedRememberMeServices
/**
* Escape hatch for the check on the maximum date for the expiration duration of the remember me cookie
*/
- @Restricted(NoExternalUse.class)
public static /* Script Console modifiable */ boolean SKIP_TOO_FAR_EXPIRATION_DATE_CHECK =
SystemProperties.getBoolean(TokenBasedRememberMeServices2.class.getName() + ".skipTooFarExpirationDateCheck");
/**
* Decorate {@link UserDetailsService} so that we can use information stored in
* {@link LastGrantedAuthoritiesProperty}.
- *
- * We wrap by {@link ImpersonatingUserDetailsService} in other places too,
- * so this is possibly redundant, but there are many {@link AbstractPasswordBasedSecurityRealm#loadUserByUsername(String)}
+ *
+ * We wrap by {@link ImpersonatingUserDetailsService2} in other places too,
+ * so this is possibly redundant, but there are many {@link AbstractPasswordBasedSecurityRealm#loadUserByUsername2(String)}
* implementations that do not do it, so doing it helps retrofit old plugins to benefit from
- * the user impersonation improvements. Plus multiple {@link ImpersonatingUserDetailsService}
+ * the user impersonation improvements. Plus multiple {@link ImpersonatingUserDetailsService2}
* do not incur any real performance penalty.
+ *
+ * {@link TokenBasedRememberMeServices} needs to be used in conjunction with {@link RememberMeAuthenticationProvider}
+ * (see {@link AbstractPasswordBasedSecurityRealm#createSecurityComponents})
+ * and both need to use the same key and various security plugins need to do the same.
*/
- @Override
- public void setUserDetailsService(UserDetailsService userDetailsService) {
- super.setUserDetailsService(new ImpersonatingUserDetailsService(userDetailsService));
+ @SuppressWarnings("deprecation")
+ public TokenBasedRememberMeServices2(UserDetailsService userDetailsService) {
+ super(Jenkins.get().getSecretKey(), new ImpersonatingUserDetailsService2(userDetailsService));
}
@Override
- protected String makeTokenSignature(long tokenExpiryTime, UserDetails userDetails) {
+ protected String makeTokenSignature(long tokenExpiryTime, String username, String password) {
String userSeed;
if (UserSeedProperty.DISABLE_USER_SEED) {
userSeed = "no-seed";
} else {
- User user = User.getById(userDetails.getUsername(), false);
- if (user == null) {
- return "no-user";
- }
+ User user = User.getById(username, true);
UserSeedProperty userSeedProperty = user.getProperty(UserSeedProperty.class);
if (userSeedProperty == null) {
// if you want to filter out the user seed property, you should consider using the DISABLE_USER_SEED instead
@@ -109,7 +101,7 @@ protected String makeTokenSignature(long tokenExpiryTime, UserDetails userDetail
}
userSeed = userSeedProperty.getSeed();
}
- String token = String.join(":", userDetails.getUsername(), Long.toString(tokenExpiryTime), userSeed, getKey());
+ String token = String.join(":", username, Long.toString(tokenExpiryTime), userSeed, getKey());
return MAC.mac(token);
}
@@ -119,7 +111,7 @@ protected String retrievePassword(Authentication successfulAuthentication) {
}
@Override
- public void loginSuccess(HttpServletRequest request, HttpServletResponse response,
+ public void onLoginSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication successfulAuthentication) {
// Exit if the principal hasn't asked to be remembered
if (!rememberMeRequested(request, getParameter())) {
@@ -141,17 +133,18 @@ public void loginSuccess(HttpServletRequest request, HttpServletResponse respons
return;
}
- Assert.notNull(successfulAuthentication.getPrincipal());
- Assert.notNull(successfulAuthentication.getCredentials());
- Assert.isInstanceOf(UserDetails.class, successfulAuthentication.getPrincipal());
+ // TODO is it really still necessary to reimplement all of the below, or could we simply override rememberMeRequested?
- long expiryTime = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(tokenValiditySeconds);
+ Objects.requireNonNull(successfulAuthentication.getPrincipal());
+ UserDetails.class.cast(successfulAuthentication.getPrincipal());
+
+ long expiryTime = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(getTokenValiditySeconds());
String username = ((UserDetails) successfulAuthentication.getPrincipal()).getUsername();
- String signatureValue = makeTokenSignature(expiryTime, (UserDetails)successfulAuthentication.getPrincipal());
- String tokenValue = username + ":" + expiryTime + ":" + signatureValue;
- String tokenValueBase64 = Base64.getEncoder().encodeToString(tokenValue.getBytes());
- response.addCookie(makeValidCookie(tokenValueBase64, request, tokenValiditySeconds));
+ String signatureValue = makeTokenSignature(expiryTime, username, ((UserDetails) successfulAuthentication.getPrincipal()).getPassword());
+ int tokenLifetime = calculateLoginLifetime(request, successfulAuthentication);
+ setCookie(new String[] { username, Long.toString(expiryTime), signatureValue },
+ tokenLifetime, request, response);
if (logger.isDebugEnabled()) {
logger.debug("Added remember-me cookie for user '" + username + "', expiry: '" + new Date(expiryTime)
@@ -160,123 +153,27 @@ public void loginSuccess(HttpServletRequest request, HttpServletResponse respons
}
@Override
- public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
+ protected UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request, HttpServletResponse response) {
Jenkins j = Jenkins.getInstanceOrNull();
if (j == null) {
// as this filter could be called during restart, this corrects at least the symptoms
- return null;
+ throw new InvalidCookieException("Jenkins is not yet running");
}
if (j.isDisableRememberMe()) {
- cancelCookie(request, response, null);
- return null;
+ cancelCookie(request, response);
+ throw new InvalidCookieException("rememberMe is disabled");
} else {
- try {
- // we use a patched version of the super.autoLogin
- String rememberMeValue = findRememberMeCookieValue(request, response);
- if (rememberMeValue == null) {
- return null;
- }
- return retrieveAuthFromCookie(request, response, rememberMeValue);
- } catch (Exception e) {
- cancelCookie(request, response, "Failed to handle remember-me cookie: " + Functions.printThrowable(e));
- return null;
- }
+ return super.processAutoLoginCookie(cookieTokens, request, response);
}
}
- /**
- * Patched version of the super.autoLogin with a time-independent equality check for the token validation
- */
- private String findRememberMeCookieValue(HttpServletRequest request, HttpServletResponse response) {
- Cookie[] cookies = request.getCookies();
-
- if ((cookies == null) || (cookies.length == 0)) {
- return null;
- }
-
- for (Cookie cookie : cookies) {
- if (ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY.equals(cookie.getName())) {
- return cookie.getValue();
- }
- }
-
- return null;
- }
-
- // Taken from TokenBasedRememberMeService with slight modification
- // around the token equality check to avoid timing attack
- // and reducing drastically the information provided in the log to avoid potential disclosure
- private @CheckForNull Authentication retrieveAuthFromCookie(HttpServletRequest request, HttpServletResponse response, String cookieValueBase64){
- String cookieValue = decodeCookieBase64(cookieValueBase64);
- if (cookieValue == null) {
- String reason = "Cookie token was not Base64 encoded; value was '" + cookieValueBase64 + "'";
- cancelCookie(request, response, reason);
- return null;
- }
- if (logger.isDebugEnabled()) {
- logger.debug("Remember-me cookie detected");
- }
-
- String[] cookieTokens = StringUtils.delimitedListToStringArray(cookieValue, ":");
-
- if (cookieTokens.length != 3) {
- cancelCookie(request, response, "Cookie token did not contain 3 tokens separated by [:]");
- return null;
- }
-
- long tokenExpiryTime;
-
- try {
- tokenExpiryTime = Long.parseLong(cookieTokens[1]);
- }
- catch (NumberFormatException nfe) {
- cancelCookie(request, response, "Cookie token[1] did not contain a valid number");
- return null;
- }
-
- if (isTokenExpired(tokenExpiryTime)) {
- cancelCookie(request, response, "Cookie token[1] has expired");
- return null;
- }
-
- // Check the user exists
- // Defer lookup until after expiry time checked, to
- // possibly avoid expensive lookup
- UserDetails userDetails = loadUserDetails(request, response, cookieTokens);
-
- if (userDetails == null) {
- cancelCookie(request, response, "Cookie token[0] contained a username without user associated");
- return null;
- }
-
- if (!isValidUserDetails(request, response, userDetails, cookieTokens)) {
- return null;
- }
-
- String receivedTokenSignature = cookieTokens[2];
- String expectedTokenSignature = makeTokenSignature(tokenExpiryTime, userDetails);
-
- boolean tokenValid = MessageDigest.isEqual(
- expectedTokenSignature.getBytes(StandardCharsets.US_ASCII),
- receivedTokenSignature.getBytes(StandardCharsets.US_ASCII)
- );
- if (!tokenValid) {
- cancelCookie(request, response, "Cookie token[2] contained invalid signature");
- return null;
- }
-
- // By this stage we have a valid token
- if (logger.isDebugEnabled()) {
- logger.debug("Remember-me cookie accepted");
- }
-
- RememberMeAuthenticationToken auth = new RememberMeAuthenticationToken(this.getKey(), userDetails,
- userDetails.getAuthorities());
- auth.setDetails(authenticationDetailsSource.buildDetails(request));
+ @Override
+ protected Authentication createSuccessfulAuthentication(HttpServletRequest request, UserDetails userDetails) {
+ Authentication auth = super.createSuccessfulAuthentication(request, userDetails);
// Ensure this session is linked to the user's seed
if (!UserSeedProperty.DISABLE_USER_SEED) {
- User user = User.get(auth);
+ User user = User.get2(auth);
UserSeedProperty userSeed = user.getProperty(UserSeedProperty.class);
String sessionSeed = userSeed.getSeed();
request.getSession().setAttribute(UserSeedProperty.USER_SESSION_SEED, sessionSeed);
@@ -285,58 +182,6 @@ private String findRememberMeCookieValue(HttpServletRequest request, HttpServlet
return auth;
}
- /**
- * @return the decoded base64 of the cookie or {@code null} if the value was not correctly encoded
- */
- private @CheckForNull String decodeCookieBase64(@NonNull String base64EncodedValue){
- StringBuilder base64EncodedValueBuilder = new StringBuilder(base64EncodedValue);
- for (int j = 0; j < base64EncodedValueBuilder.length() % 4; j++) {
- base64EncodedValueBuilder.append("=");
- }
- base64EncodedValue = base64EncodedValueBuilder.toString();
-
- try {
- // any charset should be fine but better safe than sorry
- byte[] decodedPlainValue = Base64.getDecoder().decode(base64EncodedValue.getBytes(StandardCharsets.UTF_8));
- return new String(decodedPlainValue, StandardCharsets.UTF_8);
- } catch (IllegalArgumentException e) {
- return null;
- }
- }
-
- @Override
- protected Cookie makeValidCookie(String tokenValueBase64, HttpServletRequest request, long maxAge) {
- Cookie cookie = super.makeValidCookie(tokenValueBase64, request, maxAge);
- secureCookie(cookie, request);
- return cookie;
- }
-
- @Override
- protected Cookie makeCancelCookie(HttpServletRequest request) {
- Cookie cookie = super.makeCancelCookie(request);
- secureCookie(cookie, request);
- return cookie;
- }
-
- /**
- * Force always the http-only flag and depending on the request, put also the secure flag.
- */
- private void secureCookie(Cookie cookie, HttpServletRequest request){
- // if we can mark the cookie HTTP only, do so to protect this cookie even in case of XSS vulnerability.
- if (SET_HTTP_ONLY!=null) {
- try {
- SET_HTTP_ONLY.invoke(cookie,true);
- } catch (IllegalAccessException | InvocationTargetException e) {
- // ignore
- }
- }
-
- // if the user is running Jenkins over HTTPS, we also want to prevent the cookie from leaking in HTTP.
- // whether the login is done over HTTPS or not would be a good enough approximation of whether Jenkins runs in
- // HTTPS or not, so use that.
- cookie.setSecure(request.isSecure());
- }
-
/**
* In addition to the expiration requested by the super class, we also check the expiration is not too far in the future.
* Especially to detect maliciously crafted cookie.
@@ -344,7 +189,7 @@ private void secureCookie(Cookie cookie, HttpServletRequest request){
@Override
protected boolean isTokenExpired(long tokenExpiryTimeMs) {
long nowMs = System.currentTimeMillis();
- long maxExpirationMs = TimeUnit.SECONDS.toMillis(tokenValiditySeconds) + nowMs;
+ long maxExpirationMs = TimeUnit.SECONDS.toMillis(getTokenValiditySeconds()) + nowMs;
if(!SKIP_TOO_FAR_EXPIRATION_DATE_CHECK && tokenExpiryTimeMs > maxExpirationMs){
// attempt to use a cookie that has more than the maximum allowed expiration duration
// was either created before a change of configuration or maliciously crafted
@@ -359,19 +204,21 @@ protected boolean isTokenExpired(long tokenExpiryTimeMs) {
return false;
}
+ @VisibleForTesting
+ @Override
+ protected int getTokenValiditySeconds() {
+ return super.getTokenValiditySeconds();
+ }
+
+ @VisibleForTesting
+ @Override
+ protected String getCookieName() {
+ return super.getCookieName();
+ }
+
/**
* Used to compute the token signature securely.
*/
private static final HMACConfidentialKey MAC = new HMACConfidentialKey(TokenBasedRememberMeServices.class,"mac");
- private static final Method SET_HTTP_ONLY;
-
- static {
- Method m = null;
- try {
- m = Cookie.class.getMethod("setHttpOnly", boolean.class);
- } catch (NoSuchMethodException x) { // 3.0+
- }
- SET_HTTP_ONLY = m;
- }
}
diff --git a/core/src/main/java/hudson/security/UnwrapSecurityExceptionFilter.java b/core/src/main/java/hudson/security/UnwrapSecurityExceptionFilter.java
index 8b428de6a454..e894da774a6f 100644
--- a/core/src/main/java/hudson/security/UnwrapSecurityExceptionFilter.java
+++ b/core/src/main/java/hudson/security/UnwrapSecurityExceptionFilter.java
@@ -23,20 +23,20 @@
*/
package hudson.security;
-import org.apache.commons.jelly.JellyTagException;
-import org.acegisecurity.AcegiSecurityException;
-import org.acegisecurity.ui.ExceptionTranslationFilter;
-
+import java.io.IOException;
import javax.servlet.Filter;
+import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
-import javax.servlet.FilterChain;
-import java.io.IOException;
+import org.apache.commons.jelly.JellyTagException;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.access.ExceptionTranslationFilter;
/**
- * If {@link AcegiSecurityException} caused {@link JellyTagException},
+ * If a security exception caused {@link JellyTagException},
* rethrow it accordingly so that {@link ExceptionTranslationFilter}
* can pick it up and initiate the redirection.
*
@@ -61,9 +61,8 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
if (t != null && t instanceof JellyTagException) {
JellyTagException jte = (JellyTagException) t;
Throwable cause = jte.getCause();
- if (cause instanceof AcegiSecurityException) {
- AcegiSecurityException se = (AcegiSecurityException) cause;
- throw new ServletException(se);
+ if (cause instanceof AccessDeniedException || cause instanceof AuthenticationException) {
+ throw new ServletException(cause);
}
}
throw e;
diff --git a/core/src/main/java/hudson/security/UserDetailsServiceProxy.java b/core/src/main/java/hudson/security/UserDetailsServiceProxy.java
index 2ac0f39625b1..55db71a12e13 100644
--- a/core/src/main/java/hudson/security/UserDetailsServiceProxy.java
+++ b/core/src/main/java/hudson/security/UserDetailsServiceProxy.java
@@ -23,24 +23,26 @@
*/
package hudson.security;
-import org.acegisecurity.userdetails.UserDetails;
-import org.acegisecurity.userdetails.UserDetailsService;
-import org.acegisecurity.userdetails.UsernameNotFoundException;
-import org.springframework.dao.DataAccessException;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
/**
* {@link UserDetailsService} proxy that delegates to another instance.
*
* @author Kohsuke Kawaguchi
*/
+@Restricted(NoExternalUse.class)
public class UserDetailsServiceProxy implements UserDetailsService {
private volatile UserDetailsService delegate;
- public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
+ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDetailsService uds = delegate; // fix the reference for concurrency support
if(uds ==null)
- throw new UserMayOrMayNotExistException(Messages.UserDetailsServiceProxy_UnableToQuery(username));
+ throw new UserMayOrMayNotExistException2(Messages.UserDetailsServiceProxy_UnableToQuery(username));
return uds.loadUserByUsername(username);
}
diff --git a/core/src/main/java/hudson/security/UserMayOrMayNotExistException.java b/core/src/main/java/hudson/security/UserMayOrMayNotExistException.java
index 6b6e3c9e6547..343dcea1f565 100644
--- a/core/src/main/java/hudson/security/UserMayOrMayNotExistException.java
+++ b/core/src/main/java/hudson/security/UserMayOrMayNotExistException.java
@@ -24,21 +24,12 @@
package hudson.security;
import org.acegisecurity.userdetails.UsernameNotFoundException;
-import org.acegisecurity.userdetails.UserDetailsService;
/**
- * Thrown from {@link UserDetailsService#loadUserByUsername(String)}
- * to indicate that the underlying {@link SecurityRealm} is incapable
- * of retrieving the information, and furthermore, the system cannot
- * tell if such an user exists or not.
- *
- *
- * This happens, for example, when the security realm is on top of the servlet implementation,
- * there's no way of even knowing if an user of a given name exists or not.
- *
- * @author Kohsuke Kawaguchi
* @since 1.280
+ * @deprecated use {@link UserMayOrMayNotExistException2}
*/
+@Deprecated
public class UserMayOrMayNotExistException extends UsernameNotFoundException {
public UserMayOrMayNotExistException(String msg) {
super(msg);
@@ -51,4 +42,14 @@ public UserMayOrMayNotExistException(String msg, Object extraInformation) {
public UserMayOrMayNotExistException(String msg, Throwable t) {
super(msg, t);
}
+
+ @Override
+ public UserMayOrMayNotExistException2 toSpring() {
+ return new UserMayOrMayNotExistException2(toString(), this);
+ }
+
+ public static UserMayOrMayNotExistException fromSpring(UserMayOrMayNotExistException2 x) {
+ return new UserMayOrMayNotExistException(x.toString(), x);
+ }
+
}
diff --git a/war/src/main/webapp/WEB-INF/security/AbstractPasswordBasedSecurityRealm.groovy b/core/src/main/java/hudson/security/UserMayOrMayNotExistException2.java
similarity index 56%
rename from war/src/main/webapp/WEB-INF/security/AbstractPasswordBasedSecurityRealm.groovy
rename to core/src/main/java/hudson/security/UserMayOrMayNotExistException2.java
index 4aaf71b19615..b7814c85ded7 100644
--- a/war/src/main/webapp/WEB-INF/security/AbstractPasswordBasedSecurityRealm.groovy
+++ b/core/src/main/java/hudson/security/UserMayOrMayNotExistException2.java
@@ -1,18 +1,18 @@
/*
* The MIT License
- *
+ *
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
- *
+ *
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
- *
+ *
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
- *
+ *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -21,29 +21,30 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-/*
- Configure Hudson's own user database as the authentication realm.
-*/
-import org.acegisecurity.providers.ProviderManager
-import org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider
-import org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider
-import jenkins.model.Jenkins
+package hudson.security;
+
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+
+/**
+ * Thrown from {@link UserDetailsService#loadUserByUsername(String)}
+ * to indicate that the underlying {@link SecurityRealm} is incapable
+ * of retrieving the information, and furthermore, the system cannot
+ * tell if such an user exists or not.
+ *
+ *
+ * This happens, for example, when the security realm is on top of the servlet implementation,
+ * there's no way of even knowing if an user of a given name exists or not.
+ *
+ * @since TODO
+ */
+public class UserMayOrMayNotExistException2 extends UsernameNotFoundException {
-authenticationManager(ProviderManager) {
- providers = [
- // this does all the hard work.
- // injected by the parsing code
- authenticator,
+ public UserMayOrMayNotExistException2(String msg) {
+ super(msg);
+ }
- // these providers apply everywhere
- bean(RememberMeAuthenticationProvider) {
- key = Jenkins.get().getSecretKey()
- },
- // this doesn't mean we allow anonymous access.
- // we just authenticate anonymous users as such,
- // so that later authorization can reject them if so configured
- bean(AnonymousAuthenticationProvider) {
- key = "anonymous"
- }
- ]
+ public UserMayOrMayNotExistException2(String msg, Throwable t) {
+ super(msg, t);
+ }
}
diff --git a/core/src/main/java/hudson/security/WhoAmI.java b/core/src/main/java/hudson/security/WhoAmI.java
index 35f1e99b9ef2..44d94cb68bae 100644
--- a/core/src/main/java/hudson/security/WhoAmI.java
+++ b/core/src/main/java/hudson/security/WhoAmI.java
@@ -16,8 +16,6 @@
import jenkins.util.MemoryReductionUtil;
import jenkins.model.Jenkins;
-import org.acegisecurity.Authentication;
-import org.acegisecurity.GrantedAuthority;
import org.jenkinsci.Symbol;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
@@ -25,6 +23,8 @@
import org.kohsuke.stapler.export.ExportedBean;
import edu.umd.cs.findbugs.annotations.NonNull;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
/**
* Expose the data needed for /whoAmI, so it can be exposed by Api.
@@ -71,7 +71,7 @@ public String getToString() {
}
private @NonNull Authentication auth() {
- return Jenkins.getAuthentication();
+ return Jenkins.getAuthentication2();
}
@Exported
diff --git a/core/src/main/java/hudson/security/csrf/CrumbFilter.java b/core/src/main/java/hudson/security/csrf/CrumbFilter.java
index c8a0f80b6129..dcf341f70883 100644
--- a/core/src/main/java/hudson/security/csrf/CrumbFilter.java
+++ b/core/src/main/java/hudson/security/csrf/CrumbFilter.java
@@ -8,7 +8,6 @@
import hudson.util.MultipartFormDataParser;
import jenkins.model.Jenkins;
import jenkins.util.SystemProperties;
-import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken;
import org.kohsuke.MetaInfServices;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
@@ -32,6 +31,7 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
/**
* Checks for and validates crumbs on requests that cause state changes, to
@@ -139,7 +139,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
}
// JENKINS-40344: Don't spam the log just because a session is expired
- Level level = Jenkins.getAuthentication() instanceof AnonymousAuthenticationToken ? Level.FINE : Level.WARNING;
+ Level level = Jenkins.getAuthentication2() instanceof AnonymousAuthenticationToken ? Level.FINE : Level.WARNING;
if (crumb != null) {
if (crumbIssuer.validateCrumb(httpRequest, crumbSalt, crumb)) {
@@ -152,7 +152,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
if (valid) {
chain.doFilter(request, response);
} else {
- LOGGER.log(level, "No valid crumb was included in request for {0} by {1}. Returning {2}.", new Object[] {httpRequest.getRequestURI(), Jenkins.getAuthentication().getName(), HttpServletResponse.SC_FORBIDDEN});
+ LOGGER.log(level, "No valid crumb was included in request for {0} by {1}. Returning {2}.", new Object[] {httpRequest.getRequestURI(), Jenkins.getAuthentication2().getName(), HttpServletResponse.SC_FORBIDDEN});
httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN,"No valid crumb was included in the request");
}
} else {
diff --git a/core/src/main/java/hudson/security/csrf/DefaultCrumbIssuer.java b/core/src/main/java/hudson/security/csrf/DefaultCrumbIssuer.java
index 5fc7bf959148..818cef9bc763 100644
--- a/core/src/main/java/hudson/security/csrf/DefaultCrumbIssuer.java
+++ b/core/src/main/java/hudson/security/csrf/DefaultCrumbIssuer.java
@@ -25,12 +25,12 @@
import net.sf.json.JSONObject;
-import org.acegisecurity.Authentication;
import org.jenkinsci.Symbol;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.StaplerRequest;
+import org.springframework.security.core.Authentication;
/**
* A crumb issuing algorithm based on the request principal and the remote address.
@@ -75,7 +75,7 @@ protected synchronized String issueCrumb(ServletRequest request, String salt) {
if (md != null) {
HttpServletRequest req = (HttpServletRequest) request;
StringBuilder buffer = new StringBuilder();
- Authentication a = Jenkins.getAuthentication();
+ Authentication a = Jenkins.getAuthentication2();
buffer.append(a.getName());
buffer.append(';');
if (!isExcludeClientIPFromCrumb()) {
diff --git a/core/src/main/java/hudson/slaves/OfflineCause.java b/core/src/main/java/hudson/slaves/OfflineCause.java
index 0f10582d6b18..362d6e6c414d 100644
--- a/core/src/main/java/hudson/slaves/OfflineCause.java
+++ b/core/src/main/java/hudson/slaves/OfflineCause.java
@@ -147,7 +147,7 @@ public UserCause(@CheckForNull User user, @CheckForNull String message) {
}
private UserCause(String userId, String message) {
- super(hudson.slaves.Messages._SlaveComputer_DisconnectedBy(userId != null ? userId : Jenkins.ANONYMOUS.getName(), message));
+ super(hudson.slaves.Messages._SlaveComputer_DisconnectedBy(userId != null ? userId : Jenkins.ANONYMOUS2.getName(), message));
this.userId = userId;
}
diff --git a/core/src/main/java/hudson/slaves/SlaveComputer.java b/core/src/main/java/hudson/slaves/SlaveComputer.java
index 786da68f8efd..afe6aad491b0 100644
--- a/core/src/main/java/hudson/slaves/SlaveComputer.java
+++ b/core/src/main/java/hudson/slaves/SlaveComputer.java
@@ -285,7 +285,7 @@ protected Future> _connect(boolean forceReconnect) {
// do this on another thread so that the lengthy launch operation
// (which is typical) won't block UI thread.
- try (ACLContext ctx = ACL.as(ACL.SYSTEM)) {// background activity should run like a super user
+ try (ACLContext ctx = ACL.as2(ACL.SYSTEM2)) {// background activity should run like a super user
log.rewind();
try {
for (ComputerListener cl : ComputerListener.all())
@@ -676,7 +676,7 @@ public void onClosed(Channel c, IOException cause) {
channel.pinClassLoader(getClass().getClassLoader());
channel.call(new SlaveInitializer(DEFAULT_RING_BUFFER_SIZE));
- try (ACLContext ctx = ACL.as(ACL.SYSTEM)) {
+ try (ACLContext ctx = ACL.as2(ACL.SYSTEM2)) {
for (ComputerListener cl : ComputerListener.all()) {
cl.preOnline(this,channel,root,taskListener);
}
@@ -706,7 +706,7 @@ public void onClosed(Channel c, IOException cause) {
statusChangeLock.notifyAll();
}
}
- try (ACLContext ctx = ACL.as(ACL.SYSTEM)) {
+ try (ACLContext ctx = ACL.as2(ACL.SYSTEM2)) {
for (ComputerListener cl : ComputerListener.all()) {
try {
cl.onOnline(this,taskListener);
diff --git a/core/src/main/java/hudson/tasks/BuildStep.java b/core/src/main/java/hudson/tasks/BuildStep.java
index 2af5f84c0584..ac45c935b35d 100644
--- a/core/src/main/java/hudson/tasks/BuildStep.java
+++ b/core/src/main/java/hudson/tasks/BuildStep.java
@@ -47,10 +47,10 @@
import java.util.Iterator;
import java.util.WeakHashMap;
import jenkins.security.QueueItemAuthenticator;
-import org.acegisecurity.Authentication;
import edu.umd.cs.findbugs.annotations.NonNull;
import jenkins.model.Jenkins;
+import org.springframework.security.core.Authentication;
/**
* One step of the whole build process.
@@ -102,9 +102,9 @@ public interface BuildStep {
*
When this build step needs to make (direct or indirect) permission checks to {@link ACL}
* (for example, to locate other projects by name, build them, or access their artifacts)
* then it must be run under a specific {@link Authentication}.
- * In such a case, the implementation should check whether {@link Jenkins#getAuthentication} is {@link ACL#SYSTEM},
+ * In such a case, the implementation should check whether {@link Jenkins#getAuthentication2} is {@link ACL#SYSTEM2},
* and if so, replace it for the duration of this step with {@link Jenkins#ANONYMOUS}.
- * (Either using {@link ACL#impersonate}, or by making explicit calls to {@link ACL#hasPermission(Authentication, Permission)}.)
+ * (Either using {@link ACL#impersonate2}, or by making explicit calls to {@link ACL#hasPermission2(Authentication, Permission)}.)
* This would typically happen when no {@link QueueItemAuthenticator} was available, configured, and active.
*
* @return
diff --git a/core/src/main/java/hudson/tasks/BuildTrigger.java b/core/src/main/java/hudson/tasks/BuildTrigger.java
index aaa32b12b6f4..3a73f2fedf81 100644
--- a/core/src/main/java/hudson/tasks/BuildTrigger.java
+++ b/core/src/main/java/hudson/tasks/BuildTrigger.java
@@ -64,13 +64,13 @@
import jenkins.model.ParameterizedJobMixIn;
import jenkins.triggers.ReverseBuildTrigger;
import net.sf.json.JSONObject;
-import org.acegisecurity.Authentication;
import org.apache.commons.lang.StringUtils;
import org.jenkinsci.Symbol;
import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
+import org.springframework.security.core.Authentication;
/**
* Triggers builds of other projects.
@@ -200,7 +200,7 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListener lis
PrintStream logger = listener.getLogger();
for (Job, ?> downstream : jobs) {
if (Jenkins.get().getItemByFullName(downstream.getFullName()) != downstream) {
- LOGGER.log(Level.WARNING, "Running as {0} cannot even see {1} for trigger from {2}", new Object[] {Jenkins.getAuthentication().getName(), downstream, build.getParent()});
+ LOGGER.log(Level.WARNING, "Running as {0} cannot even see {1} for trigger from {2}", new Object[] {Jenkins.getAuthentication2().getName(), downstream, build.getParent()});
continue;
}
if (!downstream.hasPermission(Item.BUILD)) {
@@ -297,7 +297,7 @@ public boolean shouldTriggerBuild(AbstractBuild build, TaskListener listener,
List actions) {
AbstractProject downstream = getDownstreamProject();
if (Jenkins.get().getItemByFullName(downstream.getFullName()) != downstream) { // this checks Item.READ also on parent folders
- LOGGER.log(Level.WARNING, "Running as {0} cannot even see {1} for trigger from {2}", new Object[] {Jenkins.getAuthentication().getName(), downstream, getUpstreamProject()});
+ LOGGER.log(Level.WARNING, "Running as {0} cannot even see {1} for trigger from {2}", new Object[] {Jenkins.getAuthentication2().getName(), downstream, getUpstreamProject()});
return false; // do not even issue a warning to build log
}
if (!downstream.hasPermission(Item.BUILD)) {
@@ -408,8 +408,8 @@ public FormValidation doCheck(@AncestorInPath AbstractProject project, @QueryPar
if(!(item instanceof ParameterizedJobMixIn.ParameterizedJob))
return FormValidation.error(Messages.BuildTrigger_NotBuildable(projectName));
// check whether the supposed user is expected to be able to build
- Authentication auth = Tasks.getAuthenticationOf(project);
- if (!item.hasPermission(auth, Item.BUILD)) {
+ Authentication auth = Tasks.getAuthenticationOf2(project);
+ if (!item.hasPermission2(auth, Item.BUILD)) {
return FormValidation.error(Messages.BuildTrigger_you_have_no_permission_to_build_(projectName));
}
hasProjects = true;
@@ -430,7 +430,7 @@ public AutoCompletionCandidates doAutoCompleteChildProjects(@QueryParameter Stri
public static class ItemListenerImpl extends ItemListener {
@Override
public void onLocationChanged(final Item item, final String oldFullName, final String newFullName) {
- try (ACLContext acl = ACL.as(ACL.SYSTEM)) {
+ try (ACLContext acl = ACL.as2(ACL.SYSTEM2)) {
locationChanged(item, oldFullName, newFullName);
}
}
diff --git a/core/src/main/java/hudson/tasks/Fingerprinter.java b/core/src/main/java/hudson/tasks/Fingerprinter.java
index 375f69dc18b0..7eb9bf2a55b9 100644
--- a/core/src/main/java/hudson/tasks/Fingerprinter.java
+++ b/core/src/main/java/hudson/tasks/Fingerprinter.java
@@ -51,7 +51,6 @@
import hudson.util.PackedMap;
import hudson.util.RunList;
import net.sf.json.JSONObject;
-import org.acegisecurity.AccessDeniedException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.types.FileSet;
import org.jenkinsci.Symbol;
@@ -78,6 +77,7 @@
import java.util.logging.Logger;
import jenkins.model.RunAction2;
import jenkins.tasks.SimpleBuildStep;
+import org.springframework.security.access.AccessDeniedException;
/**
* Records fingerprints of the specified files.
diff --git a/core/src/main/java/hudson/triggers/SafeTimerTask.java b/core/src/main/java/hudson/triggers/SafeTimerTask.java
index bda2fe1657fe..ff91c34aa45e 100644
--- a/core/src/main/java/hudson/triggers/SafeTimerTask.java
+++ b/core/src/main/java/hudson/triggers/SafeTimerTask.java
@@ -87,7 +87,7 @@ public interface ExceptionRunnable {
public final void run() {
// background activity gets system credential,
// just like executors get it.
- try (ACLContext ctx = ACL.as(ACL.SYSTEM)) {
+ try (ACLContext ctx = ACL.as2(ACL.SYSTEM2)) {
doRun();
} catch(Throwable t) {
LOGGER.log(Level.SEVERE, "Timer task "+this+" failed",t);
diff --git a/core/src/main/java/hudson/util/FormFieldValidator.java b/core/src/main/java/hudson/util/FormFieldValidator.java
index 1de147efa2b9..2e42b4a3aaa7 100644
--- a/core/src/main/java/hudson/util/FormFieldValidator.java
+++ b/core/src/main/java/hudson/util/FormFieldValidator.java
@@ -32,7 +32,6 @@
import hudson.security.AccessControlled;
import hudson.security.Permission;
import jenkins.model.Jenkins;
-import org.acegisecurity.AccessDeniedException;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
@@ -48,6 +47,7 @@
import java.util.Locale;
import static hudson.Util.fixEmpty;
+import org.springframework.security.access.AccessDeniedException;
/**
* Base class that provides the framework for doing on-the-fly form field validation.
diff --git a/core/src/main/java/hudson/util/spring/BeanBuilder.java b/core/src/main/java/hudson/util/spring/BeanBuilder.java
deleted file mode 100644
index 33c6e1d98eed..000000000000
--- a/core/src/main/java/hudson/util/spring/BeanBuilder.java
+++ /dev/null
@@ -1,652 +0,0 @@
-/*
- * Copyright 2004-2005 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package hudson.util.spring;
-
-import groovy.lang.Binding;
-import groovy.lang.Closure;
-import groovy.lang.GString;
-import groovy.lang.GroovyObject;
-import groovy.lang.GroovyObjectSupport;
-import groovy.lang.GroovyShell;
-import groovy.lang.MetaClass;
-import groovy.lang.MissingMethodException;
-import org.apache.commons.lang.ArrayUtils;
-import org.codehaus.groovy.control.CompilerConfiguration;
-import org.codehaus.groovy.runtime.DefaultGroovyMethods;
-import org.codehaus.groovy.runtime.InvokerHelper;
-import org.springframework.beans.factory.config.BeanDefinition;
-import org.springframework.beans.factory.config.RuntimeBeanReference;
-import org.springframework.beans.factory.support.ManagedList;
-import org.springframework.beans.factory.support.ManagedMap;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.support.StaticApplicationContext;
-import org.springframework.core.io.Resource;
-import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
-import org.springframework.web.context.WebApplicationContext;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Map;
-import java.util.Map.Entry;
-
-/**
- *
Runtime bean configuration wrapper. Like a Groovy builder, but more of a DSL for
- * Spring configuration. Allows syntax like:
- * You can also use the Spring IO API to load resources containing beans defined as a Groovy
- * script using either the constructors or the loadBeans(Resource[] resources) method
- *
- *
- * @author Graeme Rocher
- * @since 0.4
- *
- */
-public class BeanBuilder extends GroovyObjectSupport {
- private static final String ANONYMOUS_BEAN = "bean";
- private RuntimeSpringConfiguration springConfig = new DefaultRuntimeSpringConfiguration();
- private BeanConfiguration currentBeanConfig;
- private Map deferredProperties = new HashMap<>();
- private ApplicationContext parentCtx;
- private Map binding = new HashMap();
- private ClassLoader classLoader = null;
-
-
- public BeanBuilder() {
- super();
- }
-
- public BeanBuilder(ClassLoader classLoader) {
- super();
- this.classLoader = classLoader;
- }
-
- public BeanBuilder(ApplicationContext parent) {
- super();
- this.parentCtx = parent;
- this.springConfig = new DefaultRuntimeSpringConfiguration(parent);
- }
-
- public BeanBuilder(ApplicationContext parent,ClassLoader classLoader) {
- super();
- this.parentCtx = parent;
- this.springConfig = new DefaultRuntimeSpringConfiguration(parent);
- this.classLoader = classLoader;
- }
-
- /**
- * Parses the bean definition groovy script.
- */
- public void parse(InputStream script) {
- parse(script,new Binding());
- }
-
- /**
- * Parses the bean definition groovy script by first exporting the given {@link Binding}.
- */
- public void parse(InputStream script, Binding binding) {
- if (script==null)
- throw new IllegalArgumentException("No script is provided");
- setBinding(binding);
- CompilerConfiguration cc = new CompilerConfiguration();
- cc.setScriptBaseClass(ClosureScript.class.getName());
- GroovyShell shell = new GroovyShell(classLoader,binding,cc);
-
- ClosureScript s = (ClosureScript)shell.parse(new InputStreamReader(script));
- s.setDelegate(this);
- s.run();
- }
-
- /**
- * Retrieves the parent ApplicationContext
- * @return The parent ApplicationContext
- */
- public ApplicationContext getParentCtx() {
- return parentCtx;
- }
-
- /**
- * Retrieves the RuntimeSpringConfiguration instance used by the BeanBuilder
- * @return The RuntimeSpringConfiguration instance
- */
- public RuntimeSpringConfiguration getSpringConfig() {
- return springConfig;
- }
-
- /**
- * Retrieves a BeanDefinition for the given name
- * @param name The bean definition
- * @return The BeanDefinition instance
- */
- public BeanDefinition getBeanDefinition(String name) {
- if(!getSpringConfig().containsBean(name))
- return null;
- return getSpringConfig().getBeanConfig(name).getBeanDefinition();
- }
-
- /**
- * Retrieves all BeanDefinitions for this BeanBuilder
- *
- * @return A map of BeanDefinition instances with the bean id as the key
- */
- public Map getBeanDefinitions() {
-
- Map beanDefinitions = new HashMap<>();
- for (String beanName : getSpringConfig().getBeanNames()) {
- BeanDefinition bd = getSpringConfig()
- .getBeanConfig(beanName)
- .getBeanDefinition();
- beanDefinitions.put(beanName, bd);
- }
- return beanDefinitions;
- }
-
- /**
- * Sets the runtime Spring configuration instance to use. This is not necessary to set
- * and is configured to default value if not, but is useful for integrating with other
- * spring configuration mechanisms @see org.codehaus.groovy.grails.commons.spring.GrailsRuntimeConfigurator
- *
- * @param springConfig The spring config
- */
- public void setSpringConfig(RuntimeSpringConfiguration springConfig) {
- this.springConfig = springConfig;
- }
-
-
-
- /**
- * This class is used to defer the adding of a property to a bean definition until later
- * This is for a case where you assign a property to a list that may not contain bean references at
- * that point of assignment, but may later hence it would need to be managed
- *
- * @author Graeme Rocher
- */
- private static class DeferredProperty {
- private BeanConfiguration config;
- private String name;
- private Object value;
-
- DeferredProperty(BeanConfiguration config, String name, Object value) {
- this.config = config;
- this.name = name;
- this.value = value;
- }
-
- public void setInBeanConfig() {
- this.config.addProperty(name, value);
- }
- }
-
- /**
- * A RuntimeBeanReference that takes care of adding new properties to runtime references
- *
- * @author Graeme Rocher
- * @since 0.4
- *
- */
- private class ConfigurableRuntimeBeanReference extends RuntimeBeanReference implements GroovyObject {
-
- private MetaClass metaClass;
- private BeanConfiguration beanConfig;
-
- public ConfigurableRuntimeBeanReference(String beanName, BeanConfiguration beanConfig) {
- this(beanName, beanConfig, false);
- }
-
- public ConfigurableRuntimeBeanReference(String beanName, BeanConfiguration beanConfig, boolean toParent) {
- super(beanName, toParent);
- this.beanConfig = beanConfig;
- if(beanConfig == null)
- throw new IllegalArgumentException("Argument [beanConfig] cannot be null");
- this.metaClass = InvokerHelper.getMetaClass(this);
- }
-
- public MetaClass getMetaClass() {
- return this.metaClass;
- }
-
- public Object getProperty(String property) {
- if(property.equals("beanName"))
- return getBeanName();
- else if(property.equals("source"))
- return getSource();
- else if(this.beanConfig != null) {
- return new WrappedPropertyValue(property,beanConfig.getPropertyValue(property));
- }
- else
- return this.metaClass.getProperty(this, property);
- }
-
-
-
- /**
- * Wraps a BeanConfiguration property an ensures that any RuntimeReference additions to it are
- * deferred for resolution later
- *
- * @author Graeme Rocher
- * @since 0.4
- *
- */
- private class WrappedPropertyValue extends GroovyObjectSupport {
- private Object propertyValue;
- private String propertyName;
- public WrappedPropertyValue(String propertyName, Object propertyValue) {
- this.propertyValue = propertyValue;
- this.propertyName = propertyName;
- }
-
- public void leftShift(Object value) {
- InvokerHelper.invokeMethod(propertyValue, "leftShift", value);
- if(value instanceof RuntimeBeanReference) {
- deferredProperties.put(beanConfig.getName(), new DeferredProperty(beanConfig, propertyName, propertyValue));
- }
- }
- }
- public Object invokeMethod(String name, Object args) {
- return this.metaClass.invokeMethod(this, name, args);
- }
-
- public void setMetaClass(MetaClass metaClass) {
- this.metaClass = metaClass;
- }
-
- public void setProperty(String property, Object newValue) {
- if(!addToDeferred(beanConfig,property, newValue)) {
- beanConfig.setPropertyValue(property, newValue);
- }
- }
- }
-
- /**
- * Loads a single Resource into the bean builder
- *
- * @param resource The resource to load
- * @throws IOException When an error occurs
- */
- public void loadBeans(Resource resource) throws IOException {
- loadBeans(new Resource[]{resource});
- }
-
- /**
- * Loads a set of given beans
- * @param resources The resources to load
- * @throws IOException
- */
- public void loadBeans(Resource[] resources) throws IOException {
- Closure beans = new Closure(this){
- @Override
- public Object call(Object... args) {
- return beans((Closure)args[0]);
- }
- };
- Binding b = new Binding();
- b.setVariable("beans", beans);
-
- GroovyShell shell = classLoader != null ? new GroovyShell(classLoader,b) : new GroovyShell(b);
- for (Resource resource : resources) {
- shell.evaluate(new InputStreamReader(resource.getInputStream()));
- }
- }
-
- public void registerBeans(StaticApplicationContext ctx) {
- finalizeDeferredProperties();
- springConfig.registerBeansWithContext(ctx);
- }
-
- public RuntimeBeanReference ref(String refName) {
- return ref(refName,false);
- }
-
- public RuntimeBeanReference parentRef(String refName) {
- return ref(refName,true);
- }
-
- public RuntimeBeanReference ref(String refName, boolean parentRef) {
- return new RuntimeBeanReference(refName, parentRef);
- }
-
- /**
- * This method is invoked by Groovy when a method that's not defined in Java is invoked.
- * We use that as a syntax for bean definition.
- */
- public Object methodMissing(String name, Object arg) {
- Object[] args = (Object[])arg;
-
- if(args.length == 0)
- throw new MissingMethodException(name,getClass(),args);
-
- if(args[0] instanceof Closure) {
- // abstract bean definition
- return invokeBeanDefiningMethod(name, args);
- }
- else if(args[0] instanceof Class || args[0] instanceof RuntimeBeanReference || args[0] instanceof Map) {
- return invokeBeanDefiningMethod(name, args);
- }
- else if (args.length > 1 && args[args.length -1] instanceof Closure) {
- return invokeBeanDefiningMethod(name, args);
- }
- WebApplicationContext ctx = springConfig.getUnrefreshedApplicationContext();
- MetaClass mc = DefaultGroovyMethods.getMetaClass(ctx);
- if(!mc.respondsTo(ctx, name, args).isEmpty()){
- return mc.invokeMethod(ctx,name, args);
- }
- return this;
- }
-
- public WebApplicationContext createApplicationContext() {
- finalizeDeferredProperties();
- return springConfig.getApplicationContext();
- }
-
- private void finalizeDeferredProperties() {
- for (DeferredProperty dp : deferredProperties.values()) {
- if (dp.value instanceof List) {
- dp.value = manageListIfNecessary((List)dp.value);
- } else if (dp.value instanceof Map) {
- dp.value = manageMapIfNecessary((Map)dp.value);
- }
- dp.setInBeanConfig();
- }
- deferredProperties.clear();
- }
-
- private boolean addToDeferred(BeanConfiguration beanConfig,String property, Object newValue) {
- if(newValue instanceof List) {
- deferredProperties.put(currentBeanConfig.getName()+property,new DeferredProperty(currentBeanConfig, property, newValue));
- return true;
- }
- else if(newValue instanceof Map) {
- deferredProperties.put(currentBeanConfig.getName()+property,new DeferredProperty(currentBeanConfig, property, newValue));
- return true;
- }
- return false;
- }
- /**
- * This method is called when a bean definition node is called
- *
- * @param name The name of the bean to define
- * @param args The arguments to the bean. The first argument is the class name, the last argument is sometimes a closure. All
- * the arguments in between are constructor arguments
- * @return The bean configuration instance
- */
- private BeanConfiguration invokeBeanDefiningMethod(String name, Object[] args) {
- BeanConfiguration old = currentBeanConfig;
- try {
- if(args[0] instanceof Class) {
- Class beanClass = args[0] instanceof Class ? (Class)args[0] : args[0].getClass();
-
- if(args.length >= 1) {
- if(args[args.length-1] instanceof Closure) {
- if(args.length-1 != 1) {
- Object[] constructorArgs = ArrayUtils.subarray(args, 1, args.length-1);
- filterGStringReferences(constructorArgs);
- if(name.equals(ANONYMOUS_BEAN))
- currentBeanConfig = springConfig.createSingletonBean(beanClass,Arrays.asList(constructorArgs));
- else
- currentBeanConfig = springConfig.addSingletonBean(name, beanClass, Arrays.asList(constructorArgs));
- }
- else {
- if(name.equals(ANONYMOUS_BEAN))
- currentBeanConfig = springConfig.createSingletonBean(beanClass);
- else
- currentBeanConfig = springConfig.addSingletonBean(name, beanClass);
- }
- }
- else {
- Object[] constructorArgs = ArrayUtils.subarray(args, 1, args.length);
- filterGStringReferences(constructorArgs);
- if(name.equals(ANONYMOUS_BEAN))
- currentBeanConfig = springConfig.createSingletonBean(beanClass,Arrays.asList(constructorArgs));
- else
- currentBeanConfig = springConfig.addSingletonBean(name, beanClass, Arrays.asList(constructorArgs));
- }
-
- }
- }
- else if(args[0] instanceof RuntimeBeanReference) {
- currentBeanConfig = springConfig.addSingletonBean(name);
- currentBeanConfig.setFactoryBean(((RuntimeBeanReference)args[0]).getBeanName());
- }
- else if(args[0] instanceof Map) {
- currentBeanConfig = springConfig.addSingletonBean(name);
- Map.Entry factoryBeanEntry = (Map.Entry)((Map)args[0]).entrySet().iterator().next();
- currentBeanConfig.setFactoryBean(factoryBeanEntry.getKey().toString());
- currentBeanConfig.setFactoryMethod(factoryBeanEntry.getValue().toString());
- }
- else if(args[0] instanceof Closure) {
- currentBeanConfig = springConfig.addAbstractBean(name);
- }
- else {
- Object[] constructorArgs;
- if(args[args.length-1] instanceof Closure) {
- constructorArgs= ArrayUtils.subarray(args, 0, args.length-1);
- }
- else {
- constructorArgs= ArrayUtils.subarray(args, 0, args.length);
- }
- filterGStringReferences(constructorArgs);
- currentBeanConfig = new DefaultBeanConfiguration(name, null, Arrays.asList(constructorArgs));
- springConfig.addBeanConfiguration(name,currentBeanConfig);
- }
- if(args[args.length-1] instanceof Closure) {
- Closure callable = (Closure)args[args.length-1];
- callable.setDelegate(this);
- callable.setResolveStrategy(Closure.DELEGATE_FIRST);
- callable.call(new Object[]{currentBeanConfig});
-
- }
-
- return currentBeanConfig;
- } finally {
- currentBeanConfig = old;
- }
- }
-
- private void filterGStringReferences(Object[] constructorArgs) {
- for (int i = 0; i < constructorArgs.length; i++) {
- Object constructorArg = constructorArgs[i];
- if(constructorArg instanceof GString) constructorArgs[i] = constructorArg.toString();
- }
- }
-
- /**
- * When an methods argument is only a closure it is a set of bean definitions
- *
- * @param callable The closure argument
- */
- public BeanBuilder beans(Closure callable) {
- callable.setDelegate(this);
- // callable.setResolveStrategy(Closure.DELEGATE_FIRST);
- callable.call();
- finalizeDeferredProperties();
-
- return this;
- }
-
- /**
- * This method overrides property setting in the scope of the BeanBuilder to set
- * properties on the current BeanConfiguration
- */
- @Override
- public void setProperty(String name, Object value) {
- if(currentBeanConfig != null) {
- if(value instanceof GString)value = value.toString();
- if(addToDeferred(currentBeanConfig, name, value)) {
- return;
- }
- else if(value instanceof Closure) {
- BeanConfiguration current = currentBeanConfig;
- try {
- Closure callable = (Closure)value;
-
- Class parameterType = callable.getParameterTypes()[0];
- if(parameterType.equals(Object.class)) {
- currentBeanConfig = springConfig.createSingletonBean("");
- callable.call(new Object[]{currentBeanConfig});
- }
- else {
- currentBeanConfig = springConfig.createSingletonBean(parameterType);
- callable.call(null);
- }
-
- value = currentBeanConfig.getBeanDefinition();
- }
- finally {
- currentBeanConfig = current;
- }
- }
- currentBeanConfig.addProperty(name, value);
- } else {
- binding.put(name,value);
- }
- }
-
- /**
- * Checks whether there are any runtime refs inside a Map and converts
- * it to a ManagedMap if necessary
- *
- * @param value The current map
- * @return A ManagedMap or a normal map
- */
- private Object manageMapIfNecessary(Map