File D:\maven-qcr-plugin-testsunitaires\qcr4java\jenkins-master\target\site\qc\core\src\main\java\hudson\security\HudsonPrivateSecurityRealm.java.html

1 /*
2 * The MIT License
3 *
4 * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi, David Calavera, Seiji Sogabe
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24 package hudson.security;
25
26 import com.thoughtworks.xstream.converters.UnmarshallingContext;
27 import hudson.Extension;
28 import hudson.Util;
29 import hudson.diagnosis.OldDataMonitor;
30 import hudson.model.Descriptor;
31 import jenkins.model.Jenkins;
32 import hudson.model.ManagementLink;
33 import hudson.model.ModelObject;
34 import hudson.model.User;
35 import hudson.model.UserProperty;
36 import hudson.model.UserPropertyDescriptor;
37 import hudson.security.FederatedLoginService.FederatedIdentity;
38 import hudson.security.captcha.CaptchaSupport;
39 import hudson.util.PluginServletFilter;
40 import hudson.util.Protector;
41 import hudson.util.Scrambler;
42 import hudson.util.XStream2;
43 import net.sf.json.JSONObject;
44 import org.acegisecurity.Authentication;
45 import org.acegisecurity.AuthenticationException;
46 import org.acegisecurity.BadCredentialsException;
47 import org.acegisecurity.GrantedAuthority;
48 import org.acegisecurity.context.SecurityContextHolder;
49 import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
50 import org.acegisecurity.providers.encoding.PasswordEncoder;
51 import org.acegisecurity.providers.encoding.ShaPasswordEncoder;
52 import org.acegisecurity.userdetails.UserDetails;
53 import org.acegisecurity.userdetails.UsernameNotFoundException;
54 import org.apache.tools.ant.taskdefs.email.Mailer;
55 import org.kohsuke.stapler.DataBoundConstructor;
56 import org.kohsuke.stapler.ForwardToView;
57 import org.kohsuke.stapler.HttpResponse;
58 import org.kohsuke.stapler.HttpResponses;
59 import org.kohsuke.stapler.Stapler;
60 import org.kohsuke.stapler.StaplerRequest;
61 import org.kohsuke.stapler.StaplerResponse;
62 import org.mindrot.jbcrypt.BCrypt;
63 import org.springframework.dao.DataAccessException;
64
65 import javax.servlet.Filter;
66 import javax.servlet.FilterChain;
67 import javax.servlet.FilterConfig;
68 import javax.servlet.ServletException;
69 import javax.servlet.ServletRequest;
70 import javax.servlet.ServletResponse;
71 import javax.servlet.http.HttpServletRequest;
72 import javax.servlet.http.HttpServletResponse;
73 import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
74 import java.io.IOException;
75 import java.lang.reflect.Constructor;
76 import java.lang.reflect.InvocationTargetException;
77 import java.security.SecureRandom;
78 import java.util.ArrayList;
79 import java.util.Collections;
80 import java.util.List;
81 import java.util.logging.Level;
82 import java.util.logging.Logger;
83
84 /**
85 * {@link SecurityRealm} that performs authentication by looking up {@link User}.
86 *
87 * <p>
88 * Implements {@link AccessControlled} to satisfy view rendering, but in reality the access control
89 * is done against the {@link jenkins.model.Jenkins} object.
90 *
91 * @author Kohsuke Kawaguchi
92 */
93 public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRealm implements ModelObject, AccessControlled {
94 /**
95 * If true, sign up is not allowed.
96 * <p>
97 * This is a negative switch so that the default value 'false' remains compatible with older installations.
98 */
99 private final boolean disableSignup;
100
101 /**
102 * If true, captcha will be enabled.
103 */
104 private final boolean enableCaptcha;
105
106 @Deprecated
107 public HudsonPrivateSecurityRealm(boolean allowsSignup) {
108 this(allowsSignup, false, (CaptchaSupport) null);
109 }
110
111 @DataBoundConstructor
112 public HudsonPrivateSecurityRealm(boolean allowsSignup, boolean enableCaptcha, CaptchaSupport captchaSupport) {
113 this.disableSignup = !allowsSignup;
114 this.enableCaptcha = enableCaptcha;
115 setCaptchaSupport(captchaSupport);
116 if(!allowsSignup && !hasSomeUser()) {
117 // if Hudson is newly set up with the security realm and there's no user account created yet,
118 // insert a filter that asks the user to create one
119 try {
120 PluginServletFilter.addFilter(CREATE_FIRST_USER_FILTER);
121 } catch (ServletException e) {
122 throw new AssertionError(e); // never happen because our Filter.init is no-op
123 }
124 }
125 }
126
127 @Override
128 public boolean allowsSignup() {
129 return !disableSignup;
130 }
131
132 /**
133 * Checks if captcha is enabled on user signup.
134 *
135 * @return true if captcha is enabled on signup.
136 */
137 public boolean isEnableCaptcha() {
138 return enableCaptcha;
139 }
140
141 /**
142 * Computes if this Hudson has some user accounts configured.
143 *
144 * <p>
145 * This is used to check for the initial
146 */
147 private static boolean hasSomeUser() {
148 for (User u : User.getAll())
149 if(u.getProperty(Details.class)!=null)
150 return true;
151 return false;
152 }
153
154 /**
155 * This implementation doesn't support groups.
156 */
157 @Override
158 public GroupDetails loadGroupByGroupname(String groupname) throws UsernameNotFoundException, DataAccessException {
159 throw new UsernameNotFoundException(groupname);
160 }
161
162 @Override
163 public Details loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
164 User u = User.get(username,false);
165 Details p = u!=null ? u.getProperty(Details.class) : null;
166 if(p==null)
167 throw new UsernameNotFoundException("Password is not set: "+username);
168 if(p.getUser()==null)
169 throw new AssertionError();
170 return p;
171 }
172
173 @Override
174 protected Details authenticate(String username, String password) throws AuthenticationException {
175 Details u = loadUserByUsername(username);
176 if (!u.isPasswordCorrect(password))
177 throw new BadCredentialsException("Failed to login as "+username);
178 return u;
179 }
180
181 /**
182 * Show the sign up page with the data from the identity.
183 */
184 @Override
185 public HttpResponse commenceSignup(final FederatedIdentity identity) {
186 // store the identity in the session so that we can use this later
187 Stapler.getCurrentRequest().getSession().setAttribute(FEDERATED_IDENTITY_SESSION_KEY,identity);
188 return new ForwardToView(this,"signupWithFederatedIdentity.jelly") {
189 @Override
190 public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node) throws IOException, ServletException {
191 SignupInfo si = new SignupInfo(identity);
192 si.errorMessage = Messages.HudsonPrivateSecurityRealm_WouldYouLikeToSignUp(identity.getPronoun(),identity.getIdentifier());
193 req.setAttribute("data", si);
194 super.generateResponse(req, rsp, node);
195 }
196 };
197 }
198
199 /**
200 * Creates an account and associates that with the given identity. Used in conjunction
201 * with {@link #commenceSignup(FederatedIdentity)}.
202 */
203 public User doCreateAccountWithFederatedIdentity(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
204 User u = _doCreateAccount(req,rsp,"signupWithFederatedIdentity.jelly");
205 if (u!=null)
206 ((FederatedIdentity)req.getSession().getAttribute(FEDERATED_IDENTITY_SESSION_KEY)).addTo(u);
207 return u;
208 }
209
210 private static final String FEDERATED_IDENTITY_SESSION_KEY = HudsonPrivateSecurityRealm.class.getName()+".federatedIdentity";
211
212 /**
213 * Creates an user account. Used for self-registration.
214 */
215 public User doCreateAccount(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
216 return _doCreateAccount(req, rsp, "signup.jelly");
217 }
218
219 private User _doCreateAccount(StaplerRequest req, StaplerResponse rsp, String formView) throws ServletException, IOException {
220 if(!allowsSignup())
221 throw HttpResponses.error(SC_UNAUTHORIZED,new Exception("User sign up is prohibited"));
222
223 boolean firstUser = !hasSomeUser();
224 User u = createAccount(req, rsp, enableCaptcha, formView);
225 if(u!=null) {
226 if(firstUser)
227 tryToMakeAdmin(u); // the first user should be admin, or else there's a risk of lock out
228 loginAndTakeBack(req, rsp, u);
229 }
230 return u;
231 }
232
233 /**
234 * Lets the current user silently login as the given user and report back accordingly.
235 */
236 private void loginAndTakeBack(StaplerRequest req, StaplerResponse rsp, User u) throws ServletException, IOException {
237 // ... and let him login
238 Authentication a = new UsernamePasswordAuthenticationToken(u.getId(),req.getParameter("password1"));
239 a = this.getSecurityComponents().manager.authenticate(a);
240 SecurityContextHolder.getContext().setAuthentication(a);
241
242 // then back to top
243 req.getView(this,"success.jelly").forward(req,rsp);
244 }
245
246 /**
247 * Creates an user account. Used by admins.
248 *
249 * This version behaves differently from {@link #doCreateAccount(StaplerRequest, StaplerResponse)} in that
250 * this is someone creating another user.
251 */
252 public void doCreateAccountByAdmin(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
253 checkPermission(Jenkins.ADMINISTER);
254 if(createAccount(req, rsp, false, "addUser.jelly")!=null) {
255 rsp.sendRedirect("."); // send the user back to the listing page
256 }
257 }
258
259 /**
260 * Creates a first admin user account.
261 *
262 * <p>
263 * This can be run by anyone, but only to create the very first user account.
264 */
265 public void doCreateFirstAccount(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
266 if(hasSomeUser()) {
267 rsp.sendError(SC_UNAUTHORIZED,"First user was already created");
268 return;
269 }
270 User u = createAccount(req, rsp, false, "firstUser.jelly");
271 if (u!=null) {
272 tryToMakeAdmin(u);
273 loginAndTakeBack(req, rsp, u);
274 }
275 }
276
277 /**
278 * Try to make this user a super-user
279 */
280 private void tryToMakeAdmin(User u) {
281 AuthorizationStrategy as = Jenkins.getInstance().getAuthorizationStrategy();
282 if (as instanceof GlobalMatrixAuthorizationStrategy) {
283 GlobalMatrixAuthorizationStrategy ma = (GlobalMatrixAuthorizationStrategy) as;
284 ma.add(Jenkins.ADMINISTER,u.getId());
285 }
286 }
287
288 /**
289 * @return
290 * null if failed. The browser is already redirected to retry by the time this method returns.
291 * a valid {@link User} object if the user creation was successful.
292 */
293 private User createAccount(StaplerRequest req, StaplerResponse rsp, boolean selfRegistration, String formView) throws ServletException, IOException {
294 // form field validation
295 // this pattern needs to be generalized and moved to stapler
296 SignupInfo si = new SignupInfo(req);
297
298 if(selfRegistration && !validateCaptcha(si.captcha))
299 si.errorMessage = "Text didn't match the word shown in the image";
300
301 if(si.password1 != null && !si.password1.equals(si.password2))
302 si.errorMessage = "Password didn't match";
303
304 if(!(si.password1 != null && si.password1.length() != 0))
305 si.errorMessage = "Password is required";
306
307 if(si.username==null || si.username.length()==0)
308 si.errorMessage = "User name is required";
309 else {
310 User user = User.get(si.username, false);
311 if (null != user)
312 si.errorMessage = "User name is already taken";
313 }
314
315 if(si.fullname==null || si.fullname.length()==0)
316 si.fullname = si.username;
317
318 if(si.email==null || !si.email.contains("@"))
319 si.errorMessage = "Invalid e-mail address";
320
321 if(si.errorMessage!=null) {
322 // failed. ask the user to try again.
323 req.setAttribute("data",si);
324 req.getView(this, formView).forward(req,rsp);
325 return null;
326 }
327
328 // register the user
329 User user = createAccount(si.username,si.password1);
330 user.setFullName(si.fullname);
331 try {
332 // legacy hack. mail support has moved out to a separate plugin
333 Class<?> up = Jenkins.getInstance().pluginManager.uberClassLoader.loadClass("hudson.tasks.Mailer$UserProperty");
334 Constructor<?> c = up.getDeclaredConstructor(String.class);
335 user.addProperty((UserProperty)c.newInstance(si.email));
336 } catch (RuntimeException e) {
337 throw e;
338 } catch (Exception e) {
339 LOGGER.log(Level.WARNING, "Failed to set the e-mail address",e);
340 }
341 user.save();
342 return user;
343 }
344
345 /**
346 * Creates a new user account by registering a password to the user.
347 */
348 public User createAccount(String userName, String password) throws IOException {
349 User user = User.get(userName);
350 user.addProperty(Details.fromPlainPassword(password));
351 return user;
352 }
353
354 /**
355 * This is used primarily when the object is listed in the breadcrumb, in the user management screen.
356 */
357 public String getDisplayName() {
358 return "User Database";
359 }
360
361 public ACL getACL() {
362 return Jenkins.getInstance().getACL();
363 }
364
365 public void checkPermission(Permission permission) {
366 Jenkins.getInstance().checkPermission(permission);
367 }
368
369 public boolean hasPermission(Permission permission) {
370 return Jenkins.getInstance().hasPermission(permission);
371 }
372
373
374 /**
375 * All users who can login to the system.
376 */
377 public List<User> getAllUsers() {
378 List<User> r = new ArrayList<User>();
379 for (User u : User.getAll()) {
380 if(u.getProperty(Details.class)!=null)
381 r.add(u);
382 }
383 Collections.sort(r);
384 return r;
385 }
386
387 /**
388 * This is to map users under the security realm URL.
389 * This in turn helps us set up the right navigation breadcrumb.
390 */
391 public User getUser(String id) {
392 return User.get(id);
393 }
394
395 // TODO
396 private static final GrantedAuthority[] TEST_AUTHORITY = {AUTHENTICATED_AUTHORITY};
397
398 public static final class SignupInfo {
399 public String username,password1,password2,fullname,email,captcha;
400
401 /**
402 * To display an error message, set it here.
403 */
404 public String errorMessage;
405
406 public SignupInfo() {
407 }
408
409 public SignupInfo(StaplerRequest req) {
410 req.bindParameters(this);
411 }
412
413 public SignupInfo(FederatedIdentity i) {
414 this.username = i.getNickname();
415 this.fullname = i.getFullName();
416 this.email = i.getEmailAddress();
417 }
418 }
419
420 /**
421 * {@link UserProperty} that provides the {@link UserDetails} view of the User object.
422 *
423 * <p>
424 * When a {@link User} object has this property on it, it means the user is configured
425 * for log-in.
426 *
427 * <p>
428 * When a {@link User} object is re-configured via the UI, the password
429 * is sent to the hidden input field by using {@link Protector}, so that
430 * the same password can be retained but without leaking information to the browser.
431 */
432 public static final class Details extends UserProperty implements InvalidatableUserDetails {
433 /**
434 * Hashed password.
435 */
436 private /*almost final*/ String passwordHash;
437
438 /**
439 * @deprecated Scrambled password.
440 * Field kept here to load old (pre 1.283) user records,
441 * but now marked transient so field is no longer saved.
442 */
443 private transient String password;
444
445 private Details(String passwordHash) {
446 this.passwordHash = passwordHash;
447 }
448
449 static Details fromHashedPassword(String hashed) {
450 return new Details(hashed);
451 }
452
453 static Details fromPlainPassword(String rawPassword) {
454 return new Details(PASSWORD_ENCODER.encodePassword(rawPassword,null));
455 }
456
457 public GrantedAuthority[] getAuthorities() {
458 // TODO
459 return TEST_AUTHORITY;
460 }
461
462 public String getPassword() {
463 return passwordHash;
464 }
465
466 public boolean isPasswordCorrect(String candidate) {
467 return PASSWORD_ENCODER.isPasswordValid(getPassword(),candidate,null);
468 }
469
470 public String getProtectedPassword() {
471 // put session Id in it to prevent a replay attack.
472 return Protector.protect(Stapler.getCurrentRequest().getSession().getId()+':'+getPassword());
473 }
474
475 public String getUsername() {
476 return user.getId();
477 }
478
479 /*package*/ User getUser() {
480 return user;
481 }
482
483 public boolean isAccountNonExpired() {
484 return true;
485 }
486
487 public boolean isAccountNonLocked() {
488 return true;
489 }
490
491 public boolean isCredentialsNonExpired() {
492 return true;
493 }
494
495 public boolean isEnabled() {
496 return true;
497 }
498
499 public boolean isInvalid() {
500 return user==null;
501 }
502
503 public static class ConverterImpl extends XStream2.PassthruConverter<Details> {
504 public ConverterImpl(XStream2 xstream) { super(xstream); }
505 @Override protected void callback(Details d, UnmarshallingContext context) {
506 // Convert to hashed password and report to monitor if we load old data
507 if (d.password!=null && d.passwordHash==null) {
508 d.passwordHash = PASSWORD_ENCODER.encodePassword(Scrambler.descramble(d.password),null);
509 OldDataMonitor.report(context, "1.283");
510 }
511 }
512 }
513
514 @Extension
515 public static final class DescriptorImpl extends UserPropertyDescriptor {
516 public String getDisplayName() {
517 // this feature is only when HudsonPrivateSecurityRealm is enabled
518 if(isEnabled())
519 return Messages.HudsonPrivateSecurityRealm_Details_DisplayName();
520 else
521 return null;
522 }
523
524 @Override
525 public Details newInstance(StaplerRequest req, JSONObject formData) throws FormException {
526 String pwd = Util.fixEmpty(req.getParameter("user.password"));
527 String pwd2= Util.fixEmpty(req.getParameter("user.password2"));
528
529 if(!Util.fixNull(pwd).equals(Util.fixNull(pwd2)))
530 throw new FormException("Please confirm the password by typing it twice","user.password2");
531
532 String data = Protector.unprotect(pwd);
533 if(data!=null) {
534 String prefix = Stapler.getCurrentRequest().getSession().getId() + ':';
535 if(data.startsWith(prefix))
536 return Details.fromHashedPassword(data.substring(prefix.length()));
537 }
538 return Details.fromPlainPassword(Util.fixNull(pwd));
539 }
540
541 @Override
542 public boolean isEnabled() {
543 return Jenkins.getInstance().getSecurityRealm() instanceof HudsonPrivateSecurityRealm;
544 }
545
546 public UserProperty newInstance(User user) {
547 return null;
548 }
549 }
550 }
551
552 /**
553 * Displays "manage users" link in the system config if {@link HudsonPrivateSecurityRealm}
554 * is in effect.
555 */
556 @Extension
557 public static final class ManageUserLinks extends ManagementLink {
558 public String getIconFileName() {
559 if(Jenkins.getInstance().getSecurityRealm() instanceof HudsonPrivateSecurityRealm)
560 return "user.png";
561 else
562 return null; // not applicable now
563 }
564
565 public String getUrlName() {
566 return "securityRealm/";
567 }
568
569 public String getDisplayName() {
570 return Messages.HudsonPrivateSecurityRealm_ManageUserLinks_DisplayName();
571 }
572
573 @Override
574 public String getDescription() {
575 return Messages.HudsonPrivateSecurityRealm_ManageUserLinks_Description();
576 }
577 }
578
579 /**
580 * {@link PasswordEncoder} based on SHA-256 and random salt generation.
581 *
582 * <p>
583 * The salt is prepended to the hashed password and returned. So the encoded password is of the form
584 * <tt>SALT ':' hash(PASSWORD,SALT)</tt>.
585 *
586 * <p>
587 * This abbreviates the need to store the salt separately, which in turn allows us to hide the salt handling
588 * in this little class. The rest of the Acegi thinks that we are not using salt.
589 */
590 /*package*/ static final PasswordEncoder CLASSIC = new PasswordEncoder() {
591 private final PasswordEncoder passwordEncoder = new ShaPasswordEncoder(256);
592
593 public String encodePassword(String rawPass, Object _) throws DataAccessException {
594 return hash(rawPass);
595 }
596
597 public boolean isPasswordValid(String encPass, String rawPass, Object _) throws DataAccessException {
598 // pull out the sale from the encoded password
599 int i = encPass.indexOf(':');
600 if(i<0) return false;
601 String salt = encPass.substring(0,i);
602 return encPass.substring(i+1).equals(passwordEncoder.encodePassword(rawPass,salt));
603 }
604
605 /**
606 * Creates a hashed password by generating a random salt.
607 */
608 private String hash(String password) {
609 String salt = generateSalt();
610 return salt+':'+passwordEncoder.encodePassword(password,salt);
611 }
612
613 /**
614 * Generates random salt.
615 */
616 private String generateSalt() {
617 StringBuilder buf = new StringBuilder();
618 SecureRandom sr = new SecureRandom();
619 for( int i=0; i<6; i++ ) {// log2(52^6)=34.20... so, this is about 32bit strong.
620 boolean upper = sr.nextBoolean();
621 char ch = (char)(sr.nextInt(26) + 'a');
622 if(upper) ch=Character.toUpperCase(ch);
623 buf.append(ch);
624 }
625 return buf.toString();
626 }
627 };
628
629 /**
630 * {@link PasswordEncoder} that uses jBCrypt.
631 */
632 private static final PasswordEncoder JBCRYPT_ENCODER = new PasswordEncoder() {
633 public String encodePassword(String rawPass, Object _) throws DataAccessException {
634 return BCrypt.hashpw(rawPass,BCrypt.gensalt());
635 }
636
637 public boolean isPasswordValid(String encPass, String rawPass, Object _) throws DataAccessException {
638 return BCrypt.checkpw(rawPass,encPass);
639 }
640 };
641
642 /**
643 * Combines {@link #JBCRYPT_ENCODER} and {@link #CLASSIC} into one so that we can continue
644 * to accept {@link #CLASSIC} format but new encoding will always done via {@link #JBCRYPT_ENCODER}.
645 */
646 public static final PasswordEncoder PASSWORD_ENCODER = new PasswordEncoder() {
647 /*
648 CLASSIC encoder outputs "salt:hash" where salt is [a-z]+, so we use unique prefix '#jbcyrpt"
649 to designate JBCRYPT-format hash.
650
651 '#' is neither in base64 nor hex, which makes it a good choice.
652 */
653 public String encodePassword(String rawPass, Object salt) throws DataAccessException {
654 return JBCRYPT_HEADER+JBCRYPT_ENCODER.encodePassword(rawPass,salt);
655 }
656
657 public boolean isPasswordValid(String encPass, String rawPass, Object salt) throws DataAccessException {
658 if (encPass.startsWith(JBCRYPT_HEADER))
659 return JBCRYPT_ENCODER.isPasswordValid(encPass.substring(JBCRYPT_HEADER.length()),rawPass,salt);
660 else
661 return CLASSIC.isPasswordValid(encPass,rawPass,salt);
662 }
663
664 private static final String JBCRYPT_HEADER = "#jbcrypt:";
665 };
666
667 @Extension
668 public static final class DescriptorImpl extends Descriptor<SecurityRealm> {
669 public String getDisplayName() {
670 return Messages.HudsonPrivateSecurityRealm_DisplayName();
671 }
672
673 @Override
674 public String getHelpFile() {
675 return "/help/security/private-realm.html";
676 }
677 }
678
679 private static final Filter CREATE_FIRST_USER_FILTER = new Filter() {
680 public void init(FilterConfig config) throws ServletException {
681 }
682
683 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
684 HttpServletRequest req = (HttpServletRequest) request;
685
686 if(req.getRequestURI().equals(req.getContextPath()+"/")) {
687 if (needsToCreateFirstUser()) {
688 ((HttpServletResponse)response).sendRedirect("securityRealm/firstUser");
689 } else {// the first user already created. the role of this filter is over.
690 PluginServletFilter.removeFilter(this);
691 chain.doFilter(request,response);
692 }
693 } else
694 chain.doFilter(request,response);
695 }
696
697 private boolean needsToCreateFirstUser() {
698 return !hasSomeUser()
699 && Jenkins.getInstance().getSecurityRealm() instanceof HudsonPrivateSecurityRealm;
700 }
701
702 public void destroy() {
703 }
704 };
705
706 private static final Logger LOGGER = Logger.getLogger(HudsonPrivateSecurityRealm.class.getName());
707 }
708