From da3bb81fadc966cc2df74b18c20a6eb01cf4806e Mon Sep 17 00:00:00 2001 From: kzantow Date: Mon, 28 Mar 2016 20:16:42 -0400 Subject: [PATCH 1/5] JENKINS-33800 - fileNotFound exception if jenkins 1 installed, security settings saved but global config is not - show normal login in this case --- .../java/jenkins/install/SetupWizard.java | 48 ++++++++++++------- .../jenkins/model/Jenkins/login.jelly | 10 ++-- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/core/src/main/java/jenkins/install/SetupWizard.java b/core/src/main/java/jenkins/install/SetupWizard.java index 6c9b53a6df..a8b520603b 100644 --- a/core/src/main/java/jenkins/install/SetupWizard.java +++ b/core/src/main/java/jenkins/install/SetupWizard.java @@ -107,23 +107,25 @@ public class SetupWizard { } } - String setupKey = iapf.readToString().trim(); - String ls = System.lineSeparator(); - LOGGER.info(ls + ls + "*************************************************************" + ls - + "*************************************************************" + ls - + "*************************************************************" + ls - + ls - + "Jenkins initial setup is required. An admin user has been created and " - + "a password generated." + ls - + "Please use the following password to proceed to installation:" + ls - + ls - + setupKey + ls - + ls - + "This may also be found at: " + iapf.getRemote() + ls - + ls - + "*************************************************************" + ls - + "*************************************************************" + ls - + "*************************************************************" + ls); + if(iapf.exists()) { + String setupKey = iapf.readToString().trim(); + String ls = System.lineSeparator(); + LOGGER.info(ls + ls + "*************************************************************" + ls + + "*************************************************************" + ls + + "*************************************************************" + ls + + ls + + "Jenkins initial setup is required. An admin user has been created and " + + "a password generated." + ls + + "Please use the following password to proceed to installation:" + ls + + ls + + setupKey + ls + + ls + + "This may also be found at: " + iapf.getRemote() + ls + + ls + + "*************************************************************" + ls + + "*************************************************************" + ls + + "*************************************************************" + ls); + } try { PluginServletFilter.addFilter(FORCE_SETUP_WIZARD_FILTER); @@ -131,6 +133,18 @@ public class SetupWizard { throw new AssertionError(e); } } + + /** + * Indicates a generated password should be used - e.g. this is a new install, no security realm set up + */ + public boolean useGeneratedPassword() { + try { + return getInitialAdminPasswordFile().exists(); + } catch (Exception e) { + // ignore + } + return false; + } /** * Gets the file used to store the initial admin password diff --git a/core/src/main/resources/jenkins/model/Jenkins/login.jelly b/core/src/main/resources/jenkins/model/Jenkins/login.jelly index b3c91c7b5b..0b91f418b7 100644 --- a/core/src/main/resources/jenkins/model/Jenkins/login.jelly +++ b/core/src/main/resources/jenkins/model/Jenkins/login.jelly @@ -24,10 +24,11 @@ THE SOFTWARE. - + + - - + + @@ -74,5 +75,6 @@ THE SOFTWARE. - + + From 91b5590f2718c0720d86774120cc74abba2d4980 Mon Sep 17 00:00:00 2001 From: kzantow Date: Tue, 29 Mar 2016 12:06:11 -0400 Subject: [PATCH 2/5] Issue with timing determining install state; mark jenkins as upgrade if jobs exist or security configuration was set up --- core/src/main/java/jenkins/install/InstallUtil.java | 4 +++- core/src/main/java/jenkins/model/Jenkins.java | 10 +++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/jenkins/install/InstallUtil.java b/core/src/main/java/jenkins/install/InstallUtil.java index 475d1a3e01..c87044c3c1 100644 --- a/core/src/main/java/jenkins/install/InstallUtil.java +++ b/core/src/main/java/jenkins/install/InstallUtil.java @@ -45,6 +45,7 @@ import com.thoughtworks.xstream.XStream; import hudson.Functions; import hudson.model.UpdateCenter.DownloadJob.InstallationStatus; import hudson.model.UpdateCenter.DownloadJob.Installing; +import hudson.security.SecurityRealm; import hudson.model.UpdateCenter.InstallationJob; import hudson.model.UpdateCenter.UpdateCenterJob; import hudson.util.VersionNumber; @@ -88,7 +89,8 @@ public class InstallUtil { // Edge case: used Jenkins 1 but did not save the system config page, // the version is not persisted and returns 1.0, so try to check if // they actually did anything - if (!Jenkins.getInstance().getItemMap().isEmpty()) { + if (!Jenkins.getInstance().getItemMap().isEmpty() + || Jenkins.getInstance().getSecurityRealm() != SecurityRealm.NO_AUTHENTICATION) { return InstallState.UPGRADE; } return InstallState.NEW; diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index 5e3d1a8227..1853b4da23 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -810,11 +810,6 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve throw new IllegalStateException("second instance"); theInstance = this; - installState = InstallUtil.getInstallState(); - if (installState == InstallState.RESTART || installState == InstallState.DOWNGRADE) { - InstallUtil.saveLastExecVersion(); - } - if (!new File(root,"jobs").exists()) { // if this is a fresh install, use more modern default layout that's consistent with agents workspaceDir = "${JENKINS_HOME}/workspace/${ITEM_FULLNAME}"; @@ -875,6 +870,11 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve if(KILL_AFTER_LOAD) System.exit(0); + installState = InstallUtil.getInstallState(); + if (installState == InstallState.RESTART || installState == InstallState.DOWNGRADE) { + InstallUtil.saveLastExecVersion(); + } + if(!installState.isSetupComplete()) { // Start immediately with the setup wizard for new installs setupWizard = new SetupWizard(this); From 1d4ca77cc913ba0879aebe1f74b70703ef34cdba Mon Sep 17 00:00:00 2001 From: kzantow Date: Tue, 29 Mar 2016 14:28:11 -0400 Subject: [PATCH 3/5] Update loginError.jelly to match login.jelly setup wizard check --- core/src/main/resources/jenkins/model/Jenkins/loginError.jelly | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/jenkins/model/Jenkins/loginError.jelly b/core/src/main/resources/jenkins/model/Jenkins/loginError.jelly index 61f227dfb3..64f0ec4a7d 100644 --- a/core/src/main/resources/jenkins/model/Jenkins/loginError.jelly +++ b/core/src/main/resources/jenkins/model/Jenkins/loginError.jelly @@ -27,7 +27,7 @@ THE SOFTWARE. - + From e8d2668fc298ada58e7fb444a22aca1f1b975aee Mon Sep 17 00:00:00 2001 From: kzantow Date: Thu, 31 Mar 2016 21:55:11 -0400 Subject: [PATCH 4/5] Fix setup wizard cases: update wizard may show erroneously, crash/restart during install may result in installer not completed, ensure the user may not get locked out if they've created an admin user but not completed the installer --- core/src/main/java/hudson/PluginManager.java | 1 - .../java/jenkins/install/InstallUtil.java | 30 +++++++++++++++++-- .../java/jenkins/install/SetupWizard.java | 18 +++++++---- .../jenkins/model/Jenkins/login.jelly | 2 +- .../jenkins/model/Jenkins/loginError.jelly | 2 +- 5 files changed, 43 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java index 546b0e574a..52e6e5ba13 100644 --- a/core/src/main/java/hudson/PluginManager.java +++ b/core/src/main/java/hudson/PluginManager.java @@ -1193,7 +1193,6 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas } updateCenter.persistInstallStatus(); jenkins.setInstallState(InstallState.INITIAL_PLUGINS_INSTALLING.getNextState()); - InstallUtil.saveLastExecVersion(); } }.start(); } diff --git a/core/src/main/java/jenkins/install/InstallUtil.java b/core/src/main/java/jenkins/install/InstallUtil.java index 21adcfd83a..35e8cb95c6 100644 --- a/core/src/main/java/jenkins/install/InstallUtil.java +++ b/core/src/main/java/jenkins/install/InstallUtil.java @@ -45,6 +45,9 @@ import com.thoughtworks.xstream.XStream; import hudson.Functions; import hudson.model.UpdateCenter.DownloadJob.InstallationStatus; import hudson.model.UpdateCenter.DownloadJob.Installing; +import hudson.security.AuthorizationStrategy; +import hudson.security.FullControlOnceLoggedInAuthorizationStrategy; +import hudson.security.HudsonPrivateSecurityRealm; import hudson.security.SecurityRealm; import hudson.model.UpdateCenter.InstallationJob; import hudson.model.UpdateCenter.UpdateCenterJob; @@ -99,8 +102,8 @@ public class InstallUtil { // Edge case: used Jenkins 1 but did not save the system config page, // the version is not persisted and returns 1.0, so try to check if // they actually did anything - if (!Jenkins.getInstance().getItemMap().isEmpty() - || Jenkins.getInstance().getSecurityRealm() != SecurityRealm.NO_AUTHENTICATION) { + Jenkins j = Jenkins.getInstance(); + if (!j.getItemMap().isEmpty() || !mayBeJenkins2SecurityDefaults(j)) { return InstallState.UPGRADE; } return InstallState.NEW; @@ -119,6 +122,29 @@ public class InstallUtil { } } + /** + * This could be an upgrade, detect a non-default security realm for the stupid case + * where someone installed 1.x and did not save global config or create any items... + */ + private static boolean mayBeJenkins2SecurityDefaults(Jenkins j) { + if(j.getSecurityRealm() == SecurityRealm.NO_AUTHENTICATION) { // called before security set up first + return true; + } + if(j.getSecurityRealm() instanceof HudsonPrivateSecurityRealm) { // might be called after a restart, setup isn't complete + HudsonPrivateSecurityRealm securityRealm = (HudsonPrivateSecurityRealm)j.getSecurityRealm(); + if(securityRealm.getAllUsers().size() == 1 && securityRealm.getUser(SetupWizard.initialSetupAdminUserName) != null) { + AuthorizationStrategy authStrategy = j.getAuthorizationStrategy(); + if(authStrategy instanceof FullControlOnceLoggedInAuthorizationStrategy) { + // must have been using 2.0+ to set this, as it wasn't present in 1.x and the default is true, to _allow_ anon read + if(!((FullControlOnceLoggedInAuthorizationStrategy)authStrategy).isAllowAnonymousRead()) { + return true; + } + } + } + } + return false; + } + /** * Save the current Jenkins instance version as the last executed version. *

diff --git a/core/src/main/java/jenkins/install/SetupWizard.java b/core/src/main/java/jenkins/install/SetupWizard.java index 795823f2fa..c3366221b6 100644 --- a/core/src/main/java/jenkins/install/SetupWizard.java +++ b/core/src/main/java/jenkins/install/SetupWizard.java @@ -32,7 +32,6 @@ import hudson.security.SecurityRealm; import hudson.security.csrf.DefaultCrumbIssuer; import hudson.util.HttpResponses; import hudson.util.PluginServletFilter; -import hudson.util.VersionNumber; import jenkins.model.Jenkins; import jenkins.security.s2m.AdminWhitelistRule; @@ -55,6 +54,12 @@ public class SetupWizard { public SetupWizard(Jenkins j) throws IOException, InterruptedException { this.jenkins = j; + + // this was determined to be a new install, don't run the update wizard here + UpgradeWizard uw = jenkins.getInjector().getInstance(UpgradeWizard.class); + if (uw!=null) + uw.setCurrentLevel(Jenkins.getVersion()); + // Create an admin user by default with a // difficult password FilePath iapf = getInitialAdminPasswordFile(); @@ -157,6 +162,13 @@ public class SetupWizard { admin = null; } + // Success! Delete the temporary password file: + try { + getInitialAdminPasswordFile().delete(); + } catch (InterruptedException e) { + throw new IOException(e); + } + j.setInstallState(InstallState.CREATE_ADMIN_USER.getNextState()); // ... and then login @@ -188,10 +200,6 @@ public class SetupWizard { // Also, clean up the setup wizard if it's completed jenkins.setSetupWizard(null); - UpgradeWizard uw = jenkins.getInjector().getInstance(UpgradeWizard.class); - if (uw!=null) - uw.setCurrentLevel(new VersionNumber("2.0")); - return HttpResponses.okJSON(); } diff --git a/core/src/main/resources/jenkins/model/Jenkins/login.jelly b/core/src/main/resources/jenkins/model/Jenkins/login.jelly index 0b91f418b7..29d9192eda 100644 --- a/core/src/main/resources/jenkins/model/Jenkins/login.jelly +++ b/core/src/main/resources/jenkins/model/Jenkins/login.jelly @@ -25,7 +25,7 @@ THE SOFTWARE. - + diff --git a/core/src/main/resources/jenkins/model/Jenkins/loginError.jelly b/core/src/main/resources/jenkins/model/Jenkins/loginError.jelly index 64f0ec4a7d..d17d3eaff6 100644 --- a/core/src/main/resources/jenkins/model/Jenkins/loginError.jelly +++ b/core/src/main/resources/jenkins/model/Jenkins/loginError.jelly @@ -27,7 +27,7 @@ THE SOFTWARE. - + From fcc547e846baeced4f5e7073e9c34d0138185e6e Mon Sep 17 00:00:00 2001 From: kzantow Date: Fri, 1 Apr 2016 13:25:49 -0400 Subject: [PATCH 5/5] Fix tests, move forced update site download to setup wizard --- core/src/main/java/hudson/model/UpdateCenter.java | 11 +++-------- core/src/main/java/jenkins/install/InstallState.java | 7 +++++++ core/src/main/java/jenkins/install/SetupWizard.java | 12 +++++++++++- core/src/main/java/jenkins/model/Jenkins.java | 2 +- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/hudson/model/UpdateCenter.java b/core/src/main/java/hudson/model/UpdateCenter.java index 5cdf66cad7..ceddcb0b20 100644 --- a/core/src/main/java/hudson/model/UpdateCenter.java +++ b/core/src/main/java/hudson/model/UpdateCenter.java @@ -53,7 +53,6 @@ import hudson.util.IOUtils; import hudson.util.PersistedList; import hudson.util.XStream2; import jenkins.RestartRequiredException; -import jenkins.install.InstallState; import jenkins.install.InstallUtil; import jenkins.model.Jenkins; import jenkins.util.io.OnMaster; @@ -1913,18 +1912,14 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas @Initializer(after=PLUGINS_STARTED, fatal=false) public static void init(Jenkins h) throws IOException { h.getUpdateCenter().load(); - if (Jenkins.getActiveInstance().getInstallState() == InstallState.NEW) { - LOGGER.log(INFO, "This is a new Jenkins instance. The Plugin Install Wizard will be launched."); - // Force update of the default site file (updates/default.json). - updateDefaultSite(); - } } - private static void updateDefaultSite() { + @Restricted(NoExternalUse.class) + public static void updateDefaultSite() { try { // Need to do the following because the plugin manager will attempt to access // $JENKINS_HOME/updates/default.json. Needs to be up to date. - Jenkins.getActiveInstance().getUpdateCenter().getSite(UpdateCenter.ID_DEFAULT).updateDirectlyNow(true); + Jenkins.getInstance().getUpdateCenter().getSite(UpdateCenter.ID_DEFAULT).updateDirectlyNow(true); } catch (Exception e) { LOGGER.log(WARNING, "Upgrading Jenkins. Failed to update default UpdateSite. Plugin upgrades may fail.", e); } diff --git a/core/src/main/java/jenkins/install/InstallState.java b/core/src/main/java/jenkins/install/InstallState.java index c634180bc8..42d223815f 100644 --- a/core/src/main/java/jenkins/install/InstallState.java +++ b/core/src/main/java/jenkins/install/InstallState.java @@ -23,6 +23,8 @@ */ package jenkins.install; +import javax.annotation.CheckForNull; + import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -33,6 +35,10 @@ import org.kohsuke.accmod.restrictions.NoExternalUse; */ @Restricted(NoExternalUse.class) public enum InstallState { + /** + * Need InstallState != NEW for tests by default + */ + UNKNOWN(true, null), /** * The initial set up has been completed */ @@ -90,6 +96,7 @@ public enum InstallState { /** * Gets the next state */ + @CheckForNull public InstallState getNextState() { return nextState; } diff --git a/core/src/main/java/jenkins/install/SetupWizard.java b/core/src/main/java/jenkins/install/SetupWizard.java index bc970bd511..4b221f19f8 100644 --- a/core/src/main/java/jenkins/install/SetupWizard.java +++ b/core/src/main/java/jenkins/install/SetupWizard.java @@ -3,6 +3,7 @@ package jenkins.install; import java.io.IOException; import java.util.Locale; import java.util.UUID; +import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.Filter; @@ -25,6 +26,7 @@ import org.kohsuke.stapler.StaplerResponse; import hudson.BulkChange; import hudson.FilePath; +import hudson.model.UpdateCenter; import hudson.model.User; import hudson.security.FullControlOnceLoggedInAuthorizationStrategy; import hudson.security.HudsonPrivateSecurityRealm; @@ -32,6 +34,7 @@ import hudson.security.SecurityRealm; import hudson.security.csrf.DefaultCrumbIssuer; import hudson.util.HttpResponses; import hudson.util.PluginServletFilter; +import hudson.util.VersionNumber; import jenkins.model.Jenkins; import jenkins.security.s2m.AdminWhitelistRule; @@ -58,7 +61,7 @@ public class SetupWizard { // this was determined to be a new install, don't run the update wizard here UpgradeWizard uw = jenkins.getInjector().getInstance(UpgradeWizard.class); if (uw!=null) - uw.setCurrentLevel(Jenkins.getVersion()); + uw.setCurrentLevel(new VersionNumber("2.0")); // Create an admin user by default with a // difficult password @@ -126,6 +129,13 @@ public class SetupWizard { } catch (ServletException e) { throw new AssertionError(e); } + + try { + // Make sure plugin metadata is up to date + UpdateCenter.updateDefaultSite(); + } catch (Exception e) { + LOGGER.log(Level.WARNING, e.getMessage(), e); + } } /** diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index e0be488955..660a229135 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -342,7 +342,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve /** * The Jenkins instance startup type i.e. NEW, UPGRADE etc */ - private transient InstallState installState = InstallState.NEW; + private transient InstallState installState = InstallState.UNKNOWN; /** * If we're in the process of an initial setup,