Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
JENKINS-33770 - fix issue directly submitting firstUser page
  • Loading branch information
kzantow committed Mar 28, 2016
1 parent 2968285 commit 3cf8de0
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 79 deletions.
49 changes: 17 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,25 @@ 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}
*/
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 +287,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
90 changes: 47 additions & 43 deletions core/src/main/java/jenkins/install/SetupWizard.java
@@ -1,12 +1,9 @@
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;
import java.util.regex.Pattern;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
Expand All @@ -17,7 +14,11 @@
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;
Expand All @@ -31,32 +32,17 @@
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) // use by Jelly
@Restricted(NoExternalUse.class)
public class SetupWizard {
/**
* The security token parameter name
Expand Down Expand Up @@ -134,6 +120,42 @@ public SetupWizard(Jenkins j) throws IOException, InterruptedException {
throw new AssertionError(e);
}
}

/**
* 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", j.getRootUrl());
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
Expand Down Expand Up @@ -163,40 +185,22 @@ public HttpResponse doCompleteInstall() throws IOException, ServletException {
* This filter will validate that the security token is provided
*/
private final Filter FORCE_SETUP_WIZARD_FILTER = new Filter() {
private Pattern RESOURCE_PATTERN = Pattern.compile(".*[.](css|ttf|gif|woff|eot|png|js)");
private Pattern ALLOWED_PATHS = Pattern.compile(
"\\Q/login\\E"
+ "|\\Q/loginError\\E"
+ "|\\Q/securityRealm/" + new HudsonPrivateSecurityRealm(true, false, null).getAuthenticationGatewayUrl() + "\\E");
@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
// Force root requests to the setup wizard
if (request instanceof HttpServletRequest) {
HttpServletRequest req = (HttpServletRequest)request;
// Allow js & css requests through
if (!RESOURCE_PATTERN.matcher(req.getRequestURI()).matches()) { // allow resources
boolean isAdmin = jenkins.getACL().hasPermission(Jenkins.ADMINISTER);
final String adjustedPath = req.getRequestURI().substring(req.getContextPath().length());
final String destination;
// if the user hasn't authenticated, don't allow anything other than the login submit and error page
if (!isAdmin && !ALLOWED_PATHS.matcher(adjustedPath).matches()) {
destination = req.getContextPath() + "/login";
} else if (isAdmin && "/".equals(adjustedPath)) {
destination = req.getContextPath() + "/setupWizard/";
} else {
destination = req.getRequestURI();
}
if((req.getContextPath() + "/").equals(req.getRequestURI())) {
chain.doFilter(new HttpServletRequestWrapper(req) {
public String getRequestURI() {
return destination;
return getContextPath() + "/setupWizard/";
}
}, response);
return;
}
// fall through to handling the request normally
}
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
2 changes: 1 addition & 1 deletion war/src/main/js/api/securityConfig.js
Expand Up @@ -9,7 +9,7 @@ var jenkins = require('../util/jenkins');
*/
exports.saveFirstUser = function($form, success, error) {
jenkins.staplerPost(
'/securityRealm/createFirstAccount',
'/setupWizard/createAdminUser',
$form,
success, {
dataType: 'html',
Expand Down
2 changes: 1 addition & 1 deletion war/src/main/js/templates/firstUserPanel.hbs
Expand Up @@ -3,7 +3,7 @@
</div>
<div class="modal-body">
<div class="jumbotron welcome-panel security-panel">
<iframe src="{{baseUrl}}/securityRealm/setupWizardFirstUser"></iframe>
<iframe src="{{baseUrl}}/setupWizard/setupWizardFirstUser"></iframe>
</div>
</div>
<div class="modal-footer">
Expand Down

0 comments on commit 3cf8de0

Please sign in to comment.