http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/thrift/SentryGenericPolicyProcessor.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/thrift/SentryGenericPolicyProcessor.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/thrift/SentryGenericPolicyProcessor.java new file mode 100644 index 0000000..e59d12a --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/thrift/SentryGenericPolicyProcessor.java @@ -0,0 +1,835 @@ +/** + * 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.sentry.provider.db.generic.service.thrift; + +import static org.apache.sentry.core.common.utils.SentryConstants.AUTHORIZABLE_JOINER; +import static org.apache.sentry.core.common.utils.SentryConstants.KV_JOINER; + +import java.lang.reflect.Constructor; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.hadoop.conf.Configuration; +import org.apache.sentry.core.common.exception.SentryUserException; +import org.apache.sentry.core.common.Authorizable; +import org.apache.sentry.core.common.utils.SentryConstants; +import org.apache.sentry.core.common.exception.SentrySiteConfigurationException; +import org.apache.sentry.core.model.db.AccessConstants; +import org.apache.sentry.core.common.utils.KeyValue; +import org.apache.sentry.core.common.utils.AuthorizationComponent; +import org.apache.sentry.core.common.exception.SentryAccessDeniedException; +import org.apache.sentry.core.common.exception.SentryAlreadyExistsException; +import org.apache.sentry.core.common.exception.SentryInvalidInputException; +import org.apache.sentry.core.common.exception.SentryNoSuchObjectException; +import org.apache.sentry.core.common.exception.SentryThriftAPIMismatchException; +import org.apache.sentry.provider.db.generic.service.persistent.PrivilegeObject; +import org.apache.sentry.provider.db.generic.service.persistent.PrivilegeObject.Builder; +import org.apache.sentry.provider.db.generic.service.persistent.SentryStoreLayer; +import org.apache.sentry.provider.db.log.entity.JsonLogEntityFactory; +import org.apache.sentry.provider.db.log.util.Constants; +import org.apache.sentry.provider.db.service.model.MSentryGMPrivilege; +import org.apache.sentry.provider.db.service.model.MSentryRole; +import org.apache.sentry.provider.db.service.persistent.CommitContext; +import org.apache.sentry.provider.db.service.thrift.PolicyStoreConstants; +import org.apache.sentry.provider.db.service.thrift.SentryPolicyStoreProcessor; +import org.apache.sentry.service.thrift.ServiceConstants.ServerConfig; +import org.apache.sentry.service.thrift.ServiceConstants.ThriftConstants; +import org.apache.sentry.service.thrift.ServiceConstants; +import org.apache.sentry.service.thrift.Status; +import org.apache.sentry.service.thrift.TSentryResponseStatus; +import org.apache.thrift.TException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Splitter; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +public class SentryGenericPolicyProcessor implements SentryGenericPolicyService.Iface { + private static final Logger LOGGER = LoggerFactory.getLogger(SentryGenericPolicyProcessor.class); + private static final Logger AUDIT_LOGGER = LoggerFactory + .getLogger(Constants.AUDIT_LOGGER_NAME_GENERIC); + private final Configuration conf; + private final ImmutableSet<String> adminGroups; + private final SentryStoreLayer store; + private final NotificationHandlerInvoker handerInvoker; + + private static final String ACCESS_DENIAL_MESSAGE = "Access denied to "; + + public SentryGenericPolicyProcessor(Configuration conf) throws Exception { + this.store = createStore(conf); + this.handerInvoker = new NotificationHandlerInvoker(createHandlers(conf)); + this.conf = conf; + adminGroups = ImmutableSet.copyOf((Sets.newHashSet(conf.getStrings( + ServerConfig.ADMIN_GROUPS, new String[]{})))); + } + + @VisibleForTesting + public SentryGenericPolicyProcessor(Configuration conf, SentryStoreLayer store) throws Exception { + this.store = store; + this.handerInvoker = new NotificationHandlerInvoker(createHandlers(conf)); + this.conf = conf; + adminGroups = ImmutableSet.copyOf(toTrimmed(Sets.newHashSet(conf.getStrings( + ServerConfig.ADMIN_GROUPS, new String[]{})))); + } + + private void authorize(String requestorUser, Set<String> requestorGroups) + throws SentryAccessDeniedException { + if (!inAdminGroups(requestorGroups)) { + String msg = "User: " + requestorUser + " is part of " + requestorGroups + + " which does not, intersect admin groups " + adminGroups; + LOGGER.warn(msg); + throw new SentryAccessDeniedException(ACCESS_DENIAL_MESSAGE + requestorUser); + } + } + + private Set<String> toTrimmedLower(Set<String> s) { + if (null == s) { + return new HashSet<String>(); + } + Set<String> result = Sets.newHashSet(); + for (String v : s) { + result.add(v.trim().toLowerCase()); + } + return result; + } + + private Set<String> toTrimmed(Set<String> s) { + if (null == s) { + return new HashSet<String>(); + } + Set<String> result = Sets.newHashSet(); + for (String v : s) { + result.add(v.trim()); + } + return result; + } + + private String toTrimmedLower(String s) { + if (Strings.isNullOrEmpty(s)){ + return ""; + } + return s.trim().toLowerCase(); + } + + public static Set<String> getRequestorGroups(Configuration conf, String userName) throws SentryUserException { + return SentryPolicyStoreProcessor.getGroupsFromUserName(conf, userName); + } + + private boolean inAdminGroups(Set<String> requestorGroups) { + if (Sets.intersection(adminGroups, requestorGroups).isEmpty()) { + return false; + } + return true; + } + + public static SentryStoreLayer createStore(Configuration conf) throws SentrySiteConfigurationException { + SentryStoreLayer storeLayer = null; + String store = conf.get(PolicyStoreConstants.SENTRY_GENERIC_POLICY_STORE, PolicyStoreConstants.SENTRY_GENERIC_POLICY_STORE_DEFAULT); + + if (Strings.isNullOrEmpty(store)) { + throw new SentrySiteConfigurationException("sentry.generic.policy.store can not be empty"); + } + try { + storeLayer = createInstance(store, conf, SentryStoreLayer.class); + } catch (Exception e) { + throw new SentrySiteConfigurationException("Create sentryStore error: " + e.getMessage(), e); + } + return storeLayer; + } + + public static List<NotificationHandler> createHandlers(Configuration conf) throws SentrySiteConfigurationException { + + List<NotificationHandler> handlers = Lists.newArrayList(); + Iterable<String> notificationHandlers = Splitter.onPattern("[\\s,]").trimResults() + .omitEmptyStrings().split(conf.get(PolicyStoreConstants.SENTRY_GENERIC_POLICY_NOTIFICATION, "")); + try { + for (String notificationHandler : notificationHandlers) { + handlers.add(createInstance(notificationHandler, conf, NotificationHandler.class)); + } + } catch (Exception e) { + throw new SentrySiteConfigurationException("Create notificationHandlers error: " + e.getMessage(), e); + } + return handlers; + } + + @SuppressWarnings("unchecked") + public static <T> T createInstance(String className, Configuration conf, Class<T> iface) throws Exception { + T result; + try { + Class<?> clazz = Class.forName(className); + if (!iface.isAssignableFrom(clazz)) { + throw new IllegalArgumentException("Class " + clazz + " is not a " + + iface.getName()); + } + Constructor<T> meth = (Constructor<T>)clazz.getDeclaredConstructor(Configuration.class); + meth.setAccessible(true); + result = meth.newInstance(new Object[]{conf}); + } catch (Exception e) { + throw new RuntimeException(e); + } + return result; + } + + private <T> Response<T> requestHandle(RequestHandler<T> handler) { + Response<T> response = new Response<T>(); + try { + response = handler.handle(); + } catch (SentryAccessDeniedException e) { + String msg = "Sentry access denied: " + e.getMessage(); + LOGGER.error(msg, e); + response.status = Status.AccessDenied(e.getMessage(), e); + } catch (SentryAlreadyExistsException e) { + String msg = "Sentry object already exists: " + e.getMessage(); + LOGGER.error(msg, e); + response.status = Status.AlreadyExists(e.getMessage(), e); + } catch (SentryNoSuchObjectException e) { + String msg = "Sentry object doesn't exist: " + e.getMessage(); + LOGGER.error(msg, e); + response.status = Status.NoSuchObject(e.getMessage(), e); + } catch (SentryInvalidInputException e) { + String msg = "Invalid input privilege object: " + e.getMessage(); + LOGGER.error(msg, e); + response.status = Status.InvalidInput(msg, e); + } catch (SentryThriftAPIMismatchException e) { + String msg = "Sentry thrift API mismatch error: " + e.getMessage(); + LOGGER.error(msg, e); + response.status = Status.THRIFT_VERSION_MISMATCH(e.getMessage(), e); + } catch (Exception e) { + String msg = "Unknown error:" + e.getMessage(); + LOGGER.error(msg, e); + response.status = Status.RuntimeError(msg, e); + } + return response; + } + + private PrivilegeObject toPrivilegeObject(TSentryPrivilege tSentryPrivilege) { + Boolean grantOption; + if (tSentryPrivilege.getGrantOption().equals(TSentryGrantOption.TRUE)) { + grantOption = true; + } else if (tSentryPrivilege.getGrantOption().equals(TSentryGrantOption.FALSE)) { + grantOption = false; + } else { + grantOption = null; + } + return new Builder().setComponent(tSentryPrivilege.getComponent()) + .setService(tSentryPrivilege.getServiceName()) + .setAuthorizables(toAuthorizables(tSentryPrivilege.getAuthorizables())) + .setAction(tSentryPrivilege.getAction()) + .withGrantOption(grantOption) + .build(); + } + + private TSentryPrivilege fromPrivilegeObject(PrivilegeObject privilege) { + + TSentryPrivilege tPrivilege = new TSentryPrivilege(privilege.getComponent(), privilege.getService(), + fromAuthorizable(privilege.getAuthorizables()), + privilege.getAction()); + if (privilege.getGrantOption() == null) { + tPrivilege.setGrantOption(TSentryGrantOption.UNSET); + } else if (privilege.getGrantOption()) { + tPrivilege.setGrantOption(TSentryGrantOption.TRUE); + } else { + tPrivilege.setGrantOption(TSentryGrantOption.FALSE); + } + return tPrivilege; + } + + private List<TAuthorizable> fromAuthorizable(List<? extends Authorizable> authorizables) { + List<TAuthorizable> tAuthorizables = Lists.newArrayList(); + for (Authorizable authorizable : authorizables) { + tAuthorizables.add(new TAuthorizable(authorizable.getTypeName(), authorizable.getName())); + } + return tAuthorizables; + } + + private String fromAuthorizableToStr(List<? extends Authorizable> authorizables) { + if (authorizables != null && !authorizables.isEmpty()) { + List<String> privileges = Lists.newArrayList(); + + for (Authorizable authorizable : authorizables) { + + privileges.add(SentryConstants.KV_JOINER.join(authorizable.getTypeName(), + authorizable.getName())); + } + + return SentryConstants.AUTHORIZABLE_JOINER.join(privileges); + } else { + return ""; + } + } + + private List<? extends Authorizable> toAuthorizables(List<TAuthorizable> tAuthorizables) { + List<Authorizable> authorizables = Lists.newArrayList(); + if (tAuthorizables == null) { + return authorizables; + } + for (final TAuthorizable tAuthorizable : tAuthorizables) { + authorizables.add(new Authorizable() { + @Override + public String getTypeName() { + return tAuthorizable.getType(); + } + @Override + public String getName() { + return tAuthorizable.getName(); + } + }); + } + return authorizables; + } + + private List<? extends Authorizable> toAuthorizables(String privilegeStr) { + List<Authorizable> authorizables = Lists.newArrayList(); + if (privilegeStr == null) { + return authorizables; + } + + for (String authorizable : SentryConstants.AUTHORIZABLE_SPLITTER.split(privilegeStr)) { + KeyValue tempKV = new KeyValue(authorizable); + final String key = tempKV.getKey(); + final String value = tempKV.getValue(); + + authorizables.add(new Authorizable() { + @Override + public String getTypeName() { + return key; + } + + @Override + public String getName() { + return value; + } + }); + } + + return authorizables; + } + + // Construct the role to set of privileges mapping based on the + // MSentryGMPrivilege information. + private TSentryPrivilegeMap toTSentryPrivilegeMap(Set<MSentryGMPrivilege> mPrivileges) { + + // Mapping of <Role, Set<Privilege>>. + Map<String, Set<TSentryPrivilege>> tPrivilegeMap = Maps.newTreeMap(); + + for (MSentryGMPrivilege mPrivilege : mPrivileges) { + for (MSentryRole role : mPrivilege.getRoles()) { + + TSentryPrivilege tPrivilege = toTSentryPrivilege(mPrivilege); + + if (tPrivilegeMap.containsKey(role.getRoleName())) { + tPrivilegeMap.get(role.getRoleName()).add(tPrivilege); + } else { + Set<TSentryPrivilege> tPrivilegeSet = Sets.newTreeSet(); + tPrivilegeSet.add(tPrivilege); + tPrivilegeMap.put(role.getRoleName(), tPrivilegeSet); + } + } + } + + return new TSentryPrivilegeMap(tPrivilegeMap); + } + + // Construct TSentryPrivilege based on MSentryGMPrivilege information. + private TSentryPrivilege toTSentryPrivilege(MSentryGMPrivilege mPrivilege) { + + TSentryPrivilege tPrivilege = new TSentryPrivilege(mPrivilege.getComponentName(), + mPrivilege.getServiceName(), fromAuthorizable(mPrivilege.getAuthorizables()), mPrivilege.getAction()); + + if (mPrivilege.getGrantOption() == null) { + tPrivilege.setGrantOption(TSentryGrantOption.UNSET); + } else if (mPrivilege.getGrantOption()) { + tPrivilege.setGrantOption(TSentryGrantOption.TRUE); + } else { + tPrivilege.setGrantOption(TSentryGrantOption.FALSE); + } + + return tPrivilege; + } + + private Set<String> buildPermissions(Set<PrivilegeObject> privileges) { + Set<String> permissions = Sets.newHashSet(); + for (PrivilegeObject privilege : privileges) { + List<String> hierarchy = Lists.newArrayList(); + if (hasComponentServerPrivilege(privilege.getComponent())) { + hierarchy.add(KV_JOINER.join("server", privilege.getService())); + } + for (Authorizable authorizable : privilege.getAuthorizables()) { + hierarchy.add(KV_JOINER.join(authorizable.getTypeName(),authorizable.getName())); + } + hierarchy.add(KV_JOINER.join("action", privilege.getAction())); + permissions.add(AUTHORIZABLE_JOINER.join(hierarchy)); + } + return permissions; + } + + private boolean hasComponentServerPrivilege(String component) { + //judge the component whether has the server privilege, for example: sqoop has the privilege on the server + return AuthorizationComponent.SQOOP.equalsIgnoreCase(component); + } + + @Override + public TCreateSentryRoleResponse create_sentry_role( + final TCreateSentryRoleRequest request) throws TException { + Response<Void> respose = requestHandle(new RequestHandler<Void>() { + @Override + public Response<Void> handle() throws Exception { + validateClientVersion(request.getProtocol_version()); + authorize(request.getRequestorUserName(), + getRequestorGroups(conf, request.getRequestorUserName())); + CommitContext context = store.createRole(request.getComponent(), request.getRoleName(), request.getRequestorUserName()); + return new Response<Void>(Status.OK(), context); + } + }); + + TCreateSentryRoleResponse tResponse = new TCreateSentryRoleResponse(respose.status); + if (Status.OK.getCode() == respose.status.getValue()) { + handerInvoker.create_sentry_role(respose.context, request, tResponse); + } + + try { + AUDIT_LOGGER.info(JsonLogEntityFactory.getInstance() + .createJsonLogEntity(request, tResponse, conf).toJsonFormatLog()); + } catch (Exception e) { + // if any exception, log the exception. + String msg = "Error in creating audit log for create role: " + e.getMessage(); + LOGGER.error(msg, e); + } + return tResponse; + } + + @Override + public TDropSentryRoleResponse drop_sentry_role(final TDropSentryRoleRequest request) + throws TException { + Response<Void> respose = requestHandle(new RequestHandler<Void>() { + @Override + public Response<Void> handle() throws Exception { + validateClientVersion(request.getProtocol_version()); + authorize(request.getRequestorUserName(), + getRequestorGroups(conf, request.getRequestorUserName())); + CommitContext context = store.dropRole(request.getComponent(), request.getRoleName(), request.getRequestorUserName()); + return new Response<Void>(Status.OK(), context); + } + }); + + TDropSentryRoleResponse tResponse = new TDropSentryRoleResponse(respose.status); + if (Status.OK.getCode() == respose.status.getValue()) { + handerInvoker.drop_sentry_role(respose.context, request, tResponse); + } + + try { + AUDIT_LOGGER.info(JsonLogEntityFactory.getInstance() + .createJsonLogEntity(request, tResponse, conf).toJsonFormatLog()); + } catch (Exception e) { + // if any exception, log the exception. + String msg = "Error in creating audit log for drop role: " + e.getMessage(); + LOGGER.error(msg, e); + } + return tResponse; + } + + @Override + public TAlterSentryRoleGrantPrivilegeResponse alter_sentry_role_grant_privilege( + final TAlterSentryRoleGrantPrivilegeRequest request) throws TException { + Response<Void> respose = requestHandle(new RequestHandler<Void>() { + @Override + public Response<Void> handle() throws Exception { + validateClientVersion(request.getProtocol_version()); + CommitContext context = store.alterRoleGrantPrivilege(request.getComponent(), request.getRoleName(), toPrivilegeObject(request.getPrivilege()), request.getRequestorUserName()); + return new Response<Void>(Status.OK(), context); + } + }); + + TAlterSentryRoleGrantPrivilegeResponse tResponse = new TAlterSentryRoleGrantPrivilegeResponse(respose.status); + if (Status.OK.getCode() == respose.status.getValue()) { + handerInvoker.alter_sentry_role_grant_privilege(respose.context, request, tResponse); + } + + try { + AUDIT_LOGGER.info(JsonLogEntityFactory.getInstance() + .createJsonLogEntity(request, tResponse, conf).toJsonFormatLog()); + } catch (Exception e) { + // if any exception, log the exception. + String msg = "Error in creating audit log for grant privilege to role: " + e.getMessage(); + LOGGER.error(msg, e); + } + return tResponse; + } + + @Override + public TAlterSentryRoleRevokePrivilegeResponse alter_sentry_role_revoke_privilege( + final TAlterSentryRoleRevokePrivilegeRequest request) throws TException { + Response<Void> respose = requestHandle(new RequestHandler<Void>() { + @Override + public Response<Void> handle() throws Exception { + validateClientVersion(request.getProtocol_version()); + CommitContext context = store.alterRoleRevokePrivilege(request.getComponent(), request.getRoleName(), toPrivilegeObject(request.getPrivilege()), request.getRequestorUserName()); + return new Response<Void>(Status.OK(), context); + } + }); + + TAlterSentryRoleRevokePrivilegeResponse tResponse = new TAlterSentryRoleRevokePrivilegeResponse(respose.status); + if (Status.OK.getCode() == respose.status.getValue()) { + handerInvoker.alter_sentry_role_revoke_privilege(respose.context, request, tResponse); + } + + try { + AUDIT_LOGGER.info(JsonLogEntityFactory.getInstance() + .createJsonLogEntity(request, tResponse, conf).toJsonFormatLog()); + } catch (Exception e) { + // if any exception, log the exception. + String msg = "Error in creating audit log for revoke privilege from role: " + e.getMessage(); + LOGGER.error(msg, e); + } + return tResponse; + } + + @Override + public TAlterSentryRoleAddGroupsResponse alter_sentry_role_add_groups( + final TAlterSentryRoleAddGroupsRequest request) throws TException { + Response<Void> respose = requestHandle(new RequestHandler<Void>() { + @Override + public Response<Void> handle() throws Exception { + validateClientVersion(request.getProtocol_version()); + authorize(request.getRequestorUserName(), + getRequestorGroups(conf, request.getRequestorUserName())); + CommitContext context = store.alterRoleAddGroups(request.getComponent(), request.getRoleName(), request.getGroups(), request.getRequestorUserName()); + return new Response<Void>(Status.OK(), context); + } + }); + + TAlterSentryRoleAddGroupsResponse tResponse = new TAlterSentryRoleAddGroupsResponse(respose.status); + if (Status.OK.getCode() == respose.status.getValue()) { + handerInvoker.alter_sentry_role_add_groups(respose.context, request, tResponse); + } + + try { + AUDIT_LOGGER.info(JsonLogEntityFactory.getInstance() + .createJsonLogEntity(request, tResponse, conf).toJsonFormatLog()); + } catch (Exception e) { + // if any exception, log the exception. + String msg = "Error in creating audit log for add role to group: " + e.getMessage(); + LOGGER.error(msg, e); + } + return tResponse; + } + + @Override + public TAlterSentryRoleDeleteGroupsResponse alter_sentry_role_delete_groups( + final TAlterSentryRoleDeleteGroupsRequest request) throws TException { + Response<Void> respose = requestHandle(new RequestHandler<Void>() { + @Override + public Response<Void> handle() throws Exception { + validateClientVersion(request.getProtocol_version()); + authorize(request.getRequestorUserName(), + getRequestorGroups(conf, request.getRequestorUserName())); + CommitContext context = store.alterRoleDeleteGroups(request.getComponent(), request.getRoleName(), request.getGroups(), request.getRequestorUserName()); + return new Response<Void>(Status.OK(), context); + } + }); + + TAlterSentryRoleDeleteGroupsResponse tResponse = new TAlterSentryRoleDeleteGroupsResponse(respose.status); + if (Status.OK.getCode() == respose.status.getValue()) { + handerInvoker.alter_sentry_role_delete_groups(respose.context, request, tResponse); + } + + try { + AUDIT_LOGGER.info(JsonLogEntityFactory.getInstance() + .createJsonLogEntity(request, tResponse, conf).toJsonFormatLog()); + } catch (Exception e) { + // if any exception, log the exception. + String msg = "Error in creating audit log for delete role from group: " + e.getMessage(); + LOGGER.error(msg, e); + } + return tResponse; + } + + @Override + public TListSentryRolesResponse list_sentry_roles_by_group( + final TListSentryRolesRequest request) throws TException { + Response<Set<TSentryRole>> respose = requestHandle(new RequestHandler<Set<TSentryRole>>() { + @Override + public Response<Set<TSentryRole>> handle() throws Exception { + validateClientVersion(request.getProtocol_version()); + Set<String> groups = getRequestorGroups(conf, request.getRequestorUserName()); + if (!AccessConstants.ALL.equalsIgnoreCase(request.getGroupName())) { + boolean admin = inAdminGroups(groups); + //Only admin users can list all roles in the system ( groupname = null) + //Non admin users are only allowed to list only groups which they belong to + if(!admin && (request.getGroupName() == null || !groups.contains(request.getGroupName()))) { + throw new SentryAccessDeniedException(ACCESS_DENIAL_MESSAGE + request.getRequestorUserName()); + } + groups.clear(); + groups.add(request.getGroupName()); + } + + Set<String> roleNames = store.getRolesByGroups(request.getComponent(), groups); + Set<TSentryRole> tSentryRoles = Sets.newHashSet(); + for (String roleName : roleNames) { + Set<String> groupsForRoleName = store.getGroupsByRoles(request.getComponent(), Sets.newHashSet(roleName)); + tSentryRoles.add(new TSentryRole(roleName, groupsForRoleName)); + } + return new Response<Set<TSentryRole>>(Status.OK(), tSentryRoles); + } + }); + TListSentryRolesResponse tResponse = new TListSentryRolesResponse(); + tResponse.setStatus(respose.status); + tResponse.setRoles(respose.content); + return tResponse; + } + + @Override + public TListSentryPrivilegesResponse list_sentry_privileges_by_role( + final TListSentryPrivilegesRequest request) throws TException { + Response<Set<TSentryPrivilege>> respose = requestHandle(new RequestHandler<Set<TSentryPrivilege>>() { + @Override + public Response<Set<TSentryPrivilege>> handle() throws Exception { + validateClientVersion(request.getProtocol_version()); + Set<String> groups = getRequestorGroups(conf, request.getRequestorUserName()); + if (!inAdminGroups(groups)) { + Set<String> roleNamesForGroups = toTrimmedLower(store.getRolesByGroups(request.getComponent(), groups)); + if (!roleNamesForGroups.contains(toTrimmedLower(request.getRoleName()))) { + throw new SentryAccessDeniedException(ACCESS_DENIAL_MESSAGE + request.getRequestorUserName()); + } + } + Set<PrivilegeObject> privileges = store.getPrivilegesByProvider(request.getComponent(), + request.getServiceName(), + Sets.newHashSet(request.getRoleName()), + null, toAuthorizables(request.getAuthorizables())); + Set<TSentryPrivilege> tSentryPrivileges = Sets.newHashSet(); + for (PrivilegeObject privilege : privileges) { + tSentryPrivileges.add(fromPrivilegeObject(privilege)); + } + return new Response<Set<TSentryPrivilege>>(Status.OK(), tSentryPrivileges); + } + }); + TListSentryPrivilegesResponse tResponse = new TListSentryPrivilegesResponse(); + tResponse.setStatus(respose.status); + tResponse.setPrivileges(respose.content); + return tResponse; + } + + @Override + public TListSentryPrivilegesForProviderResponse list_sentry_privileges_for_provider( + final TListSentryPrivilegesForProviderRequest request) throws TException { + Response<Set<String>> respose = requestHandle(new RequestHandler<Set<String>>() { + @Override + public Response<Set<String>> handle() throws Exception { + validateClientVersion(request.getProtocol_version()); + Set<String> activeRoleNames = toTrimmedLower(request.getRoleSet().getRoles()); + Set<String> roleNamesForGroups = store.getRolesByGroups(request.getComponent(), request.getGroups()); + Set<String> rolesToQuery = request.getRoleSet().isAll() ? roleNamesForGroups : Sets.intersection(activeRoleNames, roleNamesForGroups); + Set<PrivilegeObject> privileges = store.getPrivilegesByProvider(request.getComponent(), + request.getServiceName(), + rolesToQuery, null, + toAuthorizables(request.getAuthorizables())); + return new Response<Set<String>>(Status.OK(), buildPermissions(privileges)); + } + }); + TListSentryPrivilegesForProviderResponse tResponse = new TListSentryPrivilegesForProviderResponse(); + tResponse.setStatus(respose.status); + tResponse.setPrivileges(respose.content); + return tResponse; + } + + @Override + public TListSentryPrivilegesByAuthResponse list_sentry_privileges_by_authorizable(TListSentryPrivilegesByAuthRequest request) throws TException { + + TListSentryPrivilegesByAuthResponse response = new TListSentryPrivilegesByAuthResponse(); + Map<String, TSentryPrivilegeMap> authRoleMap = Maps.newHashMap(); + + // Group names are case sensitive. + Set<String> requestedGroups = request.getGroups(); + String subject = request.getRequestorUserName(); + TSentryActiveRoleSet activeRoleSet = request.getRoleSet(); + Set<String> validActiveRoles = Sets.newHashSet(); + + try { + validateClientVersion(request.getProtocol_version()); + Set<String> memberGroups = getRequestorGroups(conf, subject); + + // Disallow non-admin users to lookup groups that + // they are not part of. + if(!inAdminGroups(memberGroups)) { + + if (requestedGroups != null && !requestedGroups.isEmpty()) { + for (String requestedGroup : requestedGroups) { + + // If user doesn't belong to one of the requested groups, + // then raise security exception. + if (!memberGroups.contains(requestedGroup)) { + throw new SentryAccessDeniedException(ACCESS_DENIAL_MESSAGE + subject); + } + } + } else { + // Non-admin's search is limited to its own groups. + requestedGroups = memberGroups; + } + + Set<String> grantedRoles = toTrimmedLower(store.getRolesByGroups(request.getComponent(), requestedGroups)); + + // If activeRoleSet is not null, disallow non-admin to lookup roles that they are not part of. + if (activeRoleSet != null && !activeRoleSet.isAll()) { + + Set<String> activeRoleNames = toTrimmedLower(activeRoleSet.getRoles()); + for (String activeRole : activeRoleNames) { + if (!grantedRoles.contains(activeRole)) { + throw new SentryAccessDeniedException(ACCESS_DENIAL_MESSAGE + + subject); + } + } + + // For non-admin, valid active roles are intersection of active roles and granted roles. + validActiveRoles.addAll(activeRoleSet.isAll() ? grantedRoles : Sets.intersection(activeRoleNames, grantedRoles)); + } else { + // For non-admin, if activeRoleSet is null, valid active roles would be the granted roles. + validActiveRoles.addAll(grantedRoles); + } + } else { + // For admin, if requestedGroups are empty, requested roles will be all roles. + Set<String> requestedRoles = toTrimmedLower(store.getAllRoleNames()); + if (requestedGroups != null && !requestedGroups.isEmpty()) { + requestedRoles = toTrimmedLower(store.getRolesByGroups(request.getComponent(), requestedGroups)); + } + + // If activeRoleSet (which is optional) is not null, valid active role will be intersection + // of active roles and requested roles. Otherwise, valid active roles are the requested roles. + if (activeRoleSet != null && !activeRoleSet.isAll()) { + validActiveRoles.addAll(Sets.intersection(toTrimmedLower(activeRoleSet.getRoles()), requestedRoles)); + } else { + validActiveRoles.addAll(requestedRoles); + } + } + + // If user is not part of any group.. return empty response + if (request.getAuthorizablesSet() != null) { + for (String authorizablesStr : request.getAuthorizablesSet()) { + + List<? extends Authorizable> authorizables = toAuthorizables(authorizablesStr); + Set<MSentryGMPrivilege> sentryPrivileges = store.getPrivilegesByAuthorizable(request.getComponent(), request.getServiceName(), validActiveRoles, authorizables); + authRoleMap.put(fromAuthorizableToStr(authorizables), toTSentryPrivilegeMap(sentryPrivileges)); + } + } + + response.setPrivilegesMapByAuth(authRoleMap); + response.setStatus(Status.OK()); + } catch (SentryAccessDeniedException e) { + LOGGER.error(e.getMessage(), e); + response.setStatus(Status.AccessDenied(e.getMessage(), e)); + } catch (SentryThriftAPIMismatchException e) { + LOGGER.error(e.getMessage(), e); + response.setStatus(Status.THRIFT_VERSION_MISMATCH(e.getMessage(), e)); + } catch (Exception e) { + String msg = "Unknown error for request: " + request + ", message: " + + e.getMessage(); + LOGGER.error(msg, e); + response.setStatus(Status.RuntimeError(msg, e)); + } + + return response; + } + + @Override + public TDropPrivilegesResponse drop_sentry_privilege( + final TDropPrivilegesRequest request) throws TException { + Response<Void> respose = requestHandle(new RequestHandler<Void>() { + @Override + public Response<Void> handle() throws Exception { + validateClientVersion(request.getProtocol_version()); + authorize(request.getRequestorUserName(), + getRequestorGroups(conf, request.getRequestorUserName())); + CommitContext context = store.dropPrivilege(request.getComponent(), + toPrivilegeObject(request.getPrivilege()), + request.getRequestorUserName()); + return new Response<Void>(Status.OK(), context); + } + }); + + TDropPrivilegesResponse tResponse = new TDropPrivilegesResponse(respose.status); + if (Status.OK.getCode() == respose.status.getValue()) { + handerInvoker.drop_sentry_privilege(respose.context, request, tResponse); + } + return tResponse; + } + + @Override + public TRenamePrivilegesResponse rename_sentry_privilege( + final TRenamePrivilegesRequest request) throws TException { + Response<Void> respose = requestHandle(new RequestHandler<Void>() { + @Override + public Response<Void> handle() throws Exception { + validateClientVersion(request.getProtocol_version()); + authorize(request.getRequestorUserName(), + getRequestorGroups(conf, request.getRequestorUserName())); + CommitContext context = store.renamePrivilege(request.getComponent(), request.getServiceName(), + toAuthorizables(request.getOldAuthorizables()), + toAuthorizables(request.getNewAuthorizables()), + request.getRequestorUserName()); + return new Response<Void>(Status.OK(),context); + } + }); + + TRenamePrivilegesResponse tResponse = new TRenamePrivilegesResponse(respose.status); + if (Status.OK.getCode() == respose.status.getValue()) { + handerInvoker.rename_sentry_privilege(respose.context, request, tResponse); + } + return tResponse; + } + + private static class Response<T> { + private TSentryResponseStatus status; + private CommitContext context; + private T content; + + Response() { + } + + Response(TSentryResponseStatus status, CommitContext context) { + this(status,context,null); + } + + Response(TSentryResponseStatus status, T content) { + this(status,null,content); + } + + Response(TSentryResponseStatus status, CommitContext context, T content) { + this.status = status; + this.context = context; + this.content = content; + } + } + private interface RequestHandler <T>{ + Response<T> handle() throws Exception ; + } + + private static void validateClientVersion(int protocolVersion) throws SentryThriftAPIMismatchException { + if (ServiceConstants.ThriftConstants.TSENTRY_SERVICE_VERSION_CURRENT != protocolVersion) { + String msg = "Sentry thrift API protocol version mismatch: Client thrift version " + + "is: " + protocolVersion + " , server thrift version " + + "is " + ThriftConstants.TSENTRY_SERVICE_VERSION_CURRENT; + throw new SentryThriftAPIMismatchException(msg); + } + } +}
http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/thrift/SentryGenericPolicyProcessorFactory.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/thrift/SentryGenericPolicyProcessorFactory.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/thrift/SentryGenericPolicyProcessorFactory.java new file mode 100644 index 0000000..e9ff627 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/thrift/SentryGenericPolicyProcessorFactory.java @@ -0,0 +1,41 @@ +/** + * 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.sentry.provider.db.generic.service.thrift; + +import org.apache.hadoop.conf.Configuration; +import org.apache.sentry.service.thrift.ProcessorFactory; +import org.apache.sentry.service.thrift.ServiceConstants; +import org.apache.thrift.TMultiplexedProcessor; +import org.apache.thrift.TProcessor; + +public class SentryGenericPolicyProcessorFactory extends ProcessorFactory { + + public SentryGenericPolicyProcessorFactory(Configuration conf) { + super(conf); + } + + @Override + public boolean register(TMultiplexedProcessor multiplexedProcessor) throws Exception { + SentryGenericPolicyProcessor processHandler = new SentryGenericPolicyProcessor(conf); + TProcessor processor = new SentryGenericPolicyProcessorWrapper<SentryGenericPolicyService.Iface>( + processHandler); + multiplexedProcessor.registerProcessor(ServiceConstants.SENTRY_GENERIC_SERVICE_NAME, processor); + return true; + } + +} http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/thrift/SentryGenericPolicyProcessorWrapper.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/thrift/SentryGenericPolicyProcessorWrapper.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/thrift/SentryGenericPolicyProcessorWrapper.java new file mode 100644 index 0000000..d320d0f --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/thrift/SentryGenericPolicyProcessorWrapper.java @@ -0,0 +1,39 @@ +/** + * 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.sentry.provider.db.generic.service.thrift; + +import org.apache.sentry.provider.db.service.thrift.ThriftUtil; +import org.apache.thrift.TException; +import org.apache.thrift.protocol.TProtocol; + +public class SentryGenericPolicyProcessorWrapper<I extends SentryGenericPolicyService.Iface> + extends SentryGenericPolicyService.Processor<SentryGenericPolicyService.Iface> { + + public SentryGenericPolicyProcessorWrapper(I iface) { + super(iface); + } + + @Override + public boolean process(TProtocol in, TProtocol out) throws TException { + // set the ip and impersonator for audit log + ThriftUtil.setIpAddress(in); + ThriftUtil.setImpersonator(in); + return super.process(in, out); + } +} http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/appender/AuditLoggerTestAppender.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/appender/AuditLoggerTestAppender.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/appender/AuditLoggerTestAppender.java new file mode 100644 index 0000000..8000ebd --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/appender/AuditLoggerTestAppender.java @@ -0,0 +1,52 @@ +/* + * 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.sentry.provider.db.log.appender; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.AppenderSkeleton; +import org.apache.log4j.Level; +import org.apache.log4j.spi.LoggingEvent; + +import com.google.common.annotations.VisibleForTesting; + +@VisibleForTesting +public class AuditLoggerTestAppender extends AppenderSkeleton { + public static final List<LoggingEvent> events = new ArrayList<LoggingEvent>(); + + public void close() { + } + + public boolean requiresLayout() { + return false; + } + + @Override + protected void append(LoggingEvent event) { + events.add(event); + } + + public static String getLastLogEvent() { + return events.get(events.size() - 1).getMessage().toString(); + } + + public static Level getLastLogLevel() { + return events.get(events.size() - 1).getLevel(); + } +} http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/appender/RollingFileWithoutDeleteAppender.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/appender/RollingFileWithoutDeleteAppender.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/appender/RollingFileWithoutDeleteAppender.java new file mode 100644 index 0000000..fd133f3 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/appender/RollingFileWithoutDeleteAppender.java @@ -0,0 +1,175 @@ +/** + * 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.sentry.provider.db.log.appender; + +import java.io.File; +import java.io.IOException; +import java.io.InterruptedIOException; +import java.io.Writer; + +import org.apache.log4j.FileAppender; +import org.apache.log4j.Layout; +import org.apache.log4j.helpers.CountingQuietWriter; +import org.apache.log4j.helpers.LogLog; +import org.apache.log4j.helpers.OptionConverter; +import org.apache.log4j.spi.LoggingEvent; + +public class RollingFileWithoutDeleteAppender extends FileAppender { + /** + * The default maximum file size is 10MB. + */ + protected long maxFileSize = 10 * 1024 * 1024; + + private long nextRollover = 0; + + /** + * The default constructor simply calls its {@link FileAppender#FileAppender + * parents constructor}. + */ + public RollingFileWithoutDeleteAppender() { + super(); + } + + /** + * Instantiate a RollingFileAppender and open the file designated by + * <code>filename</code>. The opened filename will become the ouput + * destination for this appender. + * <p> + * If the <code>append</code> parameter is true, the file will be appended to. + * Otherwise, the file desginated by <code>filename</code> will be truncated + * before being opened. + */ + public RollingFileWithoutDeleteAppender(Layout layout, String filename, + boolean append) throws IOException { + super(layout, getLogFileName(filename), append); + } + + /** + * Instantiate a FileAppender and open the file designated by + * <code>filename</code>. The opened filename will become the output + * destination for this appender. + * <p> + * The file will be appended to. + */ + public RollingFileWithoutDeleteAppender(Layout layout, String filename) + throws IOException { + super(layout, getLogFileName(filename)); + } + + /** + * Get the maximum size that the output file is allowed to reach before being + * rolled over to backup files. + */ + public long getMaximumFileSize() { + return maxFileSize; + } + + /** + * Implements the usual roll over behaviour. + * <p> + * <code>File</code> is renamed <code>File.yyyyMMddHHmmss</code> and closed. A + * new <code>File</code> is created to receive further log output. + */ + // synchronization not necessary since doAppend is alreasy synched + public void rollOver() { + if (qw != null) { + long size = ((CountingQuietWriter) qw).getCount(); + LogLog.debug("rolling over count=" + size); + // if operation fails, do not roll again until + // maxFileSize more bytes are written + nextRollover = size + maxFileSize; + } + + this.closeFile(); // keep windows happy. + + String newFileName = getLogFileName(fileName); + try { + // This will also close the file. This is OK since multiple + // close operations are safe. + this.setFile(newFileName, false, bufferedIO, bufferSize); + nextRollover = 0; + } catch (IOException e) { + if (e instanceof InterruptedIOException) { + Thread.currentThread().interrupt(); + } + LogLog.error("setFile(" + newFileName + ", false) call failed: " + e.getMessage(), e); + } + } + + public synchronized void setFile(String fileName, boolean append, + boolean bufferedIO, int bufferSize) throws IOException { + super.setFile(fileName, append, this.bufferedIO, this.bufferSize); + if (append) { + File f = new File(fileName); + ((CountingQuietWriter) qw).setCount(f.length()); + } + } + + /** + * Set the maximum size that the output file is allowed to reach before being + * rolled over to backup files. + * <p> + * This method is equivalent to {@link #setMaxFileSize} except that it is + * required for differentiating the setter taking a <code>long</code> argument + * from the setter taking a <code>String</code> argument by the JavaBeans + * {@link java.beans.Introspector Introspector}. + * + * @see #setMaxFileSize(String) + */ + public void setMaximumFileSize(long maxFileSize) { + this.maxFileSize = maxFileSize; + } + + /** + * Set the maximum size that the output file is allowed to reach before being + * rolled over to backup files. + * <p> + * In configuration files, the <b>MaxFileSize</b> option takes an long integer + * in the range 0 - 2^63. You can specify the value with the suffixes "KB", + * "MB" or "GB" so that the integer is interpreted being expressed + * respectively in kilobytes, megabytes or gigabytes. For example, the value + * "10KB" will be interpreted as 10240. + */ + public void setMaxFileSize(String value) { + maxFileSize = OptionConverter.toFileSize(value, maxFileSize + 1); + } + + protected void setQWForFiles(Writer writer) { + this.qw = new CountingQuietWriter(writer, errorHandler); + } + + /** + * This method differentiates RollingFileAppender from its super class. + */ + protected void subAppend(LoggingEvent event) { + super.subAppend(event); + + if (fileName != null && qw != null) { + long size = ((CountingQuietWriter) qw).getCount(); + if (size >= maxFileSize && size >= nextRollover) { + rollOver(); + } + } + } + + // Mangled file name. Append the current timestamp + private static String getLogFileName(String oldFileName) { + return oldFileName + "." + Long.toString(System.currentTimeMillis()); + } +} http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/entity/AuditMetadataLogEntity.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/entity/AuditMetadataLogEntity.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/entity/AuditMetadataLogEntity.java new file mode 100644 index 0000000..a5fe4ec --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/entity/AuditMetadataLogEntity.java @@ -0,0 +1,155 @@ +/** + * 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.sentry.provider.db.log.entity; + +import java.io.IOException; + +import org.codehaus.jackson.JsonFactory; +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.map.MappingJsonFactory; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.node.ContainerNode; + +abstract public class AuditMetadataLogEntity implements JsonLogEntity { + + static final JsonFactory factory = new MappingJsonFactory(); + private String serviceName; + private String userName; + private String impersonator; + private String ipAddress; + private String operation; + private String eventTime; + private String operationText; + private String allowed; + private String objectType; + private String component; + + void setCommonAttr(String serviceName, String userName, String impersonator, String ipAddress, + String operation, String eventTime, String operationText, String allowed, String objectType, + String component) { + this.serviceName = serviceName; + this.userName = userName; + this.impersonator = impersonator; + this.ipAddress = ipAddress; + this.operation = operation; + this.eventTime = eventTime; + this.operationText = operationText; + this.allowed = allowed; + this.objectType = objectType; + this.component = component; + } + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getImpersonator() { + return impersonator; + } + + public void setImpersonator(String impersonator) { + this.impersonator = impersonator; + } + + public String getIpAddress() { + return ipAddress; + } + + public void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + } + + public String getOperation() { + return operation; + } + + public void setOperation(String operation) { + this.operation = operation; + } + + public String getEventTime() { + return eventTime; + } + + public void setEventTime(String eventTime) { + this.eventTime = eventTime; + } + + public String getOperationText() { + return operationText; + } + + public void setOperationText(String operationText) { + this.operationText = operationText; + } + + public String getAllowed() { + return allowed; + } + + public void setAllowed(String allowed) { + this.allowed = allowed; + } + + public String getObjectType() { + return objectType; + } + + public void setObjectType(String objectType) { + this.objectType = objectType; + } + + public String getComponent() { + return component; + } + + public void setComponent(String component) { + this.component = component; + } + + /** + * For use in tests + * + * @param json + * incoming JSON to parse + * @return a node tree + * @throws IOException + * on any parsing problems + */ + public static ContainerNode parse(String json) throws IOException { + ObjectMapper mapper = new ObjectMapper(factory); + JsonNode jsonNode = mapper.readTree(json); + if (!(jsonNode instanceof ContainerNode)) { + throw new IOException("Wrong JSON data: " + json); + } + return (ContainerNode) jsonNode; + } +} http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/entity/DBAuditMetadataLogEntity.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/entity/DBAuditMetadataLogEntity.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/entity/DBAuditMetadataLogEntity.java new file mode 100644 index 0000000..4949ac7 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/entity/DBAuditMetadataLogEntity.java @@ -0,0 +1,124 @@ +/** + * 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.sentry.provider.db.log.entity; + +import java.io.IOException; +import java.io.StringWriter; + +import org.apache.sentry.provider.db.log.util.Constants; +import org.codehaus.jackson.JsonGenerator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DBAuditMetadataLogEntity extends AuditMetadataLogEntity { + private static final Logger LOGGER = LoggerFactory.getLogger(DBAuditMetadataLogEntity.class); + + private String databaseName; + private String tableName; + private String columnName; + private String resourcePath; + + public DBAuditMetadataLogEntity() { + } + + public DBAuditMetadataLogEntity(String serviceName, String userName, String impersonator, + String ipAddress, String operation, String eventTime, String operationText, String allowed, + String objectType, String component, String databaseName, String tableName, + String columnName, String resourcePath) { + setCommonAttr(serviceName, userName, impersonator, ipAddress, operation, eventTime, + operationText, allowed, objectType, component); + this.databaseName = databaseName; + this.tableName = tableName; + this.columnName = columnName; + this.resourcePath = resourcePath; + } + + public String getDatabaseName() { + return databaseName; + } + + public void setDatabaseName(String databaseName) { + this.databaseName = databaseName; + } + + public String getTableName() { + return tableName; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + public String getColumnName() { + return columnName; + } + + public void setColumnName(String columnName) { + this.columnName = columnName; + } + + public String getResourcePath() { + return resourcePath; + } + + public void setResourcePath(String resourcePath) { + this.resourcePath = resourcePath; + } + + @Override + public String toJsonFormatLog() throws Exception { + StringWriter stringWriter = new StringWriter(); + JsonGenerator json = null; + try { + json = factory.createJsonGenerator(stringWriter); + json.writeStartObject(); + json.writeStringField(Constants.LOG_FIELD_SERVICE_NAME, getServiceName()); + json.writeStringField(Constants.LOG_FIELD_USER_NAME, getUserName()); + json.writeStringField(Constants.LOG_FIELD_IMPERSONATOR, getImpersonator()); + json.writeStringField(Constants.LOG_FIELD_IP_ADDRESS, getIpAddress()); + json.writeStringField(Constants.LOG_FIELD_OPERATION, getOperation()); + json.writeStringField(Constants.LOG_FIELD_EVENT_TIME, getEventTime()); + json.writeStringField(Constants.LOG_FIELD_OPERATION_TEXT, getOperationText()); + json.writeStringField(Constants.LOG_FIELD_ALLOWED, getAllowed()); + json.writeStringField(Constants.LOG_FIELD_DATABASE_NAME, databaseName); + json.writeStringField(Constants.LOG_FIELD_TABLE_NAME, tableName); + json.writeStringField(Constants.LOG_FIELD_COLUMN_NAME, columnName); + json.writeStringField(Constants.LOG_FIELD_RESOURCE_PATH, resourcePath); + json.writeStringField(Constants.LOG_FIELD_OBJECT_TYPE, getObjectType()); + json.writeEndObject(); + json.flush(); + } catch (IOException e) { + String msg = "Error creating audit log in json format: " + e.getMessage(); + LOGGER.error(msg, e); + throw e; + } finally { + try { + if (json != null) { + json.close(); + } + } catch (IOException e) { + String msg = "Error when close json object: " + e.getMessage(); + LOGGER.error(msg, e); + throw e; + } + } + + return stringWriter.toString(); + } +} http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/entity/GMAuditMetadataLogEntity.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/entity/GMAuditMetadataLogEntity.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/entity/GMAuditMetadataLogEntity.java new file mode 100644 index 0000000..6911772 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/entity/GMAuditMetadataLogEntity.java @@ -0,0 +1,97 @@ +/** + * 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.sentry.provider.db.log.entity; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.apache.sentry.provider.db.log.util.Constants; +import org.codehaus.jackson.JsonGenerator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GMAuditMetadataLogEntity extends AuditMetadataLogEntity { + + private static final Logger LOGGER = LoggerFactory.getLogger(GMAuditMetadataLogEntity.class); + private Map<String, String> privilegesMap; + + public GMAuditMetadataLogEntity() { + privilegesMap = new LinkedHashMap<String, String>(); + } + + public GMAuditMetadataLogEntity(String serviceName, String userName, String impersonator, + String ipAddress, String operation, String eventTime, String operationText, String allowed, + String objectType, String component, Map<String, String> privilegesMap) { + setCommonAttr(serviceName, userName, impersonator, ipAddress, operation, eventTime, + operationText, allowed, objectType, component); + this.privilegesMap = privilegesMap; + } + + @Override + public String toJsonFormatLog() throws Exception { + StringWriter stringWriter = new StringWriter(); + JsonGenerator json = null; + try { + json = factory.createJsonGenerator(stringWriter); + json.writeStartObject(); + json.writeStringField(Constants.LOG_FIELD_SERVICE_NAME, getServiceName()); + json.writeStringField(Constants.LOG_FIELD_USER_NAME, getUserName()); + json.writeStringField(Constants.LOG_FIELD_IMPERSONATOR, getImpersonator()); + json.writeStringField(Constants.LOG_FIELD_IP_ADDRESS, getIpAddress()); + json.writeStringField(Constants.LOG_FIELD_OPERATION, getOperation()); + json.writeStringField(Constants.LOG_FIELD_EVENT_TIME, getEventTime()); + json.writeStringField(Constants.LOG_FIELD_OPERATION_TEXT, getOperationText()); + json.writeStringField(Constants.LOG_FIELD_ALLOWED, getAllowed()); + for (Map.Entry<String, String> entry : privilegesMap.entrySet()) { + json.writeStringField(entry.getKey(), entry.getValue()); + } + json.writeStringField(Constants.LOG_FIELD_OBJECT_TYPE, getObjectType()); + json.writeStringField(Constants.LOG_FIELD_COMPONENT, getComponent()); + json.writeEndObject(); + json.flush(); + } catch (IOException e) { + String msg = "Error creating audit log in json format: " + e.getMessage(); + LOGGER.error(msg, e); + throw e; + } finally { + try { + if (json != null) { + json.close(); + } + } catch (IOException e) { + String msg = "Error when close json object: " + e.getMessage(); + LOGGER.error(msg, e); + throw e; + } + } + + return stringWriter.toString(); + } + + public Map<String, String> getPrivilegesMap() { + return privilegesMap; + } + + public void setPrivilegesMap(Map<String, String> privilegesMap) { + this.privilegesMap = privilegesMap; + } + +} http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/entity/JsonLogEntity.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/entity/JsonLogEntity.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/entity/JsonLogEntity.java new file mode 100644 index 0000000..913f125 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/entity/JsonLogEntity.java @@ -0,0 +1,25 @@ +/** + * 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.sentry.provider.db.log.entity; + +public interface JsonLogEntity { + + String toJsonFormatLog() throws Exception; + +} http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/entity/JsonLogEntityFactory.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/entity/JsonLogEntityFactory.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/entity/JsonLogEntityFactory.java new file mode 100644 index 0000000..f6bb8a5 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/log/entity/JsonLogEntityFactory.java @@ -0,0 +1,351 @@ +/** + * 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.sentry.provider.db.log.entity; + +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.hadoop.conf.Configuration; +import org.apache.sentry.provider.db.generic.service.thrift.TAuthorizable; +import org.apache.sentry.provider.db.log.util.CommandUtil; +import org.apache.sentry.provider.db.log.util.Constants; +import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleAddGroupsRequest; +import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleAddGroupsResponse; +import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleAddUsersRequest; +import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleAddUsersResponse; +import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleDeleteGroupsRequest; +import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleDeleteGroupsResponse; +import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleDeleteUsersRequest; +import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleDeleteUsersResponse; +import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleGrantPrivilegeRequest; +import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleGrantPrivilegeResponse; +import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleRevokePrivilegeRequest; +import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleRevokePrivilegeResponse; +import org.apache.sentry.provider.db.service.thrift.TCreateSentryRoleRequest; +import org.apache.sentry.provider.db.service.thrift.TCreateSentryRoleResponse; +import org.apache.sentry.provider.db.service.thrift.TDropSentryRoleRequest; +import org.apache.sentry.provider.db.service.thrift.TDropSentryRoleResponse; +import org.apache.sentry.provider.db.service.thrift.TSentryGroup; +import org.apache.sentry.provider.db.service.thrift.TSentryPrivilege; +import org.apache.sentry.provider.db.service.thrift.ThriftUtil; +import org.apache.sentry.service.thrift.ServiceConstants.ServerConfig; +import org.apache.sentry.service.thrift.Status; +import org.apache.sentry.service.thrift.TSentryResponseStatus; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableSet; + +public final class JsonLogEntityFactory { + + private static JsonLogEntityFactory factory = new JsonLogEntityFactory(); + + private JsonLogEntityFactory() { + } + + public static JsonLogEntityFactory getInstance() { + return factory; + } + + // log entity for hive/impala create role + public JsonLogEntity createJsonLogEntity(TCreateSentryRoleRequest request, + TCreateSentryRoleResponse response, Configuration conf) { + DBAuditMetadataLogEntity hamle = createCommonHAMLE(conf, response.getStatus(), + request.getRequestorUserName(), request.getClass().getName()); + hamle.setOperationText(CommandUtil.createCmdForCreateOrDropRole( + request.getRoleName(), true)); + + return hamle; + } + + // log entity for hive/impala drop role + public JsonLogEntity createJsonLogEntity(TDropSentryRoleRequest request, + TDropSentryRoleResponse response, Configuration conf) { + DBAuditMetadataLogEntity hamle = createCommonHAMLE(conf, response.getStatus(), + request.getRequestorUserName(), request.getClass().getName()); + hamle.setOperationText(CommandUtil.createCmdForCreateOrDropRole( + request.getRoleName(), false)); + + return hamle; + } + + // log entity for hive/impala grant privilege + public Set<JsonLogEntity> createJsonLogEntitys( + TAlterSentryRoleGrantPrivilegeRequest request, + TAlterSentryRoleGrantPrivilegeResponse response, Configuration conf) { + ImmutableSet.Builder<JsonLogEntity> setBuilder = ImmutableSet.builder(); + if (request.isSetPrivileges()) { + for (TSentryPrivilege privilege : request.getPrivileges()) { + JsonLogEntity logEntity = createJsonLogEntity(request, privilege, response, conf); + setBuilder.add(logEntity); + } + } + return setBuilder.build(); + } + + private JsonLogEntity createJsonLogEntity( + TAlterSentryRoleGrantPrivilegeRequest request, TSentryPrivilege privilege, + TAlterSentryRoleGrantPrivilegeResponse response, Configuration conf) { + DBAuditMetadataLogEntity hamle = createCommonHAMLE(conf, response.getStatus(), + request.getRequestorUserName(), request.getClass().getName()); + hamle.setOperationText(CommandUtil.createCmdForGrantPrivilege(request)); + hamle.setDatabaseName(privilege.getDbName()); + hamle.setTableName(privilege.getTableName()); + hamle.setResourcePath(privilege.getURI()); + return hamle; + } + + // log entity for hive/impala revoke privilege + public Set<JsonLogEntity> createJsonLogEntitys( + TAlterSentryRoleRevokePrivilegeRequest request, + TAlterSentryRoleRevokePrivilegeResponse response, Configuration conf) { + ImmutableSet.Builder<JsonLogEntity> setBuilder = ImmutableSet.builder(); + if (request.isSetPrivileges()) { + for (TSentryPrivilege privilege : request.getPrivileges()) { + JsonLogEntity logEntity = createJsonLogEntity(request, privilege, response, conf); + setBuilder.add(logEntity); + } + } + return setBuilder.build(); + } + + private JsonLogEntity createJsonLogEntity( + TAlterSentryRoleRevokePrivilegeRequest request, TSentryPrivilege privilege, + TAlterSentryRoleRevokePrivilegeResponse response, Configuration conf) { + DBAuditMetadataLogEntity hamle = createCommonHAMLE(conf, response.getStatus(), + request.getRequestorUserName(), request.getClass().getName()); + hamle.setOperationText(CommandUtil.createCmdForRevokePrivilege(request)); + hamle.setDatabaseName(privilege.getDbName()); + hamle.setTableName(privilege.getTableName()); + hamle.setResourcePath(privilege.getURI()); + + return hamle; + } + + // log entity for hive/impala add role to group + public JsonLogEntity createJsonLogEntity( + TAlterSentryRoleAddGroupsRequest request, + TAlterSentryRoleAddGroupsResponse response, Configuration conf) { + DBAuditMetadataLogEntity hamle = createCommonHAMLE(conf, response.getStatus(), + request.getRequestorUserName(), request.getClass().getName()); + String groups = getGroupsStr(request.getGroupsIterator()); + hamle.setOperationText(CommandUtil.createCmdForRoleAddGroup(request.getRoleName(), groups)); + + return hamle; + } + + // log entity for hive/impala delete role from group + public JsonLogEntity createJsonLogEntity( + TAlterSentryRoleDeleteGroupsRequest request, + TAlterSentryRoleDeleteGroupsResponse response, Configuration conf) { + DBAuditMetadataLogEntity hamle = createCommonHAMLE(conf, response.getStatus(), + request.getRequestorUserName(), request.getClass().getName()); + String groups = getGroupsStr(request.getGroupsIterator()); + hamle.setOperationText(CommandUtil.createCmdForRoleDeleteGroup(request.getRoleName(), groups)); + + return hamle; + } + + private String getGroupsStr(Iterator<TSentryGroup> iter) { + StringBuilder groups = new StringBuilder(""); + if (iter != null) { + boolean commaFlg = false; + while (iter.hasNext()) { + if (commaFlg) { + groups.append(", "); + } else { + commaFlg = true; + } + groups.append(iter.next().getGroupName()); + } + } + return groups.toString(); + } + + public JsonLogEntity createJsonLogEntity(TAlterSentryRoleAddUsersRequest request, + TAlterSentryRoleAddUsersResponse response, Configuration conf) { + AuditMetadataLogEntity amle = createCommonHAMLE(conf, response.getStatus(), + request.getRequestorUserName(), request.getClass().getName()); + String users = getUsersStr(request.getUsersIterator()); + amle.setOperationText(CommandUtil.createCmdForRoleAddUser(request.getRoleName(), users)); + + return amle; + } + + public JsonLogEntity createJsonLogEntity(TAlterSentryRoleDeleteUsersRequest request, + TAlterSentryRoleDeleteUsersResponse response, Configuration conf) { + AuditMetadataLogEntity amle = createCommonHAMLE(conf, response.getStatus(), + request.getRequestorUserName(), request.getClass().getName()); + String users = getUsersStr(request.getUsersIterator()); + amle.setOperationText(CommandUtil.createCmdForRoleDeleteUser(request.getRoleName(), users)); + + return amle; + } + + private String getUsersStr(Iterator<String> iter) { + StringBuilder users = new StringBuilder(""); + if (iter != null) { + boolean commaFlg = false; + while (iter.hasNext()) { + if (commaFlg) { + users.append(", "); + } else { + commaFlg = true; + } + users.append(iter.next()); + } + } + return users.toString(); + } + + public String isAllowed(TSentryResponseStatus status) { + if (status.equals(Status.OK())) { + return Constants.TRUE; + } + return Constants.FALSE; + } + + // log entity for generic model create role + public JsonLogEntity createJsonLogEntity( + org.apache.sentry.provider.db.generic.service.thrift.TCreateSentryRoleRequest request, + org.apache.sentry.provider.db.generic.service.thrift.TCreateSentryRoleResponse response, + Configuration conf) { + GMAuditMetadataLogEntity gmamle = createCommonGMAMLE(conf, response.getStatus(), + request.getRequestorUserName(), request.getClass().getName(), request.getComponent()); + gmamle.setOperationText(CommandUtil.createCmdForCreateOrDropRole(request.getRoleName(), true)); + + return gmamle; + } + + // log entity for generic model drop role + public JsonLogEntity createJsonLogEntity( + org.apache.sentry.provider.db.generic.service.thrift.TDropSentryRoleRequest request, + org.apache.sentry.provider.db.generic.service.thrift.TDropSentryRoleResponse response, + Configuration conf) { + GMAuditMetadataLogEntity gmamle = createCommonGMAMLE(conf, response.getStatus(), + request.getRequestorUserName(), request.getClass().getName(), request.getComponent()); + gmamle.setOperationText(CommandUtil.createCmdForCreateOrDropRole(request.getRoleName(), false)); + + return gmamle; + } + + // log entity for generic model grant privilege + public JsonLogEntity createJsonLogEntity( + org.apache.sentry.provider.db.generic.service.thrift.TAlterSentryRoleGrantPrivilegeRequest request, + org.apache.sentry.provider.db.generic.service.thrift.TAlterSentryRoleGrantPrivilegeResponse response, + Configuration conf) { + GMAuditMetadataLogEntity gmamle = createCommonGMAMLE(conf, response.getStatus(), + request.getRequestorUserName(), request.getClass().getName(), request.getComponent()); + if (request.getPrivilege() != null) { + List<TAuthorizable> authorizables = request.getPrivilege().getAuthorizables(); + Map<String, String> privilegesMap = new LinkedHashMap<String, String>(); + if (authorizables != null) { + for (TAuthorizable authorizable : authorizables) { + privilegesMap.put(authorizable.getType(), authorizable.getName()); + } + } + gmamle.setPrivilegesMap(privilegesMap); + } + gmamle.setOperationText(CommandUtil.createCmdForGrantGMPrivilege(request)); + + return gmamle; + } + + // log entity for generic model revoke privilege + public JsonLogEntity createJsonLogEntity( + org.apache.sentry.provider.db.generic.service.thrift.TAlterSentryRoleRevokePrivilegeRequest request, + org.apache.sentry.provider.db.generic.service.thrift.TAlterSentryRoleRevokePrivilegeResponse response, + Configuration conf) { + GMAuditMetadataLogEntity gmamle = createCommonGMAMLE(conf, response.getStatus(), + request.getRequestorUserName(), request.getClass().getName(), request.getComponent()); + if (request.getPrivilege() != null) { + List<TAuthorizable> authorizables = request.getPrivilege().getAuthorizables(); + Map<String, String> privilegesMap = new LinkedHashMap<String, String>(); + if (authorizables != null) { + for (TAuthorizable authorizable : authorizables) { + privilegesMap.put(authorizable.getType(), authorizable.getName()); + } + } + gmamle.setPrivilegesMap(privilegesMap); + } + gmamle.setOperationText(CommandUtil.createCmdForRevokeGMPrivilege(request)); + + return gmamle; + } + + // log entity for generic model add role to group + public JsonLogEntity createJsonLogEntity( + org.apache.sentry.provider.db.generic.service.thrift.TAlterSentryRoleAddGroupsRequest request, + org.apache.sentry.provider.db.generic.service.thrift.TAlterSentryRoleAddGroupsResponse response, + Configuration conf) { + GMAuditMetadataLogEntity gmamle = createCommonGMAMLE(conf, response.getStatus(), + request.getRequestorUserName(), request.getClass().getName(), request.getComponent()); + Joiner joiner = Joiner.on(","); + String groups = joiner.join(request.getGroupsIterator()); + gmamle.setOperationText(CommandUtil.createCmdForRoleAddGroup(request.getRoleName(), groups)); + + return gmamle; + } + + // log entity for hive delete role from group + public JsonLogEntity createJsonLogEntity( + org.apache.sentry.provider.db.generic.service.thrift.TAlterSentryRoleDeleteGroupsRequest request, + org.apache.sentry.provider.db.generic.service.thrift.TAlterSentryRoleDeleteGroupsResponse response, + Configuration conf) { + GMAuditMetadataLogEntity gmamle = createCommonGMAMLE(conf, response.getStatus(), + request.getRequestorUserName(), request.getClass().getName(), request.getComponent()); + Joiner joiner = Joiner.on(","); + String groups = joiner.join(request.getGroupsIterator()); + gmamle.setOperationText(CommandUtil.createCmdForRoleDeleteGroup(request.getRoleName(), groups)); + + return gmamle; + } + + private DBAuditMetadataLogEntity createCommonHAMLE(Configuration conf, + TSentryResponseStatus responseStatus, String userName, String requestClassName) { + DBAuditMetadataLogEntity hamle = new DBAuditMetadataLogEntity(); + setCommAttrForAMLE(hamle, conf, responseStatus, userName, requestClassName); + return hamle; + } + + private GMAuditMetadataLogEntity createCommonGMAMLE(Configuration conf, + TSentryResponseStatus responseStatus, String userName, String requestClassName, + String component) { + GMAuditMetadataLogEntity gmamle = new GMAuditMetadataLogEntity(); + setCommAttrForAMLE(gmamle, conf, responseStatus, userName, requestClassName); + gmamle.setComponent(component); + return gmamle; + } + + private void setCommAttrForAMLE(AuditMetadataLogEntity amle, Configuration conf, + TSentryResponseStatus responseStatus, String userName, String requestClassName) { + amle.setUserName(userName); + amle.setServiceName(conf.get(ServerConfig.SENTRY_SERVICE_NAME, + ServerConfig.SENTRY_SERVICE_NAME_DEFAULT).trim()); + amle.setImpersonator(ThriftUtil.getImpersonator()); + amle.setIpAddress(ThriftUtil.getIpAddress()); + amle.setOperation(Constants.requestTypeToOperationMap.get(requestClassName)); + amle.setEventTime(Long.toString(System.currentTimeMillis())); + amle.setAllowed(isAllowed(responseStatus)); + amle.setObjectType(Constants.requestTypeToObjectTypeMap + .get(requestClassName)); + } +}
