blob: 3f1b0e869496851b792aefca4b3ac917ccda7965 [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.impala.authorization.sentry;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import org.apache.impala.authorization.Authorizable.Type;
import org.apache.impala.authorization.AuthorizationConfig;
import org.apache.impala.authorization.AuthorizationContext;
import org.apache.impala.authorization.AuthorizationException;
import org.apache.impala.authorization.BaseAuthorizationChecker;
import org.apache.impala.authorization.Privilege;
import org.apache.impala.authorization.PrivilegeRequest;
import org.apache.impala.authorization.User;
import org.apache.impala.authorization.AuthorizationPolicy;
import org.apache.impala.common.InternalException;
import org.apache.impala.thrift.TSessionState;
import org.apache.impala.util.EventSequence;
import org.apache.sentry.core.common.ActiveRoleSet;
import org.apache.sentry.core.common.Subject;
import org.apache.sentry.core.model.db.DBModelAuthorizable;
import org.apache.sentry.provider.common.ResourceAuthorizationProvider;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
/**
* An implementation of AuthorizationChecker that uses Sentry.
*/
public class SentryAuthorizationChecker extends BaseAuthorizationChecker {
private final ResourceAuthorizationProvider provider_;
private final SentryAuthorizableServer server_;
public SentryAuthorizationChecker(AuthorizationConfig config,
AuthorizationPolicy policy) {
super(config);
if (config.isEnabled()) {
server_ = new SentryAuthorizableServer(config.getServerName());
provider_ = createProvider(config, new SentryAuthorizationPolicy(policy));
Preconditions.checkNotNull(provider_);
} else {
provider_ = null;
server_ = null;
}
}
/**
* Returns the set of groups this user belongs to.
*/
public Set<String> getUserGroups(User user) throws InternalException {
try {
return provider_.getGroupMapping().getGroups(user.getShortName());
} catch (Exception e) {
if (SentryUtil.isSentryGroupNotFound(e)) {
// Sentry 2.1+ throws exceptions when user does not exist; swallow the
// exception and just return an empty set for this case.
return Collections.emptySet();
}
throw new RuntimeException(e);
}
}
@Override
protected void authorizeRowFilterAndColumnMask(User user,
List<PrivilegeRequest> privilegeRequests)
throws AuthorizationException, InternalException {
}
@Override
public void invalidateAuthorizationCache() {
// Authorization refresh in Sentry is done by updating {@link AuthorizationPolicy}.
}
@Override
public boolean needsMaskingOrFiltering(User user, String dbName, String tableName,
List<String> requiredColumns) {
return false;
}
@Override
public String createColumnMask(User user, String dbName, String tableName,
String columnName) {
return columnName;
}
@Override
public AuthorizationContext createAuthorizationContext(boolean doAudits,
String sqlStmt, TSessionState sessionState, Optional<EventSequence> timeline) {
return new AuthorizationContext(timeline);
}
/*
* Creates a new ResourceAuthorizationProvider based on the given configuration.
*/
private static ResourceAuthorizationProvider createProvider(AuthorizationConfig config,
SentryAuthorizationPolicy policy) {
return SentryAuthProvider.createProvider(config, policy);
}
@Override
public boolean authorizeResource(AuthorizationContext authzCtx, User user,
PrivilegeRequest request) throws InternalException {
EnumSet<ImpalaAction> actions = ImpalaAction.from(request.getPrivilege());
List<DBModelAuthorizable> authorizables = Lists.newArrayList(
server_.getDBModelAuthorizableHierarchy());
// If request.getAuthorizable() is null, the request is for server-level permission.
if (request.getAuthorizable() != null) {
Preconditions.checkState(request.getAuthorizable() instanceof SentryAuthorizable);
authorizables.addAll(((SentryAuthorizable) request.getAuthorizable())
.getDBModelAuthorizableHierarchy());
}
// The Hive Access API does not currently provide a way to check if the user
// has any privileges on a given resource.
if (request.getPrivilege().hasAnyOf()) {
for (ImpalaAction action: actions) {
if (provider_.hasAccess(new Subject(user.getShortName()), authorizables,
EnumSet.of(action), request.hasGrantOption(), ActiveRoleSet.ALL)) {
return true;
}
}
return false;
// AuthorizableFn is special due to Sentry not having the concept of a function in
// DBModelAuthorizable.AuthorizableType. As a result, the list of authorizables for
// an AuthorizableFn only contains the server and database, but not the function
// itself. So there is no need to remove the last authorizable here.
} else if (request.getPrivilege() == Privilege.CREATE && authorizables.size() > 1 &&
!(request.getAuthorizable().getType() == Type.FUNCTION)) {
// CREATE on an object requires CREATE on the parent,
// so don't check access on the object we're creating.
authorizables.remove(authorizables.size() - 1);
}
return provider_.hasAccess(new Subject(user.getShortName()), authorizables, actions,
request.hasGrantOption(), ActiveRoleSet.ALL);
}
}