AuthorizationManager.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.utils.service.authorization;

import java.util.List;
import java.util.stream.Collectors;

import org.openspcoop2.utils.service.context.IContext;
import org.openspcoop2.utils.service.fault.jaxrs.FaultCode;
import org.springframework.security.core.GrantedAuthority;

/**
 * AuthorizationManager
 * 
 * @author Andrea Poli (apoli@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 */
public class AuthorizationManager {

	public static void authorize(IContext context, AuthorizationConfig config) {		
		
		if(config==null || config.getAclList()==null || config.getAclList().isEmpty()) {
			return; // non esistono regole acl
		}
			
		try {
			
			// 1. Cerco acl che ha un match con la richiesta in essere
			String method = context.getServletRequest().getMethod();
			String restPath = context.getRestPath(); // a differenza del context.getServletRequest().getRequestURI(), il path è normalizzato per i parametri dinamici {}
			AuthorizationConfigACL acl = null;
			for (AuthorizationConfigACL check : config.getAclList()) {
				
				// method
				String methodCheck = check.getMethod();
				if(!AuthorizationConfigACL.WILDCARD.equals(methodCheck)) {
					if(!methodCheck.toUpperCase().equals(method.toUpperCase())) {
						continue;
					}
				}
				
				// path
				String pathCheck = check.getPath();
				if(!AuthorizationConfigACL.WILDCARD.equals(pathCheck)) {
					if(pathCheck.endsWith(AuthorizationConfigACL.WILDCARD)) {
						String patchCheckWitoutStar = pathCheck.substring(0, (pathCheck.length()-AuthorizationConfigACL.WILDCARD.length()));
						if(!normalizePath(restPath).startsWith(normalizePath(patchCheckWitoutStar))) {
							continue;
						}
					}
					else {
						if(!normalizePath(restPath).equals(normalizePath(pathCheck))) {
							continue;
						}
					}
				}
				
				acl = check;
				break;
			}
			if(acl==null) {
				throw new Exception("Acl rule match for request not found");
			}
			
			// 2. Verifico acl trovata
			if(acl.getPrincipal().isEmpty() && acl.getRoles().isEmpty()) {
				return; // invocazione pubblica
			}
			if(!acl.getPrincipal().isEmpty()) {
				for (String check : acl.getPrincipal()) {
					if(check.equals(context.getAuthentication().getName())) {
						return; // principal match
					}
				}
			}
			if(!acl.getRoles().isEmpty()) {
				for (String check : acl.getRoles()) {
					boolean found = false;
					if(context.getAuthentication().getAuthorities()!=null) {
						List<String> listaRuoliUtenza = context.getAuthentication().getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());
						if(listaRuoliUtenza!=null && !listaRuoliUtenza.isEmpty()) {
							for (String role : listaRuoliUtenza) {
								if(check.equals(role)) {
									if(!acl.isRolesMatchAll()) {
										return; // role match
									}
									else {
										found = true;
										break;
									}
								}			
							}
						}
					}
					if(acl.isRolesMatchAll() && !found) {
						break;
					}
				}
			}
			throw new Exception("Acl rule '"+acl.getName()+"' not satisfied");

		}catch(Exception eAuthorized) {
			FaultCode.AUTORIZZAZIONE.throwException(String.format("L'utente '%s' non è autorizzato ad invocare l'operazione '%s': %s",
					context.getAuthentication().getName(), context.getMethodName(), eAuthorized.getMessage()));
		}
	}
	
	private static String normalizePath(String path) {
		if(path.endsWith("/")) {
			return path.substring(0, path.length()-1);
		}
		else {
			return path;
		}
	}

}