CLOUDSTACK-8485: listAPIs are taking too long to return results

- Removed regex. based search/replace of sensitive data on API response introduced as part of commit b0c6d4734724358df97b6fa4d8c5beb0f447745e
- Added new response serializer to skip sensitive data from getting logged based on annotation present in resposne object fields
- Added new parameter 'isSensitive' to @Param for marking a field as sensitive in response objects
This commit is contained in:
Koushik Das 2015-11-02 14:59:00 +05:30
parent acce645119
commit e13df96348
16 changed files with 154 additions and 78 deletions

View File

@ -37,4 +37,6 @@ public @interface Param {
String since() default ""; String since() default "";
RoleType[] authorized() default {}; RoleType[] authorized() default {};
boolean isSensitive() default false;
} }

View File

@ -17,13 +17,12 @@
package org.apache.cloudstack.api.response; package org.apache.cloudstack.api.response;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import com.cloud.serializer.Param; import com.cloud.serializer.Param;
public class CreateSSHKeyPairResponse extends SSHKeyPairResponse { public class CreateSSHKeyPairResponse extends SSHKeyPairResponse {
@SerializedName("privatekey") @SerializedName("privatekey")
@Param(description = "Private key") @Param(description = "Private key", isSensitive = true)
private String privateKey; private String privateKey;
public CreateSSHKeyPairResponse() { public CreateSSHKeyPairResponse() {

View File

@ -25,7 +25,7 @@ import com.cloud.serializer.Param;
public class GetVMPasswordResponse extends BaseResponse { public class GetVMPasswordResponse extends BaseResponse {
@SerializedName("encryptedpassword") @SerializedName("encryptedpassword")
@Param(description = "The base64 encoded encrypted password of the VM") @Param(description = "The base64 encoded encrypted password of the VM", isSensitive = true)
private String encryptedPassword; private String encryptedPassword;
public GetVMPasswordResponse() { public GetVMPasswordResponse() {

View File

@ -63,7 +63,7 @@ public class LoginCmdResponse extends AuthenticationCmdResponse {
private String registered; private String registered;
@SerializedName(value = ApiConstants.SESSIONKEY) @SerializedName(value = ApiConstants.SESSIONKEY)
@Param(description = "Session key that can be passed in subsequent Query command calls") @Param(description = "Session key that can be passed in subsequent Query command calls", isSensitive = true)
private String sessionKey; private String sessionKey;
public String getUsername() { public String getUsername() {

View File

@ -24,11 +24,11 @@ import com.cloud.serializer.Param;
public class RegisterResponse extends BaseResponse { public class RegisterResponse extends BaseResponse {
@SerializedName("apikey") @SerializedName("apikey")
@Param(description = "the api key of the registered user") @Param(description = "the api key of the registered user", isSensitive = true)
private String apiKey; private String apiKey;
@SerializedName("secretkey") @SerializedName("secretkey")
@Param(description = "the secret key of the registered user") @Param(description = "the secret key of the registered user", isSensitive = true)
private String secretKey; private String secretKey;
public String getApiKey() { public String getApiKey() {

View File

@ -42,7 +42,7 @@ public class RemoteAccessVpnResponse extends BaseResponse implements ControlledE
private String ipRange; private String ipRange;
@SerializedName("presharedkey") @SerializedName("presharedkey")
@Param(description = "the ipsec preshared key") @Param(description = "the ipsec preshared key", isSensitive = true)
private String presharedKey; private String presharedKey;
@SerializedName(ApiConstants.ACCOUNT) @SerializedName(ApiConstants.ACCOUNT)

View File

@ -51,7 +51,7 @@ public class Site2SiteCustomerGatewayResponse extends BaseResponse implements Co
private String guestCidrList; private String guestCidrList;
@SerializedName(ApiConstants.IPSEC_PSK) @SerializedName(ApiConstants.IPSEC_PSK)
@Param(description = "IPsec preshared-key of customer gateway") @Param(description = "IPsec preshared-key of customer gateway", isSensitive = true)
private String ipsecPsk; private String ipsecPsk;
@SerializedName(ApiConstants.IKE_POLICY) @SerializedName(ApiConstants.IKE_POLICY)

View File

@ -58,7 +58,7 @@ public class Site2SiteVpnConnectionResponse extends BaseResponse implements Cont
private String guestCidrList; private String guestCidrList;
@SerializedName(ApiConstants.IPSEC_PSK) @SerializedName(ApiConstants.IPSEC_PSK)
@Param(description = "IPsec Preshared-Key of the customer gateway") @Param(description = "IPsec Preshared-Key of the customer gateway", isSensitive = true)
//from CustomerGateway //from CustomerGateway
private String ipsecPsk; private String ipsecPsk;

View File

@ -78,11 +78,11 @@ public class UserResponse extends BaseResponse {
private String timezone; private String timezone;
@SerializedName("apikey") @SerializedName("apikey")
@Param(description = "the api key of the user") @Param(description = "the api key of the user", isSensitive = true)
private String apiKey; private String apiKey;
@SerializedName("secretkey") @SerializedName("secretkey")
@Param(description = "the secret key of the user") @Param(description = "the secret key of the user", isSensitive = true)
private String secretKey; private String secretKey;
@SerializedName("accountid") @SerializedName("accountid")

View File

@ -221,7 +221,7 @@ public class UserVmResponse extends BaseResponse implements ControlledEntityResp
private Set<SecurityGroupResponse> securityGroupList; private Set<SecurityGroupResponse> securityGroupList;
@SerializedName(ApiConstants.PASSWORD) @SerializedName(ApiConstants.PASSWORD)
@Param(description = "the password (if exists) of the virtual machine") @Param(description = "the password (if exists) of the virtual machine", isSensitive = true)
private String password; private String password;
@SerializedName("nic") @SerializedName("nic")

View File

@ -54,7 +54,7 @@ public class BigSwitchBcfDeviceResponse extends BaseResponse {
@SerializedName(ApiConstants.USERNAME) @Param(description="the controller username") @SerializedName(ApiConstants.USERNAME) @Param(description="the controller username")
private String username; private String username;
@SerializedName(ApiConstants.PASSWORD) @Param(description="the controller password") @SerializedName(ApiConstants.PASSWORD) @Param(description="the controller password", isSensitive = true)
private String password; private String password;
@SerializedName(BcfConstants.BIGSWITCH_BCF_DEVICE_NAT) @SerializedName(BcfConstants.BIGSWITCH_BCF_DEVICE_NAT)

View File

@ -53,7 +53,7 @@ public class LDAPConfigResponse extends BaseResponse {
private String bindDN; private String bindDN;
@SerializedName(ApiConstants.BIND_PASSWORD) @SerializedName(ApiConstants.BIND_PASSWORD)
@Param(description = "DN password") @Param(description = "DN password", isSensitive = true)
private String bindPassword; private String bindPassword;
public String getHostname() { public String getHostname() {

View File

@ -27,30 +27,40 @@ import com.google.gson.FieldAttributes;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
/** /**
* The ApiResonseGsonHelper is different from ApiGsonHelper - it registeres one more adapter for String type required for api response encoding * The ApiResonseGsonHelper is different from ApiGsonHelper - it registers one more adapter for String type required for api response encoding
*/ */
public class ApiResponseGsonHelper { public class ApiResponseGsonHelper {
private static final GsonBuilder s_gBuilder; private static final GsonBuilder s_gBuilder;
private static final GsonBuilder s_gLogBuilder;
static { static {
s_gBuilder = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); s_gBuilder = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
s_gBuilder.setVersion(1.3); s_gBuilder.setVersion(1.3);
s_gBuilder.registerTypeAdapter(ResponseObject.class, new ResponseObjectTypeAdapter()); s_gBuilder.registerTypeAdapter(ResponseObject.class, new ResponseObjectTypeAdapter());
s_gBuilder.registerTypeAdapter(String.class, new EncodedStringTypeAdapter()); s_gBuilder.registerTypeAdapter(String.class, new EncodedStringTypeAdapter());
s_gBuilder.setExclusionStrategies(new ExclStrat()); s_gBuilder.setExclusionStrategies(new ApiResponseExclusionStrategy());
s_gLogBuilder = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
s_gLogBuilder.setVersion(1.3);
s_gLogBuilder.registerTypeAdapter(ResponseObject.class, new ResponseObjectTypeAdapter());
s_gLogBuilder.registerTypeAdapter(String.class, new EncodedStringTypeAdapter());
s_gLogBuilder.setExclusionStrategies(new LogExclusionStrategy());
} }
public static GsonBuilder getBuilder() { public static GsonBuilder getBuilder() {
return s_gBuilder; return s_gBuilder;
} }
private static class ExclStrat implements ExclusionStrategy { public static GsonBuilder getLogBuilder() {
return s_gLogBuilder;
}
private static class ApiResponseExclusionStrategy implements ExclusionStrategy {
public boolean shouldSkipClass(Class<?> arg0) { public boolean shouldSkipClass(Class<?> arg0) {
return false; return false;
} }
public boolean shouldSkipField(FieldAttributes f) {
public boolean shouldSkipField(FieldAttributes f) {
Param param = f.getAnnotation(Param.class); Param param = f.getAnnotation(Param.class);
if (param != null) { if (param != null) {
RoleType[] allowedRoles = param.authorized(); RoleType[] allowedRoles = param.authorized();
@ -71,4 +81,19 @@ public class ApiResponseGsonHelper {
return false; return false;
} }
} }
private static class LogExclusionStrategy extends ApiResponseExclusionStrategy implements ExclusionStrategy {
public boolean shouldSkipClass(Class<?> arg0) {
return false;
}
public boolean shouldSkipField(FieldAttributes f) {
Param param = f.getAnnotation(Param.class);
boolean skip = (param != null && param.isSensitive());
if (!skip) {
skip = super.shouldSkipField(f);
}
return skip;
}
}
} }

View File

@ -525,14 +525,9 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
cmdObj.setHttpMethod(paramMap.get(ApiConstants.HTTPMETHOD).toString()); cmdObj.setHttpMethod(paramMap.get(ApiConstants.HTTPMETHOD).toString());
// This is where the command is either serialized, or directly dispatched // This is where the command is either serialized, or directly dispatched
response = queueCommand(cmdObj, paramMap); StringBuilder log = new StringBuilder();
if (annotation.responseHasSensitiveInfo()) response = queueCommand(cmdObj, paramMap, log);
{ buildAuditTrail(auditTrailSb, command[0], log.toString());
buildAuditTrail(auditTrailSb, command[0],
StringUtils.cleanString(response));
}
else
buildAuditTrail(auditTrailSb, command[0], response);
} else { } else {
final String errorString = "Unknown API command: " + command[0]; final String errorString = "Unknown API command: " + command[0];
s_logger.warn(errorString); s_logger.warn(errorString);
@ -617,7 +612,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
return ApiResponseSerializer.toSerializedString(response, cmd.getResponseType()); return ApiResponseSerializer.toSerializedString(response, cmd.getResponseType());
} }
private String queueCommand(final BaseCmd cmdObj, final Map<String, String> params) throws Exception { private String queueCommand(final BaseCmd cmdObj, final Map<String, String> params, StringBuilder log) throws Exception {
final CallContext ctx = CallContext.current(); final CallContext ctx = CallContext.current();
final Long callerUserId = ctx.getCallingUserId(); final Long callerUserId = ctx.getCallingUserId();
final Account caller = ctx.getCallingAccount(); final Account caller = ctx.getCallingAccount();
@ -717,7 +712,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
} }
SerializationContext.current().setUuidTranslation(true); SerializationContext.current().setUuidTranslation(true);
return ApiResponseSerializer.toSerializedString((ResponseObject)cmdObj.getResponseObject(), cmdObj.getResponseType()); return ApiResponseSerializer.toSerializedStringWithSecureLogs((ResponseObject)cmdObj.getResponseObject(), cmdObj.getResponseType(), log);
} }
} }

View File

@ -147,8 +147,9 @@ public class ApiServlet extends HttpServlet {
// logging the request start and end in management log for easy debugging // logging the request start and end in management log for easy debugging
String reqStr = ""; String reqStr = "";
String cleanQueryString = StringUtils.cleanString(req.getQueryString());
if (s_logger.isDebugEnabled()) { if (s_logger.isDebugEnabled()) {
reqStr = auditTrailSb.toString() + " " + StringUtils.cleanString(req.getQueryString()); reqStr = auditTrailSb.toString() + " " + cleanQueryString;
s_logger.debug("===START=== " + reqStr); s_logger.debug("===START=== " + reqStr);
} }
@ -233,7 +234,7 @@ public class ApiServlet extends HttpServlet {
} }
} }
auditTrailSb.append(StringUtils.cleanString(req.getQueryString())); auditTrailSb.append(cleanQueryString);
final boolean isNew = ((session == null) ? true : session.isNew()); final boolean isNew = ((session == null) ? true : session.isNew());
// Initialize an empty context and we will update it after we have verified the request below, // Initialize an empty context and we will update it after we have verified the request below,

View File

@ -27,6 +27,7 @@ import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.exception.ExceptionProxyObject; import com.cloud.utils.exception.ExceptionProxyObject;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.BaseCmd;
@ -56,9 +57,18 @@ public class ApiResponseSerializer {
public static String toSerializedString(ResponseObject result, String responseType) { public static String toSerializedString(ResponseObject result, String responseType) {
s_logger.trace("===Serializing Response==="); s_logger.trace("===Serializing Response===");
if (HttpUtils.RESPONSE_TYPE_JSON.equalsIgnoreCase(responseType)) { if (HttpUtils.RESPONSE_TYPE_JSON.equalsIgnoreCase(responseType)) {
return toJSONSerializedString(result); return toJSONSerializedString(result, new StringBuilder());
} else { } else {
return toXMLSerializedString(result); return toXMLSerializedString(result, new StringBuilder());
}
}
public static String toSerializedStringWithSecureLogs(ResponseObject result, String responseType, StringBuilder log) {
s_logger.trace("===Serializing Response===");
if (HttpUtils.RESPONSE_TYPE_JSON.equalsIgnoreCase(responseType)) {
return toJSONSerializedString(result, log);
} else {
return toXMLSerializedString(result, log);
} }
} }
@ -73,51 +83,65 @@ public class ApiResponseSerializer {
return str; return str;
} }
public static String toJSONSerializedString(ResponseObject result) { public static String toJSONSerializedString(ResponseObject result, StringBuilder log) {
if (result != null) { if (result != null && log != null) {
Gson gson = ApiResponseGsonHelper.getBuilder().excludeFieldsWithModifiers(Modifier.TRANSIENT).create(); Gson responseBuilder = ApiResponseGsonHelper.getBuilder().excludeFieldsWithModifiers(Modifier.TRANSIENT).create();
Gson logBuilder = ApiResponseGsonHelper.getLogBuilder().excludeFieldsWithModifiers(Modifier.TRANSIENT).create();
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("{\"").append(result.getResponseName()).append("\":"); sb.append("{\"").append(result.getResponseName()).append("\":");
log.append("{\"").append(result.getResponseName()).append("\":");
if (result instanceof ListResponse) { if (result instanceof ListResponse) {
List<? extends ResponseObject> responses = ((ListResponse)result).getResponses(); List<? extends ResponseObject> responses = ((ListResponse)result).getResponses();
Integer count = ((ListResponse)result).getCount(); Integer count = ((ListResponse)result).getCount();
boolean nonZeroCount = (count != null && count.longValue() != 0); boolean nonZeroCount = (count != null && count.longValue() != 0);
if (nonZeroCount) { if (nonZeroCount) {
sb.append("{\"").append(ApiConstants.COUNT).append("\":").append(count); sb.append("{\"").append(ApiConstants.COUNT).append("\":").append(count);
log.append("{\"").append(ApiConstants.COUNT).append("\":").append(count);
} }
if ((responses != null) && !responses.isEmpty()) { if ((responses != null) && !responses.isEmpty()) {
String jsonStr = gson.toJson(responses.get(0)); String jsonStr = responseBuilder.toJson(responses.get(0));
jsonStr = unescape(jsonStr); jsonStr = unescape(jsonStr);
String logStr = logBuilder.toJson(responses.get(0));
logStr = unescape(logStr);
if (nonZeroCount) { if (nonZeroCount) {
sb.append(",\"").append(responses.get(0).getObjectName()).append("\":[").append(jsonStr); sb.append(",\"").append(responses.get(0).getObjectName()).append("\":[").append(jsonStr);
log.append(",\"").append(responses.get(0).getObjectName()).append("\":[").append(logStr);
} }
for (int i = 1; i < ((ListResponse)result).getResponses().size(); i++) { for (int i = 1; i < ((ListResponse)result).getResponses().size(); i++) {
jsonStr = gson.toJson(responses.get(i)); jsonStr = responseBuilder.toJson(responses.get(i));
jsonStr = unescape(jsonStr); jsonStr = unescape(jsonStr);
logStr = logBuilder.toJson(responses.get(i));
logStr = unescape(logStr);
sb.append(",").append(jsonStr); sb.append(",").append(jsonStr);
log.append(",").append(logStr);
} }
sb.append("]}"); sb.append("]}");
log.append("]}");
} else { } else {
if (!nonZeroCount) { if (!nonZeroCount) {
sb.append("{"); sb.append("{");
log.append("{");
} }
sb.append("}"); sb.append("}");
log.append("}");
} }
} else if (result instanceof SuccessResponse) { } else if (result instanceof SuccessResponse) {
sb.append("{\"success\":\"").append(((SuccessResponse)result).getSuccess()).append("\"}"); sb.append("{\"success\":\"").append(((SuccessResponse)result).getSuccess()).append("\"}");
log.append("{\"success\":\"").append(((SuccessResponse)result).getSuccess()).append("\"}");
} else if (result instanceof ExceptionResponse) { } else if (result instanceof ExceptionResponse) {
String jsonErrorText = gson.toJson(result); String jsonErrorText = responseBuilder.toJson(result);
jsonErrorText = unescape(jsonErrorText); jsonErrorText = unescape(jsonErrorText);
sb.append(jsonErrorText); sb.append(jsonErrorText);
log.append(jsonErrorText);
} else { } else {
String jsonStr = gson.toJson(result); String jsonStr = responseBuilder.toJson(result);
if ((jsonStr != null) && !"".equals(jsonStr)) { if (jsonStr != null && !jsonStr.isEmpty()) {
jsonStr = unescape(jsonStr); jsonStr = unescape(jsonStr);
if (result instanceof AsyncJobResponse || result instanceof CreateCmdResponse || result instanceof AuthenticationCmdResponse) { if (result instanceof AsyncJobResponse || result instanceof CreateCmdResponse || result instanceof AuthenticationCmdResponse) {
sb.append(jsonStr); sb.append(jsonStr);
@ -127,53 +151,74 @@ public class ApiResponseSerializer {
} else { } else {
sb.append("{}"); sb.append("{}");
} }
String logStr = logBuilder.toJson(result);
if (logStr != null && !logStr.isEmpty()) {
logStr = unescape(logStr);
if (result instanceof AsyncJobResponse || result instanceof CreateCmdResponse || result instanceof AuthenticationCmdResponse) {
log.append(logStr);
} else {
log.append("{\"").append(result.getObjectName()).append("\":").append(logStr).append("}");
}
} else {
log.append("{}");
}
} }
sb.append("}"); sb.append("}");
log.append("}");
return sb.toString(); return sb.toString();
} }
return null; return null;
} }
private static String toXMLSerializedString(ResponseObject result) { private static String toXMLSerializedString(ResponseObject result, StringBuilder log) {
if (result != null && log != null) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
sb.append("<").append(result.getResponseName()).append(" cloud-stack-version=\"").append(ApiDBUtils.getVersion()).append("\">"); sb.append("<").append(result.getResponseName()).append(" cloud-stack-version=\"").append(ApiDBUtils.getVersion()).append("\">");
log.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
log.append("<").append(result.getResponseName()).append(" cloud-stack-version=\"").append(ApiDBUtils.getVersion()).append("\">");
if (result instanceof ListResponse) { if (result instanceof ListResponse) {
Integer count = ((ListResponse)result).getCount(); Integer count = ((ListResponse)result).getCount();
if (count != null && count != 0) { if (count != null && count != 0) {
sb.append("<").append(ApiConstants.COUNT).append(">").append(((ListResponse)result).getCount()).append("</").append(ApiConstants.COUNT).append(">"); sb.append("<").append(ApiConstants.COUNT).append(">").append(((ListResponse)result).getCount()).append("</").append(ApiConstants.COUNT).append(">");
log.append("<").append(ApiConstants.COUNT).append(">").append(((ListResponse)result).getCount()).append("</").append(ApiConstants.COUNT).append(">");
} }
List<? extends ResponseObject> responses = ((ListResponse)result).getResponses(); List<? extends ResponseObject> responses = ((ListResponse)result).getResponses();
if ((responses != null) && !responses.isEmpty()) { if ((responses != null) && !responses.isEmpty()) {
for (ResponseObject obj : responses) { for (ResponseObject obj : responses) {
serializeResponseObjXML(sb, obj); serializeResponseObjXML(sb, log, obj);
} }
} }
} else { } else {
if (result instanceof CreateCmdResponse || result instanceof AsyncJobResponse || result instanceof AuthenticationCmdResponse) { if (result instanceof CreateCmdResponse || result instanceof AsyncJobResponse || result instanceof AuthenticationCmdResponse) {
serializeResponseObjFieldsXML(sb, result); serializeResponseObjFieldsXML(sb, log, result);
} else { } else {
serializeResponseObjXML(sb, result); serializeResponseObjXML(sb, log, result);
} }
} }
sb.append("</").append(result.getResponseName()).append(">"); sb.append("</").append(result.getResponseName()).append(">");
log.append("</").append(result.getResponseName()).append(">");
return sb.toString(); return sb.toString();
} }
return null;
}
private static void serializeResponseObjXML(StringBuilder sb, ResponseObject obj) { private static void serializeResponseObjXML(StringBuilder sb, StringBuilder log, ResponseObject obj) {
if (!(obj instanceof SuccessResponse) && !(obj instanceof ExceptionResponse)) { if (!(obj instanceof SuccessResponse) && !(obj instanceof ExceptionResponse)) {
sb.append("<").append(obj.getObjectName()).append(">"); sb.append("<").append(obj.getObjectName()).append(">");
log.append("<").append(obj.getObjectName()).append(">");
} }
serializeResponseObjFieldsXML(sb, obj); serializeResponseObjFieldsXML(sb, log, obj);
if (!(obj instanceof SuccessResponse) && !(obj instanceof ExceptionResponse)) { if (!(obj instanceof SuccessResponse) && !(obj instanceof ExceptionResponse)) {
sb.append("</").append(obj.getObjectName()).append(">"); sb.append("</").append(obj.getObjectName()).append(">");
log.append("</").append(obj.getObjectName()).append(">");
} }
} }
public static Field[] getFlattenFields(Class<?> clz) { private static Field[] getFlattenFields(Class<?> clz) {
List<Field> fields = new ArrayList<Field>(); List<Field> fields = new ArrayList<Field>();
fields.addAll(Arrays.asList(clz.getDeclaredFields())); fields.addAll(Arrays.asList(clz.getDeclaredFields()));
if (clz.getSuperclass() != null) { if (clz.getSuperclass() != null) {
@ -182,24 +227,23 @@ public class ApiResponseSerializer {
return fields.toArray(new Field[] {}); return fields.toArray(new Field[] {});
} }
private static void serializeResponseObjFieldsXML(StringBuilder sb, ResponseObject obj) { private static void serializeResponseObjFieldsXML(StringBuilder sb, StringBuilder log, ResponseObject obj) {
boolean isAsync = false; boolean isAsync = false;
if (obj instanceof AsyncJobResponse) if (obj instanceof AsyncJobResponse)
isAsync = true; isAsync = true;
//Field[] fields = obj.getClass().getDeclaredFields();
Field[] fields = getFlattenFields(obj.getClass()); Field[] fields = getFlattenFields(obj.getClass());
for (Field field : fields) { for (Field field : fields) {
if ((field.getModifiers() & Modifier.TRANSIENT) != 0) { if ((field.getModifiers() & Modifier.TRANSIENT) != 0) {
continue; // skip transient fields continue; // skip transient fields
} }
SerializedName serializedName = field.getAnnotation(SerializedName.class); SerializedName serializedName = field.getAnnotation(SerializedName.class);
if (serializedName == null) { if (serializedName == null) {
continue; // skip fields w/o serialized name continue; // skip fields w/o serialized name
} }
boolean logField = true;
Param param = field.getAnnotation(Param.class); Param param = field.getAnnotation(Param.class);
if (param != null) { if (param != null) {
RoleType[] allowedRoles = param.authorized(); RoleType[] allowedRoles = param.authorized();
@ -213,10 +257,13 @@ public class ApiResponseSerializer {
} }
} }
if (!permittedParameter) { if (!permittedParameter) {
s_logger.trace("Ignoring paremeter " + param.name() + " as the caller is not authorized to see it"); s_logger.trace("Ignoring parameter " + param.name() + " as the caller is not authorized to see it");
continue; continue;
} }
} }
if (param.isSensitive()) {
logField = false;
}
} }
field.setAccessible(true); field.setAccessible(true);
@ -233,10 +280,12 @@ public class ApiResponseSerializer {
ResponseObject subObj = (ResponseObject)fieldValue; ResponseObject subObj = (ResponseObject)fieldValue;
if (isAsync) { if (isAsync) {
sb.append("<jobresult>"); sb.append("<jobresult>");
log.append("<jobresult>");
} }
serializeResponseObjXML(sb, subObj); serializeResponseObjXML(sb, log, subObj);
if (isAsync) { if (isAsync) {
sb.append("</jobresult>"); sb.append("</jobresult>");
log.append("</jobresult>");
} }
} else if (fieldValue instanceof Collection<?>) { } else if (fieldValue instanceof Collection<?>) {
Collection<?> subResponseList = (Collection<?>)fieldValue; Collection<?> subResponseList = (Collection<?>)fieldValue;
@ -247,7 +296,7 @@ public class ApiResponseSerializer {
if (serializedName != null) { if (serializedName != null) {
subObj.setObjectName(serializedName.value()); subObj.setObjectName(serializedName.value());
} }
serializeResponseObjXML(sb, subObj); serializeResponseObjXML(sb, log, subObj);
} else if (value instanceof ExceptionProxyObject) { } else if (value instanceof ExceptionProxyObject) {
// Only exception reponses carry a list of // Only exception reponses carry a list of
// ExceptionProxyObject objects. // ExceptionProxyObject objects.
@ -256,30 +305,32 @@ public class ApiResponseSerializer {
// encountered, put in a uuidList tag. // encountered, put in a uuidList tag.
if (!usedUuidList) { if (!usedUuidList) {
sb.append("<" + serializedName.value() + ">"); sb.append("<" + serializedName.value() + ">");
log.append("<" + serializedName.value() + ">");
usedUuidList = true; usedUuidList = true;
} }
sb.append("<" + "uuid" + ">" + idProxy.getUuid() + "</" + "uuid" + ">"); sb.append("<" + "uuid" + ">" + idProxy.getUuid() + "</" + "uuid" + ">");
log.append("<" + "uuid" + ">" + idProxy.getUuid() + "</" + "uuid" + ">");
// Append the new descriptive property also. // Append the new descriptive property also.
String idFieldName = idProxy.getDescription(); String idFieldName = idProxy.getDescription();
if (idFieldName != null) { if (idFieldName != null) {
sb.append("<" + "uuidProperty" + ">" + idFieldName + "</" + "uuidProperty" + ">"); sb.append("<" + "uuidProperty" + ">" + idFieldName + "</" + "uuidProperty" + ">");
log.append("<" + "uuidProperty" + ">" + idFieldName + "</" + "uuidProperty" + ">");
} }
} else if (value instanceof String) { } else if (value instanceof String) {
sb.append("<").append(serializedName.value()).append(">").append(value).append("</").append(serializedName.value()).append(">"); sb.append("<").append(serializedName.value()).append(">").append(value).append("</").append(serializedName.value()).append(">");
if (logField) {
log.append("<").append(serializedName.value()).append(">").append(value).append("</").append(serializedName.value()).append(">");
}
} }
} }
if (usedUuidList) { if (usedUuidList) {
// close the uuidList. // close the uuidList.
sb.append("</").append(serializedName.value()).append(">"); sb.append("</").append(serializedName.value()).append(">");
log.append("</").append(serializedName.value()).append(">");
} }
} else if (fieldValue instanceof Date) { } else if (fieldValue instanceof Date) {
sb.append("<") sb.append("<").append(serializedName.value()).append(">").append(BaseCmd.getDateString((Date)fieldValue)).append("</").append(serializedName.value()).append(">");
.append(serializedName.value()) log.append("<").append(serializedName.value()).append(">").append(BaseCmd.getDateString((Date)fieldValue)).append("</").append(serializedName.value()).append(">");
.append(">")
.append(BaseCmd.getDateString((Date)fieldValue))
.append("</")
.append(serializedName.value())
.append(">");
} else { } else {
String resultString = escapeSpecialXmlChars(fieldValue.toString()); String resultString = escapeSpecialXmlChars(fieldValue.toString());
if (!(obj instanceof ExceptionResponse)) { if (!(obj instanceof ExceptionResponse)) {
@ -287,6 +338,9 @@ public class ApiResponseSerializer {
} }
sb.append("<").append(serializedName.value()).append(">").append(resultString).append("</").append(serializedName.value()).append(">"); sb.append("<").append(serializedName.value()).append(">").append(resultString).append("</").append(serializedName.value()).append(">");
if (logField) {
log.append("<").append(serializedName.value()).append(">").append(resultString).append("</").append(serializedName.value()).append(">");
}
} }
} }
} }