Impala
Impalaistheopensource,nativeanalyticdatabaseforApacheHadoop.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
SentryProxy.java
Go to the documentation of this file.
1 // Copyright 2014 Cloudera Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 package com.cloudera.impala.util;
16 
17 import java.util.Set;
18 import java.util.concurrent.Executors;
19 import java.util.concurrent.ScheduledExecutorService;
20 import java.util.concurrent.TimeUnit;
21 
22 import org.apache.log4j.Logger;
23 import org.apache.sentry.provider.db.service.thrift.TSentryGroup;
24 import org.apache.sentry.provider.db.service.thrift.TSentryPrivilege;
25 import org.apache.sentry.provider.db.service.thrift.TSentryRole;
26 
36 import com.cloudera.impala.thrift.TPrivilege;
37 import com.google.common.base.Preconditions;
38 import com.google.common.collect.Sets;
39 
52 public class SentryProxy {
53  private static final Logger LOG = Logger.getLogger(SentryProxy.class);
54 
55  // Used to periodically poll the Sentry service and updates the catalog with any
56  // changes.
57  private final ScheduledExecutorService policyReader_ =
58  Executors.newScheduledThreadPool(1);
59 
60  // The Catalog the SentryPolicyUpdater is associated with.
62 
63  // The interface to access the Sentry Policy Service to read policy metadata.
65 
66  // This is user that the Catalog Service is running as. This user should always be a
67  // Sentry Service admin => have full rights to read/update the Sentry Service.
68  private final User processUser_ = new User(System.getProperty("user.name"));
69 
70  public SentryProxy(SentryConfig sentryConfig, CatalogServiceCatalog catalog) {
71  Preconditions.checkNotNull(catalog);
72  Preconditions.checkNotNull(sentryConfig);
73  catalog_ = catalog;
74  sentryPolicyService_ = new SentryPolicyService(sentryConfig);
75  // Sentry Service is enabled.
76  // TODO: Make this configurable
77  policyReader_.scheduleAtFixedRate(new PolicyReader(), 0, 60,
78  TimeUnit.SECONDS);
79  }
80 
95  private class PolicyReader implements Runnable {
96  public void run() {
97  synchronized (SentryProxy.this) {
98  // Assume all roles should be removed. Then query the Policy Service and remove
99  // roles from this set that actually exist.
100  Set<String> rolesToRemove = catalog_.getAuthPolicy().getAllRoleNames();
101  try {
102  // Read the full policy, adding new/modified roles to "updatedRoles".
103  for (TSentryRole sentryRole:
105  // This role exists and should not be removed, delete it from the
106  // rolesToRemove set.
107  rolesToRemove.remove(sentryRole.getRoleName().toLowerCase());
108 
109  Set<String> grantGroups = Sets.newHashSet();
110  for (TSentryGroup group: sentryRole.getGroups()) {
111  grantGroups.add(group.getGroupName());
112  }
113  Role existingRole =
114  catalog_.getAuthPolicy().getRole(sentryRole.getRoleName());
115  Role role;
116  // These roles are the same, use the current role.
117  if (existingRole != null &&
118  existingRole.getGrantGroups().equals(grantGroups)) {
119  role = existingRole;
120  } else {
121  role = catalog_.addRole(sentryRole.getRoleName(), grantGroups);
122  }
123 
124  // Assume all privileges should be removed. Privileges that still exist are
125  // deleted from this set and we are left with the set of privileges that need
126  // to be removed.
127  Set<String> privilegesToRemove = role.getPrivilegeNames();
128 
129  // Check all the privileges that are part of this role.
130  for (TSentryPrivilege sentryPriv:
132  TPrivilege thriftPriv =
133  SentryPolicyService.sentryPrivilegeToTPrivilege(sentryPriv);
134  thriftPriv.setRole_id(role.getId());
135  privilegesToRemove.remove(thriftPriv.getPrivilege_name().toLowerCase());
136 
137  RolePrivilege existingPriv =
138  role.getPrivilege(thriftPriv.getPrivilege_name());
139  // We already know about this privilege (privileges cannot be modified).
140  if (existingPriv != null &&
141  existingPriv.getCreateTimeMs() == sentryPriv.getCreateTime()) {
142  continue;
143  }
144  catalog_.addRolePrivilege(role.getName(), thriftPriv);
145  }
146 
147  // Remove the privileges that no longer exist.
148  for (String privilegeName: privilegesToRemove) {
149  TPrivilege privilege = new TPrivilege();
150  privilege.setPrivilege_name(privilegeName);
151  catalog_.removeRolePrivilege(role.getName(), privilege);
152  }
153  }
154  } catch (Exception e) {
155  LOG.error("Error refreshing Sentry policy: ", e);
156  return;
157  }
158 
159  // Remove all the roles, incrementing the catalog version to indicate
160  // a change.
161  for (String roleName: rolesToRemove) {
162  catalog_.removeRole(roleName);
163  }
164  }
165  }
166  }
167 
174  public void checkUserSentryAdmin(User requestingUser)
175  throws AuthorizationException {
176  // Check if the user has access by issuing a read-only RPC.
177  // TODO: This is not an elegant way to verify whether the user has privileges to
178  // access Sentry. This should be modified in the future when Sentry has
179  // a more robust mechanism to perform these checks.
180  try {
181  sentryPolicyService_.listAllRoles(requestingUser);
182  } catch (ImpalaException e) {
183  throw new AuthorizationException(String.format("User '%s' does not have " +
184  "privileges to access the requested policy metadata or Sentry Service is " +
185  "unavailable.", requestingUser.getName()));
186  }
187  }
188 
200  public synchronized Role createRole(User user, String roleName)
201  throws ImpalaException {
202  Role role = null;
203  if (catalog_.getAuthPolicy().getRole(roleName) != null) {
204  throw new CatalogException("Role already exists: " + roleName);
205  }
206  sentryPolicyService_.createRole(user, roleName, false);
207  // Initially the role has no grant groups (empty set).
208  role = catalog_.addRole(roleName, Sets.<String>newHashSet());
209  return role;
210  }
211 
219  public synchronized Role dropRole(User user, String roleName) throws ImpalaException {
220  sentryPolicyService_.dropRole(user, roleName, false);
221  return catalog_.removeRole(roleName);
222  }
223 
231  public synchronized Role grantRoleGroup(User user, String roleName, String groupName)
232  throws ImpalaException {
233  sentryPolicyService_.grantRoleToGroup(user, roleName, groupName);
234  return catalog_.addRoleGrantGroup(roleName, groupName);
235  }
236 
244  public synchronized Role revokeRoleGroup(User user, String roleName, String groupName)
245  throws ImpalaException {
246  sentryPolicyService_.revokeRoleFromGroup(user, roleName, groupName);
247  return catalog_.removeRoleGrantGroup(roleName, groupName);
248  }
249 
257  public synchronized RolePrivilege grantRolePrivilege(User user, String roleName,
258  TPrivilege privilege) throws ImpalaException {
259  sentryPolicyService_.grantRolePrivilege(user, roleName, privilege);
260  return catalog_.addRolePrivilege(roleName, privilege);
261  }
262 
271  public synchronized RolePrivilege revokeRolePrivilege(User user, String roleName,
272  TPrivilege privilege) throws ImpalaException {
273  if (!privilege.isHas_grant_opt()) {
274  sentryPolicyService_.revokeRolePrivilege(user, roleName, privilege);
275  return catalog_.removeRolePrivilege(roleName, privilege);
276  } else {
277  // If the REVOKE GRANT OPTION has been specified the privilege should not be
278  // removed, it should just be updated to clear the GRANT OPTION flag.
279  RolePrivilege existingPriv = catalog_.getRolePrivilege(roleName, privilege);
280  if (existingPriv == null || !existingPriv.toThrift().isHas_grant_opt()) {
281  // Nothing to do. The privilege doesn't exist or the grant option flag is not set
282  return existingPriv;
283  }
284 
285  // Sentry does not yet provide an "alter privilege" API so we need to remove the
286  // privilege and re-add it.
287  sentryPolicyService_.revokeRolePrivilege(user, roleName, privilege);
288  TPrivilege updatedPriv = existingPriv.toThrift();
289  updatedPriv.setHas_grant_opt(false);
290  sentryPolicyService_.grantRolePrivilege(user, roleName, updatedPriv);
291  return catalog_.addRolePrivilege(roleName, updatedPriv);
292  }
293  }
294 
300  public void refresh() throws ImpalaRuntimeException {
301  try {
302  policyReader_.submit(new PolicyReader()).get();
303  } catch (Exception e) {
304  // We shouldn't make it here. It means an exception leaked from the
305  // AuthorizationPolicyReader.
306  throw new ImpalaRuntimeException("Error refreshing authorization policy, " +
307  "current policy state may be inconsistent. Running 'invalidate metadata' " +
308  "may resolve this problem: ", e);
309  }
310  }
311 }
synchronized Role grantRoleGroup(User user, String roleName, String groupName)
final SentryPolicyService sentryPolicyService_
void checkUserSentryAdmin(User requestingUser)
List< TSentryRole > listAllRoles(User requestingUser)
List< TSentryPrivilege > listRolePrivileges(User requestingUser, String roleName)
final ScheduledExecutorService policyReader_
final CatalogServiceCatalog catalog_
Set< String > getGrantGroups()
Definition: Role.java:125
SentryProxy(SentryConfig sentryConfig, CatalogServiceCatalog catalog)
synchronized Role createRole(User user, String roleName)
synchronized Role dropRole(User user, String roleName)
synchronized RolePrivilege revokeRolePrivilege(User user, String roleName, TPrivilege privilege)
synchronized Role revokeRoleGroup(User user, String roleName, String groupName)
synchronized RolePrivilege grantRolePrivilege(User user, String roleName, TPrivilege privilege)