blob: 6a85da89d8205148f60c29ab1848ba59cfef72ee [file] [log] [blame]
/*
* 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;
// }
}