/*
* jPOS Project [http://jpos.org]
* Copyright (C) 2000-2017 jPOS Software SRL
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.jpos.rest;
import com.fasterxml.jackson.databind.JsonNode;
import com.github.fge.jackson.JsonLoader;
import com.github.fge.jsonschema.core.exceptions.ProcessingException;
import com.github.fge.jsonschema.core.report.ProcessingReport;
import com.github.fge.jsonschema.main.JsonSchema;
import com.github.fge.jsonschema.main.JsonSchemaFactory;
import org.jdom2.Element;
import org.jpos.core.ConfigurationException;
import org.jpos.core.XmlConfigurable;
import org.jpos.ee.BLException;
import org.jpos.transaction.Context;
import org.jpos.transaction.TxnSupport;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Validate mandatory and optional parameters. Support JsonSchema.
*
* @author jr@jpos.org
*
* Sample usage:
*
* <participant class="org.jpos.jcard.rest.ValidateParams" logger="Q2" realm="validate-params">
* <mandatory>
* <param name="version">"^1\.0$"</param>
* <param name="consumer">"^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$</param>
* </mandatory>
* </participant>
*
*
* <participant class="org.jpos.jcard.rest.ValidateParams" logger="Q2" realm="validate-params">
* <mandatory>
* <param name="JSON_REQUEST" type="json-schema">
* {
* "$schema": "http://json-schema.org/draft-04/schema#",
* "title": "SysConfig",
* "description": "A SysConfig to post",
* "type": "object",
* "properties": {
* "id": {
* "type": "string"
* },
* "value": {
* "type": "string"
* },
* "readPerm": {
* "type" : "string"
* },
* "writePerm": {
* "type": "string"
* }
* },
* "additionalProperties" : false,
* "required": [ "id","value","readPerm","writePerm"]
* }
* </param>
* </mandatory>
* </participant>
*
*/
@SuppressWarnings("unused")
public class ValidateParams extends TxnSupport implements XmlConfigurable {
Map<String,Pattern> mandatory;
Map<String,Pattern> optional;
Map<String,JsonSchema> mandatoryJson;
Map<String,JsonSchema> optionalJson;
@Override
protected int doPrepare(long id, Context ctx) throws BLException {
checkMandatory(ctx);
checkOptional(ctx);
checkMandatoryJson(ctx);
checkOptionalJson(ctx);
return PREPARED | NO_JOIN | READONLY;
}
@Override
public void commit(long id, Serializable context) { }
@Override
public void abort(long id, Serializable context) { }
@SuppressWarnings("unchecked")
@Override
public void setConfiguration(Element cfg) throws ConfigurationException{
mandatory = new HashMap<>();
mandatoryJson = new HashMap<>();
Element m = cfg.getChild("mandatory");
if (m != null) {
for (Element e : (List<Element>) m.getChildren("param")) {
if ("json-schema".equals(e.getAttributeValue("type"))) {
try {
String json = e.getValue();
JsonNode nodeSchema = JsonLoader.fromString(json);
JsonSchema schema = JsonSchemaFactory.byDefault().getJsonSchema(nodeSchema);
mandatoryJson.put(e.getAttributeValue("name"), schema);
} catch (Exception ex) {
//todo: change this
ex.printStackTrace();
}
} else {
mandatory.put(e.getAttributeValue("name"), Pattern.compile(e.getValue()));
}
}
}
optional = new HashMap<>();
optionalJson = new HashMap<>();
Element o = cfg.getChild("optional");
if (o != null) {
for (Element e : (List<Element>) o.getChildren("param")) {
if ("json-schema".equals(e.getAttributeValue("type"))) {
String json = e.getValue();
JsonNode nodeSchema;
try {
nodeSchema = JsonLoader.fromString(json);
JsonSchema schema = JsonSchemaFactory.byDefault().getJsonSchema(nodeSchema);
optionalJson.put(e.getAttributeValue("name"), schema);
} catch (IOException | ProcessingException ex) {
throw new ConfigurationException(ex);
}
} else {
optional.put(e.getAttributeValue("name"), Pattern.compile(e.getValue()));
}
}
}
}
private void checkMandatory (Context ctx) throws BLException {
for (Map.Entry<String,Pattern> entry : mandatory.entrySet()) {
Object v = ctx.get(entry.getKey());
String value = v != null ? v.toString() : null;
assertNotNull (value, "Mandatory param " + entry.getKey().toLowerCase() + " not present");
Pattern p = entry.getValue();
Matcher m = p.matcher(value);
assertTrue (m.matches(), "Invalid param " + entry.getKey().toLowerCase());
}
}
private void checkOptional (Context ctx) throws BLException {
for (Map.Entry<String,Pattern> entry : optional.entrySet()) {
String value = ctx.getString(entry.getKey());
if (value != null) {
Pattern p = entry.getValue();
Matcher m = p.matcher(value);
assertTrue (m.matches(), "Invalid param " + entry.getKey().toLowerCase());
}
}
}
private void checkMandatoryJson (Context ctx) throws BLException {
for (Map.Entry<String,JsonSchema> entry : mandatoryJson.entrySet()) {
String value = ctx.getString(entry.getKey());
ProcessingReport report;
if (value != null) {
try {
JsonSchema schema = entry.getValue();
JsonNode node = JsonLoader.fromString(value);
report = schema.validate(node);
} catch(Exception ex) {
throw new BLException(ex.getMessage());
}
assertTrue(report.isSuccess(), report.toString());
}
}
}
private void checkOptionalJson (Context ctx) throws BLException {
for (Map.Entry<String,JsonSchema> entry : optionalJson.entrySet()) {
String value = ctx.getString(entry.getKey());
ProcessingReport report;
if (value != null) {
try {
JsonSchema schema = entry.getValue();
JsonNode node = JsonLoader.fromString(value);
report = schema.validate(node);
} catch(Exception ex) {
throw new BLException(ex.getMessage());
}
assertTrue(report.isSuccess(), report.toString());
}
}
}
}