Skip to content
This repository was archived by the owner on Mar 11, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Unreleased
- [DEPRECATED] This library is now deprecated and will be EOL on Dec 31 2021.
- [FIXED] Type of `sinceSeq` can be also a `String` besides an `Integer`.
- [IMPROVED] - Document IDs and attachment names are now rejected if they could cause an unexpected
Cloudant request. We have seen that some applications pass unsantized document IDs to SDK functions
(e.g. direct from user requests). In response to this we have updated many functions to reject
obviously invalid paths. However, for complete safety applications must still validate that
document IDs and attachment names match expected patterns.

# 2.19.2 (2021-04-07)
- [NEW] Add migration guide to the newly supported cloudant-java-sdk (coordinates: com.ibm.cloud:cloudant).
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright © 2016, 2019 IBM Corp. All rights reserved.
* Copyright © 2016, 2021 IBM Corp. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
Expand Down Expand Up @@ -47,6 +47,7 @@
import com.cloudant.client.org.lightcouch.DocumentConflictException;
import com.cloudant.client.org.lightcouch.NoDocumentException;
import com.cloudant.client.org.lightcouch.Response;
import com.cloudant.client.org.lightcouch.internal.CouchDbUtil;
import com.cloudant.http.Http;
import com.cloudant.http.HttpConnection;
import com.google.gson.Gson;
Expand Down Expand Up @@ -605,8 +606,8 @@ public void deleteIndex(String indexName, String designDocId, String type) {
assertNotEmpty(indexName, "indexName");
assertNotEmpty(designDocId, "designDocId");
assertNotNull(type, "type");
if (!designDocId.startsWith("_design")) {
designDocId = "_design/" + designDocId;
if (!designDocId.startsWith(CouchDbUtil.DESIGN_PREFIX)) {
designDocId = CouchDbUtil.DESIGN_PREFIX + designDocId;
}
URI uri = new DatabaseURIHelper(db.getDBUri()).path("_index").path(designDocId)
.path(type).path(indexName).build();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 IBM Corp. All rights reserved.
* Copyright (c) 2015, 2021 IBM Corp. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
Expand Down Expand Up @@ -76,7 +76,7 @@
*/
public class DesignDocumentManager {

private static final String DESIGN_PREFIX = "_design/";
private static final String DESIGN_PREFIX = CouchDbUtil.DESIGN_PREFIX;

private final CloudantClient client;
private final Database db;
Expand Down Expand Up @@ -220,7 +220,7 @@ public Response remove(DesignDocument designDocument) {
*/
public List<DesignDocument> list() throws IOException {
return db.getAllDocsRequestBuilder()
.startKey("_design/")
.startKey(DESIGN_PREFIX)
.endKey("_design0")
.inclusiveEnd(false)
.includeDocs(true)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2011 lightcouch.org
* Copyright (c) 2015 IBM Corp. All rights reserved.
* Copyright © 2015, 2021 IBM Corp. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
Expand All @@ -15,8 +15,10 @@

package com.cloudant.client.org.lightcouch;

import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.assertDocumentTypeId;
import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.assertNotEmpty;
import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.assertNull;
import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.assertValidAttachmentName;
import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.close;
import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.generateUUID;
import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.getAsString;
Expand Down Expand Up @@ -81,6 +83,7 @@ public abstract class CouchDatabaseBase {
public <T> T find(Class<T> classType, String id) {
assertNotEmpty(classType, "Class");
assertNotEmpty(id, "id");
assertDocumentTypeId(id);
final URI uri = new DatabaseURIHelper(dbUri).documentUri(id);
return couchDbClient.get(uri, classType);
}
Expand All @@ -98,6 +101,7 @@ public <T> T find(Class<T> classType, String id) {
public <T> T find(Class<T> classType, String id, Params params) {
assertNotEmpty(classType, "Class");
assertNotEmpty(id, "id");
assertDocumentTypeId(id);
final URI uri = new DatabaseURIHelper(dbUri).documentUri(id, params);
return couchDbClient.get(uri, classType);
}
Expand All @@ -116,6 +120,7 @@ public <T> T find(Class<T> classType, String id, String rev) {
assertNotEmpty(classType, "Class");
assertNotEmpty(id, "id");
assertNotEmpty(id, "rev");
assertDocumentTypeId(id);
final URI uri = new DatabaseURIHelper(dbUri).documentUri(id, "rev", rev);
return couchDbClient.get(uri, classType);
}
Expand Down Expand Up @@ -145,6 +150,7 @@ public <T> T findAny(Class<T> classType, String uri) {
*/
public InputStream find(String id) {
assertNotEmpty(id, "id");
assertDocumentTypeId(id);
return couchDbClient.get(new DatabaseURIHelper(dbUri).documentUri(id));
}

Expand All @@ -160,6 +166,7 @@ public InputStream find(String id) {
public InputStream find(String id, String rev) {
assertNotEmpty(id, "id");
assertNotEmpty(rev, "rev");
assertDocumentTypeId(id);
final URI uri = new DatabaseURIHelper(dbUri).documentUri(id, "rev", rev);
return couchDbClient.get(uri);
}
Expand All @@ -172,6 +179,7 @@ public InputStream find(String id, String rev) {
*/
public boolean contains(String id) {
assertNotEmpty(id, "id");
assertDocumentTypeId(id);
InputStream response = null;
try {
response = couchDbClient.head(new DatabaseURIHelper(dbUri).documentUri(id));
Expand Down Expand Up @@ -255,6 +263,7 @@ public Response remove(Object object) {
public Response remove(String id, String rev) {
assertNotEmpty(id, "id");
assertNotEmpty(rev, "rev");
assertDocumentTypeId(id);
final URI uri = new DatabaseURIHelper(dbUri).documentUri(id, rev);
return couchDbClient.delete(uri);
}
Expand Down Expand Up @@ -308,6 +317,8 @@ public List<Response> bulk(List<?> objects, boolean allOrNothing) {
* @return the attachment in the form of an {@code InputStream}.
*/
public InputStream getAttachment(String docId, String attachmentName, String revId) {
assertDocumentTypeId(docId);
assertValidAttachmentName(attachmentName);
final URI uri = new DatabaseURIHelper(dbUri).attachmentUri(docId, revId, attachmentName);
return getAttachment(uri);
}
Expand Down Expand Up @@ -362,6 +373,7 @@ public Response saveAttachment(InputStream in, String name, String contentType,
String docRev) {
assertNotEmpty(in, "in");
assertNotEmpty(name, "name");
assertValidAttachmentName(name);
assertNotEmpty(contentType, "ContentType");
if (docId == null) {
docId = generateUUID();
Expand All @@ -375,6 +387,7 @@ public Response saveAttachment(InputStream in, String name, String contentType,
assertNotEmpty(docRev, "docRev");
}
}
assertDocumentTypeId(docId);
final URI uri = new DatabaseURIHelper(dbUri).attachmentUri(docId, docRev, name);
return couchDbClient.put(uri, in, contentType);
}
Expand Down Expand Up @@ -410,8 +423,10 @@ public Response removeAttachment(Object object, String attachmentName) {
*/
public Response removeAttachment(String id, String rev, String attachmentName) {
assertNotEmpty(id, "id");
assertDocumentTypeId(id);
assertNotEmpty(rev, "rev");
assertNotEmpty(attachmentName, "attachmentName");
assertValidAttachmentName(attachmentName);
final URI uri = new DatabaseURIHelper(dbUri).attachmentUri(id, rev, attachmentName);
return couchDbClient.delete(uri);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2011 lightcouch.org
* Copyright © 2015, 2019 IBM Corp. All rights reserved.
* Copyright © 2015, 2021 IBM Corp. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
Expand All @@ -14,6 +14,7 @@
*/
package com.cloudant.client.org.lightcouch;

import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.assertDocumentTypeId;
import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.assertNotEmpty;
import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.assertNull;
import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.close;
Expand Down Expand Up @@ -480,6 +481,7 @@ public Response put(URI uri, Object object, boolean newEntity, int writeQuorum)
assertNotEmpty(id, "id");
assertNotEmpty(rev, "rev");
}
assertDocumentTypeId(id);
URI httpUri = null;
if (writeQuorum > -1) {
httpUri = new DatabaseURIHelper(uri).documentUri(id, "w", writeQuorum);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2011 lightcouch.org
* Copyright (c) 2015 IBM Corp. All rights reserved.
* Copyright © 2015, 2021 IBM Corp. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
Expand Down Expand Up @@ -49,6 +49,10 @@
*/
final public class CouchDbUtil {

public static final String DESIGN_PREFIX = "_design/";
public static final String LOCAL_PREFIX = "_local/";


private CouchDbUtil() {
// Utility class
}
Expand All @@ -73,6 +77,36 @@ public static void assertNull(Object object, String prefix) throws IllegalArgume
}
}

/*
* Throws an IllegalArgument exception if the ID is an _ prefixed name that isn't
* either _design or _local.
*
* @param id
* @throws IllegalArgumentException
*/
public static void assertDocumentTypeId(String id) throws IllegalArgumentException {
boolean invalid = false;
if (id.startsWith("_")) {
if (id.startsWith(DESIGN_PREFIX) && !DESIGN_PREFIX.equals(id)) {
invalid = false;
} else if (id.startsWith(LOCAL_PREFIX) && !LOCAL_PREFIX.equals(id)) {
invalid = false;
} else {
invalid = true;
}

}
if (invalid) {
throw new IllegalArgumentException(format("%s is not a valid document ID.", id));
}
}

public static void assertValidAttachmentName(String attachmentName) throws IllegalArgumentException {
if (attachmentName.startsWith("_")) {
throw new IllegalArgumentException(format("%s is not a valid attachment name.", attachmentName));
}
}

public static String generateUUID() {
return UUID.randomUUID().toString().replace("-", "");
}
Expand Down
Loading