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