All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.aliyun.odps.security.SecurityManager Maven / Gradle / Ivy

The newest version!
/*
 * 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 com.aliyun.odps.security;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import com.aliyun.odps.OdpsException;
import com.aliyun.odps.commons.transport.Headers;
import com.aliyun.odps.commons.transport.Response;
import com.aliyun.odps.rest.ResourceBuilder;
import com.aliyun.odps.rest.RestClient;
import com.aliyun.odps.rest.SimpleXmlUtils;
import com.aliyun.odps.security.CheckPermissionConstants.ActionType;
import com.aliyun.odps.security.CheckPermissionConstants.CheckPermissionResult;
import com.aliyun.odps.security.CheckPermissionConstants.ObjectType;
import com.aliyun.odps.security.Role.RoleModel;
import com.aliyun.odps.security.User.UserModel;
import com.aliyun.odps.simpleframework.xml.Element;
import com.aliyun.odps.simpleframework.xml.ElementList;
import com.aliyun.odps.simpleframework.xml.Root;
import com.aliyun.odps.simpleframework.xml.convert.Convert;
import com.aliyun.odps.utils.GsonObjectBuilder;
import com.aliyun.odps.utils.OdpsConstants;
import com.aliyun.odps.utils.StringUtils;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;

/**
 * ODPS安全管理类
 *
 * @author [email protected]
 */
public class SecurityManager {

  private String project;
  private RestClient client;
  private SecurityConfiguration securityConfigration;

  @Root(name = "Authorization", strict = false)
  static class AuthorizationQueryRequest {

    @Element(name = "Query", required = false)
    @Convert(SimpleXmlUtils.EmptyStringConverter.class)
    private String query;

    @Element(name = "ResponseInJsonFormat", required = false)
    private boolean responseInJsonFormat;

    @Element(name = "Settings", required = false)
    private String settings;

    @SuppressWarnings("unused")
    public AuthorizationQueryRequest() {
      this.query = "";
      this.responseInJsonFormat = true;
    }

    public AuthorizationQueryRequest(String query, boolean jsonFormat) {
      this.query = query;
      this.responseInJsonFormat = jsonFormat;
    }

    public AuthorizationQueryRequest(String query, boolean jsonFormat, String settings) {
      this.query = query;
      this.responseInJsonFormat = jsonFormat;
      this.settings = settings;
    }

  }

  @Root(name = "Authorization", strict = false)
  static class AuthorizationQueryResponse {

    @Element(name = "Result", required = false)
    @Convert(SimpleXmlUtils.EmptyStringConverter.class)
    private String result;

    public static AuthorizationQueryResponse from(byte[] body) {
      if (body == null) {
        return null;
      }

      return fromXml(body);
    }

    private static AuthorizationQueryResponse fromXml(byte[] body) {
      try {
        return SimpleXmlUtils.unmarshal(body, AuthorizationQueryResponse.class);
      } catch (Exception ignore) {
        return null;
      }
    }

    public String getResult() {
      return result;
    }
  }

  public enum AuthorizationQueryStatus {
    TERMINATED,
    RUNNING,
    FAILED;
  }

  @Root(name = "AuthorizationQuery", strict = false)
  private static class AuthorizationQueryStatusModel {

    @Element(name = "Result", required = false)
    @Convert(SimpleXmlUtils.EmptyStringConverter.class)
    private String result;

    @Element(name = "Status", required = false)
    @Convert(SimpleXmlUtils.EmptyStringConverter.class)
    private String status;

    public String getResult() {
      return result;
    }

    public AuthorizationQueryStatus getStatus() throws OdpsException {
      if (StringUtils.isNullOrEmpty(status)) {
        throw new IllegalArgumentException("Cannot get authorization query status.");
      }

      try {
        return AuthorizationQueryStatus.valueOf(status.toUpperCase());
      } catch (IllegalArgumentException e) {
        throw new OdpsException("Unknown authorization query status: " + status);
      }
    }
  }

  public static class AuthorizationQueryInstance {

    private String queryResult;
    private String instanceId;
    private String projectName;
    private RestClient client;
    private boolean isSync;


    public AuthorizationQueryInstance(String result) {
      this.queryResult = result;
      this.isSync = true;
    }

    public AuthorizationQueryInstance(String projectName, String instanceId, SecurityManager sm) {
      if (StringUtils.isNullOrEmpty(projectName) || StringUtils.isNullOrEmpty(instanceId)) {
        throw new IllegalArgumentException(
            "Arguments: project and instance id cannot be null or empty.");
      }
      this.projectName = projectName;
      this.instanceId = instanceId;
      this.client = sm.client;
      this.isSync = false;
    }

    public String getId() {
      if (isSync) {
        return null;
      }

      return instanceId;
    }

    public boolean isSync() {
      return isSync;
    }

    public String waitForSuccess() throws OdpsException {
      return waitForSuccess(1000);
    }

    public String waitForSuccess(long interval) throws OdpsException {
      if (isSync) {
        return queryResult;
      }

      while (!isTerminated()) {
        try {
          Thread.sleep(interval);
        } catch (InterruptedException e) {
          throw new OdpsException(e);
        }
      }

      AuthorizationQueryStatusModel model = getModel();
      if (model.getStatus() == AuthorizationQueryStatus.TERMINATED) {
        return model.getResult();
      } else {
        throw new OdpsException("Authorization query failed: " + model.getResult());
      }
    }

    private AuthorizationQueryStatusModel getModel() throws OdpsException {
      String resource =
          ResourceBuilder.buildProjectAuthorizationInstanceResource(projectName, instanceId);
      return client.request(AuthorizationQueryStatusModel.class, resource, "GET", null, null, null);
    }

    public boolean isTerminated() throws OdpsException {
      return (getStatus() != AuthorizationQueryStatus.RUNNING);
    }

    public AuthorizationQueryStatus getStatus() throws OdpsException {
      if (!isSync) {
        return getModel().getStatus();
      }

      return AuthorizationQueryStatus.TERMINATED;
    }

    public String getResult() throws OdpsException {
      if (!isSync) {
        queryResult = getModel().getResult();
      }
      return queryResult;
    }
  }

  @Root(name = "Users", strict = false)
  private static class ListUsersResponse {

    @ElementList(entry = "User", inline = true, required = false)
    private List users = new ArrayList();
  }

  @Root(name = "Roles", strict = false)
  private static class ListRolesResponse {

    @ElementList(entry = "Role", inline = true, required = false)
    private List roles = new ArrayList();
  }

  @Root(name = "Auth", strict = false)
  private static class CheckPermissionResponse {

    @Element(name = "Result", required = false)
    @Convert(SimpleXmlUtils.EmptyStringConverter.class)
    private String result;

    @Element(name = "Message", required = false)
    @Convert(SimpleXmlUtils.EmptyStringConverter.class)
    private String message;

    public String getResult() {
      return result;
    }

    public String getMessage() {
      return message;
    }
  }

  public SecurityManager(String project, RestClient client) {
    this.project = project;
    this.client = client;
    this.securityConfigration = null;
  }

  public SecurityConfiguration getSecurityConfiguration() {
    return getSecurityConfiguration(false);
  }

  public SecurityConfiguration getSecurityConfiguration(boolean strip) {
    SecurityConfiguration securityConfiguration;
    if (strip) {
      if (this.securityConfigration == null) {
        securityConfiguration = new SecurityConfiguration(project, client, true);
      } else {
        // If security configuration is cached, return it
        securityConfiguration = this.securityConfigration;
      }
    } else {
      if (this.securityConfigration == null) {
        // If strip is false and security configuration is not cached, cache it
        this.securityConfigration = new SecurityConfiguration(project, client, false);
      }
      securityConfiguration = this.securityConfigration;
    }

    return securityConfiguration;
  }

  public void setSecurityConfiguration(
      SecurityConfiguration securityConfigration
  ) throws OdpsException {
    securityConfigration.update(null);
  }

  public void setSecurityConfiguration(
      SecurityConfiguration securityConfigration, String supervisionToken
  )
      throws OdpsException {
    securityConfigration.update(supervisionToken);
  }

  public String getProjectPolicy() throws OdpsException {
    String resource = ResourceBuilder.buildProjectResource(project);
    Map params = new HashMap();
    params.put("policy", null);
    Response response = client.request(resource, "GET", params, null,
                                       null
    );
    try {
      return new String(response.getBody(), "UTF-8");
    } catch (UnsupportedEncodingException e) {
      throw new OdpsException("Can't parse response!", e);
    }
  }

  public void putProjectPolicy(String policy) throws OdpsException {
    String resource = ResourceBuilder.buildProjectResource(project);
    Map params = new HashMap();
    params.put("policy", null);
    client.stringRequest(resource, "PUT", params, null, policy);
  }

  public String getSecurityPolicy() throws OdpsException {
    String resource = ResourceBuilder.buildProjectResource(project);
    Map params = new HashMap();
    params.put("security_policy", null);
    Response response = client.request(resource, "GET", params, null, null);
    try {
      return new String(response.getBody(), "UTF-8");
    } catch (UnsupportedEncodingException e) {
      throw new OdpsException("Can't parse response!", e);
    }
  }

  public void putSecurityPolicy(String securityPolicy) throws OdpsException {
    String resource = ResourceBuilder.buildProjectResource(project);
    Map params = new HashMap();
    params.put("security_policy", null);
    Map headers = new HashMap();
    headers.put(Headers.CONTENT_TYPE, "application/json");
    client.stringRequest(resource, "PUT", params, headers, securityPolicy);
  }

  public String getRolePolicy(String roleName) throws OdpsException {
    String resource = ResourceBuilder.buildRoleResource(project, roleName);
    Map params = new HashMap();
    params.put("policy", null);
    Response response = client.request(resource, "GET", params, null,
                                       null
    );
    try {
      return new String(response.getBody(), "UTF-8");
    } catch (UnsupportedEncodingException e) {
      throw new OdpsException("Can't parse response!", e);
    }
  }

  public void putRolePolicy(String roleName, String policy)
      throws OdpsException {
    String resource = ResourceBuilder.buildRoleResource(project, roleName);
    Map params = new HashMap();
    params.put("policy", null);
    client.stringRequest(resource, "PUT", params, null, policy);
  }

  public User getUserById(String id) throws OdpsException {
    String resource = ResourceBuilder.buildUserResource(project, id);
    UserModel resp = client.request(UserModel.class, resource, "GET");
    return new User(resp, project, client);
  }

  public User getUserByName(String name) throws OdpsException {
    String resource = ResourceBuilder.buildUserResource(project, name);
    Map params = new HashMap();
    params.put("type", "displayname");
    UserModel resp = client.request(UserModel.class, resource, "GET", params);
    return new User(resp, project, client);
  }

  public List listUsers() throws OdpsException {
    String resource = ResourceBuilder.buildUsersResource(project);
    ListUsersResponse resp = client.request(ListUsersResponse.class,
                                            resource, "GET"
    );
    List users = new ArrayList();
    for (UserModel model : resp.users) {
      User t = new User(model, project, client);
      users.add(t);
    }
    return users;
  }

  public List listRoles() throws OdpsException {
    String resource = ResourceBuilder.buildRolesResource(project);
    ListRolesResponse resp = client.request(ListRolesResponse.class,
                                            resource, "GET"
    );
    List roles = new ArrayList();
    for (RoleModel model : resp.roles) {
      Role t = new Role(model, project, client);
      roles.add(t);
    }
    return roles;
  }

  @Deprecated
  public List listRolesForUser(String uid) throws OdpsException {
    return listRolesForUserID(uid);
  }

  /**
   * 获取指定用户 id 的角色列表
   *
   * @param uid
   * @return
   * @throws OdpsException
   */
  public List listRolesForUserID(String uid) throws OdpsException {
    return listRolesForUserInternal(uid, null);
  }

  /**
   * 获取制定用户名的角色列表
   *
   * @param userName
   * @return
   * @throws OdpsException
   */
  public List listRolesForUserName(String userName) throws OdpsException {
    return listRolesForUserInternal(userName, "displayname");
  }

  private List listRolesForUserInternal(String user, String type) throws OdpsException {
    String resource = ResourceBuilder.buildUserResource(project, user);
    Map params = new HashMap();
    params.put("roles", null);
    if (!StringUtils.isNullOrEmpty(type)) {
      params.put("type", type);
    }
    ListRolesResponse resp = client.request(ListRolesResponse.class,
                                            resource, "GET", params, null, null
    );
    List roles = new ArrayList();
    for (RoleModel model : resp.roles) {
      Role t = new Role(model, project, client);
      roles.add(t);
    }
    return roles;
  }

  public List listUsersForRole(String roleName) throws OdpsException {
    String resource = ResourceBuilder.buildRoleResource(project, roleName);
    Map params = new HashMap();
    params.put("users", null);
    ListUsersResponse resp = client.request(ListUsersResponse.class,
                                            resource, "GET", params, null, null
    );
    List users = new ArrayList();
    for (UserModel model : resp.users) {
      User t = new User(model, project, client);
      users.add(t);
    }
    return users;
  }

  public static class CheckPermissionResultInfo {
    public CheckPermissionResult result;
    public String message;

    public CheckPermissionResultInfo(CheckPermissionResult result, String message) {
      this.result = Objects.requireNonNull(result);
      this.message = message;
    }

    public CheckPermissionResult getResult() {
      return result;
    }

    public String getMessage() {
      return message;
    }
  }

  public static class PermissionDesc {
    private String projectName;
    private String schemaName;
    private ObjectType objectType;
    private String objectName;
    private ActionType actionType;
    private Map params;

    public PermissionDesc(String projectName,
                          ObjectType objectType,
                          String objectName,
                          ActionType actionType) {
      this(projectName, null, objectType, objectName, actionType);
    }

    public PermissionDesc(String projectName,
                          String schemaName,
                          ObjectType objectType,
                          String objectName,
                          ActionType actionType) {
      this.projectName = Objects.requireNonNull(projectName);
      this.schemaName = schemaName;
      this.objectType = Objects.requireNonNull(objectType);
      this.objectName = Objects.requireNonNull(objectName);
      this.actionType = Objects.requireNonNull(actionType);
      params = new HashMap<>();
    }

    public String getProjectName() {
      return projectName;
    }

    public String getSchemaName() {
      return schemaName;
    }

    public ObjectType getObjectType() {
      return objectType;
    }

    public String getObjectName() {
      return objectName;
    }

    public ActionType getActionType() {
      return actionType;
    }

    public Map getParams() {
      return new HashMap<>(params);
    }

    public void setPrincipal(String principal) {
      params.put("Principal", principal);
    }

    public void setColumns(List columns) {
      params.put("odps:SelectColumns", GsonObjectBuilder.get().toJson(columns));
    }

    public void addParam(String key, String value) {
      params.put(key, value);
    }

    private String buildResourceString(ObjectType objectType, String objectName) {
      switch (objectType) {
        case Project:
          return ResourceBuilder.buildProjectResource(projectName);
        case Table:
          return ResourceBuilder.buildTableResource(projectName, objectName);
        case Function:
          return ResourceBuilder.buildFunctionResource(projectName, objectName);
        case Instance:
          return ResourceBuilder.buildInstanceResource(projectName, objectName);
        case Resource:
          return ResourceBuilder.buildResourceResource(projectName, objectName);
        default:
          throw new IllegalArgumentException("Unsupported object type");
      }
    }

    public String toJson() {
      JsonArray jsonArray = new JsonArray();
      JsonObject jsonObject = new JsonObject();
      jsonObject.addProperty("Action", actionType.name());
      String resource = buildResourceString(objectType, objectName);
      if (getSchemaName() != null) {
        resource = resource.substring(1);
      }
      jsonObject.addProperty("Resource", resource);

      for (Map.Entry param : params.entrySet()) {
        jsonObject.addProperty(param.getKey(), param.getValue());
      }

      jsonArray.add(jsonObject);
      return GsonObjectBuilder.get().toJson(jsonArray);
    }
  }

  /**
   * Check permission
   *
   * @param desc Permission description. See {@link PermissionDesc}
   * @return {@link CheckPermissionResultInfo}
   * @throws OdpsException
   */
  public CheckPermissionResultInfo checkPermission(PermissionDesc desc)
      throws OdpsException {
    String resource = "/projects/" + ResourceBuilder.encodeObjectName(desc.projectName)
                      + "/auth/";
    Map headers = new HashMap<>();
    Map params = new HashMap<>();
    if (desc.getSchemaName() != null) {
      params.put("curr_schema", desc.getSchemaName());
    }
    headers.put(Headers.CONTENT_TYPE, "application/json");
    CheckPermissionResponse response = client.stringRequest(CheckPermissionResponse.class,
                                                            resource,
                                                            "POST",
                                                            params,
                                                            headers,
                                                            desc.toJson());
    CheckPermissionResult result = CheckPermissionResult.valueOf(response.result);
    return new CheckPermissionResultInfo(result, response.message);
  }

  /**
   * 查看是否有操作权限
   *
   * @param type
   *     查看权限的对象类型
   * @param objectName
   *     查看权限的对象名称
   * @param action
   *     查看的权限类型
   * @param projectName
   *     查看对象所在项目名称
   * @param columns
   *     查看权限的列名,若查看列操作权限,type 值为 Table {@link ObjectType}
   * @return CheckPermissionResult
   * @throws OdpsException
   */
  @Deprecated
  public CheckPermissionResult checkPermission(
      ObjectType type, String objectName,
      ActionType action, String projectName,
      List columns
  ) throws OdpsException {

    Map params = new HashMap();
    params.put("type", type.toString());
    params.put("name", objectName);
    params.put("grantee", action.toString());
    if ((columns != null) && !columns.isEmpty()) {
      params.put("columns", StringUtils.join(columns.toArray(), ","));
    }

    String resource = "/projects/" + ResourceBuilder.encodeObjectName(projectName)
                      + "/auth/";
    CheckPermissionResponse response = client.request(CheckPermissionResponse.class,
                                                      resource,
                                                      "GET",
                                                      params,
                                                      null,
                                                      null);
    return response.getResult().toUpperCase().equals("ALLOW") ?
           CheckPermissionResult.Allow : CheckPermissionResult.Deny;
  }

  /**
   * 查看是否有操作权限
   *
   * @param type
   *     查看权限的对象类型
   * @param objectName
   *     查看权限的对象名称
   * @param action
   *     查看的权限类型
   * @param columns
   *     查看权限的列名,若查看列操作权限,type 值为 Table {@link ObjectType}
   * @return CheckPermissionResult
   * @throws OdpsException
   */
  @Deprecated
  public CheckPermissionResult checkPermission(
      ObjectType type, String objectName,
      ActionType action, List columns
  )
      throws OdpsException {
    return checkPermission(type, objectName, action, project, columns);
  }

  /**
   * 查看是否有操作权限
   *
   * @param type
   *     查看权限的对象类型
   * @param objectName
   *     查看权限的对象名称
   * @param action
   *     查看的权限类型
   * @return CheckPermissionResult
   * @throws OdpsException
   */
  @Deprecated
  public CheckPermissionResult checkPermission(
      ObjectType type, String objectName,
      ActionType action
  ) throws OdpsException {
    return checkPermission(type, objectName, action, project);
  }

  /**
   * 查看是否有操作权限
   *
   * @param type
   *     查看权限的对象类型
   * @param objectName
   *     查看权限的对象名称
   * @param action
   *     查看的权限类型
   * @param projectName
   *     查看对象所在项目名称
   * @return CheckPermissionResult
   * @throws OdpsException
   */
  @Deprecated
  public CheckPermissionResult checkPermission(
      ObjectType type, String objectName,
      ActionType action, String projectName
  ) throws OdpsException {
    return checkPermission(type, objectName, action, projectName, null);
  }

  /**
   * 执行安全命令, 返回 query 实例
   *
   * @param query
   * @param jsonOutput
   * @return query 实例
   * @throws OdpsException
   */
  public AuthorizationQueryInstance run(String query, Boolean jsonOutput) throws OdpsException {
    return run(query, jsonOutput, null);
  }

  /**
   * 执行安全命令, 返回 query 实例
   *
   * @param query
   * @param jsonOutput
   * @return query 实例
   * @throws OdpsException
   */
  public AuthorizationQueryInstance run(String query, Boolean jsonOutput, String supervisionToken) throws OdpsException {
    return run(query, jsonOutput, supervisionToken, null);
  }

  /**
   * 执行安全命令, 返回 query 实例
   *
   * @param query
   * @param jsonOutput
   * @param supervisionToken
   * @return query 实例
   * @throws OdpsException
   */
  public AuthorizationQueryInstance run(String query, Boolean jsonOutput, String supervisionToken, Map settings)
      throws OdpsException {
    String resource = ResourceBuilder.buildProjectSecurityManagerResource(project);

    AuthorizationQueryRequest request;
    if (settings == null) {
      request = new AuthorizationQueryRequest(query, jsonOutput);
    } else {
      // default schema priority: hints.get(ODPS_DEFAULT_SCHEMA) > odps.getCurrentSchema()
      if (!settings.containsKey(OdpsConstants.ODPS_DEFAULT_SCHEMA)) {
        settings.put(OdpsConstants.ODPS_DEFAULT_SCHEMA, client.getCurrentSchema());
      }

      String settingsString = new GsonBuilder().create().toJson(settings);
      request = new AuthorizationQueryRequest(query, jsonOutput, settingsString);
    }

    String xmlRequest;
    try {
      xmlRequest = SimpleXmlUtils.marshal(request);
    } catch (Exception e) {
      throw new OdpsException(e.getMessage(), e);
    }
    HashMap headers = new HashMap();
    if (supervisionToken != null) {
      headers.put(Headers.ODPS_SUPERVISION_TOKEN, supervisionToken);
    }
    headers.put(Headers.CONTENT_TYPE, "application/xml");

    Response response =
        client.stringRequest(resource, "POST", null, headers, xmlRequest);
    try {
      AuthorizationQueryResponse queryResponse =
          AuthorizationQueryResponse.from(response.getBody());

      if (response.getStatus() == 200) {
        return new AuthorizationQueryInstance(queryResponse.getResult());
      }

      return new AuthorizationQueryInstance(project, queryResponse.getResult(), this);
    } catch (Exception e) {
      throw new OdpsException(e.getMessage(), e);
    }
  }

  /**
   * 执行安全命令, 返回 query 结果
   *
   * @param query
   * @param jsonOutput
   * @return query 结果
   * @throws OdpsException
   */
  public String runQuery(String query, Boolean jsonOutput) throws OdpsException {
    return runQuery(query, jsonOutput, null);
  }

  /**
   * 执行安全命令, 返回 query 结果
   *
   * @param query
   * @param jsonOutput
   * @param supervisionToken
   * @return query 结果
   * @throws OdpsException
   */
  public String runQuery(String query, Boolean jsonOutput, String supervisionToken)
      throws OdpsException {
    return run(query, jsonOutput, supervisionToken).waitForSuccess();
  }

  public String runQuery(String query, Boolean jsonOutput, String supervisionToken, Map settings)
      throws OdpsException {
    return run(query, jsonOutput, supervisionToken, settings).waitForSuccess();
  }

  public String generateAuthorizationToken(String policy, String type)
      throws OdpsException {
    if ("Bearer".equalsIgnoreCase(type)) {
      String resource = ResourceBuilder.buildProjectSecurityManagerResource(project);
      HashMap headers = new HashMap();
      headers.put(Headers.CONTENT_TYPE, "application/json");
      Map params = new HashMap();
      params.put("sign_bearer_token", null);
      AuthorizationQueryResponse response = client.stringRequest(
          AuthorizationQueryResponse.class, resource, "POST", params,
          headers, policy
      );
      return response.getResult();
    } else {
      // 目前只支持bearer类型的token
      throw new OdpsException("Unsupport token type" + type);
    }

  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy