AuthorizationManagerBase.java

/*
 * GovWay - A customizable API Gateway 
 * https://govway.org
 * 
 * Copyright (c) 2005-2024 Link.it srl (https://link.it).
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3, as published by
 * the Free Software Foundation.
 *
 * This program 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 License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package org.openspcoop2.generic_project.utils;

import java.util.Enumeration;
import java.util.Properties;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.openspcoop2.generic_project.exception.NotAuthorizedException;
import org.openspcoop2.generic_project.exception.ServiceException;
import org.openspcoop2.utils.transport.http.HttpServletCredential;

/**
 * AuthorizationManager
 * 
 * @author Poli Andrea (apoli@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 */
public class AuthorizationManagerBase {
	
	private boolean basic = false;
	private boolean ssl = false;
	private boolean principal = false;
	
	private boolean authorizedRequired;

	private boolean identifiedMethodOrRelation = true;
	
	private Properties basicAuthorizedIdentitiesSearch = null;
	private Properties basicAuthorizedIdentitiesCRUD = null;
	
	private Properties sslAuthorizedIdentitiesSearch = null;
	private Properties sslAuthorizedIdentitiesCRUD = null;
	
	private Properties principalAuthorizedIdentitiesSearch = null;
	private Properties principalAuthorizedIdentitiesCRUD = null;
	
	public AuthorizationManagerBase(Properties serverProperties) throws ServiceException{
		this(new ServerPropertiesBase(serverProperties));
	}
	public AuthorizationManagerBase(ServerPropertiesBase serverProperties) throws ServiceException{
		
		String identifiedMethodOrRelationTmp = serverProperties.getProperty("identifiedMethod.orRelation", false);
		if(identifiedMethodOrRelationTmp!=null){
			this.identifiedMethodOrRelation = Boolean.parseBoolean(identifiedMethodOrRelationTmp);
		}
		
		String authorizationMethodEnabled = serverProperties.getProperty("identifiedMethod", false);
		if(authorizationMethodEnabled!=null){
			String [] split = authorizationMethodEnabled.split(",");
			for (int i = 0; i < split.length; i++) {
				String method = split[i].trim();
				if("basic".equalsIgnoreCase(method)){
					this.basic = true;
					
					Properties authBasicAll = serverProperties.readProperties("authorized.basic.");
					this.basicAuthorizedIdentitiesSearch = new Properties();
					this.basicAuthorizedIdentitiesSearch.putAll(authBasicAll);
					this.basicAuthorizedIdentitiesCRUD = new Properties();
					this.basicAuthorizedIdentitiesCRUD.putAll(authBasicAll);
					
					Properties authBasicSearch = serverProperties.readProperties("authorized.search.basic.");
					if(authBasicSearch.size()>0){
						Enumeration<?> en = authBasicSearch.keys();
						while (en.hasMoreElements()) {
							String key = (String) en.nextElement();
							if(this.basicAuthorizedIdentitiesSearch.containsKey(key)){
								throw new ServiceException("Property ["+key+"] already defined for basic authentication of search method (see authorized.basic.* and authorized.search.basic.*)");
							}
							this.basicAuthorizedIdentitiesSearch.put(key, authBasicSearch.get(key));
						}
					}
					
					Properties authBasicCRUD = serverProperties.readProperties("authorized.crud.basic.");
					if(authBasicCRUD.size()>0){
						Enumeration<?> en = authBasicCRUD.keys();
						while (en.hasMoreElements()) {
							String key = (String) en.nextElement();
							if(this.basicAuthorizedIdentitiesCRUD.containsKey(key)){
								throw new ServiceException("Property ["+key+"] already defined for basic authentication of crud method (see authorized.basic.* and authorized.crud.basic.*)");
							}
							this.basicAuthorizedIdentitiesCRUD.put(key, authBasicCRUD.get(key));
						}
					}
					
					if(this.basicAuthorizedIdentitiesCRUD.size()<=0 && this.basicAuthorizedIdentitiesSearch.size()<=0){
						throw new ServiceException("Required authorizedMethod ["+method+"] without a list of authorized users");
					}
				}
				else if("ssl".equalsIgnoreCase(method)){
					this.ssl = true;
					
					Properties authSslAll = serverProperties.readProperties("authorized.ssl.");
					this.sslAuthorizedIdentitiesSearch = new Properties();
					this.sslAuthorizedIdentitiesSearch.putAll(authSslAll);
					this.sslAuthorizedIdentitiesCRUD = new Properties();
					this.sslAuthorizedIdentitiesCRUD.putAll(authSslAll);
					
					Properties authSslSearch = serverProperties.readProperties("authorized.search.ssl.");
					if(authSslSearch.size()>0){
						Enumeration<?> en = authSslSearch.keys();
						while (en.hasMoreElements()) {
							String key = (String) en.nextElement();
							if(this.sslAuthorizedIdentitiesSearch.containsKey(key)){
								throw new ServiceException("Property ["+key+"] already defined for ssl authentication of search method (see authorized.ssl.* and authorized.search.ssl.*)");
							}
							this.sslAuthorizedIdentitiesSearch.put(key, authSslSearch.get(key));
						}
					}
					
					Properties authSslCRUD = serverProperties.readProperties("authorized.crud.ssl.");
					if(authSslCRUD.size()>0){
						Enumeration<?> en = authSslCRUD.keys();
						while (en.hasMoreElements()) {
							String key = (String) en.nextElement();
							if(this.sslAuthorizedIdentitiesCRUD.containsKey(key)){
								throw new ServiceException("Property ["+key+"] already defined for ssl authentication of crud method (see authorized.ssl.* and authorized.crud.ssl.*)");
							}
							this.sslAuthorizedIdentitiesCRUD.put(key, authSslCRUD.get(key));
						}
					}
					
					if(this.sslAuthorizedIdentitiesCRUD.size()<=0 && this.sslAuthorizedIdentitiesSearch.size()<=0){
						throw new ServiceException("Required authorizedMethod ["+method+"] without a list of authorized users");
					}
				}
				else if("principal".equalsIgnoreCase(method)){
					this.principal = true;
					
					Properties authPrincipalAll = serverProperties.readProperties("authorized.principal.");
					this.principalAuthorizedIdentitiesSearch = new Properties();
					this.principalAuthorizedIdentitiesSearch.putAll(authPrincipalAll);
					this.principalAuthorizedIdentitiesCRUD = new Properties();
					this.principalAuthorizedIdentitiesCRUD.putAll(authPrincipalAll);
					
					Properties authPrincipalSearch = serverProperties.readProperties("authorized.search.principal.");
					if(authPrincipalSearch.size()>0){
						Enumeration<?> en = authPrincipalSearch.keys();
						while (en.hasMoreElements()) {
							String key = (String) en.nextElement();
							if(this.principalAuthorizedIdentitiesSearch.containsKey(key)){
								throw new ServiceException("Property ["+key+"] already defined for principal authentication of search method (see authorized.principal.* and authorized.search.principal.*)");
							}
							this.principalAuthorizedIdentitiesSearch.put(key, authPrincipalSearch.get(key));
						}
					}
					
					Properties authPrincipalCRUD = serverProperties.readProperties("authorized.crud.principal.");
					if(authPrincipalCRUD.size()>0){
						Enumeration<?> en = authPrincipalCRUD.keys();
						while (en.hasMoreElements()) {
							String key = (String) en.nextElement();
							if(this.principalAuthorizedIdentitiesCRUD.containsKey(key)){
								throw new ServiceException("Property ["+key+"] already defined for principal authentication of crud method (see authorized.principal.* and authorized.crud.principal.*)");
							}
							this.principalAuthorizedIdentitiesCRUD.put(key, authPrincipalCRUD.get(key));
						}
					}
					
					if(this.principalAuthorizedIdentitiesCRUD.size()<=0 && this.principalAuthorizedIdentitiesSearch.size()<=0){
						throw new ServiceException("Required authorizedMethod ["+method+"] without a list of authorized users");
					}
				}
				else{
					throw new ServiceException("AuthorizedMethod ["+method+"] unknow");
				}
			}
		}
		
		this.authorizedRequired = this.basic || this.ssl || this.principal;
	}

	private void logError(Logger log,String message,StringBuilder bf){
		if(bf.length()>0){
			bf.append("\n");
		}
		bf.append(message);
		log.error(message);
	}
	
	public void authorize(HttpServletRequest httpServletRequest,Logger log,boolean searchMethod) throws NotAuthorizedException{
		
		if(this.authorizedRequired==false){
			return;
		}
		
		HttpServletCredential identity = new HttpServletCredential(httpServletRequest,log,true);
		StringBuilder bf = new StringBuilder();
		
		boolean basicOk = false;
		if(this.basic){
			String username = identity.getUsername();
			String password = identity.getPassword();
			if(username!=null){
				if(password!=null){
					boolean isRegistered = false;
					String passwordRegistrata = null;
					if(searchMethod){
						isRegistered = this.basicAuthorizedIdentitiesSearch.containsKey(username);
						if(isRegistered){
							passwordRegistrata = this.basicAuthorizedIdentitiesSearch.getProperty(username);
						}
					}
					else{
						isRegistered = this.basicAuthorizedIdentitiesCRUD.containsKey(username);
						if(isRegistered){
							passwordRegistrata = this.basicAuthorizedIdentitiesCRUD.getProperty(username);
						}
					}
					if(isRegistered){
						if(password.equals(passwordRegistrata)){
							log.debug("Detected in the http request a credential basic (username:"+username+") that identifies an authorized user");
							basicOk = true;
						}else{
							logError(log, "Detected in the http request a credential basic (username:"+username
									+") that contains a wrong password ["+password+"]", bf);
						}
					}else{
						logError(log, "Detected in the http request a credential basic (username:"+username+" password"+password
								+") not authorized", bf);
					}
				}else{
					logError(log, "Detected in the http request a credential basic not usable (username:"+username
							+"), password not defined???", bf);
				}
			}
		}
		
		boolean sslOk = false;
		if(this.ssl){
			String subject = identity.getSubject();
			if(subject!=null){
				boolean isRegistered = false;
				if(searchMethod){
					isRegistered = this.sslAuthorizedIdentitiesSearch.containsValue(subject);
				}
				else{
					isRegistered = this.sslAuthorizedIdentitiesCRUD.containsValue(subject);
				}
				if(isRegistered){
					log.debug("Detected in the http request a credential ssl (subject:"+subject+") that identifies an authorized user");
					sslOk = true;
				}else{
					logError(log, "Detected in the http request a credential ssl (subject:"+subject+") not authorized", bf);
				}
			}
		}
		
		boolean principalOk = false;
		if(this.principal){
			String principalId = identity.getPrincipal();
			if(principalId!=null){
				boolean isRegistered = false;
				if(searchMethod){
					isRegistered = this.principalAuthorizedIdentitiesSearch.containsValue(principalId);
				}
				else{
					isRegistered = this.principalAuthorizedIdentitiesCRUD.containsValue(principalId);
				}
				if(isRegistered){
					log.debug("Detected in the http request a credential principal (id:"+principalId+") that identifies an authorized user");
					principalOk = true;
				}else{
					logError(log, "Detected in the http request a credential principal (id:"+principalId+") not authorized", bf);
				}
			}
		}
		
		if(this.identifiedMethodOrRelation){
			if( !basicOk && !sslOk && !principalOk){
				if(bf.length()>0){
					throw new NotAuthorizedException(bf.toString());
				}
				else{
					throw new NotAuthorizedException("No credentials found in the request");
				}
			}
		}
		else{
			if(this.basic && !basicOk){
				if(bf.length()>0){
					throw new NotAuthorizedException(bf.toString());
				}
				else{
					throw new NotAuthorizedException("No basic credentials found in the request");
				}
			}
			if(this.ssl && !sslOk){
				if(bf.length()>0){
					throw new NotAuthorizedException(bf.toString());
				}
				else{
					throw new NotAuthorizedException("No ssl credentials found in the request");
				}
			}
			if(this.principal && !principalOk){
				if(bf.length()>0){
					throw new NotAuthorizedException(bf.toString());
				}
				else{
					throw new NotAuthorizedException("No principal credentials found in the request");
				}
			}
		}
		
	}
	
}