Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

References

2018-10-03 AAI Meeting Notes - open for agenda items

2018-09-26 AAI Meeting Notes

Jira Legacy
serverSystem Jira
serverId4733707d-2057-3a0f-ae5e-4fd8aff50176
keyAAI-628

Jackson Replacement

Security subcommittee has recommended teams move away from jackson, and will be presenting alternatives and asking for an assessment from each project. Our team will need to do an analysis - this would not be trivial, especially given how many of our repos are impacted. As of now, this would be a very high LOE for the team, we need to understand what the recommendation from the SECCOM is before we can provide better details on what the LOE would be.


Code Analysis

Search on AAI source code shows:

  • approx 661 hits in 227 files for "fasterxml", which includes pom.xml and Java imports
  • approx 978 hits in 215 files for "gson", which includes pom.xml and Java imports and initialising Java object

Attachments
previewfalse
uploadfalse
patterns.*txt

Code examples from:

  • aai\aai-common\aai-auth\src\main\java\org\onap\aaiauth\auth\AuthCore.java
  • aai\aai-common\aai-core\src\main\java\org\onap\aai\auth\AAIAuthCore.java
Code Block
languagejava
titleFasterXML Jackson example
linenumberstrue
collapsetrue
    public synchronized void loadUsers(String authFilename) throws Exception {
        users = new HashMap<>();

        mapper = new ObjectMapper(); // can reuse, share globally
        JsonNode rootNode = mapper.readTree(new File(authFilename));
        JsonNode rolesNode = rootNode.path(AuthConstants.ROLES_NODE_PATH);

        for (JsonNode roleNode : rolesNode) {
            String roleName = roleNode.path(AuthConstants.ROLE_NAME_PATH).asText();

            AuthRole role = new AuthRole();
            JsonNode usersNode = roleNode.path(AuthConstants.USERS_NODE_PATH);
            JsonNode functionsNode = roleNode.path(AuthConstants.FUNCTIONS_NODE_PATH);
            for (JsonNode functionNode : functionsNode) {
                String function = functionNode.path(AuthConstants.FUNCTION_NAME_PATH).asText();
                JsonNode methodsNode = functionNode.path(AuthConstants.METHODS_NODE_PATH);
                boolean hasMethods = handleMethodNode(methodsNode, role, function);

                if (!hasMethods) {
                    // iterate the list from HTTP_METHODS
                    for (HTTP_METHODS meth : HTTP_METHODS.values()) {
                        String thisFunction = meth.toString() + ":" + function;

                        role.addAllowedFunction(thisFunction);
                    }
                }
            }

            handleUserNode(usersNode, roleName, role);
        }

        usersInitialized = true;
    }
Code Block
languagejava
titlegson example
linenumberstrue
collapsetrue
	private synchronized void reloadUsers() {

		Map<String, AAIUser> tempUsers = new HashMap<>();

		try {
			LOGGER.debug("Reading from " + globalAuthFileName);
			String authFile = new String(Files.readAllBytes(Paths.get(globalAuthFileName)));
			
			JsonParser parser = new JsonParser();
			JsonObject authObject = parser.parse(authFile).getAsJsonObject();
			if (authObject.has("roles")) {
				JsonArray roles = authObject.getAsJsonArray("roles");
				for (JsonElement role : roles) {
					if (role.isJsonObject()) {
						JsonObject roleObject = role.getAsJsonObject();
						String roleName = roleObject.get("name").getAsString();
						Map<String, Boolean> usrs = this.getUsernamesFromRole(roleObject);
						List<String> aaiFunctions = this.getAAIFunctions(roleObject);
						
						usrs.forEach((key, value) -> {
							final AAIUser au = tempUsers.getOrDefault(key, new AAIUser(key, value));
							au.addRole(roleName);
								aaiFunctions.forEach(f -> {
								List<String> httpMethods = this.getRoleHttpMethods(f, roleObject);
								httpMethods.forEach(hm -> au.setUserAccess(f, hm));
								this.validFunctions.add(f);
							});
								
							tempUsers.put(key, au);
							
						});
					}
				}
				if (!tempUsers.isEmpty()) {
					users = tempUsers;
				}	
			}
		} catch (FileNotFoundException e) {
			ErrorLogHelper.logError("AAI_4001", globalAuthFileName + ". Exception: " + e);
		} catch (JsonProcessingException e) {
			ErrorLogHelper.logError("AAI_4001", globalAuthFileName + ". Not valid JSON: " + e);
		} catch (Exception e) {
			ErrorLogHelper.logError("AAI_4001", globalAuthFileName + ". Exception caught: " + e);
		}
	}

Side-by-side comparison

FasterXML Jackson versionGoogle gson versionComments
Code Block
mapper = new ObjectMapper();
Code Block
JsonParser parser = new JsonParser();

Code Block
JsonNode rootNode = mapper.readTree(new File(authFilename));
JsonNode rolesNode = rootNode.path(AuthConstants.ROLES_NODE_PATH);
Code Block
JsonObject authObject = parser.parse(authFile).getAsJsonObject();
JsonArray roles = authObject.getAsJsonArray("roles");

Code Block
String function = functionNode.path(AuthConstants.FUNCTION_NAME_PATH).asText();
Code Block
String roleName = roleObject.get("name").getAsString();
Code structure differs at this point (function name vs role name) but the general intent of the code is equivalent (get the element name as a string).
Code Block
(no exception handling in this method)
Code Block
} catch (JsonProcessingException e) {
			ErrorLogHelper.logError("AAI_4001", globalAuthFileName + ". Not valid JSON: " + e);
For some reason, this version still catches com.fasterxml.jackson.core.JsonProcessingException even though it uses Google gson for parsing.


Suggestions

Article https://blog.takipi.com/the-ultimate-json-library-json-simple-vs-gson-vs-jackson-vs-json/

links to benchmark in comments: https://github.com/fabienrenaud/java-json-benchmark

which links to about 20 libraries as options to be explored, including:

Quick CVE comparison: