/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.dqp.internal.process;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.teiid.PolicyDecider;
import org.teiid.adminapi.DataPolicy.Context;
import org.teiid.adminapi.DataPolicy.PermissionType;
import org.teiid.api.exception.query.QueryValidatorException;
import org.teiid.core.CoreConstants;
import org.teiid.core.TeiidComponentException;
import org.teiid.core.types.DataTypeManager;
import org.teiid.core.types.TransformationException;
import org.teiid.core.util.PropertiesUtils;
import org.teiid.dqp.internal.process.multisource.MultiSourceElement;
import org.teiid.metadata.AbstractMetadataRecord;
import org.teiid.metadata.FunctionMethod;
import org.teiid.metadata.Procedure;
import org.teiid.metadata.Schema;
import org.teiid.query.QueryPlugin;
import org.teiid.query.metadata.QueryMetadataInterface;
import org.teiid.query.metadata.TempMetadataID;
import org.teiid.query.sql.LanguageObject;
import org.teiid.query.sql.lang.Command;
import org.teiid.query.sql.lang.Query;
import org.teiid.query.sql.symbol.ElementSymbol;
import org.teiid.query.sql.symbol.Expression;
import org.teiid.query.sql.symbol.MultipleElementSymbol;
import org.teiid.query.util.CommandContext;
/**
* The default Teiid authorization validator
*/
public class DefaultAuthorizationValidator implements AuthorizationValidator {
public static final String IGNORE_UNAUTHORIZED_ASTERISK = "ignore_unauthorized_asterisk"; //$NON-NLS-1$
private PolicyDecider policyDecider;
private boolean ignoreUnauthorizedAsteriskDefault = PropertiesUtils.getBooleanProperty(System.getProperties(), "org.teiid.ignoreUnauthorizedAsterisk", false); //$NON-NLS-1$
public DefaultAuthorizationValidator() {
}
@Override
public boolean validate(String[] originalSql, Command command,
QueryMetadataInterface metadata, CommandContext commandContext,
CommandType commandType) throws QueryValidatorException,
TeiidComponentException {
boolean modified = false;
if (policyDecider != null && policyDecider.validateCommand(commandContext)) {
if (ignoreUnathorizedInAsterisk(command, commandContext)) {
Query query = (Query)command;
HashMap<String, LanguageObject> map = null;
for (Expression ex : query.getSelect().getSymbols()) {
if (ex instanceof MultipleElementSymbol) {
MultipleElementSymbol mes = (MultipleElementSymbol)ex;
if (map == null) {
map = new HashMap<String, LanguageObject>();
}
for (Iterator<ElementSymbol> iter = mes.getElementSymbols().iterator(); iter.hasNext();) {
ElementSymbol es = iter.next();
Object metadataObject = es.getMetadataID();
if (metadataObject instanceof MultiSourceElement || metadataObject instanceof TempMetadataID) {
continue;
}
map.clear();
AuthorizationValidationVisitor.addToNameMap(metadataObject, es, map, commandContext.getMetadata());
Set<String> results = this.policyDecider.getInaccessibleResources(PermissionType.READ, map.keySet(), Context.QUERY, commandContext);
if (!results.isEmpty()) {
iter.remove(); //remove from the select
modified = true;
}
}
}
}
if (query.getProjectedSymbols().isEmpty()) {
throw new QueryValidatorException(QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31151));
}
}
AuthorizationValidationVisitor visitor = new AuthorizationValidationVisitor(this.policyDecider, commandContext);
Request.validateWithVisitor(visitor, metadata, command);
}
return modified;
}
private boolean ignoreUnathorizedInAsterisk(Command command, CommandContext commandContext) {
if (!(command instanceof Query)) {
return false;
}
Query query = (Query)command;
if (query.getIsXML() || query.getInto() != null) {
return false;
}
if (ignoreUnauthorizedAsteriskDefault) {
return true;
}
Object value = commandContext.getSessionVariable(IGNORE_UNAUTHORIZED_ASTERISK);
if (value != null) {
try {
return Boolean.TRUE.equals(DataTypeManager.transformValue(value, DataTypeManager.DefaultDataClasses.BOOLEAN));
} catch (TransformationException e) {
}
}
return false;
}
@Override
public boolean hasRole(String roleName, CommandContext commandContext) {
if (policyDecider == null) {
return true;
}
return this.policyDecider.hasRole(roleName, commandContext);
}
public void setPolicyDecider(PolicyDecider policyDecider) {
this.policyDecider = policyDecider;
}
@Override
public boolean isAccessible(AbstractMetadataRecord record,
CommandContext commandContext) {
if (policyDecider == null || !policyDecider.validateCommand(commandContext)
//TODO - schemas cannot be hidden - unless we traverse them and find that nothing is accessible
|| record instanceof Schema) {
return true;
}
AbstractMetadataRecord parent = record;
while (parent.getParent() != null) {
parent = parent.getParent();
if (parent instanceof Procedure) {
return true; //don't check procedure params/rs columns
}
}
if (!(parent instanceof Schema) || (CoreConstants.SYSTEM_MODEL.equalsIgnoreCase(parent.getName()) || CoreConstants.ODBC_MODEL.equalsIgnoreCase(parent.getName()))) {
//access is always allowed to system tables / procedures or unrooted objects
return true;
}
PermissionType action = PermissionType.READ;
if (record instanceof FunctionMethod || record instanceof Procedure) {
action = PermissionType.EXECUTE;
}
//cache permission check
Boolean result = commandContext.isAccessible(record);
if (result != null) {
return result;
}
HashSet<String> resources = new HashSet<String>(2);
resources.add(record.getFullName());
result = this.policyDecider.getInaccessibleResources(action, resources, Context.METADATA, commandContext).isEmpty();
commandContext.setAccessible(record, result);
return result;
}
}