blob: f453a166bea9872fe52eaece1cd5c23f94b91552 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.knox.gateway.filter;
import org.apache.knox.gateway.audit.api.Action;
import org.apache.knox.gateway.audit.api.ActionOutcome;
import org.apache.knox.gateway.audit.api.AuditServiceFactory;
import org.apache.knox.gateway.audit.api.Auditor;
import org.apache.knox.gateway.audit.api.ResourceType;
import org.apache.knox.gateway.audit.log4j.audit.AuditConstants;
import org.apache.knox.gateway.i18n.messages.MessagesFactory;
import org.apache.knox.gateway.security.GroupPrincipal;
import org.apache.knox.gateway.security.ImpersonatedPrincipal;
import org.apache.knox.gateway.security.PrimaryPrincipal;
import javax.security.auth.Subject;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.AccessController;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
public class AclsAuthorizationFilter implements Filter {
private static AclsAuthorizationMessages log = MessagesFactory.get( AclsAuthorizationMessages.class );
private static Auditor auditor = AuditServiceFactory.getAuditService().getAuditor( AuditConstants.DEFAULT_AUDITOR_NAME,
AuditConstants.KNOX_SERVICE_NAME, AuditConstants.KNOX_COMPONENT_NAME );
private String resourceRole;
private String aclProcessingMode;
private AclParser parser = new AclParser();
private List<String> adminGroups = new ArrayList<>();
private List<String> adminUsers = new ArrayList<>();
@Override
public void init(FilterConfig filterConfig) throws ServletException {
String adminGroups = filterConfig.getInitParameter("knox.admin.groups");
if (adminGroups != null) {
parseAdminGroupConfig(adminGroups);
}
String adminUsers = filterConfig.getInitParameter("knox.admin.users");
if (adminUsers != null) {
parseAdminUserConfig(adminUsers);
}
resourceRole = getInitParameter(filterConfig, "resource.role");
log.initializingForResourceRole(resourceRole);
aclProcessingMode = getInitParameter(filterConfig, resourceRole + ".acl.mode");
if (aclProcessingMode == null) {
aclProcessingMode = getInitParameter(filterConfig, "acl.mode");
if (aclProcessingMode == null) {
aclProcessingMode = "AND";
}
}
log.aclProcessingMode(aclProcessingMode);
String acls = getInitParameter(filterConfig, resourceRole + ".acl");
parser.parseAcls(resourceRole, acls);
}
private String getInitParameter(FilterConfig filterConfig, String paramName) {
return filterConfig.getInitParameter(paramName.toLowerCase(Locale.ROOT));
}
private void parseAdminGroupConfig(String groups) {
Collections.addAll(adminGroups, groups.split(","));
}
private void parseAdminUserConfig(String users) {
Collections.addAll(adminUsers, users.split(","));
}
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
boolean accessGranted = enforceAclAuthorizationPolicy(request, response, chain);
log.accessGranted(accessGranted);
String sourceUrl = (String)request.getAttribute( AbstractGatewayFilter.SOURCE_REQUEST_CONTEXT_URL_ATTRIBUTE_NAME );
if (accessGranted) {
auditor.audit( Action.AUTHORIZATION, sourceUrl, ResourceType.URI, ActionOutcome.SUCCESS );
chain.doFilter(request, response);
}
else {
auditor.audit( Action.AUTHORIZATION, sourceUrl, ResourceType.URI, ActionOutcome.FAILURE );
sendForbidden((HttpServletResponse) response);
}
}
protected boolean enforceAclAuthorizationPolicy(ServletRequest request,
ServletResponse response, FilterChain chain) {
HttpServletRequest req = (HttpServletRequest) request;
// before enforcing acls check whether there are no acls defined
// which would mean that there are no restrictions
if (parser.users.isEmpty() && parser.groups.isEmpty() && parser.ipv.getIPAddresses().isEmpty()) {
return true;
}
boolean userAccess;
boolean groupAccess = false;
boolean ipAddrAccess;
Subject subject = Subject.getSubject(AccessController.getContext());
Principal primaryPrincipal = (Principal)subject.getPrincipals(PrimaryPrincipal.class).toArray()[0];
log.primaryPrincipal(primaryPrincipal.getName());
Object[] impersonations = subject.getPrincipals(ImpersonatedPrincipal.class).toArray();
if (impersonations.length > 0) {
log.impersonatedPrincipal(((Principal)impersonations[0]).getName());
userAccess = checkUserAcls((Principal)impersonations[0]);
log.impersonatedPrincipalHasAccess(userAccess);
}
else {
userAccess = checkUserAcls(primaryPrincipal);
log.primaryPrincipalHasAccess(userAccess);
}
Object[] groups = subject.getPrincipals(GroupPrincipal.class).toArray();
if (groups.length > 0) {
groupAccess = checkGroupAcls(groups);
log.groupPrincipalHasAccess(groupAccess);
}
else {
// if we have no groups in the subject then make
// it true if there is an anyGroup acl
// for AND mode and acls like *;*;127.0.0.* we need to
// make it pass
if (parser.anyGroup && "AND".equals(aclProcessingMode)) {
groupAccess = true;
}
}
log.remoteIPAddress(req.getRemoteAddr());
ipAddrAccess = checkRemoteIpAcls(req.getRemoteAddr());
log.remoteIPAddressHasAccess(ipAddrAccess);
if ("OR".equals(aclProcessingMode)) {
// need to interpret '*' as excluded for OR semantics
// to make sense and not grant access to everyone by mistake.
// exclusion in OR is equivalent to denied
// so, let's set each one that contains '*' to false.
if (parser.anyUser) {
userAccess = false;
}
if (parser.anyGroup) {
groupAccess = false;
}
if (parser.ipv.allowsAnyIP()) {
ipAddrAccess = false;
}
return (userAccess || groupAccess || ipAddrAccess);
}
else if ("AND".equals(aclProcessingMode)) {
return (userAccess && groupAccess && ipAddrAccess);
}
return false;
}
private boolean checkRemoteIpAcls(String remoteAddr) {
boolean allowed;
if (remoteAddr == null) {
return false;
}
allowed = parser.ipv.validateIpAddress(remoteAddr);
return allowed;
}
boolean checkUserAcls(Principal user) {
boolean allowed = false;
if (user == null) {
return false;
}
if (parser.anyUser) {
allowed = true;
}
else {
if (parser.users.contains(user.getName())) {
allowed = true;
}
else if (parser.users.contains("KNOX_ADMIN_USERS") &&
adminUsers.contains(user.getName())) {
allowed = true;
}
}
return allowed;
}
boolean checkGroupAcls(Object[] userGroups) {
boolean allowed;
if (userGroups == null) {
return false;
}
if (parser.anyGroup) {
allowed = true;
}
else {
allowed = hasAllowedPrincipal(parser.groups, userGroups);
if (!allowed && parser.groups.contains("KNOX_ADMIN_GROUPS")) {
allowed = hasAllowedPrincipal(adminGroups, userGroups);
}
}
return allowed;
}
private boolean hasAllowedPrincipal(List<String> allowed, Object[] userGroups) {
boolean rc = false;
for (Object userGroup : userGroups) {
if (allowed.contains(((Principal) userGroup).getName())) {
rc = true;
break;
}
}
return rc;
}
private void sendForbidden(HttpServletResponse res) {
sendErrorCode(res, 403);
}
private void sendErrorCode(HttpServletResponse res, int code) {
try {
res.sendError(code);
} catch (IOException e) {
// TODO: log appropriately
e.printStackTrace();
}
}
}