
hudson.security.SecurityRealm Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hudson-core Show documentation
Show all versions of hudson-core Show documentation
Contains the core Hudson code and view files to render HTML.
The newest version!
/*******************************************************************************
*
* Copyright (c) 2004-2011 Oracle Corporation.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*
* Kohsuke Kawaguchi, Nikita Levyankov, Winston Prakash
*
*******************************************************************************/
package hudson.security;
import hudson.DescriptorExtensionList;
import hudson.EnvVars;
import hudson.Extension;
import hudson.ExtensionPoint;
import hudson.Functions;
import hudson.cli.CLICommand;
import hudson.model.AbstractDescribableImpl;
import hudson.model.Descriptor;
import hudson.model.Hudson;
import hudson.security.FederatedLoginService.FederatedIdentity;
import org.eclipse.hudson.security.captcha.CaptchaSupport;
import hudson.util.DescriptorList;
import hudson.util.PluginServletFilter;
import java.io.IOException;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import java.util.logging.Logger;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSession;
import org.eclipse.hudson.security.HudsonSecurityEntitiesHolder;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.springframework.context.ApplicationContext;
import org.springframework.dao.DataAccessException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.core.userdetails.memory.UserAttribute;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
/**
* Pluggable security realm that connects external user database to Hudson.
*
* <
* p/>
* If additional views/URLs need to be exposed, an active {@link SecurityRealm}
* is bound to CONTEXT_ROOT/securityRealm/ through
* {@link Hudson#getSecurityRealm()}, so you can define additional pages and
* operations on your {@link SecurityRealm}.
*
* How do I implement this class?
*
* For compatibility reasons, there are two somewhat different ways to implement
* a custom SecurityRealm.
*
* <
* p/>
* One is to override the {@link #createSecurityComponents()} and create key
* Spring Security components that control the authentication process. The
* default {@link SecurityRealm#createFilter(FilterConfig)} implementation then
* assembles them into a chain of {@link Filter}s. All the incoming requests to
* Hudson go through this filter chain, and when the filter chain is done,
* {@link SecurityContext#getAuthentication()} would tell us who the current
* user is.
*
* <
* p/>
* If your {@link SecurityRealm} needs to touch the default {@link Filter} chain
* configuration (e.g., adding new ones), then you can also override
* {@link #createFilter(FilterConfig)} to do so.
*
* <
* p/>
* This model is expected to fit most {@link SecurityRealm} implementations.
*
* <
* p/>
* <
* p/>
* The other way of doing this is to ignore {@link #createSecurityComponents()}
* completely (by returning {@link SecurityComponents} created by the default
* constructor) and just concentrate on {@link #createFilter(FilterConfig)}. As
* long as the resulting filter chain properly sets up {@link Authentication}
* object at the end of the processing, Hudson doesn't really need you to fit
* the standard Spring Security models like {@link AuthenticationManager} and
* {@link UserDetailsService}.
*
* <
* p/>
* This model is for those "weird" implementations.
*
* <
* p/>
* Views
- loginLink.jelly
- This view renders the login
* link on the top right corner of every page, when the user is anonymous. For
* {@link SecurityRealm}s that support user sign-up, this is a good place to
* show a "sign up" link. See {@link HudsonPrivateSecurityRealm} implementation
* for an example of this.
*
*
- config.jelly
- This view is used to render the configuration page
* in the system config screen.
*
* @author Kohsuke Kawaguchi
* @author Nikita Levyankov
* @see PluginServletFilter
* @since 1.160
*/
public abstract class SecurityRealm extends AbstractDescribableImpl implements ExtensionPoint {
/**
* Creates fully-configured {@link AuthenticationManager} that performs
* authentication against the user realm. The implementation hides how such
* authentication manager is configured.
*
* <
* p/>
* {@link AuthenticationManager} instantiation often depends on the
* user-specified parameters (for example, if the authentication is based on
* LDAP, the user needs to specify the host name of the LDAP server.) Such
* configuration is expected to be presented to the user via
* config.jelly and then captured as instance variables inside the
* {@link SecurityRealm} implementation.
*
* <
* p/>
* Your {@link SecurityRealm} may also wants to alter {@link Filter} set up
* by overriding {@link #createFilter(FilterConfig)}.
*/
public abstract SecurityComponents createSecurityComponents();
/**
* Captcha Support to be used with this SecurityRealm for User Signup
*/
private CaptchaSupport captchaSupport;
/**
* Creates a {@link CliAuthenticator} object that authenticates an
* invocation of a CLI command. See {@link CliAuthenticator} for more
* details.
*
* @param command The command about to be executed.
* @return never null. By default, this method returns a no-op authenticator
* that always authenticates the session as authenticated by the transport
* (which is often just {@link Hudson#ANONYMOUS}.)
*/
public CliAuthenticator createCliAuthenticator(final CLICommand command) {
return new CliAuthenticator() {
public Authentication authenticate() {
return command.getTransportAuthentication();
}
};
}
/**
* {@inheritDoc}
*
* <
* p/>
* {@link SecurityRealm} is a singleton resource in Hudson, and therefore
* it's always configured through config.jelly and never with
* global.jelly.
*/
public Descriptor getDescriptor() {
return super.getDescriptor();
}
/**
* Returns the URL to submit a form for the authentication. There's no need
* to override this, except for {@link LegacySecurityRealm}.
*/
public String getAuthenticationGatewayUrl() {
return "j_spring_security_check";
}
/**
* Gets the target URL of the "login" link. There's no need to override
* this, except for {@link LegacySecurityRealm}. On legacy implementation
* this should point to {@code loginEntry}, which is protected by
* web.xml, so that the user can be eventually authenticated by the
* container.
*
* <
* p/>
* Path is relative from the context root of the Hudson application. The URL
* returned by this method will get the "from" query parameter indicating
* the page that the user was at.
*/
public String getLoginUrl() {
return "login";
}
/**
* Returns true if this {@link SecurityRealm} supports explicit logout
* operation.
*
* <
* p/>
* If the method returns false, "logout" link will not be displayed. This is
* useful when authentication doesn't require an explicit login activity
* (such as NTLM authentication or Kerberos authentication, where Hudson has
* no ability to log off the current user.)
*
* <
* p/>
* By default, this method returns true.
*
* @since 1.307
*/
public boolean canLogOut() {
return true;
}
/**
* Controls where the user is sent to after a logout. By default, it's the
* top page of Hudson, but you can return arbitrary URL.
*
* @param req {@link StaplerRequest} that represents the current request.
* Primarily so that you can get the context path. By the time this method
* is called, the session is already invalidated. Never null.
* @param auth The {@link Authentication} object that represents the user
* that was logging in. This parameter allows you to redirect people to
* different pages depending on who they are.
* @return never null.
* @see #doLogout(StaplerRequest, StaplerResponse)
* @since 1.314
*/
protected String getPostLogOutUrl(StaplerRequest req, Authentication auth) {
return Functions.getRequestRootPath(req) + "/";
}
public CaptchaSupport getCaptchaSupport() {
return captchaSupport;
}
public void setCaptchaSupport(CaptchaSupport captchaSupport) {
this.captchaSupport = captchaSupport;
}
public List> getCaptchaSupportDescriptors() {
return CaptchaSupport.all();
}
/**
* Handles the logout processing.
*
* <
* p/>
* The default implementation erases the session and do a few other clean
* up, then redirect the user to the URL specified by
* {@link #getPostLogOutUrl(StaplerRequest, Authentication)}.
*
* @since 1.314
*/
public void doLogout(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
HttpSession session = req.getSession(false);
if (session != null) {
session.invalidate();
}
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
SecurityContextHolder.clearContext();
//Clear env property.
EnvVars.clearHudsonUserEnvVar();
// reset remember-me cookie
Cookie cookie = new Cookie(TokenBasedRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, "");
cookie.setPath(Functions.getRequestRootPath(req).length() > 0 ? Functions.getRequestRootPath(req) : "/");
rsp.addCookie(cookie);
rsp.sendRedirect2(getPostLogOutUrl(req, auth));
}
/**
* Returns true if this {@link SecurityRealm} allows online sign-up. This
* creates a hyperlink that redirects users to CONTEXT_ROOT/signUp,
* which will be served by the signup.jelly view of this class.
*
* <
* p/>
* If the implementation needs to redirect the user to a different URL for
* signing up, use the following jelly script as signup.jelly
*
* <
* pre>
*
*
*/
public boolean allowsSignup() {
Class clz = getClass();
return clz.getClassLoader().getResource(clz.getName().replace('.', '/') + "/signup.jelly") != null;
}
/**
* Shortcut for {@link UserDetailsService#loadUserByUsername(String)}.
*
* @return never null.
* @throws UserMayOrMayNotExistException If the security realm cannot even
* tell if the user exists or not.
*/
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
return getSecurityComponents().userDetails.loadUserByUsername(username);
}
/**
* If this {@link SecurityRealm} supports a look up of {@link GroupDetails}
* by their names, override this method to provide the look up.
*
* <
* p/>
* This information, when available, can be used by
* {@link AuthorizationStrategy}s to improve the UI and error diagnostics
* for the user.
*/
public GroupDetails loadGroupByGroupname(String groupname) throws UsernameNotFoundException, DataAccessException {
throw new UserMayOrMayNotExistException(groupname);
}
/**
* Starts the user registration process for a new user that has the given
* verified identity.
*
* <
* p/>
* If the user logs in through a {@link FederatedLoginService}, verified
* that the current user owns an {@linkplain FederatedIdentity identity},
* but no existing user account has claimed that identity, then this method
* is invoked.
*
* <
* p/>
* The expected behaviour is to confirm that the user would like to create a
* new account, and associate this federated identity to the newly created
* account (via {@link FederatedIdentity#addToCurrentUser()}.
*
* @throws UnsupportedOperationException If this implementation doesn't
* support the signup through this mechanism. This is the default
* implementation.
* @since 1.394
*/
public HttpResponse commenceSignup(FederatedIdentity identity) {
throw new UnsupportedOperationException();
}
/**
* Generates a captcha image.
*/
public final void doCaptcha(StaplerRequest req, StaplerResponse rsp) throws IOException {
if (captchaSupport != null) {
String id = req.getSession().getId();
rsp.setContentType("image/png");
rsp.addHeader("Cache-Control", "no-cache");
captchaSupport.generateImage(id, rsp.getOutputStream());
}
}
/**
* Validates the captcha.
*/
protected final boolean validateCaptcha(String text) {
if (captchaSupport != null) {
String id = Stapler.getCurrentRequest().getSession().getId();
return captchaSupport.validateCaptcha(id, text);
}
// If no Captcha Support then bogus validation always retuers true
return true;
}
/**
* Picks up the instance of the given type from the spring context. If there
* are multiple beans of the same type or if there are none, this method
* treats that as an {@link IllegalArgumentException}.
*
* This method is intended to be used to pick up a Spring Security object
* from spring once the bean definition file is parsed.
*/
protected static © 2015 - 2025 Weber Informatics LLC | Privacy Policy