001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.security.access;
019
020import java.util.Collection;
021import java.util.List;
022import java.util.Map;
023import org.apache.hadoop.hbase.TableName;
024import org.apache.yetus.audience.InterfaceAudience;
025
026import org.apache.hbase.thirdparty.com.google.common.collect.ArrayListMultimap;
027import org.apache.hbase.thirdparty.com.google.common.collect.ListMultimap;
028import org.apache.hbase.thirdparty.com.google.protobuf.ByteString;
029
030import org.apache.hadoop.hbase.shaded.protobuf.generated.AccessControlProtos;
031import org.apache.hadoop.hbase.shaded.protobuf.generated.AccessControlProtos.GetUserPermissionsResponse;
032import org.apache.hadoop.hbase.shaded.protobuf.generated.AccessControlProtos.GrantRequest;
033import org.apache.hadoop.hbase.shaded.protobuf.generated.AccessControlProtos.HasUserPermissionsRequest;
034import org.apache.hadoop.hbase.shaded.protobuf.generated.AccessControlProtos.RevokeRequest;
035import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos;
036
037/**
038 * Convert protobuf objects in AccessControl.proto under hbase-protocol-shaded to user-oriented
039 * objects and vice versa. <br>
040 * In HBASE-15638, we create a hbase-protocol-shaded module for upgrading protobuf version to 3.x,
041 * but there are still some coprocessor endpoints(such as AccessControl, Authentication,
042 * MulitRowMutation) which depend on hbase-protocol module for CPEP compatibility. In fact, we use
043 * PB objects in AccessControl.proto under hbase-protocol for access control logic and use shaded
044 * AccessControl.proto only for serializing/deserializing permissions of .snapshotinfo.
045 */
046@InterfaceAudience.Private
047public class ShadedAccessControlUtil {
048
049  /**
050   * Convert a client user permission to a user permission shaded proto.
051   */
052  public static AccessControlProtos.Permission.Action toPermissionAction(Permission.Action action) {
053    switch (action) {
054      case READ:
055        return AccessControlProtos.Permission.Action.READ;
056      case WRITE:
057        return AccessControlProtos.Permission.Action.WRITE;
058      case EXEC:
059        return AccessControlProtos.Permission.Action.EXEC;
060      case CREATE:
061        return AccessControlProtos.Permission.Action.CREATE;
062      case ADMIN:
063        return AccessControlProtos.Permission.Action.ADMIN;
064    }
065    throw new IllegalArgumentException("Unknown action value " + action.name());
066  }
067
068  /**
069   * Convert a Permission.Action shaded proto to a client Permission.Action object.
070   */
071  public static Permission.Action toPermissionAction(AccessControlProtos.Permission.Action action) {
072    switch (action) {
073      case READ:
074        return Permission.Action.READ;
075      case WRITE:
076        return Permission.Action.WRITE;
077      case EXEC:
078        return Permission.Action.EXEC;
079      case CREATE:
080        return Permission.Action.CREATE;
081      case ADMIN:
082        return Permission.Action.ADMIN;
083    }
084    throw new IllegalArgumentException("Unknown action value " + action.name());
085  }
086
087  /**
088   * Converts a list of Permission.Action shaded proto to an array of client Permission.Action
089   * objects.
090   * @param protoActions the list of shaded protobuf Actions
091   * @return the converted array of Actions
092   */
093  public static Permission.Action[]
094    toPermissionActions(List<AccessControlProtos.Permission.Action> protoActions) {
095    Permission.Action[] actions = new Permission.Action[protoActions.size()];
096    for (int i = 0; i < protoActions.size(); i++) {
097      actions[i] = toPermissionAction(protoActions.get(i));
098    }
099    return actions;
100  }
101
102  public static org.apache.hadoop.hbase.TableName toTableName(HBaseProtos.TableName tableNamePB) {
103    return org.apache.hadoop.hbase.TableName.valueOf(
104      tableNamePB.getNamespace().asReadOnlyByteBuffer(),
105      tableNamePB.getQualifier().asReadOnlyByteBuffer());
106  }
107
108  public static HBaseProtos.TableName toProtoTableName(TableName tableName) {
109    return HBaseProtos.TableName.newBuilder()
110      .setNamespace(ByteString.copyFrom(tableName.getNamespace()))
111      .setQualifier(ByteString.copyFrom(tableName.getQualifier())).build();
112  }
113
114  /**
115   * Converts a Permission shaded proto to a client TablePermission object.
116   * @param proto the protobuf Permission
117   * @return the converted TablePermission
118   */
119  public static Permission toPermission(AccessControlProtos.Permission proto) {
120
121    if (proto.getType() == AccessControlProtos.Permission.Type.Global) {
122      AccessControlProtos.GlobalPermission perm = proto.getGlobalPermission();
123      Permission.Action[] actions = toPermissionActions(perm.getActionList());
124      return Permission.newBuilder().withActions(actions).build();
125    }
126    if (proto.getType() == AccessControlProtos.Permission.Type.Namespace) {
127      AccessControlProtos.NamespacePermission perm = proto.getNamespacePermission();
128      Permission.Action[] actions = toPermissionActions(perm.getActionList());
129
130      if (!proto.hasNamespacePermission()) {
131        throw new IllegalStateException("Namespace must not be empty in NamespacePermission");
132      }
133      String ns = perm.getNamespaceName().toStringUtf8();
134      return Permission.newBuilder(ns).withActions(actions).build();
135    }
136    if (proto.getType() == AccessControlProtos.Permission.Type.Table) {
137      AccessControlProtos.TablePermission perm = proto.getTablePermission();
138      Permission.Action[] actions = toPermissionActions(perm.getActionList());
139
140      byte[] qualifier = null;
141      byte[] family = null;
142
143      if (!perm.hasTableName()) {
144        throw new IllegalStateException("TableName cannot be empty");
145      }
146      TableName table = toTableName(perm.getTableName());
147
148      if (perm.hasFamily()) family = perm.getFamily().toByteArray();
149      if (perm.hasQualifier()) qualifier = perm.getQualifier().toByteArray();
150      return Permission.newBuilder(table).withFamily(family).withQualifier(qualifier)
151        .withActions(actions).build();
152    }
153    throw new IllegalStateException("Unrecognize Perm Type: " + proto.getType());
154  }
155
156  /**
157   * Convert a client Permission to a Permission shaded proto
158   * @param perm the client Permission
159   * @return the protobuf Permission
160   */
161  public static AccessControlProtos.Permission toPermission(Permission perm) {
162    AccessControlProtos.Permission.Builder ret = AccessControlProtos.Permission.newBuilder();
163    if (perm instanceof NamespacePermission) {
164      NamespacePermission nsPerm = (NamespacePermission) perm;
165      ret.setType(AccessControlProtos.Permission.Type.Namespace);
166      AccessControlProtos.NamespacePermission.Builder builder =
167        AccessControlProtos.NamespacePermission.newBuilder();
168      builder.setNamespaceName(org.apache.hbase.thirdparty.com.google.protobuf.ByteString
169        .copyFromUtf8(nsPerm.getNamespace()));
170      Permission.Action[] actions = perm.getActions();
171      if (actions != null) {
172        for (Permission.Action a : actions) {
173          builder.addAction(toPermissionAction(a));
174        }
175      }
176      ret.setNamespacePermission(builder);
177    } else if (perm instanceof TablePermission) {
178      TablePermission tablePerm = (TablePermission) perm;
179      ret.setType(AccessControlProtos.Permission.Type.Table);
180      AccessControlProtos.TablePermission.Builder builder =
181        AccessControlProtos.TablePermission.newBuilder();
182      builder.setTableName(toProtoTableName(tablePerm.getTableName()));
183      if (tablePerm.hasFamily()) {
184        builder.setFamily(ByteString.copyFrom(tablePerm.getFamily()));
185      }
186      if (tablePerm.hasQualifier()) {
187        builder.setQualifier(ByteString.copyFrom(tablePerm.getQualifier()));
188      }
189      Permission.Action[] actions = perm.getActions();
190      if (actions != null) {
191        for (Permission.Action a : actions) {
192          builder.addAction(toPermissionAction(a));
193        }
194      }
195      ret.setTablePermission(builder);
196    } else {
197      // perm.getAccessScope() == Permission.Scope.GLOBAL
198      ret.setType(AccessControlProtos.Permission.Type.Global);
199      AccessControlProtos.GlobalPermission.Builder builder =
200        AccessControlProtos.GlobalPermission.newBuilder();
201      Permission.Action[] actions = perm.getActions();
202      if (actions != null) {
203        for (Permission.Action a : actions) {
204          builder.addAction(toPermissionAction(a));
205        }
206      }
207      ret.setGlobalPermission(builder);
208    }
209    return ret.build();
210  }
211
212  /**
213   * Convert a shaded protobuf UserTablePermissions to a ListMultimap&lt;String, TablePermission&gt;
214   * where key is username.
215   * @param proto the protobuf UserPermission
216   * @return the converted UserPermission
217   */
218  public static ListMultimap<String, Permission>
219    toUserTablePermissions(AccessControlProtos.UsersAndPermissions proto) {
220    ListMultimap<String, Permission> perms = ArrayListMultimap.create();
221    AccessControlProtos.UsersAndPermissions.UserPermissions userPerm;
222    for (int i = 0; i < proto.getUserPermissionsCount(); i++) {
223      userPerm = proto.getUserPermissions(i);
224      for (int j = 0; j < userPerm.getPermissionsCount(); j++) {
225        Permission perm = toPermission(userPerm.getPermissions(j));
226        perms.put(userPerm.getUser().toStringUtf8(), perm);
227      }
228    }
229    return perms;
230  }
231
232  /**
233   * Convert a ListMultimap&lt;String, TablePermission&gt; where key is username to a shaded
234   * protobuf UserPermission
235   * @param perm the list of user and table permissions
236   * @return the protobuf UserTablePermissions
237   */
238  public static AccessControlProtos.UsersAndPermissions
239    toUserTablePermissions(ListMultimap<String, UserPermission> perm) {
240    AccessControlProtos.UsersAndPermissions.Builder builder =
241      AccessControlProtos.UsersAndPermissions.newBuilder();
242    for (Map.Entry<String, Collection<UserPermission>> entry : perm.asMap().entrySet()) {
243      AccessControlProtos.UsersAndPermissions.UserPermissions.Builder userPermBuilder =
244        AccessControlProtos.UsersAndPermissions.UserPermissions.newBuilder();
245      userPermBuilder.setUser(ByteString.copyFromUtf8(entry.getKey()));
246      for (UserPermission userPerm : entry.getValue()) {
247        userPermBuilder.addPermissions(toPermission(userPerm.getPermission()));
248      }
249      builder.addUserPermissions(userPermBuilder.build());
250    }
251    return builder.build();
252  }
253
254  /**
255   * Converts a user permission proto to a client user permission object.
256   * @param proto the protobuf UserPermission
257   * @return the converted UserPermission
258   */
259  public static UserPermission toUserPermission(AccessControlProtos.UserPermission proto) {
260    return new UserPermission(proto.getUser().toStringUtf8(), toPermission(proto.getPermission()));
261  }
262
263  /**
264   * Convert a client user permission to a user permission proto
265   * @param perm the client UserPermission
266   * @return the protobuf UserPermission
267   */
268  public static AccessControlProtos.UserPermission toUserPermission(UserPermission perm) {
269    return AccessControlProtos.UserPermission.newBuilder()
270      .setUser(ByteString.copyFromUtf8(perm.getUser()))
271      .setPermission(toPermission(perm.getPermission())).build();
272  }
273
274  public static GrantRequest buildGrantRequest(UserPermission userPermission,
275    boolean mergeExistingPermissions) {
276    return GrantRequest.newBuilder().setUserPermission(toUserPermission(userPermission))
277      .setMergeExistingPermissions(mergeExistingPermissions).build();
278  }
279
280  public static RevokeRequest buildRevokeRequest(UserPermission userPermission) {
281    return RevokeRequest.newBuilder().setUserPermission(toUserPermission(userPermission)).build();
282  }
283
284  public static AccessControlProtos.GetUserPermissionsRequest
285    buildGetUserPermissionsRequest(GetUserPermissionsRequest request) {
286    AccessControlProtos.GetUserPermissionsRequest.Builder builder =
287      AccessControlProtos.GetUserPermissionsRequest.newBuilder();
288    if (request.getUserName() != null && !request.getUserName().isEmpty()) {
289      builder.setUserName(ByteString.copyFromUtf8(request.getUserName()));
290    }
291    if (request.getNamespace() != null && !request.getNamespace().isEmpty()) {
292      builder.setNamespaceName(ByteString.copyFromUtf8(request.getNamespace()));
293      builder.setType(AccessControlProtos.Permission.Type.Namespace);
294    }
295    if (request.getTableName() != null) {
296      builder.setTableName(toProtoTableName(request.getTableName()));
297      builder.setType(AccessControlProtos.Permission.Type.Table);
298    }
299    if (!builder.hasType()) {
300      builder.setType(AccessControlProtos.Permission.Type.Global);
301    }
302    if (request.getFamily() != null && request.getFamily().length > 0) {
303      builder.setColumnFamily(ByteString.copyFrom(request.getFamily()));
304    }
305    if (request.getQualifier() != null && request.getQualifier().length > 0) {
306      builder.setColumnQualifier(ByteString.copyFrom(request.getQualifier()));
307    }
308    return builder.build();
309  }
310
311  public static GetUserPermissionsResponse
312    buildGetUserPermissionsResponse(final List<UserPermission> permissions) {
313    GetUserPermissionsResponse.Builder builder = GetUserPermissionsResponse.newBuilder();
314    for (UserPermission perm : permissions) {
315      builder.addUserPermission(toUserPermission(perm));
316    }
317    return builder.build();
318  }
319
320  public static HasUserPermissionsRequest buildHasUserPermissionsRequest(String userName,
321    List<Permission> permissions) {
322    HasUserPermissionsRequest.Builder builder = HasUserPermissionsRequest.newBuilder();
323    if (userName != null && !userName.isEmpty()) {
324      builder.setUserName(ByteString.copyFromUtf8(userName));
325    }
326    for (Permission permission : permissions) {
327      builder.addPermission(toPermission(permission));
328    }
329    return builder.build();
330  }
331}