/*******************************************************************************
* Copyright (c) 2013 aegif.
*
* This file is part of NemakiWare.
*
* NemakiWare is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NemakiWare is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public Licensealong with NemakiWare. If not, see <http://www.gnu.org/licenses/>.
*
* Contributors:
* linzhixing(https://github.com/linzhixing) - initial API and implementation
******************************************************************************/
package jp.aegif.nemaki.cmis.factory.auth.impl;
import jp.aegif.nemaki.businesslogic.PrincipalService;
import jp.aegif.nemaki.cmis.factory.auth.AuthenticationService;
import jp.aegif.nemaki.cmis.factory.auth.Token;
import jp.aegif.nemaki.cmis.factory.auth.TokenService;
import jp.aegif.nemaki.cmis.factory.info.RepositoryInfoMap;
import jp.aegif.nemaki.model.User;
import jp.aegif.nemaki.util.AuthenticationUtil;
import jp.aegif.nemaki.util.PropertyManager;
import jp.aegif.nemaki.util.constant.CallContextKey;
import jp.aegif.nemaki.util.constant.PropertyKey;
import org.apache.chemistry.opencmis.commons.server.CallContext;
import org.apache.chemistry.opencmis.server.impl.CallContextImpl;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mindrot.jbcrypt.BCrypt;
/**
* Authentication Service implementation.
*/
public class AuthenticationServiceImpl implements AuthenticationService {
private static final Log log = LogFactory.getLog(AuthenticationServiceImpl.class);
private PrincipalService principalService;
private TokenService tokenService;
private PropertyManager propertyManager;
private RepositoryInfoMap repositoryInfoMap;
public boolean login(CallContext callContext) {
// Set flag of SuperUsers
String suId = repositoryInfoMap.getSuperUsers().getId();
((CallContextImpl)callContext).put(CallContextKey.IS_SU,
suId.equals(callContext.getRepositoryId()));
// SSO
if (loginWithExternalAuth(callContext)) {
return true;
}
// Token for Basic auth
if (loginWithToken(callContext)) {
return true;
}
// Basic auth
return loginWithBasicAuth(callContext);
}
private boolean loginWithExternalAuth(CallContext callContext) {
final String repositoryId = "bedroom"; // TODO hard coding
String proxyHeaderKey = propertyManager.readValue(PropertyKey.EXTERNAL_AUTHENTICATION_PROXY_HEADER);
if(StringUtils.isBlank(proxyHeaderKey)){
return false;
}
String proxyUserId = (String) callContext.get(proxyHeaderKey);
if (StringUtils.isBlank(proxyUserId)) {
log.warn("Not authenticated user");
return false;
} else {
User user = principalService.getUserById(repositoryId, proxyUserId);
if (user == null) {
User newUser = new User(proxyUserId, proxyUserId, "", "", "",
BCrypt.hashpw(proxyUserId, BCrypt.gensalt()));
principalService.createUser(repositoryId, newUser);
log.debug("Authenticated userId=" + newUser.getUserId());
} else {
log.debug("Authenticated userId=" + user.getUserId());
//Admin check
boolean isAdmin = user.isAdmin() == null ? false : true;
setAdminFlagInContext(callContext, isAdmin);
}
return true;
}
}
private boolean loginWithToken(CallContext callContext) {
String userName = callContext.getUsername();
String token;
if (callContext.get("nemaki_auth_token") == null) {
return false;
} else {
token = (String) callContext.get("nemaki_auth_token");
if (StringUtils.isBlank(token)) {
return false;
}
}
Object _app = callContext.get("nemaki_auth_token_app");
String app = (_app == null) ? "" : (String) _app;
if (authenticateUserByToken(app, callContext.getRepositoryId(), userName, token)) {
if (authenticateAdminByToken(callContext.getRepositoryId(), userName)) {
setAdminFlagInContext(callContext, true);
} else {
setAdminFlagInContext(callContext, false);
}
return true;
}
return false;
}
private boolean loginWithBasicAuth(CallContext callContext) {
String repositoryId = callContext.getRepositoryId();
//Check repositoryId exists
if(!repositoryInfoMap.contains(repositoryId)){
return false;
}
// Basic auth with id/password
User user = getAuthenticatedUser(callContext.getRepositoryId(), callContext.getUsername(), callContext.getPassword());
if (user == null)
return false;
boolean isAdmin = user.isAdmin() == null ? false : true;
setAdminFlagInContext(callContext, isAdmin);
//if not exist create solr user
String solrUserId = propertyManager.readValue(PropertyKey.SOLR_NEMAKI_USERID);
User solrUser = principalService.getUserById(repositoryId, solrUserId);
if (solrUser == null) {
User newSolrUser = new User(solrUserId, solrUserId, "", "", "", BCrypt.hashpw(solrUserId, BCrypt.gensalt()));
newSolrUser.setAdmin(true);
principalService.createUser(repositoryId, newSolrUser);
}
return true;
}
private void setAdminFlagInContext(CallContext callContext, Boolean isAdmin) {
((CallContextImpl) callContext).put(CallContextKey.IS_ADMIN, isAdmin);
}
private boolean authenticateUserByToken(String app, String repositoryId, String userName, String token) {
Token registeredToken = tokenService.getToken(app, repositoryId, userName);
if (registeredToken == null) {
return false;
} else {
long expiration = registeredToken.getExpiration();
if (System.currentTimeMillis() > expiration) {
return false;
} else {
String _registeredToken = registeredToken.getToken();
return StringUtils.isNotEmpty(_registeredToken) && _registeredToken.equals(token);
}
}
}
private boolean authenticateAdminByToken(String repositoryId, String userName) {
return tokenService.isAdmin(repositoryId, userName);
}
private User getAuthenticatedUser(String repositoryId, String userName, String password) {
User u = principalService.getUserById(repositoryId, userName);
// succeeded
if (u != null) {
if (AuthenticationUtil.passwordMatches(password, u.getPasswordHash())) {
log.debug(String.format( "[%s][%s]Get authenticated user successfully ! , Is admin? : %s", repositoryId, userName , u.isAdmin()));
return u;
}
}
// Check anonymous
String anonymousId = principalService.getAnonymous(repositoryId);
if (StringUtils.isNotBlank(anonymousId) && anonymousId.equals(userName)) {
if (u != null) {
log.warn(anonymousId + " should have not been registered in the database.");
}
return null;
}
return null;
}
public void setPrincipalService(PrincipalService principalService) {
this.principalService = principalService;
}
public void setTokenService(TokenService tokenService) {
this.tokenService = tokenService;
}
public void setPropertyManager(PropertyManager propertyManager) {
this.propertyManager = propertyManager;
}
public void setRepositoryInfoMap(RepositoryInfoMap repositoryInfoMap) {
this.repositoryInfoMap = repositoryInfoMap;
}
}