All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alibaba.cloud.governance.auth.validator.AuthValidator Maven / Gradle / Ivy

There is a newer version: 2.2.11
Show newest version
/*
 * Copyright 2022-2023 the original author or authors.
 *
 * 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
 *
 *      https://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.alibaba.cloud.governance.auth.validator;

import java.util.List;
import java.util.Map;

import com.alibaba.cloud.commons.governance.auth.condition.AuthCondition;
import com.alibaba.cloud.commons.governance.auth.rule.AuthRule;
import com.alibaba.cloud.commons.governance.auth.rule.JwtRule;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.cloud.commons.matcher.Matcher;
import com.alibaba.cloud.governance.auth.repository.AuthRepository;
import com.alibaba.cloud.governance.auth.util.JwtUtil;
import org.jose4j.jwt.JwtClaims;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.http.HttpHeaders;
import org.springframework.util.MultiValueMap;

/**
 * Use a abstract rule tree to validate the request. First, if the rules are all empty, we
 * just return true. Secondly, if any deny rule matches the request, we just return false.
 * Thirdly, if the allow rules are empty, we just return true. Last, if any allow rule
 * matches the request, we just return true, or we return false.
 *
 * @author musi
 * @author 
 * @since 2.2.10-RC1
 */
public class AuthValidator {

	private static final Logger log = LoggerFactory.getLogger(AuthValidator.class);

	private final AuthRepository authRepository;

	public AuthValidator(AuthRepository authRepository) {
		this.authRepository = authRepository;
	}

	public boolean validate(UnifiedHttpRequest request) {
		if (request == null) {
			return false;
		}

		for (JwtRule jwtRule : authRepository.getJwtRules().values()) {
			// extract jwt token from request first
			String token = JwtUtil.getTokenFromJwtRule(request.getParams(),
					request.getHeaders(), jwtRule);
			if (!StringUtils.isEmpty(token)) {
				JwtClaims jwtClaims = JwtUtil.extractJwtClaims(jwtRule, token);
				if (jwtClaims == null) {
					return false;
				}
				request.jwtClaims = jwtClaims;
				break;
			}
		}

		Map denyRules = authRepository.getDenyAuthRules();
		for (AuthRule denyRule : denyRules.values()) {
			if (validateRule(denyRule, request)) {
				return false;
			}
		}

		Map allowRules = authRepository.getAllowAuthRules();

		if (allowRules.isEmpty()) {
			return true;
		}

		for (AuthRule allowRule : allowRules.values()) {
			if (validateRule(allowRule, request)) {
				return true;
			}
		}
		return false;
	}

	private boolean validateRule(AuthRule rule, UnifiedHttpRequest request) {
		if (rule.isLeaf()) {
			return validateLeafRule(rule, request);
		}

		List authRules = rule.getChildren();
		boolean flag = rule.getOp() != AuthRule.RuleOperation.OR;
		for (AuthRule authRule : authRules) {
			if (rule.getOp() == AuthRule.RuleOperation.OR && flag) {
				break;
			}
			if (rule.getOp() == AuthRule.RuleOperation.AND && !flag) {
				break;
			}
			boolean childFlag = validateRule(authRule, request);
			switch (rule.getOp()) {
			case OR:
				flag |= childFlag;
				break;
			case AND:
				flag &= childFlag;
				break;
			}
		}
		// if "is not" is true, reverse the flag
		return rule.isNot() != flag;
	}

	private boolean validateLeafRule(AuthRule rule, UnifiedHttpRequest request) {
		try {
			AuthCondition condition = rule.getCondition();
			Matcher matcher = condition.getMatcher();
			String key = condition.getKey();
			if (matcher == null) {
				return false;
			}
			switch (condition.getType()) {
			case SOURCE_IP:
				return matcher.match(request.getSourceIp());
			case REMOTE_IP:
				return matcher.match(request.getRemoteIp());
			case DEST_IP:
				return matcher.match(request.getDestIp());
			// string
			case HOSTS:
				return matcher.match(request.getHost());
			case METHODS:
				return matcher.match(request.getMethod());
			case PATHS:
				return matcher.match(request.getPath());
			case REQUEST_PRINCIPALS:
			case AUTH_AUDIENCES:
			case AUTH_PRESENTERS:
				// if there is an AuthorizationPolicy but there is not a
				// RequestAuthentication, return false
				if (request.getJwtClaims() == null) {
					return false;
				}
				JwtClaims claims = request.getJwtClaims();
				switch (condition.getType()) {
				case REQUEST_PRINCIPALS:
					String issuer = claims.getIssuer();
					String subject = claims.getSubject();
					return matcher.match(issuer + "/" + subject);
				case AUTH_AUDIENCES:
					List audiences = claims.getAudience();
					for (String audience : audiences) {
						if (matcher.match(audience)) {
							return true;
						}
					}
					return false;
				case AUTH_PRESENTERS:
					return matcher.match(claims.getClaimValueAsString("azp"));
				}
				return false;
			// int
			case PORTS:
				return matcher.match(request.getPort());
			// header
			case HEADER:
				HttpHeaders headers = request.getHeaders();
				if (!headers.containsKey(key)) {
					return false;
				}
				List headerList = headers.get(key);
				if (headerList == null) {
					return false;
				}
				for (String header : headerList) {
					if (matcher.match(header)) {
						return true;
					}
				}
				return false;
			case AUTH_CLAIMS:
				claims = request.getJwtClaims();
				if (claims == null) {
					return false;
				}
				Object claimValue = claims.getClaimValue(key);

				if (claimValue instanceof List) {
					for (String claim : claims.getStringListClaimValue(key)) {
						if (matcher.match(claim)) {
							return true;
						}
					}
				}
				else {
					return matcher.match(claims.getStringClaimValue(key));
				}
				return false;
			}
		}
		catch (Exception e) {
			log.warn("Request {} doesn't match rule {}", request, rule);
		}
		return false;
	}

	public final static class UnifiedHttpRequest {

		private final String sourceIp;

		private final String destIp;

		private final String remoteIp;

		private final String host;

		private final int port;

		private final String method;

		private final String path;

		private final HttpHeaders headers;

		private final MultiValueMap params;

		private JwtClaims jwtClaims;

		private UnifiedHttpRequest(String sourceIp, String destIp, String remoteIp,
				String host, int port, String method, String path, HttpHeaders headers,
				MultiValueMap params) {
			this.sourceIp = sourceIp;
			this.destIp = destIp;
			this.remoteIp = remoteIp;
			this.host = host;
			this.port = port;
			this.method = method;
			this.path = path;
			this.headers = headers;
			this.params = params;
		}

		public String getSourceIp() {
			return sourceIp;
		}

		public String getDestIp() {
			return destIp;
		}

		public String getRemoteIp() {
			return remoteIp;
		}

		public String getHost() {
			return host;
		}

		public int getPort() {
			return port;
		}

		public String getMethod() {
			return method;
		}

		public String getPath() {
			return path;
		}

		public HttpHeaders getHeaders() {
			return headers;
		}

		public MultiValueMap getParams() {
			return params;
		}

		public JwtClaims getJwtClaims() {
			return jwtClaims;
		}

		public static class UnifiedHttpRequestBuilder {

			private String sourceIp;

			private String destIp;

			private String remoteIp;

			private String host;

			private int port;

			private String method;

			private String path;

			private HttpHeaders headers;

			private MultiValueMap params;

			public UnifiedHttpRequestBuilder setSourceIp(String sourceIp) {
				this.sourceIp = sourceIp;
				return this;
			}

			public UnifiedHttpRequestBuilder setDestIp(String destIp) {
				this.destIp = destIp;
				return this;
			}

			public UnifiedHttpRequestBuilder setRemoteIp(String remoteIp) {
				this.remoteIp = remoteIp;
				return this;
			}

			public UnifiedHttpRequestBuilder setHost(String host) {
				this.host = host;
				return this;
			}

			public UnifiedHttpRequestBuilder setPort(int port) {
				this.port = port;
				return this;
			}

			public UnifiedHttpRequestBuilder setMethod(String method) {
				this.method = method;
				return this;
			}

			public UnifiedHttpRequestBuilder setPath(String path) {
				this.path = path;
				return this;
			}

			public UnifiedHttpRequestBuilder setHeaders(HttpHeaders headers) {
				this.headers = headers;
				return this;
			}

			public UnifiedHttpRequestBuilder setParams(
					MultiValueMap params) {
				this.params = params;
				return this;
			}

			public UnifiedHttpRequest build() {
				return new UnifiedHttpRequest(sourceIp, destIp, remoteIp, host, port,
						method, path, headers, params);
			}

		}

	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy