blob: 8a33b87e1688399c581930406936c47ff2d56c4e [file] [log] [blame]
/*
* 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.hadoop.ozone.om.multitenant;
import static java.net.HttpURLConnection.HTTP_OK;
import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ADMIN_CREATE_POLICY_HTTP_ENDPOINT;
import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ADMIN_CREATE_ROLE_HTTP_ENDPOINT;
import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ADMIN_CREATE_USER_HTTP_ENDPOINT;
import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ADMIN_DELETE_POLICY_HTTP_ENDPOINT;
import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ADMIN_DELETE_ROLE_HTTP_ENDPOINT;
import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ADMIN_DELETE_USER_HTTP_ENDPOINT;
import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ADMIN_GET_POLICY_HTTP_ENDPOINT;
import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ADMIN_GET_POLICY_ID_HTTP_ENDPOINT;
import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ADMIN_GET_ROLE_HTTP_ENDPOINT;
import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ADMIN_GET_USER_HTTP_ENDPOINT;
import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ADMIN_ROLE_ADD_USER_HTTP_ENDPOINT;
import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ALL_POLICIES_ENDPOINT;
import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_DOWNLOAD_ENDPOINT;
import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_OZONE_SERVICE_ENDPOINT;
import static org.apache.hadoop.ozone.OzoneConsts.OZONE_TENANT_RANGER_POLICY_LABEL;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_RANGER_HTTPS_ADMIN_API_PASSWD;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_RANGER_HTTPS_ADMIN_API_USER;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_HTTPS_ADDRESS_KEY;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_OM_CONNECTION_REQUEST_TIMEOUT;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_OM_CONNECTION_REQUEST_TIMEOUT_DEFAULT;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_OM_CONNECTION_TIMEOUT;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_OM_CONNECTION_TIMEOUT_DEFAULT;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_OM_IGNORE_SERVER_CERT;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_OM_IGNORE_SERVER_CERT_DEFAULT;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.hadoop.conf.Configuration;
import org.apache.kerby.util.Base64;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.security.acl.IOzoneObj;
import org.apache.hadoop.ozone.security.acl.RequestContext;
import org.apache.http.auth.BasicUserPrincipal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Implements MultiTenantAccessAuthorizer for Apache Ranger.
*/
public class MultiTenantAccessAuthorizerRangerPlugin implements
MultiTenantAccessAuthorizer {
public static final Logger LOG = LoggerFactory
.getLogger(MultiTenantAccessAuthorizerRangerPlugin.class);
private MultiTenantAccessController accessController;
private OzoneConfiguration conf;
private boolean ignoreServerCert = true;
private int connectionTimeout;
private int connectionRequestTimeout;
private String authHeaderValue;
private String rangerHttpsAddress;
// Stores Ranger cm_ozone service ID. This value should not change (unless
// somehow Ranger cm_ozone service is deleted and re-created while OM is
// still running and not reloaded / restarted).
private int rangerOzoneServiceId = -1;
@Override
public void init(Configuration configuration) throws IOException {
conf = new OzoneConfiguration(configuration);
accessController = new RangerRestMultiTenantAccessController(conf);
rangerHttpsAddress = conf.get(OZONE_RANGER_HTTPS_ADDRESS_KEY);
if (rangerHttpsAddress == null) {
throw new OMException("Config ozone.om.ranger.https-address is not set! "
+ "Multi-Tenancy feature requires Apache Ranger to function properly",
OMException.ResultCodes.INTERNAL_ERROR);
}
initializeRangerConnection();
// Get Ranger Ozone service ID
try {
rangerOzoneServiceId = retrieveRangerOzoneServiceId();
} catch (SocketTimeoutException | ConnectException e) {
// Exceptions (e.g. ConnectException: Connection refused)
// thrown here can crash OM during startup.
// Tolerate potential connection failure to Ranger during initialization
// due to cluster services starting up at the same time or not ready yet.
// Later when the Ranger Ozone service ID would be used it should try
// and retrieve the ID again if it failed earlier.
LOG.error("Failed to get Ozone service ID to Ranger. "
+ "Will retry later", e);
rangerOzoneServiceId = -1;
}
}
int getRangerOzoneServiceId() {
return rangerOzoneServiceId;
}
/**
* Helper method that checks if the RangerOzoneServiceId is properly retrieved
* during init. If not, try to get it from Ranger.
*/
private void checkRangerOzoneServiceId() throws IOException {
if (rangerOzoneServiceId < 0) {
rangerOzoneServiceId = retrieveRangerOzoneServiceId();
}
}
private void initializeRangerConnection() {
setupRangerConnectionConfig();
if (ignoreServerCert) {
setupRangerIgnoreServerCertificate();
}
setupRangerConnectionAuthHeader();
}
private void setupRangerConnectionConfig() {
connectionTimeout = (int) conf.getTimeDuration(
OZONE_RANGER_OM_CONNECTION_TIMEOUT,
conf.get(
OZONE_RANGER_OM_CONNECTION_TIMEOUT,
OZONE_RANGER_OM_CONNECTION_TIMEOUT_DEFAULT),
TimeUnit.MILLISECONDS);
connectionRequestTimeout = (int)conf.getTimeDuration(
OZONE_RANGER_OM_CONNECTION_REQUEST_TIMEOUT,
conf.get(
OZONE_RANGER_OM_CONNECTION_REQUEST_TIMEOUT,
OZONE_RANGER_OM_CONNECTION_REQUEST_TIMEOUT_DEFAULT),
TimeUnit.MILLISECONDS
);
ignoreServerCert = (boolean) conf.getBoolean(
OZONE_RANGER_OM_IGNORE_SERVER_CERT,
OZONE_RANGER_OM_IGNORE_SERVER_CERT_DEFAULT);
}
private void setupRangerIgnoreServerCertificate() {
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
}
};
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
LOG.info("Setting DefaultSSLSocketFactory failed.");
}
}
private void setupRangerConnectionAuthHeader() {
String userName = conf.get(OZONE_OM_RANGER_HTTPS_ADMIN_API_USER);
String passwd = conf.get(OZONE_OM_RANGER_HTTPS_ADMIN_API_PASSWD);
String auth = userName + ":" + passwd;
byte[] encodedAuth =
Base64.encodeBase64(auth.getBytes(StandardCharsets.UTF_8));
authHeaderValue = "Basic " +
new String(encodedAuth, StandardCharsets.UTF_8);
}
@Override
public void shutdown() throws IOException {
// TBD
}
@Override
public void grantAccess(BucketNameSpace bucketNameSpace,
BasicUserPrincipal user, ACLType aclType) {
// TBD
}
@Override
public void revokeAccess(BucketNameSpace bucketNameSpace,
BasicUserPrincipal user, ACLType aclType) {
// TBD
}
@Override
public void grantAccess(AccountNameSpace accountNameSpace,
BasicUserPrincipal user, ACLType aclType) {
// TBD
}
@Override
public void revokeAccess(AccountNameSpace accountNameSpace,
BasicUserPrincipal user, ACLType aclType) {
// TBD
}
public List<Pair<BucketNameSpace, ACLType>>
getAllBucketNameSpaceAccesses(BasicUserPrincipal user) {
// TBD
return null;
}
@Override
public boolean checkAccess(BucketNameSpace bucketNameSpace,
BasicUserPrincipal user) {
// TBD
return true;
}
@Override
public boolean checkAccess(AccountNameSpace accountNameSpace,
BasicUserPrincipal user) {
// TBD
return true;
}
@Override
public boolean checkAccess(IOzoneObj ozoneObject, RequestContext context)
throws OMException {
// TBD
return true;
}
@Override
public String getRole(OzoneTenantRolePrincipal principal) throws IOException {
String endpointUrl =
rangerHttpsAddress + OZONE_OM_RANGER_ADMIN_GET_ROLE_HTTP_ENDPOINT +
principal.getName();
HttpURLConnection conn = makeHttpGetCall(endpointUrl, "GET", false);
return getResponseData(conn);
}
@Override
public String getRole(String roleName) throws IOException {
String endpointUrl =
rangerHttpsAddress + OZONE_OM_RANGER_ADMIN_GET_ROLE_HTTP_ENDPOINT +
roleName;
HttpURLConnection conn = makeHttpGetCall(endpointUrl, "GET", false);
return getResponseData(conn);
}
@Override
public String getUserId(String userPrincipal) throws IOException {
String rangerAdminUrl =
rangerHttpsAddress + OZONE_OM_RANGER_ADMIN_GET_USER_HTTP_ENDPOINT +
userPrincipal;
HttpURLConnection conn = makeHttpGetCall(rangerAdminUrl,
"GET", false);
String response = getResponseData(conn);
String userIDCreated = null;
try {
JsonObject jResonse = new JsonParser().parse(response).getAsJsonObject();
JsonArray userinfo = jResonse.get("vXUsers").getAsJsonArray();
int numIndex = userinfo.size();
for (int i = 0; i < numIndex; ++i) {
if (userinfo.get(i).getAsJsonObject().get("name").getAsString()
.equals(userPrincipal)) {
userIDCreated =
userinfo.get(i).getAsJsonObject().get("id").getAsString();
break;
}
}
LOG.debug("User ID is: {}", userIDCreated);
} catch (JsonParseException e) {
e.printStackTrace();
throw e;
}
return userIDCreated;
}
/**
* Update the exising role details and push the changes to Ranger.
*
* @param userPrincipal user name that exists in Ranger.
* @param existingRole An existing role's JSON response String from Ranger.
* @return roleId (not useful for now)
* @throws IOException
*/
@Override
public String revokeUserFromRole(String userPrincipal,
String existingRole) throws IOException {
JsonObject roleObj = new JsonParser().parse(existingRole).getAsJsonObject();
// Parse Json
final String roleId = roleObj.get("id").getAsString();
LOG.debug("Got roleId: {}", roleId);
JsonArray oldUsersArray = roleObj.getAsJsonArray("users");
JsonArray newUsersArray = new JsonArray();
for (int i = 0; i < oldUsersArray.size(); ++i) {
JsonObject newUserEntry = oldUsersArray.get(i).getAsJsonObject();
if (!newUserEntry.get("name").getAsString().equals(userPrincipal)) {
newUsersArray.add(newUserEntry);
}
// Update Json array
}
roleObj.add("users", newUsersArray);
LOG.debug("Updated: {}", roleObj);
final String endpointUrl = rangerHttpsAddress +
OZONE_OM_RANGER_ADMIN_ROLE_ADD_USER_HTTP_ENDPOINT + roleId;
final String jsonData = roleObj.toString();
HttpURLConnection conn =
makeHttpCall(endpointUrl, jsonData, "PUT", false);
if (conn.getResponseCode() != HTTP_OK) {
throw new IOException("Ranger REST API failure: " + conn.getResponseCode()
+ " " + conn.getResponseMessage()
+ ". Error updating Ranger role.");
}
String resp = getResponseData(conn);
String returnedRoleId;
try {
JsonObject jObject = new JsonParser().parse(resp).getAsJsonObject();
returnedRoleId = jObject.get("id").getAsString();
LOG.debug("Ranger returns roleId: {}", roleId);
} catch (JsonParseException e) {
e.printStackTrace();
throw e;
}
return returnedRoleId;
}
/**
* Update the exising role details and push the changes to Ranger.
*
* @param userPrincipal user name that exists in Ranger.
* @param existingRole An existing role's JSON response String from Ranger.
* @param isAdmin Make it delegated admin of the role.
* @return roleId (not useful for now)
* @throws IOException
*/
public String assignUserToRole(String userPrincipal,
String existingRole, boolean isAdmin) throws IOException {
JsonObject roleObj = new JsonParser().parse(existingRole).getAsJsonObject();
// Parse Json
final String roleId = roleObj.get("id").getAsString();
LOG.debug("Got roleId: {}", roleId);
JsonArray usersArray = roleObj.getAsJsonArray("users");
JsonObject newUserEntry = new JsonObject();
newUserEntry.addProperty("name", userPrincipal);
newUserEntry.addProperty("isAdmin", isAdmin);
usersArray.add(newUserEntry);
// Update Json array
roleObj.add("users", usersArray);
LOG.debug("Updated: {}", roleObj);
final String endpointUrl = rangerHttpsAddress +
OZONE_OM_RANGER_ADMIN_ROLE_ADD_USER_HTTP_ENDPOINT + roleId;
final String jsonData = roleObj.toString();
HttpURLConnection conn =
makeHttpCall(endpointUrl, jsonData, "PUT", false);
if (conn.getResponseCode() != HTTP_OK) {
throw new IOException("Ranger REST API failure: " + conn.getResponseCode()
+ " " + conn.getResponseMessage()
+ ". Error updating Ranger role.");
}
String resp = getResponseData(conn);
String returnedRoleId;
try {
JsonObject jObject = new JsonParser().parse(resp).getAsJsonObject();
returnedRoleId = jObject.get("id").getAsString();
LOG.debug("Ranger returns roleId: {}", roleId);
} catch (JsonParseException e) {
e.printStackTrace();
throw e;
}
return returnedRoleId;
}
/**
* Update the exising role details and push the changes to Ranger.
*
* @param users must be existing users in Ranger.
* @param existingRole An existing role's JSON response String from Ranger.
* @return roleId (not useful for now)
* @throws IOException
*/
@Override
public String assignAllUsers(HashSet<String> users,
String existingRole) throws IOException {
JsonObject roleObj = new JsonParser().parse(existingRole).getAsJsonObject();
// Parse Json
final String roleId = roleObj.get("id").getAsString();
LOG.debug("Got roleId: {}", roleId);
JsonArray usersArray = new JsonArray();
for (String user: users) {
JsonObject newUserEntry = new JsonObject();
newUserEntry.addProperty("name", user);
newUserEntry.addProperty("isAdmin", false);
usersArray.add(newUserEntry);
}
// Update Json array
roleObj.remove("users"); // remove the old users
roleObj.add("users", usersArray);
LOG.debug("Updated: {}", roleObj);
final String endpointUrl = rangerHttpsAddress +
OZONE_OM_RANGER_ADMIN_ROLE_ADD_USER_HTTP_ENDPOINT + roleId;
final String jsonData = roleObj.toString();
HttpURLConnection conn =
makeHttpCall(endpointUrl, jsonData, "PUT", false);
if (conn.getResponseCode() != HTTP_OK) {
throw new IOException("Ranger REST API failure: " + conn.getResponseCode()
+ " " + conn.getResponseMessage()
+ ". Error updating Ranger role.");
}
String resp = getResponseData(conn);
String returnedRoleId;
try {
JsonObject jObject = new JsonParser().parse(resp).getAsJsonObject();
returnedRoleId = jObject.get("id").getAsString();
LOG.debug("Ranger returns roleId: {}", roleId);
} catch (JsonParseException e) {
e.printStackTrace();
throw e;
}
return returnedRoleId;
}
private String getCreateRoleJsonStr(String roleName, String adminRoleName) {
return "{"
+ " \"name\":\"" + roleName + "\","
+ " \"description\":\"Role created by Ozone for Multi-Tenancy\""
+ (adminRoleName == null ? "" : ", \"roles\":"
+ "[{\"name\":\"" + adminRoleName + "\",\"isAdmin\": true}]")
+ "}";
}
public String createRole(String role, String adminRoleName)
throws IOException {
String endpointUrl =
rangerHttpsAddress + OZONE_OM_RANGER_ADMIN_CREATE_ROLE_HTTP_ENDPOINT;
String jsonData = getCreateRoleJsonStr(role, adminRoleName);
final HttpURLConnection conn = makeHttpCall(endpointUrl,
jsonData, "POST", false);
if (conn.getResponseCode() != HTTP_OK) {
// TODO: Do not throw on 400 ?
throw new IOException("Ranger REST API failure: " + conn.getResponseCode()
+ " " + conn.getResponseMessage()
+ ". Role name '" + role + "' likely already exists in Ranger");
}
String roleInfo = getResponseData(conn);
String roleId;
try {
JsonObject jObject = new JsonParser().parse(roleInfo).getAsJsonObject();
roleId = jObject.get("id").getAsString();
LOG.debug("Ranger returned roleId: {}", roleId);
} catch (JsonParseException e) {
e.printStackTrace();
throw e;
}
return roleId;
}
private String getCreateUserJsonStr(String userName, String password) {
return "{"
+ " \"name\":\"" + userName + "\","
+ " \"password\":\"" + password + "\","
+ " \"firstName\":\"" + userName + "\","
+ " \"userRoleList\":[\"ROLE_USER\"]"
+ "}";
}
public String createUser(String userName, String password)
throws IOException {
String endpointUrl =
rangerHttpsAddress + OZONE_OM_RANGER_ADMIN_CREATE_USER_HTTP_ENDPOINT;
String jsonData = getCreateUserJsonStr(userName, password);
final HttpURLConnection conn = makeHttpCall(endpointUrl,
jsonData, "POST", false);
if (conn.getResponseCode() != HTTP_OK) {
throw new IOException("Ranger REST API failure: " + conn.getResponseCode()
+ " " + conn.getResponseMessage()
+ ". User name '" + userName + "' likely already exists in Ranger");
}
String userInfo = getResponseData(conn);
String userId;
try {
JsonObject jObject = new JsonParser().parse(userInfo).getAsJsonObject();
userId = jObject.get("id").getAsString();
LOG.debug("Ranger returned userId: {}", userId);
} catch (JsonParseException e) {
e.printStackTrace();
throw e;
}
return userId;
}
public String createAccessPolicy(AccessPolicy policy) throws IOException {
String rangerAdminUrl =
rangerHttpsAddress + OZONE_OM_RANGER_ADMIN_CREATE_POLICY_HTTP_ENDPOINT;
HttpURLConnection conn = makeHttpCall(rangerAdminUrl,
policy.serializePolicyToJsonString(),
"POST", false);
String policyInfo = getResponseData(conn);
String policyID;
try {
JsonObject jObject = new JsonParser().parse(policyInfo).getAsJsonObject();
// TODO: Use policy name instead of id
policyID = jObject.get("id").getAsString();
LOG.debug("policyID is: {}", policyID);
} catch (JsonParseException e) {
e.printStackTrace();
throw e;
}
return policyID;
}
public AccessPolicy getAccessPolicyByName(String policyName)
throws IOException {
String rangerAdminUrl =
rangerHttpsAddress + OZONE_OM_RANGER_ADMIN_GET_POLICY_HTTP_ENDPOINT +
policyName;
HttpURLConnection conn = makeHttpGetCall(rangerAdminUrl,
"GET", false);
String policyInfo = getResponseData(conn);
JsonArray jArry = new JsonParser().parse(policyInfo).getAsJsonArray();
if (jArry.size() > 0) {
JsonObject jsonObject = jArry.get(0).getAsJsonObject();
AccessPolicy policy = new RangerAccessPolicy(policyName);
policy.deserializePolicyFromJsonString(jsonObject);
return policy;
} else {
// Returns null when policyInfo is an empty array
return null;
}
}
@Override
public AccessPolicy getAccessPolicyById(String policyId)
throws IOException {
String rangerAdminUrl =
rangerHttpsAddress + OZONE_OM_RANGER_ADMIN_GET_POLICY_ID_HTTP_ENDPOINT +
policyId;
HttpURLConnection conn = makeHttpGetCall(rangerAdminUrl,
"GET", false);
String policyInfo = getResponseData(conn);
JsonArray jArry = new JsonParser().parse(policyInfo).getAsJsonArray();
JsonObject jsonObject = jArry.get(0).getAsJsonObject();
AccessPolicy policy =
new RangerAccessPolicy(jsonObject.get("name").getAsString());
policy.deserializePolicyFromJsonString(jsonObject);
return policy;
}
/**
* Returns the service ID for Ozone service in Ranger.
* TODO: Error handling when Ozone service doesn't exist in Ranger.
*/
public int retrieveRangerOzoneServiceId() throws IOException {
String rangerAdminUrl =
rangerHttpsAddress + OZONE_OM_RANGER_OZONE_SERVICE_ENDPOINT;
int id = 0;
HttpURLConnection conn = makeHttpGetCall(rangerAdminUrl,
"GET", false);
String sInfo = getResponseData(conn);
JsonObject jObject = new JsonParser().parse(sInfo).getAsJsonObject();
JsonArray jArry = jObject.getAsJsonArray("services");
for (int i = 0; i < jArry.size(); ++i) {
JsonObject serviceObj = jArry.get(i).getAsJsonObject();
String serviceName = serviceObj.get("type").getAsString();
if (!serviceName.equals("ozone")) {
continue;
}
id = serviceObj.get("id").getAsInt();
}
return id;
}
public long getLatestOzoneServiceVersion() throws IOException {
checkRangerOzoneServiceId();
String rangerAdminUrl = rangerHttpsAddress
+ OZONE_OM_RANGER_OZONE_SERVICE_ENDPOINT + getRangerOzoneServiceId();
HttpURLConnection conn = makeHttpGetCall(rangerAdminUrl, "GET", false);
String sInfo = getResponseData(conn);
JsonObject jObject = new JsonParser().parse(sInfo).getAsJsonObject();
return jObject.get("policyVersion").getAsLong();
}
public String getIncrementalRangerChanges(long baseVersion)
throws IOException {
String rangerAdminUrl =
rangerHttpsAddress + OZONE_OM_RANGER_DOWNLOAD_ENDPOINT + baseVersion;
HttpURLConnection conn = makeHttpGetCall(rangerAdminUrl, "GET", false);
String sInfo = getResponseData(conn);
return sInfo;
}
public String getAllMultiTenantPolicies() throws IOException {
checkRangerOzoneServiceId();
// Note: Ranger incremental policies API is broken. So we use policy label
// filter to get all Multi-Tenant policies.
String rangerAdminUrl = rangerHttpsAddress
+ OZONE_OM_RANGER_ALL_POLICIES_ENDPOINT + getRangerOzoneServiceId()
+ "?policyLabelsPartial=" + OZONE_TENANT_RANGER_POLICY_LABEL;
// Also note: policyLabels (not partial) arg doesn't seem to work for Ranger
// at this point. When Ranger fixed this we could use exact match instead,
// then we can remove the verification logic in
// loadAllPoliciesRolesFromRanger().
HttpURLConnection conn = makeHttpGetCall(rangerAdminUrl, "GET", false);
final String jsonStr = getResponseData(conn);
if (jsonStr == null) {
throw new IOException("Invalid response from " + rangerAdminUrl);
}
return jsonStr;
}
@Override
public MultiTenantAccessController getMultiTenantAccessController() {
return this.accessController;
}
public void deleteUser(String userId) throws IOException {
String rangerAdminUrl =
rangerHttpsAddress + OZONE_OM_RANGER_ADMIN_DELETE_USER_HTTP_ENDPOINT
+ userId + "?forceDelete=true";
HttpURLConnection conn = makeHttpCall(rangerAdminUrl, null,
"DELETE", false);
int respnseCode = conn.getResponseCode();
if (respnseCode != 200 && respnseCode != 204) {
throw new IOException("Couldn't delete user " + userId);
}
}
@Override
public void deleteRoleById(String roleId) throws IOException {
String rangerAdminUrl =
rangerHttpsAddress + OZONE_OM_RANGER_ADMIN_DELETE_ROLE_HTTP_ENDPOINT
+ roleId + "?forceDelete=true";
HttpURLConnection conn = makeHttpCall(rangerAdminUrl, null,
"DELETE", false);
int respnseCode = conn.getResponseCode();
if (respnseCode != 200 && respnseCode != 204) {
throw new IOException("Couldn't delete role " + roleId);
}
}
@Override
public void deleteRoleByName(String roleName) throws IOException {
String rangerAdminUrl =
rangerHttpsAddress + OZONE_OM_RANGER_ADMIN_GET_ROLE_HTTP_ENDPOINT
+ roleName + "?forceDelete=true";
HttpURLConnection conn = makeHttpCall(rangerAdminUrl, null,
"DELETE", false);
int respnseCode = conn.getResponseCode();
if (respnseCode != 200 && respnseCode != 204) {
throw new IOException("Couldn't delete role " + roleName);
}
}
@Override
public void deletePolicyByName(String policyName) throws IOException {
AccessPolicy policy = getAccessPolicyByName(policyName);
if (policy != null) {
String policyID = policy.getPolicyID();
LOG.debug("policyID is: {}", policyID);
deletePolicyById(policyID);
} else {
LOG.error("No such policy: {} was found!", policyName);
}
}
public void deletePolicyById(String policyId) throws IOException {
String rangerAdminUrl =
rangerHttpsAddress + OZONE_OM_RANGER_ADMIN_DELETE_POLICY_HTTP_ENDPOINT
+ policyId + "?forceDelete=true";
try {
HttpURLConnection conn = makeHttpCall(rangerAdminUrl, null,
"DELETE", false);
int respnseCode = conn.getResponseCode();
if (respnseCode != 200 && respnseCode != 204) {
throw new IOException("Couldn't delete policy " + policyId);
}
} catch (Exception e) {
throw new IOException("Couldn't delete policy " + policyId, e);
}
}
private String getResponseData(HttpURLConnection urlConnection)
throws IOException {
StringBuilder response = new StringBuilder();
try (BufferedReader br = new BufferedReader(
new InputStreamReader(urlConnection.getInputStream(),
StandardCharsets.UTF_8))) {
String responseLine;
while ((responseLine = br.readLine()) != null) {
response.append(responseLine.trim());
}
LOG.debug("Got response: {}", response);
// TODO: throw if urlConnection code is 400?
} catch (IOException e) {
// Common exceptions:
// 1. Server returned HTTP response code: 401
// - Possibly incorrect Ranger credentials
// 2. Server returned HTTP response code: 400
// - Policy or role does not exist
switch (urlConnection.getResponseCode()) {
case 400:
LOG.error("The policy or role likely does not exist in Ranger");
return null;
case 401:
LOG.error("Check Ranger credentials");
// break;
default:
e.printStackTrace();
throw e;
}
}
return response.toString();
}
private HttpURLConnection openURLConnection(URL url) throws IOException {
final HttpURLConnection urlConnection;
if (url.getProtocol().equals("https")) {
urlConnection = (HttpsURLConnection) url.openConnection();
} else if (url.getProtocol().equals("http")) {
urlConnection = (HttpURLConnection) url.openConnection();
} else {
throw new IOException("Unsupported protocol: " + url.getProtocol() +
"URL: " + url);
}
return urlConnection;
}
/**
* Can make either http or https request.
*/
private HttpURLConnection makeHttpCall(String urlString,
String jsonInputString, String method, boolean isSpnego)
throws IOException {
URL url = new URL(urlString);
final HttpURLConnection urlConnection = openURLConnection(url);
urlConnection.setRequestMethod(method);
urlConnection.setConnectTimeout(connectionTimeout);
urlConnection.setReadTimeout(connectionRequestTimeout);
urlConnection.setRequestProperty("Accept", "application/json");
urlConnection.setRequestProperty("Authorization", authHeaderValue);
if ((jsonInputString != null) && !jsonInputString.isEmpty()) {
urlConnection.setDoOutput(true);
urlConnection.setRequestProperty("Content-Type", "application/json;");
try (OutputStream os = urlConnection.getOutputStream()) {
byte[] input = jsonInputString.getBytes(StandardCharsets.UTF_8);
os.write(input, 0, input.length);
os.flush();
}
}
return urlConnection;
}
/**
* Can make either http or https request.
*/
private HttpURLConnection makeHttpGetCall(String urlString,
String method, boolean isSpnego) throws IOException {
URL url = new URL(urlString);
final HttpURLConnection urlConnection = openURLConnection(url);
urlConnection.setRequestMethod(method);
urlConnection.setConnectTimeout(connectionTimeout);
urlConnection.setReadTimeout(connectionRequestTimeout);
urlConnection.setRequestProperty("Accept", "application/json");
urlConnection.setRequestProperty("Authorization", authHeaderValue);
return urlConnection;
}
}