package io.cattle.platform.iaas.api.auditing;
import io.cattle.platform.api.auth.Identity;
import io.cattle.platform.api.auth.Policy;
import io.cattle.platform.core.constants.AccountConstants;
import io.cattle.platform.core.constants.ContainerEventConstants;
import io.cattle.platform.core.constants.ExternalEventConstants;
import io.cattle.platform.eventing.EventService;
import io.cattle.platform.iaas.api.auditing.dao.AuditLogDao;
import io.cattle.platform.object.ObjectManager;
import io.github.ibuildthecloud.gdapi.context.ApiContext;
import io.github.ibuildthecloud.gdapi.id.IdFormatter;
import io.github.ibuildthecloud.gdapi.json.JsonMapper;
import io.github.ibuildthecloud.gdapi.model.Resource;
import io.github.ibuildthecloud.gdapi.model.Schema;
import io.github.ibuildthecloud.gdapi.request.ApiRequest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AuditServiceImpl implements AuditService{
public static final Set<String> BLACK_LIST_TYPES = Collections.unmodifiableSet(
new HashSet<>(Arrays.asList(
"publish",
"configContent".toLowerCase(),
"externalHandler".toLowerCase(),
"externalService".toLowerCase(),
"hostApiProxyToken".toLowerCase(),
"token",
"scripts",
"serviceEvent".toLowerCase(),
"userPreference".toLowerCase(),
"dynamicSchema".toLowerCase(),
ContainerEventConstants.CONTAINER_EVENT_KIND.toLowerCase(),
ExternalEventConstants.KIND_EXTERNAL_EVENT.toLowerCase(),
ExternalEventConstants.KIND_SERVICE_EVENT.toLowerCase(),
ExternalEventConstants.KIND_VOLUME_EVENT.toLowerCase(),
ExternalEventConstants.KIND_STORAGE_POOL_EVENT.toLowerCase()
)));
@Inject
AuditLogDao auditLogDao;
@Inject
JsonMapper jsonMapper;
@Inject
EventService eventService;
@Inject
ObjectManager objectManager;
@Inject
IdFormatter idFormatter;
private static final Logger log = LoggerFactory.getLogger(AuditLogsRequestHandler.class);
@Override
public void logRequest(ApiRequest request, Policy policy) {
if (policy == null || request == null || request.getType() == null) {
return;
}
if (Schema.Method.GET.isMethod(request.getMethod()) ||
BLACK_LIST_TYPES.contains(request.getType().toLowerCase())) {
return;
}
Map<String, Object> data = new HashMap<>();
putInAsString(data, request.getType(), "requestObject", "Failed to convert request object to json.", request.getRequestObject());
putInAsString(data, request.getType(), "responseObject", "Failed to convert response object to json.", request.getResponseObject());
data.put("responseCode", request.getResponseCode());
Identity user = null;
for (Identity identity: policy.getIdentities()){
if (identity.getExternalIdType().contains("user")){
user = identity;
break;
}
}
if (user == null && policy.getIdentities().size() == 1){
user = policy.getIdentities().iterator().next();
}
long runtime = ((long) request.getAttribute("requestEndTime")) - ((long)request.getAttribute("requestStartTime"));
String authType = (String) request.getAttribute(AccountConstants.AUTH_TYPE);
String resourceId =request.getResponseObject() instanceof Resource ? ((Resource) request.getResponseObject()).getId(): null;
String resourceType = request.getType();
String eventType = "api." + convertResourceType(resourceType) + "." + (StringUtils.isNotBlank(request.getAction()) ?
request.getAction() :convertToAction(request.getMethod()));
auditLogDao.create(resourceType, parseId(resourceId), data, user,
policy.getAccountId(), policy.getAuthenticatedAsAccountId(), eventType, authType, runtime, null,
request.getClientIp());
}
private String convertResourceType(String type) {
switch (StringUtils.lowerCase(type)) {
case "environment":
return "stack";
case "project":
return "environment";
default:
return type;
}
}
private Long parseId(String resourceId) {
Long parsedResourceId;
if (resourceId == null){
parsedResourceId = null;
} else try {
if (ApiContext.getContext() != null && ApiContext.getContext().getIdFormatter() != null) {
parsedResourceId = Long.valueOf(ApiContext.getContext().getIdFormatter().parseId(resourceId));
} else {
parsedResourceId = Long.valueOf(idFormatter.parseId(resourceId));
}
} catch (NumberFormatException e) {
try {
parsedResourceId = Long.valueOf(resourceId);
} catch (NumberFormatException e1) {
parsedResourceId = null;
}
}
return parsedResourceId;
}
private AuditEventType convertToAction(String method) {
switch (Schema.Method.valueOf(method)){
case DELETE:
return AuditEventType.delete;
case POST:
return AuditEventType.create;
case PUT:
return AuditEventType.update;
default:
return AuditEventType.UNKNOWN;
}
}
private void putInAsString(Map<String, Object> data, String type, String fieldForObject, String errMsg, Object objectToPlace) {
if (objectToPlace == null) {
return;
}
@SuppressWarnings("unchecked")
Map<String, Object> obj = jsonMapper.convertValue(objectToPlace, Map.class);
if ("secret".equals(type)) {
obj.remove("value");
}
obj.remove("secretValue");
obj.remove("password");
obj.remove("newSecret");
obj.remove("oldSecret");
obj.remove("adminAccountPassword");
obj.remove("serviceAccountPassword");
Iterator<Map.Entry<String, Object>> iter = obj.entrySet().iterator();
while (iter.hasNext()) {
if (iter.next().getKey().endsWith("Config")) {
iter.remove();
}
}
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
jsonMapper.writeValue(os, obj);
data.put(fieldForObject, os.toString());
} catch (IOException e) {
log.error("Failed to log [{}]", errMsg, e);
}
}
}