Skip to content

Commit 14c0d95

Browse files
authored
Validate auth token
1 parent 5bbd803 commit 14c0d95

File tree

3 files changed

+58
-7
lines changed

3 files changed

+58
-7
lines changed

cloudinary-core/src/main/java/com/cloudinary/AuthToken.java

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public class AuthToken {
2929
private long startTime;
3030
private long expiration;
3131
private String ip;
32-
private String acl;
32+
private List<String> acl = new ArrayList<>();
3333
private long duration;
3434
private boolean isNullToken = false;
3535
private static final Pattern UNSAFE_URL_CHARS_PATTERN = Pattern.compile("[ \"#%&'/:;<=>?@\\[\\\\\\]^`{|}~]");
@@ -53,7 +53,16 @@ public AuthToken(Map options) {
5353
this.startTime = ObjectUtils.asLong(options.get("startTime"), 0L);
5454
this.expiration = ObjectUtils.asLong(options.get("expiration"), 0L);
5555
this.ip = (String) options.get("ip");
56-
this.acl = (String) options.get("acl");
56+
57+
Object acl = options.get("acl");
58+
if (acl != null) {
59+
if (acl instanceof String) {
60+
this.acl = Collections.singletonList(acl.toString());
61+
} else if (Collection.class.isAssignableFrom(acl.getClass())) {
62+
this.acl = new ArrayList<String>((Collection<String>)acl);
63+
}
64+
}
65+
5766
this.duration = ObjectUtils.asLong(options.get("duration"), 0L);
5867
}
5968
}
@@ -122,8 +131,8 @@ public AuthToken ip(String ip) {
122131
* @param acl
123132
* @return this
124133
*/
125-
public AuthToken acl(String acl) {
126-
this.acl = acl;
134+
public AuthToken acl(String... acl) {
135+
this.acl = Arrays.asList(acl);
127136
return this;
128137
}
129138

@@ -155,6 +164,11 @@ public String generate() {
155164
* @return a URL token
156165
*/
157166
public String generate(String url) {
167+
168+
if (url == null && (acl == null || acl.size() == 0)) {
169+
throw new IllegalArgumentException("Must provide acl or url");
170+
}
171+
158172
long expiration = this.expiration;
159173
if (expiration == 0) {
160174
if (duration > 0) {
@@ -172,11 +186,11 @@ public String generate(String url) {
172186
tokenParts.add("st=" + startTime);
173187
}
174188
tokenParts.add("exp=" + expiration);
175-
if (acl != null) {
176-
tokenParts.add("acl=" + escapeToLower(acl));
189+
if (acl != null && acl.size() > 0) {
190+
tokenParts.add("acl=" + escapeToLower(String.join("!", acl)));
177191
}
178192
ArrayList<String> toSign = new ArrayList<String>(tokenParts);
179-
if (url != null && acl == null) {
193+
if (url != null && (acl == null || acl.size() == 0)) {
180194
toSign.add("url=" + escapeToLower(url));
181195
}
182196
String auth = digest(StringUtils.join(toSign, "~"));

cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
package com.cloudinary;
22

33
import com.cloudinary.utils.Analytics;
4+
import com.cloudinary.utils.ObjectUtils;
5+
6+
import org.hamcrest.CoreMatchers;
47
import org.hamcrest.Matchers;
8+
import org.junit.Assert;
59
import org.junit.Before;
610
import org.junit.Rule;
711
import org.junit.Test;
812
import org.junit.rules.TestName;
913

1014
import java.io.UnsupportedEncodingException;
1115
import java.util.Calendar;
16+
import java.util.Collections;
17+
import java.util.Map;
1218
import java.util.TimeZone;
1319
import java.util.regex.Matcher;
1420
import java.util.regex.Pattern;
@@ -127,4 +133,32 @@ public void testIgnoreUrlIfAclIsProvided() {
127133
String cookieAclToken = aclToken.generate("http://res.cloudinary.com/test123/image/upload/v1486020273/sample.jpg");
128134
assertEquals(cookieToken, cookieAclToken);
129135
}
136+
137+
@Test
138+
public void testMultiplePatternsInAcl() {
139+
AuthToken token = new AuthToken(KEY).duration(3600).acl("/image/authenticated/*", "/image2/authenticated/*", "/image3/authenticated/*").startTime(22222222);
140+
String cookieToken = token.generate();
141+
Assert.assertThat(cookieToken, CoreMatchers.containsString("~acl=%2fimage%2fauthenticated%2f*!%2fimage2%2fauthenticated%2f*!%2fimage3%2fauthenticated%2f*~"));
142+
}
143+
144+
@Test
145+
public void testPublicAclInitializationFromMap() {
146+
Map options = ObjectUtils.asMap(
147+
"acl", Collections.singleton("foo"),
148+
"expiration", 100,
149+
"key", KEY,
150+
"tokenName", "token");
151+
String token = new AuthToken(options).generate();
152+
assertEquals("token=exp=100~acl=foo~hmac=88be250f3a912add862959076ee74f392fa0959a953fddd9128787d5c849efd9", token);
153+
}
154+
155+
@Test(expected = IllegalArgumentException.class)
156+
public void testMissingAclAndUrlShouldThrow() {
157+
String token = new AuthToken(KEY).duration(300).generate();
158+
}
159+
160+
@Test
161+
public void testMissingUrlNotMissingAclShouldNotThrow() {
162+
String token = new AuthToken(KEY).duration(300).generate("http://res.cloudinary.com/test123");
163+
}
130164
}

cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.lang.reflect.Field;
1919
import java.lang.reflect.Modifier;
2020
import java.lang.reflect.Type;
21+
import java.lang.reflect.ParameterizedType;
2122
import java.net.URI;
2223
import java.net.URISyntaxException;
2324
import java.net.URLDecoder;
@@ -1468,6 +1469,8 @@ private void setRandomValue(Random rand, Field field, Object instance) throws Il
14681469
field.set(instance, rand.nextInt());
14691470
} else if (fieldType.equals(long.class) || fieldType.equals(Long.class)) {
14701471
field.set(instance, rand.nextLong());
1472+
} else if (field.get(instance) instanceof List) {
1473+
field.set(instance, Collections.singletonList(cloudinary.randomPublicId()));
14711474
} else if (fieldType.equals(String.class)) {
14721475
field.set(instance, cloudinary.randomPublicId());
14731476
} else if (fieldType.equals(AuthToken.class)) {

0 commit comments

Comments
 (0)