http://git-wip-us.apache.org/repos/asf/sentry/blob/7db84b2f/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/persistent/DelegateSentryStore.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/persistent/DelegateSentryStore.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/persistent/DelegateSentryStore.java new file mode 100644 index 0000000..3026a62 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/persistent/DelegateSentryStore.java @@ -0,0 +1,422 @@ +/** + * 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.persistent; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; +import org.apache.hadoop.conf.Configuration; +import org.apache.sentry.core.common.Authorizable; +import org.apache.sentry.core.common.exception.SentryAccessDeniedException; +import org.apache.sentry.core.common.exception.SentryGrantDeniedException; +import org.apache.sentry.core.common.exception.SentryInvalidInputException; +import org.apache.sentry.core.common.exception.SentryNoSuchObjectException; +import org.apache.sentry.core.common.exception.SentryUserException; +import org.apache.sentry.provider.db.service.model.MSentryGMPrivilege; +import org.apache.sentry.provider.db.service.model.MSentryGroup; +import org.apache.sentry.provider.db.service.model.MSentryRole; +import org.apache.sentry.provider.db.service.persistent.SentryStore; +import org.apache.sentry.api.service.thrift.SentryPolicyStoreProcessor; +import org.apache.sentry.api.service.thrift.TSentryGroup; +import org.apache.sentry.api.service.thrift.TSentryRole; +import org.apache.sentry.service.common.ServiceConstants.ServerConfig; + +import javax.jdo.PersistenceManager; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * The DelegateSentryStore will supports the generic authorizable model. It stores the authorizables + * into separated column. Take the authorizables:[DATABASE=db1,TABLE=tb1,COLUMN=cl1] for example, + * The DATABASE,db1,TABLE,tb1,COLUMN and cl1 will be stored into the six columns(resourceName0=db1,resourceType0=DATABASE, + * resourceName1=tb1,resourceType1=TABLE, + * resourceName2=cl1,resourceType2=COLUMN ) of generic privilege table + */ +public class DelegateSentryStore implements SentryStoreLayer { + private SentryStore delegate; + private Configuration conf; + private Set<String> adminGroups; + private PrivilegeOperatePersistence privilegeOperator; + + public DelegateSentryStore(Configuration conf) throws Exception { + this.privilegeOperator = new PrivilegeOperatePersistence(conf); + this.conf = conf; + //delegated old sentryStore + this.delegate = new SentryStore(conf); + adminGroups = ImmutableSet.copyOf(toTrimmed(Sets.newHashSet(conf.getStrings( + ServerConfig.ADMIN_GROUPS, new String[]{})))); + } + + private MSentryRole getRole(String roleName, PersistenceManager pm) { + return delegate.getRole(pm, roleName); + } + + @Override + public Object createRole(String component, String role, + String requestor) throws Exception { + delegate.createSentryRole(role); + return null; + } + + /** + * The role is global in the generic model, such as the role may be has more than one component + * privileges, so delete role will remove all privileges related to it. + */ + @Override + public Object dropRole(final String component, final String role, final String requestor) + throws Exception { + delegate.dropSentryRole(toTrimmedLower(role)); + return null; + } + + @Override + public Set<String> getAllRoleNames() throws Exception { + return delegate.getAllRoleNames(); + } + + @Override + public Object alterRoleAddGroups(String component, String role, + Set<String> groups, String requestor) throws Exception { + delegate.alterSentryRoleAddGroups(requestor, role, toTSentryGroups(groups)); + return null; + } + + @Override + public Object alterRoleDeleteGroups(String component, String role, + Set<String> groups, String requestor) throws Exception { + delegate.alterSentryRoleDeleteGroups(role, toTSentryGroups(groups)); + return null; + } + + @Override + public Object alterRoleGrantPrivilege(final String component, final String role, + final PrivilegeObject privilege, final String grantorPrincipal) + throws Exception { + delegate.getTransactionManager().executeTransactionWithRetry( + pm -> { + pm.setDetachAllOnCommit(false); // No need to detach objects + String trimmedRole = toTrimmedLower(role); + MSentryRole mRole = getRole(trimmedRole, pm); + if (mRole == null) { + throw new SentryNoSuchObjectException("Role: " + trimmedRole); + } + + // check with grant option + grantOptionCheck(privilege, grantorPrincipal, pm); + + privilegeOperator.grantPrivilege(privilege, mRole, pm); + return null; + }); + return null; + } + + @Override + public Object alterRoleRevokePrivilege(final String component, + final String role, final PrivilegeObject privilege, final String grantorPrincipal) + throws Exception { + delegate.getTransactionManager().executeTransactionWithRetry( + pm -> { + pm.setDetachAllOnCommit(false); // No need to detach objects + String trimmedRole = toTrimmedLower(role); + MSentryRole mRole = getRole(trimmedRole, pm); + if (mRole == null) { + throw new SentryNoSuchObjectException("Role: " + trimmedRole); + } + + // check with grant option + grantOptionCheck(privilege, grantorPrincipal, pm); + + privilegeOperator.revokePrivilege(privilege, mRole, pm); + return null; + }); + return null; + } + + @Override + public Object renamePrivilege(final String component, final String service, + final List<? extends Authorizable> oldAuthorizables, + final List<? extends Authorizable> newAuthorizables, final String requestor) + throws Exception { + Preconditions.checkNotNull(component); + Preconditions.checkNotNull(service); + Preconditions.checkNotNull(oldAuthorizables); + Preconditions.checkNotNull(newAuthorizables); + + if (oldAuthorizables.size() != newAuthorizables.size()) { + throw new SentryAccessDeniedException( + "rename privilege denied: the size of oldAuthorizables must equals the newAuthorizables " + + "oldAuthorizables:" + Arrays.toString(oldAuthorizables.toArray()) + " " + + "newAuthorizables:" + Arrays.toString(newAuthorizables.toArray())); + } + + delegate.getTransactionManager().executeTransactionWithRetry( + pm -> { + pm.setDetachAllOnCommit(false); // No need to detach objects + privilegeOperator.renamePrivilege(toTrimmedLower(component), toTrimmedLower(service), + oldAuthorizables, newAuthorizables, requestor, pm); + return null; + }); + return null; + } + + @Override + public Object dropPrivilege(final String component, + final PrivilegeObject privilege, final String requestor) throws Exception { + Preconditions.checkNotNull(requestor); + + delegate.getTransactionManager().executeTransactionWithRetry( + pm -> { + pm.setDetachAllOnCommit(false); // No need to detach objects + privilegeOperator.dropPrivilege(privilege, pm); + return null; + }); + return null; + } + + /** + * Grant option check + * @throws SentryUserException + */ + private void grantOptionCheck(PrivilegeObject requestPrivilege, + String grantorPrincipal,PersistenceManager pm) + throws SentryUserException { + + if (Strings.isNullOrEmpty(grantorPrincipal)) { + throw new SentryInvalidInputException("grantorPrincipal should not be null or empty"); + } + + Set<String> groups = getRequestorGroups(grantorPrincipal); + if (groups == null || groups.isEmpty()) { + throw new SentryGrantDeniedException(grantorPrincipal + + " has no grant!"); + } + //admin group check + if (!Sets.intersection(adminGroups, toTrimmed(groups)).isEmpty()) { + return; + } + //privilege grant option check + Set<MSentryRole> mRoles = delegate.getRolesForGroups(pm, groups); + if (!privilegeOperator.checkPrivilegeOption(mRoles, requestPrivilege, pm)) { + throw new SentryGrantDeniedException(grantorPrincipal + + " has no grant!"); + } + } + + @Override + public Set<String> getRolesByGroups(String component, Set<String> groups) + throws Exception { + if (groups == null || groups.isEmpty()) { + return Collections.emptySet(); + } + + Set<String> roles = Sets.newHashSet(); + for (TSentryRole tSentryRole : delegate.getTSentryRolesByGroupName(groups, + true)) { + roles.add(tSentryRole.getRoleName()); + } + return roles; + } + + @Override + public Set<String> getGroupsByRoles(final String component, final Set<String> roles) + throws Exception { + // In all calls roles contain exactly one group + if (roles.isEmpty()) { + return Collections.emptySet(); + } + + // Collect resulting group names in a set + Set<String> groupNames = new HashSet<>(); + for (String role : roles) { + MSentryRole sentryRole = null; + try { + sentryRole = delegate.getMSentryRoleByName(role); + } + catch (SentryNoSuchObjectException e) { + // Role disappeared - not a big deal, just ognore it + continue; + } + // Collect all group names for this role. + // Since we use a set, a group can appear multiple times and will only + // show up once in a set + for (MSentryGroup group : sentryRole.getGroups()) { + groupNames.add(group.getGroupName()); + } + } + + return groupNames; + } + + @Override + public Set<PrivilegeObject> getPrivilegesByRole(final String component, + final Set<String> roles) throws Exception { + Preconditions.checkNotNull(roles); + if (roles.isEmpty()) { + return Collections.emptySet(); + } + return delegate.getTransactionManager().executeTransaction( + pm -> { + pm.setDetachAllOnCommit(false); // No need to detach objects + Set<MSentryRole> mRoles = new HashSet<>(); + for (String role : roles) { + MSentryRole mRole = getRole(toTrimmedLower(role), pm); + if (mRole != null) { + mRoles.add(mRole); + } + } + return new HashSet<>(privilegeOperator.getPrivilegesByRole(mRoles, pm)); + }); + } + + @Override + public Set<PrivilegeObject> getPrivilegesByProvider(final String component, + final String service, final Set<String> roles, final Set<String> groups, + final List<? extends Authorizable> authorizables) throws Exception { + Preconditions.checkNotNull(component); + Preconditions.checkNotNull(service); + + return delegate.getTransactionManager().executeTransaction( + pm -> { + pm.setDetachAllOnCommit(false); // No need to detach objects + String trimmedComponent = toTrimmedLower(component); + String trimmedService = toTrimmedLower(service); + + //CaseInsensitive roleNames + Set<String> trimmedRoles = SentryStore.toTrimedLower(roles); + + if (groups != null) { + trimmedRoles.addAll(delegate.getRoleNamesForGroups(groups)); + } + + if (trimmedRoles.isEmpty()) { + return Collections.emptySet(); + } + + Set<MSentryRole> mRoles = new HashSet<>(trimmedRoles.size()); + for (String role : trimmedRoles) { + MSentryRole mRole = getRole(role, pm); + if (mRole != null) { + mRoles.add(mRole); + } + } + //get the privileges + Set<PrivilegeObject> privileges = new HashSet<>(); + privileges.addAll(privilegeOperator. + getPrivilegesByProvider(trimmedComponent, + trimmedService, mRoles, authorizables, pm)); + return privileges; + }); + } + + @Override + public Set<MSentryGMPrivilege> getPrivilegesByAuthorizable(final String component, + final String service, final Set<String> validActiveRoles, + final List<? extends Authorizable> authorizables) throws Exception { + if (validActiveRoles == null || validActiveRoles.isEmpty()) { + return Collections.emptySet(); + } + + Preconditions.checkNotNull(component); + Preconditions.checkNotNull(service); + + return delegate.getTransactionManager().executeTransaction( + pm -> { + String lComponent = toTrimmedLower(component); + String lService = toTrimmedLower(service); + Set<MSentryRole> mRoles = new HashSet<>(validActiveRoles.size()); + for (String role : validActiveRoles) { + MSentryRole mRole = getRole(role, pm); + if (mRole != null) { + mRoles.add(mRole); + } + } + + //get the privileges + Set<MSentryGMPrivilege> mSentryGMPrivileges = + privilegeOperator.getPrivilegesByAuthorizable(lComponent, lService, + mRoles, authorizables, pm); + + final Set<MSentryGMPrivilege> privileges = + new HashSet<>(mSentryGMPrivileges.size()); + for (MSentryGMPrivilege mSentryGMPrivilege : mSentryGMPrivileges) { + /* + * force to load all roles related this privilege + * avoid the lazy-loading + */ + pm.retrieve(mSentryGMPrivilege); + privileges.add(mSentryGMPrivilege); + } + return privileges; + }); + } + + @Override + public void close() { + delegate.stop(); + } + + private Set<TSentryGroup> toTSentryGroups(Set<String> groups) { + if (groups.isEmpty()) { + return Collections.emptySet(); + } + Set<TSentryGroup> tSentryGroups = new HashSet<>(groups.size()); + for (String group : groups) { + tSentryGroups.add(new TSentryGroup(group)); + } + return tSentryGroups; + } + + private static Set<String> toTrimmed(Set<String> s) { + if (s.isEmpty()) { + return Collections.emptySet(); + } + Set<String> result = new HashSet<>(s.size()); + for (String v : s) { + result.add(v.trim()); + } + return result; + } + + private static String toTrimmedLower(String s) { + if (s == null) { + return ""; + } + return s.trim().toLowerCase(); + } + + private Set<String> getRequestorGroups(String userName) + throws SentryUserException { + return SentryPolicyStoreProcessor.getGroupsFromUserName(this.conf, userName); + } + + @VisibleForTesting + void clearAllTables() throws Exception { + delegate.getTransactionManager().executeTransaction( + pm -> { + pm.newQuery(MSentryRole.class).deletePersistentAll(); + pm.newQuery(MSentryGroup.class).deletePersistentAll(); + pm.newQuery(MSentryGMPrivilege.class).deletePersistentAll(); + return null; + }); + } +}
http://git-wip-us.apache.org/repos/asf/sentry/blob/7db84b2f/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/persistent/PrivilegeObject.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/persistent/PrivilegeObject.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/persistent/PrivilegeObject.java new file mode 100644 index 0000000..feab1e9 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/persistent/PrivilegeObject.java @@ -0,0 +1,231 @@ +/** + * 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.persistent; + +import static org.apache.sentry.core.common.utils.SentryConstants.KV_JOINER; +import static org.apache.sentry.core.common.utils.SentryConstants.AUTHORIZABLE_JOINER; + +import java.util.List; +import org.apache.sentry.core.common.Authorizable; +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; + +public final class PrivilegeObject { + private final String component; + private final String service; + private final String action; + private final Boolean grantOption; + private List<? extends Authorizable> authorizables; + + private PrivilegeObject(String component, String service, String action, + Boolean grantOption, + List<? extends Authorizable> authorizables) { + this.component = component; + this.service = service; + this.action = action; + this.grantOption = grantOption; + this.authorizables = authorizables; + } + + public List<? extends Authorizable> getAuthorizables() { + return authorizables; + } + + public String getAction() { + return action; + } + + public String getComponent() { + return component; + } + + public String getService() { + return service; + } + + public Boolean getGrantOption() { + return grantOption; + } + + @Override + public String toString() { + List<String> authorizable = Lists.newArrayList(); + for (Authorizable az : authorizables) { + authorizable.add(KV_JOINER.join(az.getTypeName(),az.getName())); + } + return "PrivilegeObject [" + ", service=" + service + ", component=" + + component + ", authorizables=" + AUTHORIZABLE_JOINER.join(authorizable) + + ", action=" + action + ", grantOption=" + grantOption + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((action == null) ? 0 : action.hashCode()); + result = prime * result + ((component == null) ? 0 : component.hashCode()); + result = prime * result + ((service == null) ? 0 : service.hashCode()); + result = prime * result + ((grantOption == null) ? 0 : grantOption.hashCode()); + for (Authorizable authorizable : authorizables) { + result = prime * result + authorizable.getTypeName().hashCode(); + result = prime * result + authorizable.getName().hashCode(); + } + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + PrivilegeObject other = (PrivilegeObject) obj; + if (action == null) { + if (other.action != null) { + return false; + } + } else if (!action.equals(other.action)) { + return false; + } + if (service == null) { + if (other.service != null) { + return false; + } + } else if (!service.equals(other.service)) { + return false; + } + if (component == null) { + if (other.component != null) { + return false; + } + } else if (!component.equals(other.component)) { + return false; + } + if (grantOption == null) { + if (other.grantOption != null) { + return false; + } + } else if (!grantOption.equals(other.grantOption)) { + return false; + } + + if (authorizables.size() != other.authorizables.size()) { + return false; + } + for (int i = 0; i < authorizables.size(); i++) { + String o1 = KV_JOINER.join(authorizables.get(i).getTypeName(), + authorizables.get(i).getName()); + String o2 = KV_JOINER.join(other.authorizables.get(i).getTypeName(), + other.authorizables.get(i).getName()); + if (!o1.equalsIgnoreCase(o2)) { + return false; + } + } + return true; + } + + public static class Builder { + private String component; + private String service; + private String action; + private Boolean grantOption; + private List<? extends Authorizable> authorizables; + + public Builder() { + + } + + public Builder(PrivilegeObject privilege) { + this.component = privilege.component; + this.service = privilege.service; + this.action = privilege.action; + this.grantOption = privilege.grantOption; + this.authorizables = privilege.authorizables; + } + + public Builder setComponent(String component) { + this.component = component; + return this; + } + + public Builder setService(String service) { + this.service = service; + return this; + } + + public Builder setAction(String action) { + this.action = action; + return this; + } + + public Builder withGrantOption(Boolean grantOption) { + this.grantOption = grantOption; + return this; + } + + public Builder setAuthorizables(List<? extends Authorizable> authorizables) { + this.authorizables = authorizables; + return this; + } + + /** + * TolowerCase the authorizable name, the authorizable type is define when it was created. + * Take the Solr for example, it has two Authorizable objects. They have the type Collection + * and Field, they are can't be changed. So we should unified the authorizable name tolowercase. + * @return new authorizable lists + */ + private List<? extends Authorizable> toLowerAuthorizableName(List<? extends Authorizable> authorizables) { + List<Authorizable> newAuthorizable = Lists.newArrayList(); + if (authorizables == null || authorizables.size() == 0) { + return newAuthorizable; + } + for (final Authorizable authorizable : authorizables) { + newAuthorizable.add(new Authorizable() { + @Override + public String getTypeName() { + return authorizable.getTypeName(); + } + @Override + public String getName() { + return authorizable.getName(); + } + }); + } + return newAuthorizable; + } + + public PrivilegeObject build() { + Preconditions.checkNotNull(component); + Preconditions.checkNotNull(service); + Preconditions.checkNotNull(action); + //CaseInsensitive authorizable name + List<? extends Authorizable> newAuthorizable = toLowerAuthorizableName(authorizables); + + return new PrivilegeObject(component.toLowerCase(), + service.toLowerCase(), + action.toLowerCase(), + grantOption, + newAuthorizable); + } + } +} http://git-wip-us.apache.org/repos/asf/sentry/blob/7db84b2f/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/persistent/PrivilegeOperatePersistence.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/persistent/PrivilegeOperatePersistence.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/persistent/PrivilegeOperatePersistence.java new file mode 100644 index 0000000..4e2290b --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/persistent/PrivilegeOperatePersistence.java @@ -0,0 +1,566 @@ +/** + * 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.persistent; + +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.jdo.PersistenceManager; +import javax.jdo.Query; + +import org.apache.hadoop.conf.Configuration; +import org.apache.sentry.core.common.exception.SentryUserException; +import org.apache.sentry.core.common.Action; +import org.apache.sentry.core.common.Authorizable; +import org.apache.sentry.core.common.BitFieldAction; +import org.apache.sentry.core.common.BitFieldActionFactory; +import org.apache.sentry.core.model.indexer.IndexerActionFactory; +import org.apache.sentry.core.model.kafka.KafkaActionFactory; +import org.apache.sentry.core.model.solr.SolrActionFactory; +import org.apache.sentry.core.model.sqoop.SqoopActionFactory; +import org.apache.sentry.provider.db.generic.service.persistent.PrivilegeObject.Builder; +import org.apache.sentry.provider.db.service.model.MSentryGMPrivilege; +import org.apache.sentry.provider.db.service.model.MSentryRole; + +import com.google.common.base.Strings; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import org.apache.sentry.provider.db.service.persistent.QueryParamBuilder; +import org.apache.sentry.provider.db.service.persistent.SentryStore; +import org.apache.sentry.service.common.ServiceConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Sentry Generic model privilege persistence support. + * <p> + * This class is similar to {@link SentryStore} but operates on generic + * privileges. + */ +public class PrivilegeOperatePersistence { + private static final String SERVICE_NAME = "serviceName"; + private static final String COMPONENT_NAME = "componentName"; + private static final String SCOPE = "scope"; + private static final String ACTION = "action"; + + private static final Logger LOGGER = LoggerFactory.getLogger(PrivilegeOperatePersistence.class); + private static final Map<String, BitFieldActionFactory> actionFactories = Maps.newHashMap(); + static{ + actionFactories.put("solr", new SolrActionFactory()); + actionFactories.put("sqoop", new SqoopActionFactory()); + actionFactories.put("kafka", KafkaActionFactory.getInstance()); + actionFactories.put("hbaseindexer", new IndexerActionFactory()); + } + + private final Configuration conf; + + PrivilegeOperatePersistence(Configuration conf) { + this.conf = conf; + } + + /** + * Return query builder to execute in JDO for search the given privilege + * @param privilege Privilege to extract + * @return query builder suitable for executing the query + */ + private static QueryParamBuilder toQueryParam(MSentryGMPrivilege privilege) { + QueryParamBuilder paramBuilder = QueryParamBuilder.newQueryParamBuilder(); + paramBuilder.add(SERVICE_NAME, SentryStore.toNULLCol(privilege.getServiceName()), true) + .add(COMPONENT_NAME, SentryStore.toNULLCol(privilege.getComponentName()), true) + .add(SCOPE, SentryStore.toNULLCol(privilege.getScope()), true) + .add(ACTION, SentryStore.toNULLCol(privilege.getAction()), true); + + Boolean grantOption = privilege.getGrantOption(); + paramBuilder.addObject(SentryStore.GRANT_OPTION, grantOption); + + List<? extends Authorizable> authorizables = privilege.getAuthorizables(); + int nAuthorizables = authorizables.size(); + for (int i = 0; i < MSentryGMPrivilege.AUTHORIZABLE_LEVEL; i++) { + String resourceName = MSentryGMPrivilege.PREFIX_RESOURCE_NAME + String.valueOf(i); + String resourceType = MSentryGMPrivilege.PREFIX_RESOURCE_TYPE + String.valueOf(i); + + if (i >= nAuthorizables) { + paramBuilder.addNull(resourceName); + paramBuilder.addNull(resourceType); + } else { + paramBuilder.add(resourceName, authorizables.get(i).getName(), true); + paramBuilder.add(resourceType, authorizables.get(i).getTypeName(), true); + } + } + return paramBuilder; + } + + /** + * Create a query template tha includes information from the input privilege: + * <ul> + * <li>Service name</li> + * <li>Component name</li> + * <li>Name and type for each authorizable present</li> + * </ul> + * For exmaple, for Solr may configure the following privileges: + * <ul> + * <li>{@code p1:Collection=c1->action=query}</li> + * <li>{@code p2:Collection=c1->Field=f1->action=query}</li> + * <li>{@code p3:Collection=c1->Field=f2->action=query}</li> + * </ul> + * When the request for privilege revoke has + * {@code p4:Collection=c1->action=query} + * all privileges matching {@code Collection=c1} should be revoke which means that p1, p2 and p3 + * should all be revoked. + * + * @param privilege Source privilege + * @return ParamBuilder suitable for executing the query + */ + private static QueryParamBuilder populateIncludePrivilegesParams(MSentryGMPrivilege privilege) { + QueryParamBuilder paramBuilder = QueryParamBuilder.newQueryParamBuilder(); + paramBuilder.add(SERVICE_NAME, SentryStore.toNULLCol(privilege.getServiceName()), true); + paramBuilder.add(COMPONENT_NAME, SentryStore.toNULLCol(privilege.getComponentName()), true); + + List<? extends Authorizable> authorizables = privilege.getAuthorizables(); + int i = 0; + for(Authorizable auth: authorizables) { + String resourceName = MSentryGMPrivilege.PREFIX_RESOURCE_NAME + String.valueOf(i); + String resourceType = MSentryGMPrivilege.PREFIX_RESOURCE_TYPE + String.valueOf(i); + paramBuilder.add(resourceName, auth.getName(), true); + paramBuilder.add(resourceType, auth.getTypeName(), true); + i++; + } + return paramBuilder; + } + + /** + * Verify whether specified privilege can be granted + * @param roles set of roles for the privilege + * @param privilege privilege being checked + * @param pm Persistentence manager instance + * @return true iff at least one privilege within the role allows for the + * requested privilege + */ + boolean checkPrivilegeOption(Set<MSentryRole> roles, PrivilegeObject privilege, PersistenceManager pm) { + MSentryGMPrivilege requestPrivilege = convertToPrivilege(privilege); + if (roles.isEmpty()) { + return false; + } + // get persistent privileges by roles + // Find all GM privileges for all the input roles + Query query = pm.newQuery(MSentryGMPrivilege.class); + QueryParamBuilder paramBuilder = QueryParamBuilder.addRolesFilter(query, null, + SentryStore.rolesToRoleNames(roles)); + query.setFilter(paramBuilder.toString()); + List<MSentryGMPrivilege> tPrivileges = + (List<MSentryGMPrivilege>)query.executeWithMap(paramBuilder.getArguments()); + + for (MSentryGMPrivilege tPrivilege : tPrivileges) { + if (tPrivilege.getGrantOption() && tPrivilege.implies(requestPrivilege)) { + return true; + } + } + return false; + } + + public void grantPrivilege(PrivilegeObject privilege,MSentryRole role, PersistenceManager pm) throws SentryUserException { + MSentryGMPrivilege mPrivilege = convertToPrivilege(privilege); + grantRolePartial(mPrivilege, role, pm); + } + + private void grantRolePartial(MSentryGMPrivilege grantPrivilege, + MSentryRole role,PersistenceManager pm) throws SentryUserException { + /* + * If Grant is for ALL action and other actions belongs to ALL action already exists.. + * need to remove it and GRANT ALL action + */ + String component = grantPrivilege.getComponentName(); + BitFieldAction action = getAction(component, grantPrivilege.getAction()); + BitFieldAction allAction = getAction(component, Action.ALL); + + if (action.implies(allAction)) { + /* + * ALL action is a multi-bit set action that includes some actions such as INSERT,SELECT and CREATE. + */ + List<? extends BitFieldAction> actions = getActionFactory(component).getActionsByCode(allAction.getActionCode()); + for (BitFieldAction ac : actions) { + grantPrivilege.setAction(ac.getValue()); + MSentryGMPrivilege existPriv = getPrivilege(grantPrivilege, pm); + if (existPriv != null && role.getGmPrivileges().contains(existPriv)) { + /* + * force to load all roles related this privilege + * avoid the lazy-loading risk,such as: + * if the roles field of privilege aren't loaded, then the roles is a empty set + * privilege.removeRole(role) and pm.makePersistent(privilege) + * will remove other roles that shouldn't been removed + */ + pm.retrieve(existPriv); + existPriv.removeRole(role); + pm.makePersistent(existPriv); + } + } + } else { + /* + * If ALL Action already exists.. + * do nothing. + */ + grantPrivilege.setAction(allAction.getValue()); + MSentryGMPrivilege allPrivilege = getPrivilege(grantPrivilege, pm); + if (allPrivilege != null && role.getGmPrivileges().contains(allPrivilege)) { + return; + } + } + + /* + * restore the action + */ + grantPrivilege.setAction(action.getValue()); + /* + * check the privilege is exist or not + */ + MSentryGMPrivilege mPrivilege = getPrivilege(grantPrivilege, pm); + if (mPrivilege == null) { + mPrivilege = grantPrivilege; + } + mPrivilege.appendRole(role); + pm.makePersistent(mPrivilege); + } + + + public void revokePrivilege(PrivilegeObject privilege,MSentryRole role, PersistenceManager pm) throws SentryUserException { + MSentryGMPrivilege mPrivilege = getPrivilege(convertToPrivilege(privilege), pm); + if (mPrivilege == null) { + mPrivilege = convertToPrivilege(privilege); + } else { + mPrivilege = pm.detachCopy(mPrivilege); + } + + Set<MSentryGMPrivilege> privilegeGraph = Sets.newHashSet(); + privilegeGraph.addAll(populateIncludePrivileges(Sets.newHashSet(role), mPrivilege, pm)); + + /* + * Get the privilege graph + * populateIncludePrivileges will get the privileges that needed revoke + */ + for (MSentryGMPrivilege persistedPriv : privilegeGraph) { + /* + * force to load all roles related this privilege + * avoid the lazy-loading risk,such as: + * if the roles field of privilege aren't loaded, then the roles is a empty set + * privilege.removeRole(role) and pm.makePersistent(privilege) + * will remove other roles that shouldn't been removed + */ + revokeRolePartial(mPrivilege, persistedPriv, role, pm); + } + pm.makePersistent(role); + } + + private Set<MSentryGMPrivilege> populateIncludePrivileges(Set<MSentryRole> roles, + MSentryGMPrivilege parent, PersistenceManager pm) { + Set<MSentryGMPrivilege> childrens = Sets.newHashSet(); + + Query query = pm.newQuery(MSentryGMPrivilege.class); + QueryParamBuilder paramBuilder = populateIncludePrivilegesParams(parent); + + // add filter for role names + if ((roles != null) && !roles.isEmpty()) { + QueryParamBuilder.addRolesFilter(query, paramBuilder, SentryStore.rolesToRoleNames(roles)); + } + query.setFilter(paramBuilder.toString()); + + List<MSentryGMPrivilege> privileges = + (List<MSentryGMPrivilege>)query.executeWithMap(paramBuilder.getArguments()); + childrens.addAll(privileges); + return childrens; + } + + /** + * Roles can be granted multi-bit set action like ALL action on resource object. + * Take solr component for example, When a role has been granted ALL action but + * QUERY or UPDATE or CREATE are revoked, we need to remove the ALL + * privilege and add left privileges like UPDATE and CREATE(QUERY was revoked) or + * QUERY and UPDATE(CREATEE was revoked). + */ + private void revokeRolePartial(MSentryGMPrivilege revokePrivilege, + MSentryGMPrivilege persistedPriv, MSentryRole role, + PersistenceManager pm) throws SentryUserException { + String component = revokePrivilege.getComponentName(); + BitFieldAction revokeaction = getAction(component, revokePrivilege.getAction()); + BitFieldAction persistedAction = getAction(component, persistedPriv.getAction()); + BitFieldAction allAction = getAction(component, Action.ALL); + + if (revokeaction.implies(allAction)) { + /* + * if revoke action is ALL, directly revoke its children privileges and itself + */ + persistedPriv.removeRole(role); + pm.makePersistent(persistedPriv); + } else { + /* + * if persisted action is ALL, it only revoke the requested action and left partial actions + * like the requested action is SELECT, the UPDATE and CREATE action are left + */ + if (persistedAction.implies(allAction)) { + /* + * revoke the ALL privilege + */ + persistedPriv.removeRole(role); + pm.makePersistent(persistedPriv); + + List<? extends BitFieldAction> actions = getActionFactory(component).getActionsByCode(allAction.getActionCode()); + for (BitFieldAction ac: actions) { + if (ac.getActionCode() != revokeaction.getActionCode()) { + /* + * grant the left privileges to role + */ + MSentryGMPrivilege tmpPriv = new MSentryGMPrivilege(persistedPriv); + tmpPriv.setAction(ac.getValue()); + MSentryGMPrivilege leftPersistedPriv = getPrivilege(tmpPriv, pm); + if (leftPersistedPriv == null) { + //leftPersistedPriv isn't exist + leftPersistedPriv = tmpPriv; + role.appendGMPrivilege(leftPersistedPriv); + } + leftPersistedPriv.appendRole(role); + pm.makePersistent(leftPersistedPriv); + } + } + } else if (revokeaction.implies(persistedAction)) { + /* + * if the revoke action is equal to the persisted action and they aren't ALL action + * directly remove the role from privilege + */ + persistedPriv.removeRole(role); + pm.makePersistent(persistedPriv); + } + /* + * if the revoke action is not equal to the persisted action, + * do nothing + */ + } + } + + /** + * Drop any role related to the requested privilege and its children privileges + */ + public void dropPrivilege(PrivilegeObject privilege,PersistenceManager pm) throws SentryUserException { + MSentryGMPrivilege requestPrivilege = convertToPrivilege(privilege); + + if (Strings.isNullOrEmpty(privilege.getAction())) { + requestPrivilege.setAction(getAction(privilege.getComponent(), Action.ALL).getValue()); + } + /* + * Get the privilege graph + * populateIncludePrivileges will get the privileges that need dropped, + */ + Set<MSentryGMPrivilege> privilegeGraph = Sets.newHashSet(); + privilegeGraph.addAll(populateIncludePrivileges(null, requestPrivilege, pm)); + + for (MSentryGMPrivilege mPrivilege : privilegeGraph) { + /* + * force to load all roles related this privilege + * avoid the lazy-loading + */ + pm.retrieve(mPrivilege); + Set<MSentryRole> roles = mPrivilege.getRoles(); + for (MSentryRole role : roles) { + revokeRolePartial(requestPrivilege, mPrivilege, role, pm); + } + } + } + + private MSentryGMPrivilege convertToPrivilege(PrivilegeObject privilege) { + return new MSentryGMPrivilege(privilege.getComponent(), + privilege.getService(), privilege.getAuthorizables(), + privilege.getAction(), privilege.getGrantOption()); + } + + private MSentryGMPrivilege getPrivilege(MSentryGMPrivilege privilege, PersistenceManager pm) { + Query query = pm.newQuery(MSentryGMPrivilege.class); + QueryParamBuilder paramBuilder = toQueryParam(privilege); + query.setFilter(paramBuilder.toString()); + query.setUnique(true); + MSentryGMPrivilege result = (MSentryGMPrivilege)query.executeWithMap(paramBuilder.getArguments()); + return result; + } + + /** + * Get all privileges associated with a given roles + * @param roles Set of roles + * @param pm Persistence manager instance + * @return Set (potentially empty) of privileges associated with roles + */ + Set<PrivilegeObject> getPrivilegesByRole(Set<MSentryRole> roles, PersistenceManager pm) { + if (roles == null || roles.isEmpty()) { + return Collections.emptySet(); + } + + Query query = pm.newQuery(MSentryGMPrivilege.class); + // Find privileges matching all roles + QueryParamBuilder paramBuilder = QueryParamBuilder.addRolesFilter(query, null, + SentryStore.rolesToRoleNames(roles)); + query.setFilter(paramBuilder.toString()); + List<MSentryGMPrivilege> mPrivileges = + (List<MSentryGMPrivilege>)query.executeWithMap(paramBuilder.getArguments()); + if (mPrivileges.isEmpty()) { + return Collections.emptySet(); + } + + Set<PrivilegeObject> privileges = new HashSet<>(mPrivileges.size()); + for (MSentryGMPrivilege mPrivilege : mPrivileges) { + privileges.add(new Builder() + .setComponent(mPrivilege.getComponentName()) + .setService(mPrivilege.getServiceName()) + .setAction(mPrivilege.getAction()) + .setAuthorizables(mPrivilege.getAuthorizables()) + .withGrantOption(mPrivilege.getGrantOption()) + .build()); + } + return privileges; + } + + Set<PrivilegeObject> getPrivilegesByProvider(String component, + String service, Set<MSentryRole> roles, + List<? extends Authorizable> authorizables, PersistenceManager pm) { + Set<PrivilegeObject> privileges = Sets.newHashSet(); + if (roles == null || roles.isEmpty()) { + return privileges; + } + + MSentryGMPrivilege parentPrivilege = new MSentryGMPrivilege(component, service, authorizables, null, null); + Set<MSentryGMPrivilege> privilegeGraph = Sets.newHashSet(); + privilegeGraph.addAll(populateIncludePrivileges(roles, parentPrivilege, pm)); + + for (MSentryGMPrivilege mPrivilege : privilegeGraph) { + privileges.add(new Builder() + .setComponent(mPrivilege.getComponentName()) + .setService(mPrivilege.getServiceName()) + .setAction(mPrivilege.getAction()) + .setAuthorizables(mPrivilege.getAuthorizables()) + .withGrantOption(mPrivilege.getGrantOption()) + .build()); + } + return privileges; + } + + Set<MSentryGMPrivilege> getPrivilegesByAuthorizable(String component, + String service, Set<MSentryRole> roles, + List<? extends Authorizable> authorizables, PersistenceManager pm) { + + Set<MSentryGMPrivilege> privilegeGraph = Sets.newHashSet(); + + if (roles == null || roles.isEmpty()) { + return privilegeGraph; + } + + MSentryGMPrivilege parentPrivilege = new MSentryGMPrivilege(component, service, authorizables, null, null); + privilegeGraph.addAll(populateIncludePrivileges(roles, parentPrivilege, pm)); + return privilegeGraph; + } + + public void renamePrivilege(String component, String service, + List<? extends Authorizable> oldAuthorizables, List<? extends Authorizable> newAuthorizables, + String grantorPrincipal, PersistenceManager pm) + throws SentryUserException { + MSentryGMPrivilege oldPrivilege = new MSentryGMPrivilege(component, service, oldAuthorizables, null, null); + oldPrivilege.setAction(getAction(component,Action.ALL).getValue()); + /* + * Get the privilege graph + * populateIncludePrivileges will get the old privileges that need dropped + */ + Set<MSentryGMPrivilege> privilegeGraph = Sets.newHashSet(); + privilegeGraph.addAll(populateIncludePrivileges(null, oldPrivilege, pm)); + + for (MSentryGMPrivilege dropPrivilege : privilegeGraph) { + /* + * construct the new privilege needed to add + */ + List<Authorizable> authorizables = new ArrayList<Authorizable>( + dropPrivilege.getAuthorizables()); + for (int i = 0; i < newAuthorizables.size(); i++) { + authorizables.set(i, newAuthorizables.get(i)); + } + MSentryGMPrivilege newPrivilge = new MSentryGMPrivilege( + component,service, authorizables, dropPrivilege.getAction(), + dropPrivilege.getGrantOption()); + + /* + * force to load all roles related this privilege + * avoid the lazy-loading + */ + pm.retrieve(dropPrivilege); + + Set<MSentryRole> roles = dropPrivilege.getRoles(); + for (MSentryRole role : roles) { + revokeRolePartial(oldPrivilege, dropPrivilege, role, pm); + grantRolePartial(newPrivilge, role, pm); + } + } + } + + private BitFieldAction getAction(String component, String name) throws SentryUserException { + BitFieldActionFactory actionFactory = getActionFactory(component); + BitFieldAction action = actionFactory.getActionByName(name); + if (action == null) { + throw new SentryUserException("Can not get BitFieldAction for name: " + name); + } + return action; + } + + private BitFieldActionFactory getActionFactory(String component) throws SentryUserException { + String caseInsensitiveComponent = component.toLowerCase(); + if (actionFactories.containsKey(caseInsensitiveComponent)) { + return actionFactories.get(caseInsensitiveComponent); + } + BitFieldActionFactory actionFactory = createActionFactory(caseInsensitiveComponent); + actionFactories.put(caseInsensitiveComponent, actionFactory); + LOGGER.info("Action factory for component {} is not found in cache. Loaded it from configuration as {}.", + component, actionFactory.getClass().getName()); + return actionFactory; + } + + private BitFieldActionFactory createActionFactory(String component) throws SentryUserException { + String actionFactoryClassName = + conf.get(String.format(ServiceConstants.ServerConfig.SENTRY_COMPONENT_ACTION_FACTORY_FORMAT, component)); + if (actionFactoryClassName == null) { + throw new SentryUserException("ActionFactory not defined for component " + component + + ". Please define the parameter " + + "sentry." + component + ".action.factory in configuration"); + } + Class<?> actionFactoryClass; + try { + actionFactoryClass = Class.forName(actionFactoryClassName); + } catch (ClassNotFoundException e) { + throw new SentryUserException("ActionFactory class " + actionFactoryClassName + " not found."); + } + if (!BitFieldActionFactory.class.isAssignableFrom(actionFactoryClass)) { + throw new SentryUserException("ActionFactory class " + actionFactoryClassName + " must extend " + + BitFieldActionFactory.class.getName()); + } + BitFieldActionFactory actionFactory; + try { + Constructor<?> actionFactoryConstructor = actionFactoryClass.getDeclaredConstructor(); + actionFactoryConstructor.setAccessible(true); + actionFactory = (BitFieldActionFactory) actionFactoryClass.newInstance(); + } catch (NoSuchMethodException | InstantiationException | IllegalAccessException e) { + throw new SentryUserException("Could not instantiate actionFactory " + actionFactoryClassName + + " for component: " + component, e); + } + return actionFactory; + } +} http://git-wip-us.apache.org/repos/asf/sentry/blob/7db84b2f/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/persistent/SentryStoreLayer.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/persistent/SentryStoreLayer.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/persistent/SentryStoreLayer.java new file mode 100644 index 0000000..eec2757 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/service/persistent/SentryStoreLayer.java @@ -0,0 +1,186 @@ +/** + * 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.persistent; + +import java.util.List; +import java.util.Set; + +import org.apache.sentry.core.common.Authorizable; +import org.apache.sentry.provider.db.service.model.MSentryGMPrivilege; + +/** + * Sentry store for persistent the authorize object to database + */ +public interface SentryStoreLayer { + /** + * Create a role + * @param component: The request respond to which component + * @param role: The name of role + * @param requestor: User on whose behalf the request is launched + * @throws Exception + */ + Object createRole(String component, String role, + String requestor) throws Exception; + + /** + * Drop a role + * @param component: The request respond to which component + * @param role: The name of role + * @param requestor: user on whose behalf the request is launched + * @throws Exception + */ + Object dropRole(String component, String role, + String requestor) throws Exception; + + /** + * Add a role to groups. + * @param component: The request respond to which component + * @param role: The name of role + * @param groups: The name of groups + * @param requestor: User on whose behalf the request is issued + * @throws Exception + */ + Object alterRoleAddGroups(String component, String role, + Set<String> groups, String requestor) throws Exception; + + /** + * Delete a role from groups. + * @param component: The request respond to which component + * @param role: The name of role + * @param groups: The name of groups + * @param requestor: User on whose behalf the request is launched + * @throws Exception + */ + Object alterRoleDeleteGroups(String component, String role, + Set<String> groups, String requestor) throws Exception; + + /** + * Grant a privilege to role. + * @param component: The request respond to which component + * @param role: The name of role + * @param privilege: The privilege object will be granted + * @param grantorPrincipal: User on whose behalf the request is launched + * @throws Exception + */ + Object alterRoleGrantPrivilege(String component, String role, + PrivilegeObject privilege, String grantorPrincipal) throws Exception; + + /** + * Revoke a privilege from role. + * @param component: The request respond to which component + * @param role: The name of role + * @param privilege: The privilege object will revoked + * @param grantorPrincipal: User on whose behalf the request is launched + * @throws Exception + */ + Object alterRoleRevokePrivilege(String component, String role, + PrivilegeObject privilege, String grantorPrincipal) throws Exception; + + /** + * Rename privilege + * + * @param component: The request respond to which component + * @param service: The name of service + * @param oldAuthorizables: The old list of authorize objects + * @param newAuthorizables: The new list of authorize objects + * @param requestor: User on whose behalf the request is launched + * @throws Exception + */ + Object renamePrivilege( + String component, String service, List<? extends Authorizable> oldAuthorizables, + List<? extends Authorizable> newAuthorizables, String requestor) throws Exception; + + /** + * Drop privilege + * @param component: The request respond to which component + * @param privilege: The privilege will be dropped + * @param requestor: User on whose behalf the request is launched + * @throws Exception + */ + Object dropPrivilege(String component, PrivilegeObject privilege, + String requestor) throws Exception; + + /** + * Get roles + * @param component: The request respond to which component + * @param groups: The name of groups + * @returns the set of roles + * @throws Exception + */ + Set<String> getRolesByGroups(String component, Set<String> groups) throws Exception; + + /** + * Get groups + * @param component: The request respond to which component + * @param roles: The name of roles + * @returns the set of groups + * @throws Exception + */ + Set<String> getGroupsByRoles(String component, Set<String> roles) throws Exception; + + /** + * Get privileges + * @param component: The request respond to which component + * @param roles: The name of roles + * @returns the set of privileges + * @throws Exception + */ + Set<PrivilegeObject> getPrivilegesByRole(String component, Set<String> roles) throws Exception; + + /** + * get sentry privileges from provider as followings: + * @param component: The request respond to which component + * @param service: The name of service + * @param roles: The name of roles + * @param groups: The name of groups + * @param authorizables: The list of authorize objects + * @returns the set of privileges + * @throws Exception + */ + + Set<PrivilegeObject> getPrivilegesByProvider(String component, String service, Set<String> roles, + Set<String> groups, List<? extends Authorizable> authorizables) + throws Exception; + + /** + * Get all roles name. + * + * @returns The set of roles name, + */ + Set<String> getAllRoleNames() throws Exception; + + /** + * Get sentry privileges based on valid active roles and the authorize objects. + * + * @param component: The request respond to which component + * @param service: The name of service + * @param validActiveRoles: The valid active roles + * @param authorizables: The list of authorize objects + * @returns The set of MSentryGMPrivilege + * @throws Exception + */ + Set<MSentryGMPrivilege> getPrivilegesByAuthorizable(String component, String service, + Set<String> validActiveRoles, List<? extends Authorizable> authorizables) + throws Exception; + + /** + * close sentryStore + */ + void close(); + +} http://git-wip-us.apache.org/repos/asf/sentry/blob/7db84b2f/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/tools/GenericPrivilegeConverter.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/tools/GenericPrivilegeConverter.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/tools/GenericPrivilegeConverter.java new file mode 100644 index 0000000..6a2c77f --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/tools/GenericPrivilegeConverter.java @@ -0,0 +1,191 @@ +/* + * + * 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.tools; + +import static org.apache.sentry.core.common.utils.SentryConstants.AUTHORIZABLE_SEPARATOR; +import static org.apache.sentry.core.common.utils.SentryConstants.KV_SEPARATOR; +import static org.apache.sentry.core.common.utils.SentryConstants.RESOURCE_WILDCARD_VALUE; + +import com.google.common.collect.Lists; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.apache.sentry.api.generic.thrift.TAuthorizable; +import org.apache.sentry.api.generic.thrift.TSentryGrantOption; +import org.apache.sentry.api.generic.thrift.TSentryPrivilege; +import org.apache.sentry.core.common.Authorizable; +import org.apache.sentry.core.common.exception.SentryUserException; +import org.apache.sentry.core.common.utils.KeyValue; +import org.apache.sentry.core.common.utils.PolicyFileConstants; +import org.apache.sentry.core.common.utils.SentryConstants; +import org.apache.sentry.core.common.validator.PrivilegeValidator; +import org.apache.sentry.core.common.validator.PrivilegeValidatorContext; +import org.apache.sentry.core.model.indexer.IndexerModelAuthorizables; +import org.apache.sentry.core.model.indexer.IndexerPrivilegeModel; +import org.apache.sentry.core.model.kafka.KafkaAuthorizable; +import org.apache.sentry.core.model.kafka.KafkaModelAuthorizables; +import org.apache.sentry.core.model.kafka.KafkaPrivilegeModel; +import org.apache.sentry.core.model.solr.SolrModelAuthorizables; +import org.apache.sentry.core.model.solr.SolrPrivilegeModel; +import org.apache.sentry.core.model.sqoop.SqoopModelAuthorizables; +import org.apache.sentry.core.model.sqoop.SqoopPrivilegeModel; +import org.apache.sentry.provider.common.AuthorizationComponent; +import org.apache.shiro.config.ConfigurationException; + +/** + * A TSentryPrivilegeConverter implementation for "Generic" privileges, covering Apache Kafka, Apache Solr and Apache Sqoop. + * It converts privilege Strings to TSentryPrivilege Objects, and vice versa, for Generic clients. + * + * When a privilege String is converted to a TSentryPrivilege in "fromString", the validators associated with the + * given privilege model are also called on the privilege String. + */ +public class GenericPrivilegeConverter implements TSentryPrivilegeConverter { + private String component; + private String service; + private boolean validate; + + public GenericPrivilegeConverter(String component, String service) { + this(component, service, true); + } + + public GenericPrivilegeConverter(String component, String service, boolean validate) { + this.component = component; + this.service = service; + this.validate = validate; + } + + public TSentryPrivilege fromString(String privilegeStr) throws SentryUserException { + privilegeStr = parsePrivilegeString(privilegeStr); + if (validate) { + validatePrivilegeHierarchy(privilegeStr); + } + + TSentryPrivilege tSentryPrivilege = new TSentryPrivilege(); + List<TAuthorizable> authorizables = new LinkedList<TAuthorizable>(); + for (String authorizable : SentryConstants.AUTHORIZABLE_SPLITTER.split(privilegeStr)) { + KeyValue keyValue = new KeyValue(authorizable); + String key = keyValue.getKey(); + String value = keyValue.getValue(); + + Authorizable authz = getAuthorizable(keyValue); + if (authz != null) { + authorizables.add(new TAuthorizable(authz.getTypeName(), authz.getName())); + } else if (PolicyFileConstants.PRIVILEGE_ACTION_NAME.equalsIgnoreCase(key)) { + tSentryPrivilege.setAction(value); + } else { + throw new IllegalArgumentException("Unknown key: " + key); + } + } + + if (tSentryPrivilege.getAction() == null) { + throw new IllegalArgumentException("Privilege is invalid: action required but not specified."); + } + tSentryPrivilege.setComponent(component); + tSentryPrivilege.setServiceName(service); + tSentryPrivilege.setAuthorizables(authorizables); + return tSentryPrivilege; + } + + public String toString(TSentryPrivilege tSentryPrivilege) { + List<String> privileges = Lists.newArrayList(); + if (tSentryPrivilege != null) { + List<TAuthorizable> authorizables = tSentryPrivilege.getAuthorizables(); + String action = tSentryPrivilege.getAction(); + String grantOption = (tSentryPrivilege.getGrantOption() == TSentryGrantOption.TRUE ? "true" + : "false"); + + Iterator<TAuthorizable> it = authorizables.iterator(); + if (it != null) { + while (it.hasNext()) { + TAuthorizable tAuthorizable = it.next(); + privileges.add(SentryConstants.KV_JOINER.join( + tAuthorizable.getType(), tAuthorizable.getName())); + } + } + + if (!authorizables.isEmpty()) { + privileges.add(SentryConstants.KV_JOINER.join( + PolicyFileConstants.PRIVILEGE_ACTION_NAME, action)); + } + + // only append the grant option to privilege string if it's true + if ("true".equals(grantOption)) { + privileges.add(SentryConstants.KV_JOINER.join( + PolicyFileConstants.PRIVILEGE_GRANT_OPTION_NAME, grantOption)); + } + } + return SentryConstants.AUTHORIZABLE_JOINER.join(privileges); + } + + private String parsePrivilegeString(String privilegeStr) { + if (AuthorizationComponent.KAFKA.equals(component)) { + final String hostPrefix = KafkaAuthorizable.AuthorizableType.HOST.name() + KV_SEPARATOR; + final String hostPrefixLowerCase = hostPrefix.toLowerCase(); + if (!privilegeStr.toLowerCase().startsWith(hostPrefixLowerCase)) { + return hostPrefix + RESOURCE_WILDCARD_VALUE + AUTHORIZABLE_SEPARATOR + privilegeStr; + } + } + + return privilegeStr; + } + + private void validatePrivilegeHierarchy(String privilegeStr) throws SentryUserException { + List<PrivilegeValidator> validators = getPrivilegeValidators(); + PrivilegeValidatorContext context = new PrivilegeValidatorContext(null, privilegeStr); + for (PrivilegeValidator validator : validators) { + try { + validator.validate(context); + } catch (ConfigurationException e) { + throw new IllegalArgumentException(e); + } + } + } + + protected List<PrivilegeValidator> getPrivilegeValidators() throws SentryUserException { + if (AuthorizationComponent.KAFKA.equals(component)) { + return KafkaPrivilegeModel.getInstance().getPrivilegeValidators(); + } else if ("SOLR".equals(component)) { + return SolrPrivilegeModel.getInstance().getPrivilegeValidators(); + } else if (AuthorizationComponent.SQOOP.equals(component)) { + return SqoopPrivilegeModel.getInstance().getPrivilegeValidators(service); + } else if (AuthorizationComponent.HBASE_INDEXER.equals(component)) { + return IndexerPrivilegeModel.getInstance().getPrivilegeValidators(); + } + + throw new SentryUserException("Invalid component specified for GenericPrivilegeCoverter: " + component); + } + + protected Authorizable getAuthorizable(KeyValue keyValue) throws SentryUserException { + if (AuthorizationComponent.KAFKA.equals(component)) { + return KafkaModelAuthorizables.from(keyValue); + } else if ("SOLR".equals(component)) { + return SolrModelAuthorizables.from(keyValue); + } else if (AuthorizationComponent.SQOOP.equals(component)) { + return SqoopModelAuthorizables.from(keyValue); + } else if (AuthorizationComponent.HBASE_INDEXER.equals(component)) { + return IndexerModelAuthorizables.from(keyValue); + } + + throw new SentryUserException("Invalid component specified for GenericPrivilegeCoverter: " + component); + } + +} http://git-wip-us.apache.org/repos/asf/sentry/blob/7db84b2f/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/tools/TSentryPrivilegeConverter.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/tools/TSentryPrivilegeConverter.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/tools/TSentryPrivilegeConverter.java new file mode 100644 index 0000000..fc55575 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/generic/tools/TSentryPrivilegeConverter.java @@ -0,0 +1,35 @@ +/* + * + * 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.tools; + +import org.apache.sentry.api.generic.thrift.TSentryPrivilege; +import org.apache.sentry.core.common.exception.SentryUserException; + +public interface TSentryPrivilegeConverter { + + /** + * Convert string to privilege + */ + TSentryPrivilege fromString(String privilegeStr) throws SentryUserException; + + /** + * Convert privilege to string + */ + String toString(TSentryPrivilege tSentryPrivilege); +} http://git-wip-us.apache.org/repos/asf/sentry/blob/7db84b2f/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/7db84b2f/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/7db84b2f/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/7db84b2f/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(); + } +}
