getGroups() {
return Collections.emptyList();
}
+
+ /**
+ * If true, anonymous read access will be allowed
+ */
+ public boolean isAllowAnonymousRead() {
+ return !denyAnonymousReadAccess;
+ }
+
+ @DataBoundSetter
+ public void setAllowAnonymousRead(boolean allowAnonymousRead) {
+ this.denyAnonymousReadAccess = !allowAnonymousRead;
+ }
- private static final SparseACL THE_ACL = new SparseACL(null);
+ private static final SparseACL AUTHENTICATED_READ = new SparseACL(null);
+ private static final SparseACL ANONYMOUS_READ = new SparseACL(null);
static {
- THE_ACL.add(ACL.EVERYONE, Jenkins.ADMINISTER,true);
- THE_ACL.add(ACL.ANONYMOUS, Jenkins.ADMINISTER,false);
- THE_ACL.add(ACL.ANONYMOUS,Permission.READ,true);
+ ANONYMOUS_READ.add(ACL.EVERYONE, Jenkins.ADMINISTER,true);
+ ANONYMOUS_READ.add(ACL.ANONYMOUS, Jenkins.ADMINISTER,false);
+ ANONYMOUS_READ.add(ACL.ANONYMOUS, Permission.READ,true);
+
+ AUTHENTICATED_READ.add(ACL.EVERYONE, Jenkins.ADMINISTER, true);
+ AUTHENTICATED_READ.add(ACL.ANONYMOUS, Jenkins.ADMINISTER, false);
}
/**
diff --git a/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java b/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java
index ffaf8311d7..2f45492c35 100644
--- a/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java
+++ b/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java
@@ -1,18 +1,18 @@
/*
* The MIT License
- *
+ *
* Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi, David Calavera, Seiji Sogabe
- *
+ *
* 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
@@ -29,6 +29,8 @@ import hudson.ExtensionList;
import hudson.Util;
import hudson.diagnosis.OldDataMonitor;
import hudson.model.Descriptor;
+import jenkins.install.InstallState;
+import jenkins.install.SetupWizard;
import jenkins.model.Jenkins;
import hudson.model.ManagementLink;
import hudson.model.ModelObject;
@@ -97,7 +99,7 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
/**
* If true, sign up is not allowed.
*
- * This is a negative switch so that the default value 'false' remains compatible with older installations.
+ * This is a negative switch so that the default value 'false' remains compatible with older installations.
*/
private final boolean disableSignup;
@@ -279,14 +281,36 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
* This can be run by anyone, but only to create the very first user account.
*/
public void doCreateFirstAccount(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
- if(hasSomeUser()) {
+ boolean inSetup = !Jenkins.getInstance().getInstallState().isSetupComplete();
+ if(!inSetup && hasSomeUser()) {
rsp.sendError(SC_UNAUTHORIZED,"First user was already created");
return;
}
- User u = createAccount(req, rsp, false, "firstUser.jelly");
- if (u!=null) {
- tryToMakeAdmin(u);
- loginAndTakeBack(req, rsp, u);
+
+ User admin = null;
+ try {
+ String view = "firstUser.jelly";
+ if(inSetup) {
+ admin = getUser(SetupWizard.initialSetupAdminUserName);
+ if(admin != null) {
+ admin.delete(); // assume the new user may well be 'admin'
+ }
+ view = "setupWizardFirstUser.jelly";
+ }
+
+ User u = createAccount(req, rsp, false, view);
+ if (u!=null) {
+ tryToMakeAdmin(u);
+ if(admin != null) {
+ admin = null;
+ }
+ Jenkins.getInstance().setInstallState(InstallState.CREATE_ADMIN_USER.getNextState());
+ loginAndTakeBack(req, rsp, u);
+ }
+ } finally {
+ if(admin != null) {
+ admin.save(); // recreate this initial user if something failed
+ }
}
}
diff --git a/core/src/main/java/hudson/slaves/WorkspaceList.java b/core/src/main/java/hudson/slaves/WorkspaceList.java
index a344b45aac..9b41599892 100644
--- a/core/src/main/java/hudson/slaves/WorkspaceList.java
+++ b/core/src/main/java/hudson/slaves/WorkspaceList.java
@@ -26,6 +26,7 @@ package hudson.slaves;
import hudson.FilePath;
import hudson.Functions;
import hudson.model.Computer;
+import hudson.model.DirectoryBrowserSupport;
import java.io.Closeable;
import java.util.Date;
@@ -283,6 +284,25 @@ public final class WorkspaceList {
};
}
+ /**
+ * Locates a conventional temporary directory to be associated with a workspace.
+ *
This directory is suitable for temporary files to be deleted later in the course of a build,
+ * or caches and local repositories which should persist across builds done in the same workspace.
+ * (If multiple workspaces are present for a single job built concurrently, via {@link #allocate(FilePath)}, each will get its own temporary directory.)
+ *
It may also be used for security-sensitive files which {@link DirectoryBrowserSupport} ought not serve,
+ * acknowledging that these will be readable by builds of other jobs done on the same node.
+ *
Each plugin using this directory is responsible for specifying sufficiently unique subdirectory/file names.
+ * {@link FilePath#createTempFile} may be used for this purpose if desired.
+ *
The resulting directory may not exist; you may call {@link FilePath#mkdirs} on it if you need it to.
+ * It may be deleted alongside the workspace itself during cleanup actions.
+ * @param ws a directory such as a build workspace
+ * @return a sibling directory, for example {@code …/something@tmp} for {@code …/something}
+ * @since 1.652
+ */
+ public static FilePath tempDir(FilePath ws) {
+ return ws.sibling(ws.getName() + COMBINATOR + "tmp");
+ }
+
private static final Logger LOGGER = Logger.getLogger(WorkspaceList.class.getName());
/**
diff --git a/core/src/main/java/jenkins/install/InstallState.java b/core/src/main/java/jenkins/install/InstallState.java
index 510feb6c78..c634180bc8 100644
--- a/core/src/main/java/jenkins/install/InstallState.java
+++ b/core/src/main/java/jenkins/install/InstallState.java
@@ -34,32 +34,63 @@ import org.kohsuke.accmod.restrictions.NoExternalUse;
@Restricted(NoExternalUse.class)
public enum InstallState {
/**
- * New Jenkins install.
+ * The initial set up has been completed
*/
- NEW,
+ INITIAL_SETUP_COMPLETED(true, null),
+ /**
+ * Creating an admin user for an initial Jenkins install.
+ */
+ CREATE_ADMIN_USER(false, INITIAL_SETUP_COMPLETED),
/**
* New Jenkins install. The user has kicked off the process of installing an
* initial set of plugins (via the install wizard).
*/
- INITIAL_PLUGINS_INSTALLING,
+ INITIAL_PLUGINS_INSTALLING(false, CREATE_ADMIN_USER),
/**
- * New Jenkins install. The initial set of plugins are now installed.
+ * New Jenkins install.
*/
- INITIAL_PLUGINS_INSTALLED,
+ NEW(false, INITIAL_PLUGINS_INSTALLING),
/**
* Restart of an existing Jenkins install.
*/
- RESTART,
+ RESTART(true, INITIAL_SETUP_COMPLETED),
/**
* Upgrade of an existing Jenkins install.
*/
- UPGRADE,
+ UPGRADE(true, INITIAL_SETUP_COMPLETED),
/**
* Downgrade of an existing Jenkins install.
*/
- DOWNGRADE,
+ DOWNGRADE(true, INITIAL_SETUP_COMPLETED),
/**
* Jenkins started in test mode (JenkinsRule).
*/
- TEST
+ TEST(true, INITIAL_SETUP_COMPLETED),
+ /**
+ * Jenkins started in development mode: Bolean.getBoolean("hudson.Main.development").
+ * Can be run normally with the -Djenkins.install.runSetupWizard=true
+ */
+ DEVELOPMENT(true, INITIAL_SETUP_COMPLETED);
+
+ private final boolean isSetupComplete;
+ private final InstallState nextState;
+
+ private InstallState(boolean isSetupComplete, InstallState nextState) {
+ this.isSetupComplete = isSetupComplete;
+ this.nextState = nextState;
+ }
+
+ /**
+ * Indicates the initial setup is complete
+ */
+ public boolean isSetupComplete() {
+ return isSetupComplete;
+ }
+
+ /**
+ * Gets the next state
+ */
+ public InstallState getNextState() {
+ return nextState;
+ }
}
diff --git a/core/src/main/java/jenkins/install/InstallUtil.java b/core/src/main/java/jenkins/install/InstallUtil.java
index e9622b1a9a..475d1a3e01 100644
--- a/core/src/main/java/jenkins/install/InstallUtil.java
+++ b/core/src/main/java/jenkins/install/InstallUtil.java
@@ -61,6 +61,7 @@ public class InstallUtil {
private static final Logger LOGGER = Logger.getLogger(InstallUtil.class.getName());
+ // tests need this to be 1.0
private static final VersionNumber NEW_INSTALL_VERSION = new VersionNumber("1.0");
/**
@@ -68,8 +69,15 @@ public class InstallUtil {
* @return The type of "startup" currently under way in Jenkins.
*/
public static InstallState getInstallState() {
- if (Functions.getIsUnitTest()) {
- return InstallState.TEST;
+ // install wizard will always run if environment specified
+ if (!Boolean.getBoolean("jenkins.install.runSetupWizard")) {
+ if (Functions.getIsUnitTest()) {
+ return InstallState.TEST;
+ }
+
+ if (Boolean.getBoolean("hudson.Main.development")) {
+ return InstallState.DEVELOPMENT;
+ }
}
VersionNumber lastRunVersion = new VersionNumber(getLastExecVersion());
@@ -77,6 +85,12 @@ public class InstallUtil {
// Neither the top level config or the lastExecVersionFile have a version
// stored in them, which means it's a new install.
if (lastRunVersion.compareTo(NEW_INSTALL_VERSION) == 0) {
+ // 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()) {
+ return InstallState.UPGRADE;
+ }
return InstallState.NEW;
}
diff --git a/core/src/main/java/jenkins/install/SetupWizard.java b/core/src/main/java/jenkins/install/SetupWizard.java
new file mode 100644
index 0000000000..b7e3f13a95
--- /dev/null
+++ b/core/src/main/java/jenkins/install/SetupWizard.java
@@ -0,0 +1,178 @@
+package jenkins.install;
+
+import java.io.IOException;
+import java.util.Locale;
+import java.util.UUID;
+import java.util.logging.Logger;
+
+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.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+
+import org.kohsuke.stapler.HttpResponse;
+import org.kohsuke.stapler.StaplerRequest;
+import org.kohsuke.stapler.StaplerResponse;
+
+import hudson.BulkChange;
+import hudson.ExtensionList;
+import hudson.model.User;
+import hudson.model.UserProperty;
+import hudson.security.FullControlOnceLoggedInAuthorizationStrategy;
+import hudson.security.HudsonPrivateSecurityRealm;
+import hudson.security.PermissionAdder;
+import hudson.security.SecurityRealm;
+import hudson.security.csrf.DefaultCrumbIssuer;
+import hudson.util.HttpResponses;
+import hudson.util.PluginServletFilter;
+import jenkins.model.Jenkins;
+import jenkins.security.s2m.AdminWhitelistRule;
+
+/**
+ * A Jenkins instance used during first-run to provide a limited set of services while
+ * initial installation is in progress
+ */
+public class SetupWizard {
+ /**
+ * The security token parameter name
+ */
+ public static String initialSetupAdminUserName = "admin";
+
+ private final Logger LOGGER = Logger.getLogger(SetupWizard.class.getName());
+
+ public SetupWizard(Jenkins j) throws IOException {
+ User admin;
+ // Create an admin user by default with a
+ // difficult password
+ if(j.getSecurityRealm() == null || j.getSecurityRealm() == SecurityRealm.NO_AUTHENTICATION) { // this seems very fragile
+ BulkChange bc = new BulkChange(j);
+
+ HudsonPrivateSecurityRealm securityRealm = new HudsonPrivateSecurityRealm(false, false, null);
+ j.setSecurityRealm(securityRealm);
+ String randomUUID = UUID.randomUUID().toString().replace("-", "").toLowerCase(Locale.ENGLISH);
+ admin = securityRealm.createAccount(SetupWizard.initialSetupAdminUserName, randomUUID);
+ admin.addProperty(new SetupWizard.AuthenticationKey(randomUUID));
+
+ // Lock Jenkins down:
+ FullControlOnceLoggedInAuthorizationStrategy authStrategy = new FullControlOnceLoggedInAuthorizationStrategy();
+ authStrategy.setAllowAnonymousRead(false);
+ j.setAuthorizationStrategy(authStrategy);
+
+ // Shut down all the ports we can by default:
+ j.setSlaveAgentPort(-1); // -1 to disable
+
+ // require a crumb issuer
+ j.setCrumbIssuer(new DefaultCrumbIssuer(false));
+
+ // set master -> slave security:
+ j.getInjector().getInstance(AdminWhitelistRule.class)
+ .setMasterKillSwitch(false);
+
+ try{
+ j.save(); // !!
+ } finally {
+ bc.commit();
+ }
+ }
+ else {
+ admin = j.getUser(SetupWizard.initialSetupAdminUserName);
+ }
+
+ String setupKey = null;
+ if(admin != null && admin.getProperty(SetupWizard.AuthenticationKey.class) != null) {
+ setupKey = admin.getProperty(SetupWizard.AuthenticationKey.class).getKey();
+ }
+ if(setupKey != null) {
+ LOGGER.info("\n\n*************************************************************\n"
+ + "*************************************************************\n"
+ + "*************************************************************\n"
+ + "\n"
+ + "Jenkins initial setup is required. A security token is required to proceed. \n"
+ + "Please use the following security token to proceed to installation: \n"
+ + "\n"
+ + "" + setupKey + "\n"
+ + "\n"
+ + "*************************************************************\n"
+ + "*************************************************************\n"
+ + "*************************************************************\n");
+ }
+
+ try {
+ PluginServletFilter.addFilter(FORCE_SETUP_WIZARD_FILTER);
+ } catch (ServletException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ /**
+ * Remove the setupWizard filter, ensure all updates are written to disk, etc
+ */
+ public HttpResponse doCompleteInstall(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
+ Jenkins j = Jenkins.getActiveInstance();
+ j.setInstallState(InstallState.INITIAL_SETUP_COMPLETED);
+ InstallUtil.saveLastExecVersion();
+ PluginServletFilter.removeFilter(FORCE_SETUP_WIZARD_FILTER);
+ // Also, clean up the setup wizard if it's completed
+ j.setSetupWizard(null);
+ return HttpResponses.okJSON();
+ }
+
+ // Stores a user property for the authentication key, which is really the auto-generated user's password
+ public static class AuthenticationKey extends UserProperty {
+ String key;
+
+ public AuthenticationKey() {
+ }
+
+ public AuthenticationKey(String key) {
+ this.key = key;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+ }
+
+ /**
+ * This filter will validate that the security token is provided
+ */
+ private final Filter FORCE_SETUP_WIZARD_FILTER = new Filter() {
+ @Override
+ public void init(FilterConfig cfg) throws ServletException {
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+ // As an extra measure of security, the install wizard generates a security token, and
+ // requires the user to enter it before proceeding through the installation. Once set
+ // we'll set a cookie so the subsequent operations succeed
+ if (request instanceof HttpServletRequest) {
+ HttpServletRequest req = (HttpServletRequest)request;
+ //if (!Pattern.compile(".*[.](css|ttf|gif|woff|eot|png|js)").matcher(req.getRequestURI()).matches()) {
+ // Allow js & css requests through
+ if((req.getContextPath() + "/").equals(req.getRequestURI())) {
+ chain.doFilter(new HttpServletRequestWrapper(req) {
+ public String getRequestURI() {
+ return getContextPath() + "/setupWizard/";
+ }
+ }, response);
+ return;
+ }
+ // fall through to handling the request normally
+ }
+ chain.doFilter(request, response);
+ }
+
+ @Override
+ public void destroy() {
+ }
+ };
+}
diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java
index 60154b64fe..d34fa105a4 100644
--- a/core/src/main/java/jenkins/model/Jenkins.java
+++ b/core/src/main/java/jenkins/model/Jenkins.java
@@ -194,6 +194,7 @@ import jenkins.ExtensionRefreshException;
import jenkins.InitReactorRunner;
import jenkins.install.InstallState;
import jenkins.install.InstallUtil;
+import jenkins.install.SetupWizard;
import jenkins.model.ProjectNamingStrategy.DefaultProjectNamingStrategy;
import jenkins.security.ConfidentialKey;
import jenkins.security.ConfidentialStore;
@@ -333,7 +334,13 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
/**
* The Jenkins instance startup type i.e. NEW, UPGRADE etc
*/
- private InstallState installState;
+ private transient InstallState installState = InstallState.NEW;
+
+ /**
+ * If we're in the process of an initial setup,
+ * this will be set
+ */
+ private transient SetupWizard setupWizard;
/**
* Number of executors of the master node.
@@ -759,7 +766,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
protected Jenkins(File root, ServletContext context, PluginManager pluginManager) throws IOException, InterruptedException, ReactorException {
long start = System.currentTimeMillis();
- // As Jenkins is starting, grant this process full control
+ // As Jenkins is starting, grant this process full control
ACL.impersonate(ACL.SYSTEM);
try {
this.root = root;
@@ -773,7 +780,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
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}";
@@ -834,6 +841,11 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
if(KILL_AFTER_LOAD)
System.exit(0);
+ if(!installState.isSetupComplete()) {
+ // Start immediately with the setup wizard for new installs
+ setupWizard = new SetupWizard(this);
+ }
+
launchTcpSlaveAgentListener();
if (UDPBroadcastThread.PORT != -1) {
@@ -913,6 +925,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
* Get the Jenkins {@link jenkins.install.InstallState install state}.
* @return The Jenkins {@link jenkins.install.InstallState install state}.
*/
+ @Nonnull
@Restricted(NoExternalUse.class)
public InstallState getInstallState() {
return installState;
@@ -1437,10 +1450,10 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
*/
@Exported(name="jobs")
public List getItems() {
- if (authorizationStrategy instanceof AuthorizationStrategy.Unsecured ||
- authorizationStrategy instanceof FullControlOnceLoggedInAuthorizationStrategy) {
- return new ArrayList(items.values());
- }
+ if (authorizationStrategy instanceof AuthorizationStrategy.Unsecured ||
+ authorizationStrategy instanceof FullControlOnceLoggedInAuthorizationStrategy) {
+ return new ArrayList(items.values());
+ }
List viewableItems = new ArrayList();
for (TopLevelItem item : items.values()) {
@@ -1815,11 +1828,11 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
}
public DescribableList, NodePropertyDescriptor> getNodeProperties() {
- return nodeProperties;
+ return nodeProperties;
}
public DescribableList, NodePropertyDescriptor> getGlobalNodeProperties() {
- return globalNodeProperties;
+ return globalNodeProperties;
}
/**
@@ -2428,7 +2441,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
*/
@Override public TopLevelItem getItem(String name) throws AccessDeniedException {
if (name==null) return null;
- TopLevelItem item = items.get(name);
+ TopLevelItem item = items.get(name);
if (item==null)
return null;
if (!item.hasPermission(Item.READ)) {
@@ -3079,10 +3092,10 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
}
public HttpResponse doToggleCollapse() throws ServletException, IOException {
- final StaplerRequest request = Stapler.getCurrentRequest();
- final String paneId = request.getParameter("paneId");
+ final StaplerRequest request = Stapler.getCurrentRequest();
+ final String paneId = request.getParameter("paneId");
- PaneStatusProperties.forCurrentUser().toggleCollapsed(paneId);
+ PaneStatusProperties.forCurrentUser().toggleCollapsed(paneId);
return HttpResponses.forwardToPreviousPage();
}
@@ -3113,9 +3126,9 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
LOGGER.info("Failed to get thread dump for node " + c.getName() + ": " + e.getMessage());
}
}
- if (toComputer() == null) {
- future.put("master", RemotingDiagnostics.getThreadDumpAsync(FilePath.localChannel));
- }
+ if (toComputer() == null) {
+ future.put("master", RemotingDiagnostics.getThreadDumpAsync(FilePath.localChannel));
+ }
// if the result isn't available in 5 sec, ignore that.
// this is a precaution against hang nodes
@@ -3891,6 +3904,20 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
public List getManagementLinks() {
return ManagementLink.all();
}
+
+ /**
+ * If set, a currently active setup wizard - e.g. installation
+ */
+ public SetupWizard getSetupWizard() {
+ return setupWizard;
+ }
+
+ /**
+ * Sets the setup wizard
+ */
+ public void setSetupWizard(SetupWizard setupWizard) {
+ this.setupWizard = setupWizard;
+ }
/**
* Exposes the current user to /me URL.
diff --git a/core/src/main/resources/hudson/security/FullControlOnceLoggedInAuthorizationStrategy/config.jelly b/core/src/main/resources/hudson/security/FullControlOnceLoggedInAuthorizationStrategy/config.jelly
new file mode 100644
index 0000000000..0e627effbc
--- /dev/null
+++ b/core/src/main/resources/hudson/security/FullControlOnceLoggedInAuthorizationStrategy/config.jelly
@@ -0,0 +1,7 @@
+
+
+
+
+
+
diff --git a/core/src/main/resources/hudson/security/FullControlOnceLoggedInAuthorizationStrategy/help-allowAnonymousRead.html b/core/src/main/resources/hudson/security/FullControlOnceLoggedInAuthorizationStrategy/help-allowAnonymousRead.html
new file mode 100644
index 0000000000..0f357d28e1
--- /dev/null
+++ b/core/src/main/resources/hudson/security/FullControlOnceLoggedInAuthorizationStrategy/help-allowAnonymousRead.html
@@ -0,0 +1,3 @@
+
+ If checked, this will allow users who are not authenticated to access Jenkins in a read-only mode.
+
diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/_entryForm.jelly b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/_entryForm.jelly
index bb12a30aa9..346d8c4d53 100644
--- a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/_entryForm.jelly
+++ b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/_entryForm.jelly
@@ -25,64 +25,43 @@ THE SOFTWARE.
-
-
-
-
-
-
-
-
- ${title}
-
-
-
- ${data.errorMessage}
-
-
-
+
${title}
+
+
+
+ ${data.errorMessage}
-
-
+
+
+
diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/_entryFormPage.jelly b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/_entryFormPage.jelly
new file mode 100644
index 0000000000..8009ad6518
--- /dev/null
+++ b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/_entryFormPage.jelly
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/addUser.jelly b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/addUser.jelly
index aed3d5c5c8..daacc17bfc 100644
--- a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/addUser.jelly
+++ b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/addUser.jelly
@@ -27,5 +27,5 @@ THE SOFTWARE.
-->
-
+
\ No newline at end of file
diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/firstUser.jelly b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/firstUser.jelly
index bc44b42acc..6ebd369ae0 100644
--- a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/firstUser.jelly
+++ b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/firstUser.jelly
@@ -27,5 +27,5 @@ THE SOFTWARE.
-->
-
+
\ No newline at end of file
diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/setupWizardFirstUser.jelly b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/setupWizardFirstUser.jelly
new file mode 100644
index 0000000000..4f7475462f
--- /dev/null
+++ b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/setupWizardFirstUser.jelly
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signup.jelly b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signup.jelly
index 656e96c3c9..f2477da1df 100644
--- a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signup.jelly
+++ b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signup.jelly
@@ -27,5 +27,5 @@ THE SOFTWARE.
-->
-
+
diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signupWithFederatedIdentity.jelly b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signupWithFederatedIdentity.jelly
index 01c0197a62..8daec8d381 100644
--- a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signupWithFederatedIdentity.jelly
+++ b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signupWithFederatedIdentity.jelly
@@ -27,5 +27,5 @@ THE SOFTWARE.
-->
-
+
\ No newline at end of file
diff --git a/core/src/main/resources/jenkins/install/SetupWizard/authenticate-security-token.jelly b/core/src/main/resources/jenkins/install/SetupWizard/authenticate-security-token.jelly
new file mode 100644
index 0000000000..b22936d9ad
--- /dev/null
+++ b/core/src/main/resources/jenkins/install/SetupWizard/authenticate-security-token.jelly
@@ -0,0 +1,44 @@
+
+
+
+
+
+
diff --git a/core/src/main/resources/jenkins/install/SetupWizard/index.jelly b/core/src/main/resources/jenkins/install/SetupWizard/index.jelly
new file mode 100644
index 0000000000..d001d433d5
--- /dev/null
+++ b/core/src/main/resources/jenkins/install/SetupWizard/index.jelly
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/core/src/main/resources/jenkins/install/SetupWizard/proxy-configuration.jelly b/core/src/main/resources/jenkins/install/SetupWizard/proxy-configuration.jelly
new file mode 100644
index 0000000000..7653509dca
--- /dev/null
+++ b/core/src/main/resources/jenkins/install/SetupWizard/proxy-configuration.jelly
@@ -0,0 +1,13 @@
+
+
+
+ ${%HTTP Proxy Configuration}
+
+
+
+
+
+
+
+
+
diff --git a/core/src/main/resources/jenkins/install/pluginSetupWizard.properties b/core/src/main/resources/jenkins/install/pluginSetupWizard.properties
index 03fddbf2b4..71130d75f3 100644
--- a/core/src/main/resources/jenkins/install/pluginSetupWizard.properties
+++ b/core/src/main/resources/jenkins/install/pluginSetupWizard.properties
@@ -1,16 +1,16 @@
installWizard_welcomePanel_title=Getting Started
-installWizard_welcomePanel_banner=Initial Jenkins Setup
-installWizard_welcomePanel_message=Choose plugins. This will add features to your Jenkins environment.
-installWizard_welcomePanel_recommendedActionTitle=Start with recommended plugins
-installWizard_welcomePanel_recommendedActionDetails=Install the set of plugins the community finds most useful
-installWizard_welcomePanel_customizeActionTitle=Customize your plugins
-installWizard_welcomePanel_customizeActionDetails=Select from a community approved list of plugins
+installWizard_welcomePanel_banner=Customize Jenkins
+installWizard_welcomePanel_message=Plugins extend Jenkins with additional features to support many different needs.
+installWizard_welcomePanel_recommendedActionTitle=Install suggested plugins
+installWizard_welcomePanel_recommendedActionDetails=Install plugins the Jenkins community finds most useful.
+installWizard_welcomePanel_customizeActionTitle=Select plugins to install
+installWizard_welcomePanel_customizeActionDetails=Select and install plugins most suitable for your needs.
installWizard_offline_title=Offline
installWizard_offline_message=This Jenkins instance appears to be offline. \
\
For information about installing Jenkins without an internet connection, see the \
Offline Jenkins Installation Documentation . \
-If you need to configure a proxy, you may close this wizard and use the Plugin Manager. \
+You may choose to continue by configuring a proxy or skipping plugin installation. \
installWizard_error_header=An error occurred
installWizard_error_message=An error occurred during installation:
@@ -28,12 +28,23 @@ installWizard_installing_title=Installing...
installWizard_installing_detailsLink=Details...
installWizard_installComplete_title=Installed
installWizard_installComplete_banner=Jenkins is ready!
-installWizard_installComplete_message=Your plugin installations are complete.
-installWizard_installComplete_finishButtonLabel=Get Started
+installWizard_pluginsInstalled_message=Your plugin installations are complete.
+installWizard_installComplete_message=Your Jenkins setup is complete.
+installWizard_installComplete_finishButtonLabel=Start using Jenkins
installWizard_installComplete_restartRequiredMessage=Some plugins require Jenkins to be restarted.
installWizard_installComplete_restartLabel=Restart
installWizard_installIncomplete_title=Resume Installation
installWizard_installIncomplete_banner=Resume Installation
-installWizard_installIncomplete_message=Jenknins was restarted during installation and some plugins didn't seem to get installed.
+installWizard_installIncomplete_message=Jenkins was restarted during installation and some plugins didn't seem to get installed.
installWizard_installIncomplete_resumeInstallationButtonLabel=Resume
+installWizard_saveFirstUser=Save and Finish
+installWizard_skipFirstUser=Skip
+installWizard_firstUserSkippedMessage=
\
+You've skipped creating an admin user. To log in, use the username: 'admin' and \
+the security token you used to access the setup wizard.\
+
+installWizard_addFirstUser_title=Create an Admin User
+installWizard_configureProxy_label=Configure Proxy
+installWizard_configureProxy_save=Save and Continue
+installWizard_skipPluginInstallations=Skip Plugin Installations
installWizard_installIncomplete_dependenciesLabel=Dependencies
diff --git a/core/src/main/resources/jenkins/model/Jenkins/login.jelly b/core/src/main/resources/jenkins/model/Jenkins/login.jelly
index f7e02a5f50..b3c91c7b5b 100644
--- a/core/src/main/resources/jenkins/model/Jenkins/login.jelly
+++ b/core/src/main/resources/jenkins/model/Jenkins/login.jelly
@@ -24,6 +24,10 @@ THE SOFTWARE.
+
+
+
+
@@ -70,4 +74,5 @@ 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 6f7c2bfb22..61f227dfb3 100644
--- a/core/src/main/resources/jenkins/model/Jenkins/loginError.jelly
+++ b/core/src/main/resources/jenkins/model/Jenkins/loginError.jelly
@@ -27,6 +27,10 @@ THE SOFTWARE.
+
+
+
+
+
+
+
+ Outer-most tag for a normal (non-AJAX) HTML rendering.
+ This is used with nested <header>, <side-panel>, and <main-panel>
+ to form Jenkins's basic HTML layout.
+
+
+ Title of the HTML page. Rendered into <title> tag.
+
+
+ If non-null and not "false", auto refresh is disabled on this page.
+ This is necessary for pages that include forms.
+
+
+ specify path that starts from "/" for loading additional CSS stylesheet.
+ path is interprted as relative to the context root. e.g.,
+
+ {noformat}<l:layout css="/plugin/mysuperplugin/css/myneatstyle.css">{noformat}
+
+ This was originally added to allow plugins to load their stylesheets, but
+ *the use of thie attribute is discouraged now.*
+ plugins should now do so by inserting <style> elements and/or <script> elements
+ in <l:header/> tag.
+
+
+ If given, this page is only made available to users that has the specified permission.
+ (The permission will be checked against the "it" object.)
+
+
+
+
+
+
+
+
+${h.initPageVariables(context)}
+
+
+
+
+
+
+ ${h.advertiseHeaders(response)}
+
+
+
+
+
+
+
+
+
+ ${h.checkPermission(it,permission)}
+
+
+
+
+
+ ${h.appendIfNotNull(title, ' [Jenkins]', 'Jenkins')}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/core/src/main/resources/lib/layout/layout.jelly b/core/src/main/resources/lib/layout/layout.jelly
index 7e8c4f7e27..34767950df 100644
--- a/core/src/main/resources/lib/layout/layout.jelly
+++ b/core/src/main/resources/lib/layout/layout.jelly
@@ -166,10 +166,6 @@ ${h.initPageVariables(context)}
-
-
-
-
diff --git a/pom.xml b/pom.xml
index 322de8b166..d260310d6e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -58,7 +58,7 @@ THE SOFTWARE.
scm:git:git://github.com/jenkinsci/jenkins.git
scm:git:ssh://git@github.com/jenkinsci/jenkins.git
https://github.com/jenkinsci/jenkins
- HEAD
+ jenkins-1.652
diff --git a/test/pom.xml b/test/pom.xml
index e57b8ba16b..13ba247013 100644
--- a/test/pom.xml
+++ b/test/pom.xml
@@ -65,6 +65,12 @@ THE SOFTWARE.
jenkins-test-harness
2.5
test
+
+
+ ${project.groupId}
+ jenkins-war
+
+
${project.groupId}
diff --git a/test/src/test/java/hudson/model/ManagementLinkTest.java b/test/src/test/java/hudson/model/ManagementLinkTest.java
index 4b19f2c8bb..5de5dd3d51 100644
--- a/test/src/test/java/hudson/model/ManagementLinkTest.java
+++ b/test/src/test/java/hudson/model/ManagementLinkTest.java
@@ -31,6 +31,7 @@ import com.gargoylesoftware.htmlunit.html.HtmlPage;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
+import org.jvnet.hudson.test.JenkinsRule.WebClient;
import java.util.List;
@@ -47,11 +48,15 @@ public class ManagementLinkTest {
*/
@Test
public void links() throws Exception {
- HtmlPage page = j.createWebClient().goTo("manage");
- List> anchors = DomNodeUtil.selectNodes(page, "id('management-links')//*[@class='link']/a[not(@onclick)]");
- assertTrue(anchors.size()>=8);
- for(HtmlAnchor e : (List) anchors) {
- e.click();
+ WebClient wc = j.createWebClient();
+
+ for (int i=0; ; i++) {
+ HtmlPage page = wc.goTo("manage");
+ List> anchors = DomNodeUtil.selectNodes(page, "id('management-links')//*[@class='link']/a[not(@onclick)]");
+ assertTrue(anchors.size()>=8);
+ if (i==anchors.size()) return; // done
+
+ ((HtmlAnchor)anchors.get(i)).click();
}
}
}
diff --git a/test/src/test/java/hudson/model/UpdateCenterPluginInstallTest.java b/test/src/test/java/hudson/model/UpdateCenterPluginInstallTest.java
index a9b9901304..fe651a8263 100644
--- a/test/src/test/java/hudson/model/UpdateCenterPluginInstallTest.java
+++ b/test/src/test/java/hudson/model/UpdateCenterPluginInstallTest.java
@@ -75,7 +75,8 @@ public class UpdateCenterPluginInstallTest {
String correlationId = data.getString("correlationId");
JSONObject installStatus = jenkinsRule.getJSON("updateCenter/installStatus?correlationId=" + correlationId).getJSONObject();
Assert.assertEquals("ok", json.get("status"));
- JSONArray states = installStatus.getJSONArray("data");
+ JSONObject status = installStatus.getJSONObject("data");
+ JSONArray states = status.getJSONArray("jobs");
Assert.assertEquals(2, states.size());
JSONObject pluginInstallState = states.getJSONObject(0);
diff --git a/test/src/test/java/hudson/model/WorkspaceCleanupThreadTest.java b/test/src/test/java/hudson/model/WorkspaceCleanupThreadTest.java
index 28d7dd04e1..0971276179 100644
--- a/test/src/test/java/hudson/model/WorkspaceCleanupThreadTest.java
+++ b/test/src/test/java/hudson/model/WorkspaceCleanupThreadTest.java
@@ -28,6 +28,7 @@ import hudson.FilePath;
import hudson.remoting.VirtualChannel;
import hudson.scm.NullSCM;
import hudson.slaves.DumbSlave;
+import hudson.slaves.WorkspaceList;
import hudson.util.StreamTaskListener;
import java.io.File;
@@ -180,6 +181,19 @@ public class WorkspaceCleanupThreadTest {
assertFalse(ws.exists());
}
+ @Issue("JENKINS-27152")
+ @Test
+ public void deleteTemporaryDirectory() throws Exception {
+ FreeStyleProject p = r.createFreeStyleProject();
+ FilePath ws = createOldWorkspaceOn(r.jenkins, p);
+ FilePath tmp = WorkspaceList.tempDir(ws);
+ tmp.child("stuff").write("content", null);
+ createOldWorkspaceOn(r.createOnlineSlave(), p);
+ performCleanup();
+ assertFalse(ws.exists());
+ assertFalse("temporary directory should be cleaned up as well", tmp.exists());
+ }
+
private FilePath createOldWorkspaceOn(Node slave, FreeStyleProject p) throws Exception {
p.setAssignedNode(slave);
FreeStyleBuild b1 = r.assertBuildStatusSuccess(p.scheduleBuild2(0));
diff --git a/war/src/main/js/api/pluginManager.js b/war/src/main/js/api/pluginManager.js
index 6ef1c281c5..bbe2fccb77 100644
--- a/war/src/main/js/api/pluginManager.js
+++ b/war/src/main/js/api/pluginManager.js
@@ -149,7 +149,7 @@ exports.incompleteInstallStatus = function(handler, correlationId) {
* Call this to complete the installation without installing anything
*/
exports.completeInstall = function(handler) {
- jenkins.get('/updateCenter/completeInstall', function() {
+ jenkins.get('/setupWizard/completeInstall', function() {
handler.call({ isError: false });
}, {
timeout: pluginManagerErrorTimeoutMillis,
diff --git a/war/src/main/js/api/plugins.js b/war/src/main/js/api/plugins.js
index 4f3bcb967e..9d7286af5b 100644
--- a/war/src/main/js/api/plugins.js
+++ b/war/src/main/js/api/plugins.js
@@ -8,17 +8,27 @@
// away from these.
//
exports.recommendedPlugins = [
+ "ant",
"antisamy-markup-formatter",
- "credentials",
+ "build-monitor-plugin",
+ "build-timeout",
+ "cloudbees-folder",
+ "credentials-binding",
+ "email-ext",
+ "git",
"github-branch-source",
- "junit",
+ "gradle",
+ "ldap",
"mailer",
- "matrix-auth",
- "script-security",
+ // "matrix-auth",
+ "pam-auth",
+ "pipeline-stage-view",
+ "ssh-slaves",
"subversion",
- "translation",
+ "timestamper",
"workflow-aggregator",
- "workflow-multibranch"
+ "workflow-multibranch",
+ "ws-cleanup"
];
//
@@ -27,50 +37,84 @@ exports.recommendedPlugins = [
//
exports.availablePlugins = [
{
- "category": "General",
- "description": "(a collection of things I cannot think of a better name for)",
+ "category":"Organization and Administration",
"plugins": [
- { "name": "external-monitor-job" },
- { "name": "translation" }
+ // { "name": "dashboard-view" },
+ { "name": "build-monitor-plugin" },
+ { "name": "cloudbees-folder" },
+ { "name": "antisamy-markup-formatter" }
]
},
{
- "category":"Organization and Administration",
+ "category":"Build Features",
+ "description":"Add general purpose features to your jobs",
"plugins": [
- { "name": "antisamy-markup-formatter" }
+ { "name": "ansicolor" },
+ // { "name": "build-name-setter" },
+ { "name": "build-timeout" },
+ { "name": "config-file-provider" },
+ { "name": "credentials-binding" },
+ { "name": "rebuild" },
+ { "name": "ssh-agent" },
+ // { "name": "throttle-concurrents" },
+ { "name": "timestamper" }
+ // { "name": "ws-cleanup" }
]
},
{
"category":"Build Tools",
"plugins": [
{ "name": "ant" },
- { "name": "maven-plugin" }
+ { "name": "gradle" },
+ { "name": "msbuild" },
+ { "name": "nodejs" }
]
},
{
"category":"Build Analysis and Reporting",
"plugins": [
- { "name": "javadoc" },
- { "name": "junit" }
+ // { "name": "checkstyle" },
+ // { "name": "cobertura" },
+ { "name": "htmlpublisher" },
+ { "name": "junit" },
+ // { "name": "sonar" },
+ // { "name": "warnings" },
+ { "name": "xunit" }
]
},
{
"category":"Pipelines and Continuous Delivery",
"plugins": [
{ "name": "workflow-aggregator" },
+ { "name": "workflow-multibranch" },
{ "name": "github-branch-source" },
- { "name": "workflow-multibranch" }
+ { "name": "pipeline-stage-view" },
+ { "name": "build-pipeline-plugin" },
+ // { "name": "conditional-buildstep" },
+ // { "name": "jenkins-multijob-plugin" },
+ { "name": "parameterized-trigger" },
+ { "name": "copyartifact" }
]
},
{
- "category":"SCM",
+ "category":"Source Code Management",
"plugins": [
+ { "name": "bitbucket" },
+ { "name": "clearcase" },
{ "name": "cvs" },
- { "name": "subversion" }
+ { "name": "git" },
+ { "name": "git-parameter" },
+ { "name": "github" },
+ { "name": "gitlab-plugin" },
+ { "name": "p4" },
+ { "name": "repo" },
+ { "name": "subversion" },
+ { "name": "teamconcert" },
+ { "name": "tfs" }
]
},
{
- "category":"Distributed Builds and Containers",
+ "category":"Distributed Builds",
"plugins": [
{ "name": "matrix-project" },
{ "name": "ssh-slaves" },
@@ -80,18 +124,22 @@ exports.availablePlugins = [
{
"category":"User Management and Security",
"plugins": [
- { "name": "credentials" },
- { "name": "ldap" },
- { "name": "matrix-auth" },
+ // { "name": "matrix-auth" },
{ "name": "pam-auth" },
- { "name": "script-security" },
- { "name": "ssh-credentials" }
+ { "name": "ldap" },
+ // { "name": "role-strategy" },
+ { "name": "active-directory" }
]
},
{
"category":"Notifications and Publishing",
"plugins": [
- { "name": "mailer" }
+ { "name": "email-ext" },
+ { "name": "emailext-template" },
+ { "name": "mailer" },
+ { "name": "publish-over-ssh" },
+ { "name": "slack" },
+ { "name": "ssh" }
]
}
];
\ No newline at end of file
diff --git a/war/src/main/js/api/securityConfig.js b/war/src/main/js/api/securityConfig.js
new file mode 100644
index 0000000000..5b7793e78a
--- /dev/null
+++ b/war/src/main/js/api/securityConfig.js
@@ -0,0 +1,33 @@
+/**
+ * Provides a wrapper to interact with the security configuration
+ */
+
+var jenkins = require('../util/jenkins');
+var jquery = require('jquery-detached');
+var wh = require('window-handle');
+
+/**
+ * Calls a stapler post method to save the first user settings
+ */
+exports.saveFirstUser = function($form, success, error) {
+ jenkins.staplerPost(
+ '/securityRealm/createFirstAccount',
+ $form,
+ success, {
+ dataType: 'html',
+ error: error
+ });
+};
+
+/**
+ * Calls a stapler post method to save the first user settings
+ */
+exports.saveProxy = function($form, success, error) {
+ jenkins.staplerPost(
+ '/pluginManager/proxyConfigure',
+ $form,
+ success, {
+ dataType: 'html',
+ error: error
+ });
+};
diff --git a/war/src/main/js/pluginSetupWizard.js b/war/src/main/js/pluginSetupWizard.js
index 38e577c3c8..a5d3af7222 100644
--- a/war/src/main/js/pluginSetupWizard.js
+++ b/war/src/main/js/pluginSetupWizard.js
@@ -6,5 +6,10 @@ var pluginSetupWizard = require('./pluginSetupWizardGui');
// This entry point for the bundle only bootstraps the main module in a browser
$(function() {
- pluginSetupWizard.init();
+ $('.plugin-setup-wizard-container').each(function() {
+ var $container = $(this);
+ if($container.children().length === 0) { // this may get double-initialized
+ pluginSetupWizard.init($container);
+ }
+ });
});
diff --git a/war/src/main/js/pluginSetupWizardGui.js b/war/src/main/js/pluginSetupWizardGui.js
index 637c6cc5aa..c50ad3467d 100644
--- a/war/src/main/js/pluginSetupWizardGui.js
+++ b/war/src/main/js/pluginSetupWizardGui.js
@@ -7,9 +7,13 @@ var jquery = require('jquery-detached');
var bootstrap = require('bootstrap-detached');
var jenkins = require('./util/jenkins');
var pluginManager = require('./api/pluginManager');
+var securityConfig = require('./api/securityConfig');
+var wh = require('window-handle');
+
+window.zq = jquery.getJQuery();
// Setup the dialog, exported
-var createPluginSetupWizard = function() {
+var createPluginSetupWizard = function(appendTarget) {
// call getJQuery / getBootstrap within the main function so it will work with tests -- if getJQuery etc is called in the main
var $ = jquery.getJQuery();
var $bs = bootstrap.getBootstrap();
@@ -109,6 +113,9 @@ var createPluginSetupWizard = function() {
var progressPanel = require('./templates/progressPanel.hbs');
var pluginSelectionPanel = require('./templates/pluginSelectionPanel.hbs');
var successPanel = require('./templates/successPanel.hbs');
+ var setupCompletePanel = require('./templates/setupCompletePanel.hbs');
+ var proxyConfigPanel = require('./templates/proxyConfigPanel.hbs');
+ var firstUserPanel = require('./templates/firstUserPanel.hbs');
var offlinePanel = require('./templates/offlinePanel.hbs');
var pluginSetupWizard = require('./templates/pluginSetupWizard.hbs');
var incompleteInstallationPanel = require('./templates/incompleteInstallationPanel.hbs');
@@ -133,16 +140,16 @@ var createPluginSetupWizard = function() {
// state variables for plugin data, selected plugins, etc.:
var pluginList = pluginManager.plugins();
- var allPluginNames = pluginManager.pluginNames();
- var selectedPluginNames = pluginManager.recommendedPluginNames();
+ var allPluginNames = pluginManager.pluginNames();
+ var selectedPluginNames = pluginManager.recommendedPluginNames();
var visibleDependencies = {};
- var categories = [];
- var availablePlugins = {};
- var categorizedPlugins = {};
+ var categories = [];
+ var availablePlugins = {};
+ var categorizedPlugins = {};
// Instantiate the wizard panel
var $wizard = $(pluginSetupWizard());
- $wizard.appendTo('body');
+ $wizard.appendTo(appendTarget);
var $container = $wizard.find('.modal-content');
var currentPanel;
@@ -165,10 +172,10 @@ var createPluginSetupWizard = function() {
var translations = {};
var decorations = [
- function() {
+ function() {
// any decorations after DOM replacement go here
- }
- ];
+ }
+ ];
// call this to set the panel in the app, this performs some additional things & adds common transitions
var setPanel = function(panel, data, oncomplete) {
@@ -177,7 +184,7 @@ var createPluginSetupWizard = function() {
decorations[i]($base);
}
};
- var html = panel($.extend({translations: translations}, data));
+ var html = panel($.extend({translations: translations, baseUrl: jenkins.baseUrl}, data));
if(panel === currentPanel) { // just replace id-marked elements
var $upd = $(html);
$upd.find('*[id]').each(function() {
@@ -200,6 +207,12 @@ var createPluginSetupWizard = function() {
currentPanel = panel;
$container.append(html);
decorate($container);
+
+ var $modalHeader = $container.find('.modal-header');
+ if($modalHeader.length > 0) {
+ $modalHeader.prepend(
+ '× ');
+ }
if(oncomplete) {
oncomplete();
@@ -255,25 +268,25 @@ var createPluginSetupWizard = function() {
// Initializes the set of installing plugins with pending statuses
var initInstallingPluginList = function() {
installingPlugins = [];
- installingPlugins.names = [];
+ installingPlugins.names = [];
for (var i = 0; i < selectedPluginNames.length; i++) {
- var pluginName = selectedPluginNames[i];
- var p = availablePlugins[ pluginName];
+ var pluginName = selectedPluginNames[i];
+ var p = availablePlugins[ pluginName];
if (p) {
var plug = $.extend({
installStatus : 'pending'
}, p);
installingPlugins.push(plug);
installingPlugins[plug.name] = plug;
- installingPlugins.names.push(pluginName);
+ installingPlugins.names.push(pluginName);
}
}
};
// call this to go install the selected set of plugins
- var installPlugins = function(plugins) {
+ var installPlugins = function(plugins) {
pluginManager.installPlugins(plugins, handleGenericError(function() {
- showInstallProgress();
+ showInstallProgress();
}));
setPanel(progressPanel, { installingPlugins : installingPlugins });
@@ -306,13 +319,22 @@ var createPluginSetupWizard = function() {
};
// Define actions
- var showInstallProgress = function() {
- initInstallingPluginList();
+ var showInstallProgress = function(state) {
+ if(state) {
+ if(/CREATE_ADMIN_USER/.test(state)) {
+ setupFirstUser();
+ return;
+ }
+ }
+
+ initInstallingPluginList();
setPanel(progressPanel, { installingPlugins : installingPlugins });
// call to the installStatus, update progress bar & plugin details; transition on complete
var updateStatus = function() {
- pluginManager.installStatus(handleGenericError(function(jobs) {
+ pluginManager.installStatus(handleGenericError(function(data) {
+ var jobs = data.jobs;
+
var i, j;
var complete = 0;
var total = 0;
@@ -402,10 +424,7 @@ var createPluginSetupWizard = function() {
else {
// mark complete
$('.progress-bar').css({width: '100%'});
- setPanel(successPanel, {
- installingPlugins : installingPlugins,
- restartRequired: restartRequired
- });
+ setupFirstUser();
}
}));
};
@@ -416,7 +435,7 @@ var createPluginSetupWizard = function() {
// Called to complete the installation
var finishInstallation = function() {
- jenkins.goTo('/');
+ closeInstaller();
};
// load the plugin data, callback
@@ -514,28 +533,28 @@ var createPluginSetupWizard = function() {
});
// walk the elements and search for the text
- var walk = function(elements, element, text, xform) {
- var i, child, n= element.childNodes.length;
- for (i = 0; i 200) {
+ // Nothing we can really do here
+ setPanel(errorPanel, { errorMessage: data.statusText });
+ return;
+ }
+ // we get 200 OK
+ var $page = $(data);
+ var $errors = $page.find('.error');
+ if($errors.length > 0) {
+ var $main = $page.find('#main-panel').detach();
+ if($main.length > 0) {
+ data = data.replace(/body([^>]*)[>](.|[\r\n])+[<][/]body/,'body$1>'+$main.html()+'