How to generate UUID per message in log4j's JSON Layout? - java

I need to generate a random string for a correlation key for each log message and I found that there is a way to generate UUID in log4j configuration file.
There is a mention of UUID on https://logging.apache.org/log4j/2.x/manual/layouts.html, but it doesn't say anything about how to use it.
I am trying to set this as a value for a key in my JsonLayout.
appender.rolling.layout.external-correlation-id.type = KeyValuePair
appender.rolling.layout.external-correlation-id.key = external-correlation-id
appender.rolling.layout.external-correlation-id.value = %u{"RANDOM"}
But that doesn't do anything. It just adds the literal string in the log message ... "external-correlation-id":"%u{\"RANDOM\"}" ....
How can I get a random string to set it in the log message? Is there a way for me to atleast directly call the UUID.randomUUID() in thelog4j properties file?
I don't want to use MDC for this and am looking for a way to do it directly from the log4j configuration file.
Any other help with this will be very appreciated.

I've done something similar in a JsonLayout in a KeyValuePair using a custom StrLookup.
The code below shows a simple example of returning a UUID every time a message is logged using a custom Lookup. One would add the ability to have different operations per key etc. but this works.
import java.util.UUID;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.lookup.StrLookup;
#Plugin(name = "dyn", category = "Lookup")
public class DynamicLookup implements StrLookup {
#Override
public String lookup(String key) {
if("uuid".equals(key)) {
return UUID.randomUUID().toString();
} else {
return null;
}
}
#Override
public String lookup(LogEvent event, String key) {
if("uuid".equals(key)) {
return UUID.randomUUID().toString();
} else {
return null;
}
}
}
Then reference the Lookup using double $$ to have the value evaluated every message instead of just once.
<KeyValuePair key="event_id" value="$${dyn:uuid:-}"/>

Related

Is there any alternate way to replace complex enum?

I have a complex enum class in my spring boot application which holds different status values for different systems.
package com.foo;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public enum Status {
FOO_STATUS("Status1" ,"status_1", "STATUS_1", "stat1"),
BAR_STATUS("Status2" ,"status_2", "STATUS_2", "stat2" ),
FOO1_STATUS("Status3" ,"status_3", "STATUS_3", "stat3" ),
BAR1_STATUS("Status4" ,"status_4", "STATUS_4", "stat4" ),
....
....
....
private final String system1Status;
private final String system2Status;
private final String system3Status;
private final String system4Status;
private static Map<String, String> statusMap;
Status(String system1Status, String system2Status, String system3Status, String system4Status) {
this.system1Status = system1Status;
this.system2Status = system2Status;
this.system3Status = system3Status;
this.system4Status = system4Status;
}
public String getSystem1Status() {
return system1Status;
}
public String getSystem2Status() {
return system2Status;
}
public String getSystem3Status() {
return system3Status;
}
public String getSystem4Status() {
return system4Status;
}
private static void initializeMapping() {
statusMap = new HashMap<>();
for (Status map : Status.values()) {
statusMap.put(map.getSystem1Status(), map.getSystem2Status());
}
}
public static String getSystem2StatusForSytem1Status(String status) {
if (statusMap == null) {
initializeMapping();
}
if (statusMap.containsKey(status)) {
return statusMap.get(status);
}
return null;
}
public static String getSystem3StatusForSytem1Status(String status) {
....
}
public static String getSystem4StatusForSytem2Status(String status) {
....
}
public static String getSystem3StatusForSytem2Status(String status) {
....
}
....
....
}
The enum holds status string mapping for various systems. It also has methods to get different system status by supplying the current system status.
Ex: We can get System1 status by sending the System 2 status value.
As the enum is getting more complex , is there any alternate way to hold this static data?
PS: I know this can be moved to a reference table in DB, But I am looking for any alternate within the code (like loading from yaml file).
The concern about the enum getting more and more complex is only valid if that complexity is accidental, not inherent. Otherwise, switching to a different approach would just move that complexity elsewhere (which kind of seems to be the case in your example). I think it makes sense to keep the enum (even if it grows complex) iif the following conditions are met:
There is no reasonable scenario in which you would want/need to account for new statuses or new mappings (or drop existing ones) without changing the code.
You rely on at least some enum features available out of the box, so you would have to reimplement those by hand. E.g. values() listed in a determinate order, valueOf() used with canonical String labels, ordinal() to infer position, compareTo(), name(), serialization, etc.
You use the enum constants polymorphically (and maybe you need to alter the behavior for some of them without a full-fledged class hierarchy) or you want to leverage the compiler check for exhaustive case branches in switch expressions (with newer java versions).

Validating input datatype

I am using below DTO class with respective annotations and are working fine also. But when I send a integer value for name/reqID(which is a String datatype) fields, still it is executing without any error/exception. How to avoid it or validate the datatype of incoming fields.
public class RequestDTO {
#NotEmpty(message = "Please provide reqID")
private String reqID;
#NotEmpty(message = "Please provide name")
private String name;
private Map <String, String> unknownProperties;
public AccountDTO(){
this.unknownProperties = new HashMap<String, String>();
}
public AccountDTO(String reqID, String name){
this.reqID= reqID;
this.name = name;
this.unknownProperties = new HashMap<String, String>();
}
#JsonAnySetter
public void add(String key, String value) {
this.unknownProperties.put(key, value);
}
#JsonAnyGetter
public Map <String, String> getUnknownProperties() {
return unknownProperties;
}
//getters and setters
}
working for { "reqID" : 56, "name" : 674 }. Have to check the datatype/reject the request. Any help would be appreciable.
If you're using Spring boot, by default it uses Jackson to parse JSON. There's no configuration option within Jackson to disable this feature
Here you will find interesting approaches to solving this problem:
Disable conversion of scalars to strings when deserializing with Jackson
You can disable MapperFeature ALLOW_COERCION_OF_SCALARS which is enabled by default.
Then conversions from JSON String are not allowed.
Doc Details here
public static final MapperFeature ALLOW_COERCION_OF_SCALARS
When feature is disabled, only strictly compatible input may be bound:
numbers for numbers, boolean values for booleans. When feature is
enabled, conversions from JSON String are allowed, as long as textual
value matches (for example, String "true" is allowed as equivalent of
JSON boolean token true; or String "1.0" for double).
Or create a custom json deserializer for string overriding default serializer JsonDeserializer<String>.
You could validate the input you are getting. But this is not specific to your DTO so if you have some sort of Utilities class with static methods (think about having one if you don't) it's better if you add it there and grab it for any DTO that might need this validation.
The validation method would look something like this:
public static boolean isNumber(String in) {
try{
Integer.parseInt(in);
// log something useful here
return true;
} catch(NumberFormatException e) {
return false;
}
}
You could then use this method throw your own exception. Then handle that the way you'd need:
if (Utilities.isNumber(reqID)){
throw new IllegalArgumentException("Meaningful Exception Message here");
}
I hope it helps! :)
Spring boot allows regular expression checking using #Patter annotation. So just add the following
#Pattern(regexp="[a-zA-Z]")
#NotEmpty(message = "Please provide name")
private String name;

How to protect Java variables and methods on server using REST

I'm developing desktop software with JavaFX and Java Spark which is basically a barebones framework for developing web apps, but I'm trying to use it strictly to put/get sensitive methods and variables on a server so that users can't access them. REST seems to be the correct approach but I'm struggling to understand 2 interrelated REST concepts. The Spark docs are light but I've integrated the few good tutorials into a working demo below but I've hit a wall. I'll briefly explain:
With the help of Postman I've been able to put a few records onto the server by using a path of http://localhost:4567/secrets and Body of:
{
"title" : "demofield1",
"content" : "12345"
}
Each record contains a title as an identifier (demofield1) and content as the sensitive data that should remain hidden from users at all times (12345). It's pretty trivial to put these strings onto a server and then get them by using title as a parameter, shown below. The demo code simply has a Model class for creating and returning a record (secret), a JSON conversion method, and a get and put Spark method. Secrets are stored locally in a HashMap for now, I'm assuming a real app would simply swap in a server DB.
The get method works as expected, returning the correct JSON record and storing the content as a String with this line: String secretString = model.getCertainSecret(title).getContent();
With that said...
Questions (partial answers fully appreciated too):
secretString above now holds a confidential value (12345) which is obtained using a supposedly secure REST method. But couldn't a user simply reverse-engineer my source code and write System.out.println(secretString) and have that 12345 revealed? I don't understand how a simple string is protected after retrieving it from the server, despite not being explicitly shown. The code seems correct yet the value is easily obtainable. What am I missing?
How do you put entire java methods on a server? A lot of code I need to protect isn't just strings but methods containing Tasks, Platform.runLater()->, and needs to interact with other desktop software. For example, one of my methods uses JACOB to identify when a certain area of a third-party software is clicked. I can't even fathom what a get/put would look like in that context.
My assumption was that a server-side DB would store all content from my put requests, but I don't understand how it stores and returns a method? Should I be reading about servlets or SaaS or something? I'm focused on desktop users.
Code:
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import lombok.Data;
import org.apache.log4j.BasicConfigurator;
import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import static spark.Spark.get;
import static spark.Spark.put;
public class ServerDemo
{
private static final int HTTP_BAD_REQUEST = 400;
#Data
static class NewSecretPayload {
private String title;
private String content;
public boolean isValid() {
return title != null && !title.isEmpty();
}
}
public static class Model {
private int nextId = 1;
private Map<String, Secret> secrets = new HashMap<>();
#Data
class Secret {
private int id;
private String title;
private String content;
}
public int createSecret(String title, String content){
int id = nextId++;
Secret secret = new Secret();
secret.setId(id);
secret.setTitle(title);
secret.setContent(content);
secrets.put(title, secret);
return id;
}
public Secret getCertainSecret(String titleToUse){
if(null != secrets.get(titleToUse)){
return secrets.get(titleToUse);
}else{
return null;
}
}
}
public static String dataToJson(Object data) {
try {
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
StringWriter sw = new StringWriter();
mapper.writeValue(sw, data);
return sw.toString();
} catch (IOException e) {
throw new RuntimeException("IOException from a StringWriter?");
}
}
public static void main(String[] args) {
Model model = new Model();
BasicConfigurator.configure();
put("/secrets", (request, response) -> {
try {
ObjectMapper mapper = new ObjectMapper();
NewSecretPayload creation = mapper.readValue(request.body(), NewSecretPayload.class);
if (!creation.isValid()) {
response.status(HTTP_BAD_REQUEST);
return "";
}
int id = model.createSecret(creation.getTitle(), creation.getContent());
response.status(200);
response.type("application/json");
return id;
} catch (JsonParseException jpe) {
response.status(HTTP_BAD_REQUEST);
return "";
}
});
get("/secrets/:title", (req, res) -> {
String title = req.params(":title");
if (model.getCertainSecret(title) != null) {
res.status(200);
res.type("application/json");
String secretString = model.getCertainSecret(title).getContent();
return dataToJson(model.getCertainSecret(title));
}
res.status(400);
return new ResponseError("No user with title "+title+" was found", title);
});
}
}
Lets dig down to your first problem "Keeping string secret" :--
Restrict : The simplest way is not to provide the data to malicious user.
Masking : Mask the data you are providing to end user. You will have the original data mapped to masked data. You will provide masked data to end user. Here the end user can never get the original data as it is a one way process. When end user sends masked-data you can always retrieve the original data from it.
Encrypting : If the end user needs to see the data you can encrypt it and send it. You can make a sanity check of your code before decrypting the data. The sanity check can give you idea if the code is ever modified. If code fails the sanity check you can always exit the process.

Best way to access properties from property file in java

I have properties file app.properties and it has 50 different properties.
I am accessing those in my java class using
Properties prop = new Properties();
prop.load("app.properties");
System.out.prinltn(prop.getProperty("APPNAME"));
Actually, I want to get rid of accessing property like prop.getProperty("APPNAME"). Is there any best way in java to access properties.
I can declare all variables as static in java class.
static String appName = prop.getProperty("APPNAME");
Any other best way available?
I can suggest two approaches:
1. Define a utility method which will take String as parameter and return value from properties.
For Example:
public static String GetValue(String key) {
return properties.getProperty(key);
}
And now you can use this function on callers
String value = GetValue("key"); // properties.getProperty("key");
Define above method and in addition create one class Called Constants(or something suitable). Define all your Keys here as Static final variable.
public class Constants
{
public static final String KEY = "key";
public static final String KEY2 = "key2";
}
and now make call for getting value using these variable instead of string:
String value = GetValue(KEY); //GetValue("key");
If you do only option 1, your code is becoming more readable. But I will recommend 2nd option, which is making your code readable as well as maintainable.
You can easily do following operation :
Update property name
No need to worry about mistyping key etc.
You may use "resourceBundle" package as
First import the resourceBundle API:
import java.util.ResourceBundle;
Create an instance of your property file:
private static ResourceBundle resource = ResourceBundle.getBundle("app");
Now you can get the value of the property:
String appName = resource.getString("APPNAME");
IMO, your approach of using static variables to hold the values is the best. The following structure was what I was using in a project for the same functionality.
package snippet;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
public class Constants {
public static final String APPNAME;
public static final String VERSION;
public static final int DEFAULT_TIMEOUT;
static {
Properties p = new Properties();
try {
p.load(new FileInputStream("constants.properties"));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
APPNAME = p.getProperty("APPNAME");
VERSION = p.getProperty("VERSION");
DEFAULT_TIMEOUT = Integer.parseInt(p.getProperty("DEFAULT_TIMEOUT"));
}
}
Of course, there were checks for NumberFormatException etc.

QueryStringBindable for a custom enum

I've defined an enum type Format that implements QueryStringBindable. I think I've implemented it correctly, but in my routes file, I can't specify my type as a route parameter, because the compiler can't find it, and I have no idea how to import it into the routes file.
Here's the enum:
package web;
import java.util.Map;
import play.libs.F;
import play.mvc.QueryStringBindable;
public enum Format implements QueryStringBindable<Format> {
Html,
Pdf,
Csv;
private Format value;
#Override
public F.Option<Format> bind(String key, Map<String, String[]> data) {
String[] vs = data.get(key);
if (vs != null && vs.length > 0) {
String v = vs[0];
value = Enum.valueOf(Format.class, v);
return F.Option.Some(value);
}
return F.Option.None();
}
#Override
public String unbind(String key) {
return key + "=" + value;
}
#Override
public String javascriptUnbind() {
return value.toString();
}
}
And here's my route:
GET /deposits controllers.Deposits.index(selectedAccountKey: Long ?= 0, format: Format ?= Format.Html)
How can I tell the compiler about my enum? Thanks!
Edit
I've also tried adding the path to the type in Build.scala as has been recommended in other posts:
val main = PlayProject(appName, appVersion, appDependencies, mainLang = JAVA).settings(
routesImport += "web.Format",
resolvers += Resolver.url("My GitHub Play Repository", url("http://www.joergviola.de/releases/"))(Resolver.ivyStylePatterns)
)
I changed that and restarted my server, but it appears to make no difference whatsoever.
I had the same problem and I finally found out that it is not solvable as is.
By reading the documentation for PathBindable and QueryStringBindable I found that play framework requires the Bindable to provide a No Argument public constructor. Which by definition is no possible with enum in Java.
I'd like to offer you the same solution I gave another (more recent) question.
I just wrapped the enum into a small Wrapper class that implements QueryStringBindable or PathBindable.
play framework - bind enum in routes
Use qualified name in the routes file, i.e. web.Format

Categories