| /* |
| * Copyright 2013 gitblit.com. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.gitblit.manager; |
| |
| import java.nio.charset.Charset; |
| import java.security.Principal; |
| import java.text.MessageFormat; |
| import java.util.List; |
| |
| import javax.servlet.http.Cookie; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| |
| import org.apache.wicket.RequestCycle; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import com.gitblit.Constants; |
| import com.gitblit.Constants.AuthenticationType; |
| import com.gitblit.IStoredSettings; |
| import com.gitblit.Keys; |
| import com.gitblit.models.UserModel; |
| import com.gitblit.utils.Base64; |
| import com.gitblit.utils.HttpUtils; |
| import com.gitblit.utils.StringUtils; |
| import com.gitblit.utils.X509Utils.X509Metadata; |
| import com.gitblit.wicket.GitBlitWebSession; |
| |
| /** |
| * The session manager handles user login & logout. |
| * |
| * @author James Moger |
| * |
| */ |
| public class SessionManager implements ISessionManager { |
| |
| private final Logger logger = LoggerFactory.getLogger(getClass()); |
| |
| private final IStoredSettings settings; |
| |
| private final IRuntimeManager runtimeManager; |
| |
| private final IUserManager userManager; |
| |
| public SessionManager( |
| IRuntimeManager runtimeManager, |
| IUserManager userManager) { |
| |
| this.settings = runtimeManager.getSettings(); |
| this.runtimeManager = runtimeManager; |
| this.userManager = userManager; |
| } |
| |
| @Override |
| public SessionManager start() { |
| List<String> services = settings.getStrings("realm.authenticationServices"); |
| for (String service : services) { |
| // TODO populate authentication services here |
| } |
| return this; |
| } |
| |
| @Override |
| public SessionManager stop() { |
| return this; |
| } |
| |
| /** |
| * Authenticate a user based on HTTP request parameters. |
| * |
| * Authentication by X509Certificate is tried first and then by cookie. |
| * |
| * @param httpRequest |
| * @return a user object or null |
| */ |
| @Override |
| public UserModel authenticate(HttpServletRequest httpRequest) { |
| return authenticate(httpRequest, false); |
| } |
| |
| /** |
| * Authenticate a user based on HTTP request parameters. |
| * |
| * Authentication by X509Certificate, servlet container principal, cookie, |
| * and BASIC header. |
| * |
| * @param httpRequest |
| * @param requiresCertificate |
| * @return a user object or null |
| */ |
| @Override |
| public UserModel authenticate(HttpServletRequest httpRequest, boolean requiresCertificate) { |
| // try to authenticate by certificate |
| boolean checkValidity = settings.getBoolean(Keys.git.enforceCertificateValidity, true); |
| String [] oids = settings.getStrings(Keys.git.certificateUsernameOIDs).toArray(new String[0]); |
| UserModel model = HttpUtils.getUserModelFromCertificate(httpRequest, checkValidity, oids); |
| if (model != null) { |
| // grab real user model and preserve certificate serial number |
| UserModel user = userManager.getUserModel(model.username); |
| X509Metadata metadata = HttpUtils.getCertificateMetadata(httpRequest); |
| if (user != null) { |
| flagWicketSession(AuthenticationType.CERTIFICATE); |
| logger.debug(MessageFormat.format("{0} authenticated by client certificate {1} from {2}", |
| user.username, metadata.serialNumber, httpRequest.getRemoteAddr())); |
| return user; |
| } else { |
| logger.warn(MessageFormat.format("Failed to find UserModel for {0}, attempted client certificate ({1}) authentication from {2}", |
| model.username, metadata.serialNumber, httpRequest.getRemoteAddr())); |
| } |
| } |
| |
| if (requiresCertificate) { |
| // caller requires client certificate authentication (e.g. git servlet) |
| return null; |
| } |
| |
| // try to authenticate by servlet container principal |
| Principal principal = httpRequest.getUserPrincipal(); |
| if (principal != null) { |
| String username = principal.getName(); |
| if (!StringUtils.isEmpty(username)) { |
| boolean internalAccount = isInternalAccount(username); |
| UserModel user = userManager.getUserModel(username); |
| if (user != null) { |
| // existing user |
| flagWicketSession(AuthenticationType.CONTAINER); |
| logger.debug(MessageFormat.format("{0} authenticated by servlet container principal from {1}", |
| user.username, httpRequest.getRemoteAddr())); |
| return user; |
| } else if (settings.getBoolean(Keys.realm.container.autoCreateAccounts, false) |
| && !internalAccount) { |
| // auto-create user from an authenticated container principal |
| user = new UserModel(username.toLowerCase()); |
| user.displayName = username; |
| user.password = Constants.EXTERNAL_ACCOUNT; |
| userManager.updateUserModel(user); |
| flagWicketSession(AuthenticationType.CONTAINER); |
| logger.debug(MessageFormat.format("{0} authenticated and created by servlet container principal from {1}", |
| user.username, httpRequest.getRemoteAddr())); |
| return user; |
| } else if (!internalAccount) { |
| logger.warn(MessageFormat.format("Failed to find UserModel for {0}, attempted servlet container authentication from {1}", |
| principal.getName(), httpRequest.getRemoteAddr())); |
| } |
| } |
| } |
| |
| // try to authenticate by cookie |
| if (userManager.supportsCookies()) { |
| UserModel user = authenticate(httpRequest.getCookies()); |
| if (user != null) { |
| flagWicketSession(AuthenticationType.COOKIE); |
| logger.debug(MessageFormat.format("{0} authenticated by cookie from {1}", |
| user.username, httpRequest.getRemoteAddr())); |
| return user; |
| } |
| } |
| |
| // try to authenticate by BASIC |
| final String authorization = httpRequest.getHeader("Authorization"); |
| if (authorization != null && authorization.startsWith("Basic")) { |
| // Authorization: Basic base64credentials |
| String base64Credentials = authorization.substring("Basic".length()).trim(); |
| String credentials = new String(Base64.decode(base64Credentials), |
| Charset.forName("UTF-8")); |
| // credentials = username:password |
| final String[] values = credentials.split(":", 2); |
| |
| if (values.length == 2) { |
| String username = values[0]; |
| char[] password = values[1].toCharArray(); |
| UserModel user = authenticate(username, password); |
| if (user != null) { |
| flagWicketSession(AuthenticationType.CREDENTIALS); |
| logger.debug(MessageFormat.format("{0} authenticated by BASIC request header from {1}", |
| user.username, httpRequest.getRemoteAddr())); |
| return user; |
| } else { |
| logger.warn(MessageFormat.format("Failed login attempt for {0}, invalid credentials from {1}", |
| username, httpRequest.getRemoteAddr())); |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Authenticate a user based on their cookie. |
| * |
| * @param cookies |
| * @return a user object or null |
| */ |
| protected UserModel authenticate(Cookie[] cookies) { |
| if (userManager.supportsCookies()) { |
| if (cookies != null && cookies.length > 0) { |
| for (Cookie cookie : cookies) { |
| if (cookie.getName().equals(Constants.NAME)) { |
| String value = cookie.getValue(); |
| return userManager.authenticate(value.toCharArray()); |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| protected void flagWicketSession(AuthenticationType authenticationType) { |
| RequestCycle requestCycle = RequestCycle.get(); |
| if (requestCycle != null) { |
| // flag the Wicket session, if this is a Wicket request |
| GitBlitWebSession session = GitBlitWebSession.get(); |
| session.authenticationType = authenticationType; |
| } |
| } |
| |
| /** |
| * Authenticate a user based on a username and password. |
| * |
| * @see IUserService.authenticate(String, char[]) |
| * @param username |
| * @param password |
| * @return a user object or null |
| */ |
| @Override |
| public UserModel authenticate(String username, char[] password) { |
| if (StringUtils.isEmpty(username)) { |
| // can not authenticate empty username |
| return null; |
| } |
| |
| String usernameDecoded = StringUtils.decodeUsername(username); |
| String pw = new String(password); |
| if (StringUtils.isEmpty(pw)) { |
| // can not authenticate empty password |
| return null; |
| } |
| // check to see if this is the federation user |
| // if (canFederate()) { |
| // if (usernameDecoded.equalsIgnoreCase(Constants.FEDERATION_USER)) { |
| // List<String> tokens = getFederationTokens(); |
| // if (tokens.contains(pw)) { |
| // return getFederationUser(); |
| // } |
| // } |
| // } |
| |
| UserModel user = userManager.authenticate(usernameDecoded, password); |
| |
| // try registered external authentication providers |
| if (user == null) { |
| // for (AuthenticationService service : authenticationServices) { |
| // if (service instanceof UsernamePasswordAuthenticationService) { |
| // user = service.authenticate(usernameDecoded, password); |
| // if (user != null) { |
| // // user authenticated |
| // user.accountType = service.getAccountType(); |
| // return user; |
| // } |
| // } |
| // } |
| } |
| return user; |
| } |
| |
| /** |
| * Sets a cookie for the specified user. |
| * |
| * @param response |
| * @param user |
| */ |
| @Override |
| public void setCookie(HttpServletResponse response, UserModel user) { |
| GitBlitWebSession session = GitBlitWebSession.get(); |
| boolean standardLogin = session.authenticationType.isStandard(); |
| |
| if (userManager.supportsCookies() && standardLogin) { |
| Cookie userCookie; |
| if (user == null) { |
| // clear cookie for logout |
| userCookie = new Cookie(Constants.NAME, ""); |
| } else { |
| // set cookie for login |
| String cookie = userManager.getCookie(user); |
| if (StringUtils.isEmpty(cookie)) { |
| // create empty cookie |
| userCookie = new Cookie(Constants.NAME, ""); |
| } else { |
| // create real cookie |
| userCookie = new Cookie(Constants.NAME, cookie); |
| userCookie.setMaxAge(Integer.MAX_VALUE); |
| } |
| } |
| userCookie.setPath("/"); |
| response.addCookie(userCookie); |
| } |
| } |
| |
| /** |
| * Logout a user. |
| * |
| * @param user |
| */ |
| @Override |
| public void logout(HttpServletResponse response, UserModel user) { |
| setCookie(response, null); |
| userManager.logout(user); |
| } |
| |
| /** |
| * Returns true if the username represents an internal account |
| * |
| * @param username |
| * @return true if the specified username represents an internal account |
| */ |
| protected boolean isInternalAccount(String username) { |
| return !StringUtils.isEmpty(username) |
| && (username.equalsIgnoreCase(Constants.FEDERATION_USER) |
| || username.equalsIgnoreCase(UserModel.ANONYMOUS.username)); |
| } |
| |
| // protected UserModel getFederationUser() { |
| // // the federation user is an administrator |
| // UserModel federationUser = new UserModel(Constants.FEDERATION_USER); |
| // federationUser.canAdmin = true; |
| // return federationUser; |
| // } |
| } |