Skip to content
This repository was archived by the owner on May 13, 2022. It is now read-only.

Commit 0a5e92a

Browse files
committed
Merge pull request #11 from martinq/master
Added "replaces" property and pre-bencode normalization
2 parents 212bdd2 + 992dafa commit 0a5e92a

File tree

4 files changed

+148
-82
lines changed

4 files changed

+148
-82
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<groupId>com.navnorth.learningregistry</groupId>
55
<artifactId>LRJavaLib</artifactId>
66
<packaging>jar</packaging>
7-
<version>0.1.2</version>
7+
<version>0.1.3-BKS</version>
88
<name>LRJavaLib</name>
99
<url>http://github.com/navnorth/LRJavaLib</url>
1010

src/com/navnorth/learningregistry/LREnvelope.java

Lines changed: 19 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515

1616
import com.navnorth.learningregistry.util.MapUtil;
1717

18-
import java.util.Map;
1918
import java.util.Map;
2019
import java.util.HashMap;
20+
import java.util.LinkedHashMap;
2121

2222
/**
2323
* Envelope for data to export to a learning registry node
@@ -34,9 +34,8 @@
3434
*/
3535
public abstract class LREnvelope
3636
{
37-
private static final String docVersion = "0.23.0";
37+
private static final String docVersion = "0.49.0";
3838
private static final String docType = "resource_data";
39-
private static final String active = "true";
4039

4140
private static final String docTypeField = "doc_type";
4241
private static final String docVersionField = "doc_version";
@@ -57,6 +56,7 @@ public abstract class LREnvelope
5756
private static final String payloadSchemaLocatorField = "payload_schema_locator";
5857
private static final String keysField = "keys";
5958
private static final String resourceDataField = "resource_data";
59+
private static final String replacesField = "replaces";
6060

6161
private static final String keyLocationField = "key_location";
6262
private static final String signingMethodField = "signing_method";
@@ -65,10 +65,14 @@ public abstract class LREnvelope
6565

6666
private static final String submitterTTLField = "submitter_TTL";
6767

68+
private static final String[] excludedFields = {"digital_signature", "publishing_node", "update_timestamp", "node_timestamp", "create_timestamp", "doc_ID", "_id", "_rev"};
69+
6870
protected String resourceLocator;
6971
protected String resourceDataType;
7072
protected Object resourceData;
7173

74+
protected String[] replaces;
75+
7276
protected String payloadPlacement;
7377
protected String payloadSchemaLocator;
7478
protected String[] payloadSchema;
@@ -99,31 +103,12 @@ public abstract class LREnvelope
99103
*/
100104
protected Map<String, Object> getSignableData()
101105
{
102-
Map<String, Object> doc = new HashMap<String, Object>();
103-
104-
MapUtil.put(doc, docTypeField, docType);
105-
MapUtil.put(doc, docVersionField, docVersion);
106-
MapUtil.put(doc, activeField, active);
107-
MapUtil.put(doc, resourceDataTypeField, resourceDataType);
108-
Map<String, Object> docId = new HashMap<String, Object>();
109-
MapUtil.put(docId, submitterTypeField, submitterType);
110-
MapUtil.put(docId, submitterField, submitter);
111-
MapUtil.put(docId, curatorField, curator);
112-
MapUtil.put(docId, ownerField, owner);
113-
MapUtil.put(docId, signerField, signer);
114-
MapUtil.put(doc, identityField, docId);
115-
MapUtil.put(doc, submitterTTLField, submitterTTL);
116-
Map<String, Object> docTOS = new HashMap<String, Object>();
117-
MapUtil.put(docTOS, submissionTOSField, submissionTOS);
118-
MapUtil.put(docTOS, submissionAttributionField, submissionAttribution);
119-
MapUtil.put(doc, TOSField, docTOS);
120-
MapUtil.put(doc, resourceLocatorField, resourceLocator);
121-
MapUtil.put(doc, payloadPlacementField, payloadPlacement);
122-
MapUtil.put(doc, payloadSchemaField, payloadSchema);
123-
MapUtil.put(doc, payloadSchemaLocatorField, payloadSchemaLocator);
124-
MapUtil.put(doc, keysField, tags);
125-
MapUtil.put(doc, resourceDataField, getResourceData());
126-
106+
final Map<String, Object> doc = getSendableData();
107+
108+
// remove node-specific data
109+
for (int i = 0; i < excludedFields.length; i++) {
110+
doc.remove(excludedFields[i]);
111+
}
127112
return doc;
128113
}
129114

@@ -134,7 +119,7 @@ protected Map<String, Object> getSignableData()
134119
*/
135120
protected Map<String, Object> getSendableData()
136121
{
137-
Map<String, Object> doc = new HashMap<String, Object>();
122+
Map<String, Object> doc = new LinkedHashMap<String, Object>();
138123

139124
MapUtil.put(doc, docTypeField, docType);
140125
MapUtil.put(doc, docVersionField, docVersion);
@@ -158,15 +143,16 @@ protected Map<String, Object> getSendableData()
158143
MapUtil.put(doc, payloadSchemaLocatorField, payloadSchemaLocator);
159144
MapUtil.put(doc, keysField, tags);
160145
MapUtil.put(doc, resourceDataField, getResourceData());
146+
MapUtil.put(doc, replacesField, replaces);
161147

162148
if (signed)
163149
{
164150
Map<String, Object> sig = new HashMap<String, Object>();
165151
String[] keys = {publicKeyLocation};
166-
MapUtil.put(sig, "key_location", keys);
167-
MapUtil.put(sig, "signing_method", signingMethod);
168-
MapUtil.put(sig, "signature", clearSignedMessage);
169-
MapUtil.put(doc, "digital_signature", sig);
152+
MapUtil.put(sig, keyLocationField, keys);
153+
MapUtil.put(sig, signingMethodField, signingMethod);
154+
MapUtil.put(sig, signatureField, clearSignedMessage);
155+
MapUtil.put(doc, digitalSignatureField, sig);
170156
}
171157

172158
return doc;

src/com/navnorth/learningregistry/LRJSONDocument.java

Lines changed: 64 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
public class LRJSONDocument extends LREnvelope
3737
{
3838
/**
39-
* Create a new simple documet with specified details
39+
* Create a new simple document with specified details
4040
*
4141
* @param resourceData value for "resource_data"
4242
* @param resourceDataType value for "resource_data_type"
@@ -57,32 +57,13 @@ public LRJSONDocument(JSONObject resourceData, String resourceDataType, String r
5757
String payloadPlacement, String payloadSchemaLocator, String[] payloadSchema,
5858
String submitter, String submitterType, String submissionTOS, String submissionAttribution, String signer) throws LRException
5959
{
60-
try
61-
{
62-
this.resourceData = new ObjectMapper().readValue(resourceData.toString(), HashMap.class);
63-
}
64-
catch (IOException e)
65-
{
66-
throw new LRException(LRException.INVALID_JSON);
67-
}
68-
69-
this.resourceDataType = StringUtil.nullifyBadInput(resourceDataType);
70-
this.resourceLocator = StringUtil.nullifyBadInput(resourceLocator);
71-
this.curator = StringUtil.nullifyBadInput(curator);
72-
this.owner = StringUtil.nullifyBadInput(owner);
73-
this.tags = StringUtil.removeDuplicates(tags);
74-
this.payloadPlacement = StringUtil.nullifyBadInput(payloadPlacement);
75-
this.payloadSchemaLocator = StringUtil.nullifyBadInput(payloadSchemaLocator);
76-
this.payloadSchema = StringUtil.nullifyBadInput(payloadSchema);
77-
this.submissionTOS = StringUtil.nullifyBadInput(submissionTOS);
78-
this.submissionAttribution = StringUtil.nullifyBadInput(submissionAttribution);
79-
this.submitterType = StringUtil.nullifyBadInput(submitterType);
80-
this.submitter = StringUtil.nullifyBadInput(submitter);
81-
this.signer = StringUtil.nullifyBadInput(signer);
60+
initProperties(this, resourceData, resourceDataType, resourceLocator, curator, owner, tags, payloadPlacement,
61+
payloadSchemaLocator, payloadSchema, submitter, submitterType, submissionTOS,
62+
submissionAttribution, signer, null);
8263
}
8364

8465
/**
85-
* Create a new simple documet with specified details
66+
* Create a new simple document with specified details
8667
*
8768
* @param resourceData value for "resource_data"
8869
* @param resourceDataType value for "resource_data_type"
@@ -106,30 +87,72 @@ public LRJSONDocument(String resourceData, String resourceDataType, String resou
10687
try
10788
{
10889
JSONObject resourceJSON = new JSONObject(resourceData);
109-
this.resourceData = new ObjectMapper().readValue(resourceJSON.toString(), HashMap.class);
90+
initProperties(this, resourceJSON, resourceDataType, resourceLocator, curator, owner, tags, payloadPlacement,
91+
payloadSchemaLocator, payloadSchema, submitter, submitterType, submissionTOS,
92+
submissionAttribution, signer, null);
11093
}
111-
catch (IOException e)
94+
catch (JSONException e)
11295
{
11396
throw new LRException(LRException.INVALID_JSON);
11497
}
115-
catch (JSONException e)
98+
}
99+
100+
/**
101+
* Create a new simple document with specified details
102+
*
103+
* @param resourceData value for "resource_data"
104+
* @param resourceDataType value for "resource_data_type"
105+
* @param resourceLocator value for "resource_locator"
106+
* @param curator value for "curator"
107+
* @param owner value for "owner"
108+
* @param tags value for "keys"
109+
* @param payloadPlacement value for "payload_placement"
110+
* @param payloadSchemaLocator value for "payload_schema_locator"
111+
* @param payloadSchema value for "payload_schema"
112+
* @param submitter value for "submitter"
113+
* @param submitterType value for "submitter_type"
114+
* @param submissionTOS value for "submission_TOS"
115+
* @param submissionAttribution value for "submission_attribution"
116+
* @param signer value for "signer"
117+
* @param replaces array of document IDs to be replaced by this document
118+
*/
119+
public LRJSONDocument(JSONObject resourceData, String resourceDataType, String resourceLocator, String curator, String owner, String[] tags,
120+
String payloadPlacement, String payloadSchemaLocator, String[] payloadSchema,
121+
String submitter, String submitterType, String submissionTOS, String submissionAttribution, String signer,
122+
String[] replaces) throws LRException
123+
{
124+
initProperties(this, resourceData, resourceDataType, resourceLocator, curator, owner, tags, payloadPlacement,
125+
payloadSchemaLocator, payloadSchema, submitter, submitterType, submissionTOS,
126+
submissionAttribution, signer, replaces);
127+
}
128+
129+
protected void initProperties(LRJSONDocument document, JSONObject resourceData, String resourceDataType, String resourceLocator, String curator, String owner, String[] tags,
130+
String payloadPlacement, String payloadSchemaLocator, String[] payloadSchema,
131+
String submitter, String submitterType, String submissionTOS, String submissionAttribution, String signer,
132+
String[] replaces) throws LRException
133+
{
134+
try
135+
{
136+
document.resourceData = new ObjectMapper().readValue(resourceData.toString(), HashMap.class);
137+
document.resourceDataType = StringUtil.nullifyBadInput(resourceDataType);
138+
document.resourceLocator = StringUtil.nullifyBadInput(resourceLocator);
139+
document.curator = StringUtil.nullifyBadInput(curator);
140+
document.owner = StringUtil.nullifyBadInput(owner);
141+
document.tags = StringUtil.removeDuplicates(tags);
142+
document.payloadPlacement = StringUtil.nullifyBadInput(payloadPlacement);
143+
document.payloadSchemaLocator = StringUtil.nullifyBadInput(payloadSchemaLocator);
144+
document.payloadSchema = StringUtil.nullifyBadInput(payloadSchema);
145+
document.submissionTOS = StringUtil.nullifyBadInput(submissionTOS);
146+
document.submissionAttribution = StringUtil.nullifyBadInput(submissionAttribution);
147+
document.submitterType = StringUtil.nullifyBadInput(submitterType);
148+
document.submitter = StringUtil.nullifyBadInput(submitter);
149+
document.signer = StringUtil.nullifyBadInput(signer);
150+
document.replaces = StringUtil.removeDuplicates(replaces);
151+
}
152+
catch (IOException e)
116153
{
117154
throw new LRException(LRException.INVALID_JSON);
118155
}
119-
120-
this.resourceDataType = StringUtil.nullifyBadInput(resourceDataType);
121-
this.resourceLocator = StringUtil.nullifyBadInput(resourceLocator);
122-
this.curator = StringUtil.nullifyBadInput(curator);
123-
this.owner = StringUtil.nullifyBadInput(owner);
124-
this.tags = StringUtil.removeDuplicates(tags);
125-
this.payloadPlacement = StringUtil.nullifyBadInput(payloadPlacement);
126-
this.payloadSchemaLocator = StringUtil.nullifyBadInput(payloadSchemaLocator);
127-
this.payloadSchema = StringUtil.nullifyBadInput(payloadSchema);
128-
this.submissionTOS = StringUtil.nullifyBadInput(submissionTOS);
129-
this.submissionAttribution = StringUtil.nullifyBadInput(submissionAttribution);
130-
this.submitterType = StringUtil.nullifyBadInput(submitterType);
131-
this.submitter = StringUtil.nullifyBadInput(submitter);
132-
this.signer = StringUtil.nullifyBadInput(signer);
133156
}
134157

135158
public Object getResourceData()

src/com/navnorth/learningregistry/LRSigner.java

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515

1616
import com.navnorth.learningregistry.util.StringUtil;
1717

18+
import java.util.ArrayList;
19+
import java.util.LinkedHashMap;
20+
import java.util.List;
1821
import java.util.Map;
1922

2023
import java.io.InputStream;
@@ -25,11 +28,7 @@
2528
import java.io.File;
2629

2730
import java.security.MessageDigest;
28-
import java.security.NoSuchAlgorithmException;
29-
import java.security.NoSuchProviderException;
30-
import java.security.Provider;
3131
import java.security.Security;
32-
import java.security.SignatureException;
3332

3433
import org.bouncycastle.jce.provider.BouncyCastleProvider;
3534
import org.bouncycastle.bcpg.ArmoredOutputStream;
@@ -57,6 +56,7 @@ public class LRSigner
5756
{
5857
private static final String pgpRegex = "(?s).*-----BEGIN PGP PRIVATE KEY BLOCK-----.*-----END PGP PRIVATE KEY BLOCK-----.*";
5958
private static final String signingMethod = "LR-PGP.1.0";
59+
private static final String nullLiteral = "null";
6060

6161
private String publicKeyLocation;
6262
private String privateKey;
@@ -122,6 +122,61 @@ public LRActivity sign(LRActivity envelope) throws LRException
122122
return envelope;
123123
}
124124

125+
/**
126+
* Normalizes document as LRSignature Python module does
127+
* - nulls converted to string literal "null"
128+
* - booleans converted to string literals "true" or "false"
129+
* - numeric values in lists are dropped
130+
* - nested maps/JSON documents are normalized
131+
* @param doc Document to normalize
132+
*/
133+
private Map<String, Object> normalizeMap(Map<String, Object> doc) {
134+
135+
final Map<String, Object> result = new LinkedHashMap<String, Object>();
136+
137+
for (String key : doc.keySet()) {
138+
Object value = doc.get(key);
139+
140+
if (value == null) {
141+
result.put(key, nullLiteral);
142+
} else if (value instanceof Boolean) {
143+
result.put(key, ((Boolean) value).toString());
144+
} else if (value instanceof List<?>) {
145+
result.put(key, normalizeList((List<Object>) value));
146+
} else if (value instanceof Map<?, ?>) {
147+
result.put(key, normalizeMap((Map<String, Object>) value));
148+
} else {
149+
result.put(key, value);
150+
}
151+
}
152+
153+
return result;
154+
}
155+
156+
/**
157+
* Helper for map normalization; inspects list and returns
158+
* a replacement list that has been normalized.
159+
* @param list
160+
* @return Normalized list for encoding/hashing/signing
161+
*/
162+
private List<Object> normalizeList(List<Object> list) {
163+
List<Object> result = new ArrayList<Object>();
164+
for (Object o : list) {
165+
if (o == null) {
166+
result.add(nullLiteral);
167+
} else if (o instanceof Boolean) {
168+
result.add(((Boolean) o).toString());
169+
} else if (o instanceof List<?>) {
170+
result.add(normalizeList((List<Object>) o));
171+
} else if (o instanceof Map<?, ?>) {
172+
result.add(normalizeMap((Map<String, Object>) o));
173+
} else if (!(o instanceof Number)) {
174+
result.add(o);
175+
}
176+
}
177+
return result;
178+
}
179+
125180

126181
/**
127182
* Bencodes document
@@ -130,18 +185,20 @@ public LRActivity sign(LRActivity envelope) throws LRException
130185
* @return Bencoded string of the provided document
131186
* @throws LRException BENCODE_FAILED if document cannot be bencoded
132187
*/
133-
private String bencode(Map<String, Object> doc) throws LRException
188+
private String bencode(final Map<String, Object> doc) throws LRException
134189
{
135190
String text = "";
136191
String encodedString = "";
137192

138-
// Bencode the provided document
193+
// normalize the document
194+
final Map<String, Object> normalizedDoc = normalizeMap(doc);
139195

196+
// Bencode the normalized document
140197
try
141198
{
142199
ByteArrayOutputStream s = new ByteArrayOutputStream();
143200
BencodingOutputStream bencoder = new BencodingOutputStream(s);
144-
bencoder.writeMap(doc);
201+
bencoder.writeMap(normalizedDoc);
145202
bencoder.flush();
146203
encodedString = s.toString();
147204
s.close();

0 commit comments

Comments
 (0)