Skip to content

Commit

Permalink
Merge remote-tracking branch 'primary/2.0' into JENKINS-33800-initial…
Browse files Browse the repository at this point in the history
…-password-file-not-found
  • Loading branch information
kzantow committed Mar 31, 2016
2 parents 1d4ca77 + ef08dc1 commit 28302ea
Show file tree
Hide file tree
Showing 21 changed files with 581 additions and 633 deletions.
50 changes: 18 additions & 32 deletions core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java
Expand Up @@ -29,8 +29,6 @@
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;
Expand Down Expand Up @@ -261,16 +259,26 @@ private void loginAndTakeBack(StaplerRequest req, StaplerResponse rsp, User u) t
}

/**
* Creates an user account. Used by admins.
* Creates a user account. Used by admins.
*
* This version behaves differently from {@link #doCreateAccount(StaplerRequest, StaplerResponse)} in that
* this is someone creating another user.
*/
public void doCreateAccountByAdmin(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
createAccountByAdmin(req, rsp, "addUser.jelly", "."); // send the user back to the listing page on success
}

/**
* Creates a user account. Requires {@link Jenkins#ADMINISTER}
*/
@Restricted(NoExternalUse.class)
public User createAccountByAdmin(StaplerRequest req, StaplerResponse rsp, String addUserView, String successView) throws IOException, ServletException {
checkPermission(Jenkins.ADMINISTER);
if(createAccount(req, rsp, false, "addUser.jelly")!=null) {
rsp.sendRedirect("."); // send the user back to the listing page
User u = createAccount(req, rsp, false, addUserView);
if(u != null) {
rsp.sendRedirect(successView);
}
return u;
}

/**
Expand All @@ -280,36 +288,14 @@ public void doCreateAccountByAdmin(StaplerRequest req, StaplerResponse rsp) thro
* 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 {
boolean inSetup = !Jenkins.getInstance().getInstallState().isSetupComplete();
if(!inSetup && hasSomeUser()) {
if(hasSomeUser()) {
rsp.sendError(SC_UNAUTHORIZED,"First user was already created");
return;
}

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
}
User u = createAccount(req, rsp, false, "firstUser.jelly");
if (u!=null) {
tryToMakeAdmin(u);
loginAndTakeBack(req, rsp, u);
}
}

Expand Down
10 changes: 10 additions & 0 deletions core/src/main/java/jenkins/install/InstallUtil.java
Expand Up @@ -70,6 +70,16 @@ public class InstallUtil {
* @return The type of "startup" currently under way in Jenkins.
*/
public static InstallState getInstallState() {
// Support a simple state override. Useful for testing.
String stateOverride = System.getenv("jenkins.install.state");
if (stateOverride != null) {
try {
return InstallState.valueOf(stateOverride.toUpperCase());
} catch (RuntimeException e) {
throw new IllegalStateException("Unknown install state override specified on the commandline: '" + stateOverride + "'.");
}
}

// install wizard will always run if environment specified
if (!Boolean.getBoolean("jenkins.install.runSetupWizard")) {
if (Functions.getIsUnitTest()) {
Expand Down
70 changes: 45 additions & 25 deletions core/src/main/java/jenkins/install/SetupWizard.java
@@ -1,9 +1,7 @@
package jenkins.install;

import java.io.IOException;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Logger;

Expand All @@ -16,44 +14,35 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import hudson.util.VersionNumber;
import org.acegisecurity.Authentication;
import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;

import hudson.BulkChange;
import hudson.FilePath;
import hudson.model.User;
import hudson.security.FullControlOnceLoggedInAuthorizationStrategy;
import hudson.security.HudsonPrivateSecurityRealm;
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;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.HttpResponse;

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 java.io.IOException;
import java.util.Locale;
import java.util.UUID;
import java.util.logging.Logger;

/**
* A Jenkins instance used during first-run to provide a limited set of services while
* initial installation is in progress
*
* @since 2.0
*/
@Restricted(NoExternalUse.class)
public class SetupWizard {
/**
* The security token parameter name
Expand Down Expand Up @@ -145,11 +134,46 @@ public boolean useGeneratedPassword() {
}
return false;
}

/**
* Called during the initial setup to create an admin user
*/
public void doCreateAdminUser(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
Jenkins j = Jenkins.getInstance();
j.checkPermission(Jenkins.ADMINISTER);

// This will be set up by default. if not, something changed, ok to fail
HudsonPrivateSecurityRealm securityRealm = (HudsonPrivateSecurityRealm)j.getSecurityRealm();

User admin = securityRealm.getUser(SetupWizard.initialSetupAdminUserName);
try {
if(admin != null) {
admin.delete(); // assume the new user may well be 'admin'
}

User u = securityRealm.createAccountByAdmin(req, rsp, "/jenkins/install/SetupWizard/setupWizardFirstUser.jelly", req.getContextPath() + "/");
if (u != null) {
if(admin != null) {
admin = null;
}

j.setInstallState(InstallState.CREATE_ADMIN_USER.getNextState());

// ... and then login
Authentication a = new UsernamePasswordAuthenticationToken(u.getId(),req.getParameter("password1"));
a = securityRealm.getSecurityComponents().manager.authenticate(a);
SecurityContextHolder.getContext().setAuthentication(a);
}
} finally {
if(admin != null) {
admin.save(); // recreate this initial user if something failed
}
}
}

/**
* Gets the file used to store the initial admin password
*/
@Restricted(NoExternalUse.class) // use by Jelly
public FilePath getInitialAdminPasswordFile() {
return jenkins.getRootPath().child("secrets/initialAdminPassword");
}
Expand Down Expand Up @@ -181,13 +205,9 @@ 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
// Force root requests to the setup wizard
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() {
Expand Down
63 changes: 35 additions & 28 deletions core/src/main/resources/hudson/model/View/newJob.jelly
Expand Up @@ -22,45 +22,52 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->

<!--
"New Project" page.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:s="/lib/form">
<j:getStatic var="permission" className="hudson.model.Item" field="CREATE"/>

<l:layout norefresh="true" cssclass="add-item main-panel-only" permission="${permission}" title="${%NewJob(it.newPronoun)}">
<l:layout norefresh="true" cssclass="main-panel-only" permission="${permission}" title="${%NewJob(it.newPronoun)}">

<l:js src="jsbundles/add-item.js" />
<l:css src="jsbundles/add-item.css" />

<l:main-panel>
<div id="create-item-panel" style="display: none;">
<s:form method="post" action="createItem" name="createItem">
<div id="add-item-panel" style="display: none;">
<form method="post" action="createItem" name="createItem" id="createItem">
<div class="header">
<div class="add-item-name">
<label for="name">${%ItemName.label}</label>
<input name="name" id="name" type="text" />
<div class="input-help">&#187; ${%ItemName.help}</div>
<div id="itemname-required" class="input-validation-message input-message-disabled">&#187; ${%ItemName.validation.required}</div>
<div id="itemname-invalid" class="input-validation-message input-message-disabled"></div>
<div id="itemtype-required" class="input-validation-message input-message-disabled">&#187; ${%ItemType.validation.required}</div>
</div>
</div>

<div class="categories flat" />

<j:if test="${!empty(app.itemMap)}">
<s:block>
<input type="radio" id="copy" name="mode" value="copy" onchange="updateOk(this.form)" onclick="updateOk(this.form)" />
<label class="attach-previous"><b>${attrs.copyTitle}</b></label>
</s:block>
<s:entry>
${%Copy from}
<j:set var="descriptor" value="${it.descriptor}" />
<s:textbox id="from" name="from" field="copyNewItemFrom" style="width:40em" onfocus="$('copy').click()" />
</s:entry>
</j:if>
<j:if test="${!empty(app.itemMap)}">
<div class="item-copy">
<p class="description">${%CopyOption.description}:</p>
<div class="add-item-copy">
<input type="radio" name="mode" value="copy" />
<div class="icon">
<img src="${resURL}/images/48x48/copy.png" />
</div>
<label>${%CopyOption.label}</label>
<j:set var="descriptor" value="${it.descriptor}" />
<s:textbox id="from" name="from" placeholder="${%CopyOption.placeholder}" field="copyNewItemFrom"/>
</div>
</div>
</j:if>

<s:bottomButtonBar>
<!--
when there's only one radio above, form.elements['mode]' won't return an array, which makes the script complex.
So always force non-empty array
-->
<input type="radio" name="mode" value="dummy1" style="display:none" />
<input type="radio" name="mode" value="dummy2" style="display:none" />
<input type="submit" name="Submit" value="OK" id="ok" style="margin-left:5em" />
</s:bottomButtonBar>

</s:form>
<div class="footer">
<div class="btn-decorator">
<button type="submit" id="ok-button">OK</button>
</div>
</div>
</form>
</div>

</l:main-panel>
Expand Down
8 changes: 8 additions & 0 deletions core/src/main/resources/hudson/model/View/newJob.properties
@@ -1,3 +1,11 @@
NewJob=New {0}
JobName={0} name
ItemName.help=Required field
ItemName.label=Enter an item name
ItemName.validation.required=This field cannot be empty. Please enter a valid name and press OK button.
ItemType.validation.required=Please select an item type
CopyOption.placeholder=Type to autocomplete
CopyOption.description=if you want to create a new item from other existing, you can use this option
CopyOption.label=Copy from
CopyExisting=Copy existing {0}

Expand Up @@ -29,7 +29,6 @@
<label class="control-label" for="security-token">${%Administrator password}</label>
<input name="j_username" value="${j.setupWizard.initialSetupAdminUserName}" type="hidden"/>
<input id="security-token" class="form-control" type="password" name="j_password"/>
<link rel="stylesheet" href="${j.installWizardPath}.css" type="text/css" />
</div>

</div>
Expand Down
Expand Up @@ -43,8 +43,8 @@
padding: 1px 8px;
}
</style>
<form action="${rootURL}/securityRealm/${action}" method="post">
<local:_entryForm host="${app.securityRealm}" title="${%Create First Admin User}" action="createFirstAccount" captcha="${false}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
<form action="${rootURL}/setupWizard/createAdminUser" method="post">
<local:_entryForm it="${app.securityRealm}" host="${app.securityRealm}" title="${%Create First Admin User}" captcha="${false}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
<script>
$('username').focus();
</script>
Expand Down

0 comments on commit 28302ea

Please sign in to comment.