/**
* 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.hadoop.gateway.filter;
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 org.apache.hadoop.gateway.audit.api.Action;
import org.apache.hadoop.gateway.audit.api.ActionOutcome;
import org.apache.hadoop.gateway.audit.api.AuditServiceFactory;
import org.apache.hadoop.gateway.audit.api.Auditor;
import org.apache.hadoop.gateway.audit.api.ResourceType;
import org.apache.hadoop.gateway.audit.log4j.audit.AuditConstants;
import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
import org.apache.hadoop.gateway.security.GroupPrincipal;
import org.apache.hadoop.gateway.security.ImpersonatedPrincipal;
import org.apache.hadoop.gateway.security.PrimaryPrincipal;
import org.apache.hadoop.gateway.util.IpAddressValidator;
import org.apache.hadoop.gateway.util.urltemplate.Template;
import java.io.IOException;
import java.security.AccessController;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Collections;
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 = null;
private String aclProcessingMode = null;
private AclParser parser = new AclParser();
@Override
public void init(FilterConfig filterConfig) throws ServletException {
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());
}
public void destroy() {
}
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);
}
}
private 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.size() == 0 && parser.groups.size() == 0 && parser.ipv.getIPAddresses().size() == 0) {
return true;
}
boolean userAccess = false;
boolean groupAccess = false;
boolean ipAddrAccess = false;
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) {
// System.out.println("GroupPrincipal: " + ((Principal)groups[0]).getName());
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 && aclProcessingMode.equals("AND")) {
groupAccess = true;
}
}
log.remoteIPAddress(req.getRemoteAddr());
ipAddrAccess = checkRemoteIpAcls(req.getRemoteAddr());
log.remoteIPAddressHasAccess(ipAddrAccess);
if (aclProcessingMode.equals("OR")) {
// 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 (aclProcessingMode.equals("AND")) {
return (userAccess && groupAccess && ipAddrAccess);
}
return false;
}
private boolean checkRemoteIpAcls(String remoteAddr) {
boolean allowed = false;
if (remoteAddr == null) {
return false;
}
allowed = parser.ipv.validateIpAddress(remoteAddr);
return allowed;
}
private 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;
}
}
return allowed;
}
private boolean checkGroupAcls(Object[] userGroups) {
boolean allowed = false;
if (userGroups == null) {
return false;
}
if (parser.anyGroup) {
allowed = true;
}
else {
for (int i = 0; i < userGroups.length; i++) {
if (parser.groups.contains(((Principal)userGroups[i]).getName())) {
allowed = true;
break;
}
}
}
return allowed;
}
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();
}
}
}