blob: 25567c727ae6f9b2171d57916e8d85021261390a [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.ranger.service;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.ranger.authorization.hadoop.config.RangerAdminConfig;
import org.apache.ranger.authorization.utils.StringUtil;
import org.apache.ranger.biz.GdsDBStore;
import org.apache.ranger.biz.ServiceDBStore;
import org.apache.ranger.common.AppConstants;
import org.apache.ranger.common.view.VTrxLogAttr;
import org.apache.ranger.db.RangerDaoManager;
import org.apache.ranger.entity.XXSecurityZone;
import org.apache.ranger.entity.XXServiceVersionInfo;
import org.apache.ranger.entity.XXTrxLog;
import org.apache.ranger.entity.XXUser;
import org.apache.ranger.plugin.model.RangerPolicyDelta;
import org.apache.ranger.plugin.model.RangerSecurityZone;
import org.apache.ranger.plugin.model.RangerSecurityZone.RangerSecurityZoneService;
import org.apache.ranger.util.RangerEnumUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import javax.annotation.PostConstruct;
@Service
@Scope("singleton")
public class RangerSecurityZoneServiceService extends RangerSecurityZoneServiceBase<XXSecurityZone, RangerSecurityZone> {
@Autowired
RangerEnumUtil xaEnumUtil;
@Autowired
ServiceDBStore serviceDBStore;
@Autowired
GdsDBStore gdsStore;
boolean compressJsonData = false;
private static final Logger logger = LoggerFactory.getLogger(RangerSecurityZoneServiceService.class);
private static final Gson gsonBuilder = new GsonBuilder().setDateFormat("yyyyMMdd-HH:mm:ss.SSS-Z").create();
private Map<Long, Set<String>> serviceNamesInZones = new HashMap<>();
private Map<Long, Set<String>> tagServiceNamesInZones = new HashMap<>();
static HashMap<String, VTrxLogAttr> trxLogAttrs = new HashMap<String, VTrxLogAttr>();
static {
trxLogAttrs.put("name", new VTrxLogAttr("name", "Zone Name", false));
trxLogAttrs.put("services", new VTrxLogAttr("services", "Zone Services", false));
trxLogAttrs.put("adminUsers", new VTrxLogAttr("adminUsers", "Zone Admin Users", false));
trxLogAttrs.put("adminUserGroups", new VTrxLogAttr("adminUserGroups", "Zone Admin User Groups", false));
trxLogAttrs.put("auditUsers", new VTrxLogAttr("auditUsers", "Zone Audit Users", false));
trxLogAttrs.put("auditUserGroups", new VTrxLogAttr("auditUserGroups", "Zone Audit User Groups", false));
trxLogAttrs.put("adminRoles", new VTrxLogAttr("adminRoles", "Zone Admin Roles", false));
trxLogAttrs.put("auditRoles", new VTrxLogAttr("auditRoles", "Zone Audit Roles", false));
trxLogAttrs.put("description", new VTrxLogAttr("description", "Zone Description", false));
trxLogAttrs.put("tagServices", new VTrxLogAttr("tagServices", "Zone Tag Services", false));
}
public RangerSecurityZoneServiceService() {
super();
}
@PostConstruct
public void initService() {
if (logger.isDebugEnabled()) {
logger.debug("==> RangerSecurityZoneServiceService.initService()");
}
RangerAdminConfig config = RangerAdminConfig.getInstance();
compressJsonData = config.getBoolean("ranger.admin.store.security.zone.compress.json_data", compressJsonData);
logger.info("ranger.admin.store.security.zone.compress.json_data={}", compressJsonData);
if (logger.isDebugEnabled()) {
logger.debug("<== RangerSecurityZoneServiceService.initService()");
}
}
@Override
protected void validateForCreate(RangerSecurityZone vObj) {
}
@Override
protected void validateForUpdate(RangerSecurityZone vObj, XXSecurityZone entityObj) {
// Cache service-names in existing zone object
RangerSecurityZone existingZone = new RangerSecurityZone();
existingZone = mapEntityToViewBean(existingZone, entityObj);
serviceNamesInZones.put(entityObj.getId(), existingZone.getServices().keySet());
tagServiceNamesInZones.put(entityObj.getId(), new HashSet<>(existingZone.getTagServices()));
}
@Override
protected XXSecurityZone mapViewToEntityBean(RangerSecurityZone securityZone, XXSecurityZone xxSecurityZone, int OPERATION_CONTEXT) {
XXSecurityZone ret = super.mapViewToEntityBean(securityZone, xxSecurityZone, OPERATION_CONTEXT);
String json = gsonBuilder.toJson(securityZone);
if (StringUtils.isNotEmpty(json) && compressJsonData) {
try {
ret.setJsonData(null);
ret.setGzJsonData(StringUtil.gzipCompress(json));
} catch (IOException excp) {
logger.error("mapViewToEntityBean(): json compression failed (length={}). Will save uncompressed json", json.length(), excp);
ret.setJsonData(json);
ret.setGzJsonData(null);
}
} else {
ret.setJsonData(json);
ret.setGzJsonData(null);
}
return ret;
}
@Override
protected RangerSecurityZone mapEntityToViewBean(RangerSecurityZone securityZone, XXSecurityZone xxSecurityZone) {
RangerSecurityZone ret = super.mapEntityToViewBean(securityZone, xxSecurityZone);
byte[] gzJson = xxSecurityZone.getGzJsonData();
String json;
if (gzJson != null) {
try {
json = StringUtil.gzipDecompress(gzJson);
} catch (IOException excp) {
json = xxSecurityZone.getJsonData();
logger.error("mapEntityToViewBean(): decompression of x_security_zone.gz_jsonData failed (length={}). Will use contents of x_security_zone.jsonData (length={})", gzJson.length, (json != null ? json.length() : 0), excp);
}
} else {
json = xxSecurityZone.getJsonData();
}
if (StringUtils.isNotEmpty(json)) {
RangerSecurityZone zoneFromJsonData = gsonBuilder.fromJson(json, RangerSecurityZone.class);
if (zoneFromJsonData == null) {
logger.info("Cannot read jsonData into RangerSecurityZone object in [" + json + "]!!");
} else {
ret.setName(zoneFromJsonData.getName());
ret.setServices(zoneFromJsonData.getServices());
ret.setAdminUsers(zoneFromJsonData.getAdminUsers());
ret.setAdminUserGroups(zoneFromJsonData.getAdminUserGroups());
ret.setAdminRoles(zoneFromJsonData.getAdminRoles());
ret.setAuditUsers(zoneFromJsonData.getAuditUsers());
ret.setAuditUserGroups(zoneFromJsonData.getAuditUserGroups());
ret.setAuditRoles(zoneFromJsonData.getAuditRoles());
ret.setTagServices(zoneFromJsonData.getTagServices());
}
} else {
logger.info("Empty string representing jsonData in [" + xxSecurityZone + "]!!");
}
return ret;
}
@Override
public RangerSecurityZone postCreate(XXSecurityZone xObj) {
// Ensure to update ServiceVersionInfo for each service in the zone
RangerSecurityZone ret = super.postCreate(xObj);
Set<String> serviceNames = ret.getServices().keySet();
// Create default zone policies
try {
serviceDBStore.createZoneDefaultPolicies(serviceNames, ret);
updateServiceInfos(serviceNames);
} catch (Exception exception) {
logger.error("postCreate processing failed for security-zone:[" + ret + "]", exception);
ret = null;
}
return ret;
}
@Override
public RangerSecurityZone postUpdate(XXSecurityZone xObj) {
// Update ServiceVersionInfo for all affected services
RangerSecurityZone ret = super.postUpdate(xObj);
Set<String> oldServiceNames = new HashSet(serviceNamesInZones.remove(xObj.getId()));
Set<String> updatedServiceNames = ret.getServices().keySet();
Set<String> oldTagServiceNames = new HashSet(tagServiceNamesInZones.remove(xObj.getId()));
Set<String> updatedTagServiceNames = new HashSet<String>(ret.getTagServices());
Collection<String> newServiceNames = CollectionUtils.subtract(updatedServiceNames, oldServiceNames);
Collection<String> deletedServiceNames = CollectionUtils.subtract(oldServiceNames, updatedServiceNames);
Collection<String> deletedTagServiceNames = CollectionUtils.subtract(oldTagServiceNames, updatedTagServiceNames);
try {
serviceDBStore.createZoneDefaultPolicies(newServiceNames, ret);
serviceDBStore.deleteZonePolicies(deletedServiceNames, ret.getId());
serviceDBStore.deleteZonePolicies(deletedTagServiceNames, ret.getId());
gdsStore.deleteAllGdsObjectsForServicesInSecurityZone(deletedServiceNames, ret.getId());
oldServiceNames.addAll(updatedServiceNames);
updateServiceInfos(oldServiceNames);
} catch (Exception exception) {
logger.error("postUpdate processing failed for security-zone:[" + ret + "]", exception);
ret = null;
}
return ret;
}
@Override
public XXSecurityZone preDelete(Long id) {
// Update ServiceVersionInfo for each service in the zone
XXSecurityZone ret = super.preDelete(id);
RangerSecurityZone viewObject = new RangerSecurityZone();
viewObject = mapEntityToViewBean(viewObject, ret);
Set<String> allServiceNames = new HashSet<>(viewObject.getTagServices());
allServiceNames.addAll(viewObject.getServices().keySet());
// Delete default zone policies
try {
serviceDBStore.deleteZonePolicies(allServiceNames, id);
gdsStore.deleteAllGdsObjectsForSecurityZone(id);
updateServiceInfos(allServiceNames);
} catch (Exception exception) {
logger.error("preDelete processing failed for security-zone:[" + viewObject + "]", exception);
ret = null;
}
return ret;
}
private void updateServiceInfos(Collection<String> services) {
if(CollectionUtils.isEmpty(services)) {
return;
}
List<XXServiceVersionInfo> serviceVersionInfos = new ArrayList<>(services.size());
for (String serviceName : services) {
serviceVersionInfos.add(daoMgr.getXXServiceVersionInfo().findByServiceName(serviceName));
}
for(XXServiceVersionInfo serviceVersionInfo : serviceVersionInfos) {
final RangerDaoManager finaldaoManager = daoMgr;
final Long finalServiceId = serviceVersionInfo.getServiceId();
final ServiceDBStore.VERSION_TYPE versionType = ServiceDBStore.VERSION_TYPE.POLICY_VERSION;
Runnable serviceVersionUpdater = new ServiceDBStore.ServiceVersionUpdater(finaldaoManager, finalServiceId, versionType, null, RangerPolicyDelta.CHANGE_TYPE_SERVICE_CHANGE, null);
daoMgr.getRangerTransactionSynchronizationAdapter().executeOnTransactionCommit(serviceVersionUpdater);
}
}
public List<XXTrxLog> getTransactionLog(RangerSecurityZone vSecurityZone, RangerSecurityZone securityZoneDB, String action) {
if (vSecurityZone == null || action == null || ("update".equalsIgnoreCase(action) && securityZoneDB == null)) {
return null;
}
List<XXTrxLog> trxLogList = new ArrayList<XXTrxLog>();
Field[] fields = vSecurityZone.getClass().getDeclaredFields();
try {
Field nameField = vSecurityZone.getClass().getDeclaredField("name");
nameField.setAccessible(true);
String objectName = "" + nameField.get(vSecurityZone);
for (Field field : fields) {
String fieldName = field.getName();
if (!trxLogAttrs.containsKey(fieldName)) {
continue;
}
field.setAccessible(true);
VTrxLogAttr vTrxLogAttr = trxLogAttrs.get(fieldName);
XXTrxLog xTrxLog = new XXTrxLog();
xTrxLog.setAttributeName(vTrxLogAttr
.getAttribUserFriendlyName());
xTrxLog.setAction(action);
xTrxLog.setObjectId(vSecurityZone.getId());
xTrxLog.setObjectClassType(AppConstants.CLASS_TYPE_RANGER_SECURITY_ZONE);
xTrxLog.setObjectName(objectName);
String value = null;
if (vTrxLogAttr.isEnum()) {
String enumName = XXUser.getEnumName(fieldName);
int enumValue = field.get(vSecurityZone) == null ? 0 : Integer
.parseInt("" + field.get(vSecurityZone));
value = xaEnumUtil.getLabel(enumName, enumValue);
} else {
value = "" + field.get(vSecurityZone);
if ((value == null || "null".equalsIgnoreCase(value))
&& !"update".equalsIgnoreCase(action)) {
continue;
}
}
if("services".equalsIgnoreCase(fieldName)) {
value = toTrxLog(vSecurityZone.getServices());
}
if ("create".equalsIgnoreCase(action)) {
xTrxLog.setNewValue(value);
trxLogList.add(xTrxLog);
}
else if ("delete".equalsIgnoreCase(action)) {
xTrxLog.setPreviousValue(value);
trxLogList.add(xTrxLog);
}
else if ("update".equalsIgnoreCase(action)) {
String oldValue = null;
Field[] mFields = vSecurityZone.getClass().getDeclaredFields();
for (Field mField : mFields) {
mField.setAccessible(true);
String mFieldName = mField.getName();
if (fieldName.equalsIgnoreCase(mFieldName)) {
if("services".equalsIgnoreCase(mFieldName)) {
oldValue = toTrxLog(securityZoneDB.getServices());
}
else {
oldValue = mField.get(securityZoneDB) + "";
}
break;
}
}
if (oldValue == null || oldValue.equalsIgnoreCase(value)) {
continue;
}
xTrxLog.setPreviousValue(oldValue);
xTrxLog.setNewValue(value);
trxLogList.add(xTrxLog);
}
}
if (trxLogList.isEmpty()) {
XXTrxLog xTrxLog = new XXTrxLog();
xTrxLog.setAction(action);
xTrxLog.setObjectClassType(AppConstants.CLASS_TYPE_RANGER_SECURITY_ZONE);
xTrxLog.setObjectId(vSecurityZone.getId());
xTrxLog.setObjectName(objectName);
trxLogList.add(xTrxLog);
}
} catch (IllegalAccessException e) {
logger.error("Transaction log failure.", e);
} catch (NoSuchFieldException e) {
logger.error("Transaction log failure.", e);
}
return trxLogList;
}
private String toTrxLog(Map<String, RangerSecurityZoneService> services) {
String ret;
if (services == null) {
services = Collections.emptyMap();
}
if (compressJsonData) { // when compression is enabled, summarize services info for trx log
Map<String, RangerSecurityZoneService> servicesSummary = new HashMap<>(services.size());
for (Map.Entry<String, RangerSecurityZoneService> entry : services.entrySet()) {
String serviceName = entry.getKey();
RangerSecurityZoneService zoneService = entry.getValue();
Integer resourceCount = (zoneService != null && zoneService.getResources() != null) ? zoneService.getResources().size() : 0;
RangerSecurityZoneService zoneServiceSummary = new RangerSecurityZoneService();
zoneServiceSummary.getResources().add(new HashMap<String, List<String>>() {{ put("resourceCount", Collections.singletonList(resourceCount.toString())); }});
servicesSummary.put(serviceName, zoneServiceSummary);
}
ret = new Gson().toJson(servicesSummary, Map.class);
} else {
ret = new Gson().toJson(services, Map.class);
}
return ret;
}
}