/**
* Copyright (C) 2012-2017 the original author or authors.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ninja.utils;
import static ninja.utils.CookieDataCodec.decode;
import static ninja.utils.CookieDataCodec.encode;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
/**
* CookieDataCodec and CookieDataCodecTest are imported from Play Framework.
*
* Enables us to use the same sessions as Play Framework if
* the secret is the same.
*
* Also really important because we want to make sure that our client
* side session mechanism is widely used and stable.
* We don't want to reinvent
* the wheel of securely encoding / decoding and signing cookie data.
*
* All praise goes to Play Framework and their awesome work.
*
*/
public class CookieDataCodecTest {
@Test
public void flash_cookies_should_bake_in_a_header_and_value() throws UnsupportedEncodingException {
final Map<String, String> inMap = new HashMap<String, String>(1);
inMap.put("a", "b");
final String data = encode(inMap);
final Map<String, String> outMap = new HashMap<String, String>(1);
decode(outMap, data);
assertEquals(1, outMap.size());
assertEquals("b", outMap.get("a"));
}
@Test
public void bake_in_multiple_headers_and_values() throws UnsupportedEncodingException {
final Map<String, String> inMap = new HashMap<String, String>(2);
inMap.put("a", "b");
inMap.put("c", "d");
final String data = encode(inMap);
final Map<String, String> outMap = new HashMap<String, String>(1);
decode(outMap, data);
assertEquals(outMap.size(), 2);
assertEquals("b", outMap.get("a"));
assertEquals("d", outMap.get("c"));
}
@Test
public void bake_in_a_header_an_empty_value() throws UnsupportedEncodingException {
final Map<String, String> inMap = new HashMap<String, String>(1);
inMap.put("a", "");
final String data = encode(inMap);
final Map<String, String> outMap = new HashMap<String, String>(1);
decode(outMap, data);
assertEquals(1, outMap.size());
assertEquals("", outMap.get("a"));
}
@Test
public void bake_in_a_header_a_Unicode_value() throws UnsupportedEncodingException {
final Map<String, String> inMap = new HashMap<String, String>(1);
inMap.put("a", "\u0000");
final String data = encode(inMap);
final Map<String, String> outMap = new HashMap<String, String>(1);
decode(outMap, data);
assertEquals(1, outMap.size(), 1);
assertEquals("\u0000", outMap.get("a"));
}
@Test
public void bake_in_an_empty_map() throws UnsupportedEncodingException {
final Map<String, String> inMap = new HashMap<String, String>(0);
final String data = encode(inMap);
final Map<String, String> outMap = new HashMap<String, String>(1);
decode(outMap, data);
assertEquals(0, outMap.size());
}
@Test
public void encode_values_such_that_no_extra_keys_can_be_created() throws UnsupportedEncodingException {
final Map<String, String> inMap = new HashMap<String, String>(1);
inMap.put("a", "b&c=d");
final String data = encode(inMap);
final Map<String, String> outMap = new HashMap<String, String>(1);
decode(outMap, data);
assertEquals(1, outMap.size());
assertEquals("b&c=d", outMap.get("a"));
}
@Test
public void specifically_exclude_control_chars() throws UnsupportedEncodingException {
for (int i = 0; i < 32; ++i) {
final Map<String, String> inMap = new HashMap<String, String>(1);
final String s = Arrays.toString(Character.toChars(i));
inMap.put("a", s);
final String data = encode(inMap);
assertFalse(data.contains(s));
final Map<String, String> outMap = new HashMap<String, String>(1);
decode(outMap, data);
assertEquals(1, outMap.size());
assertEquals(s, outMap.get("a"));
}
}
@Test
public void specifically_exclude_special_cookie_chars() throws UnsupportedEncodingException {
final Map<String, String> inMap = new HashMap<String, String>(1);
inMap.put("a", " \",;\\");
final String data = encode(inMap);
assertFalse(data.contains(" "));
assertFalse(data.contains("\""));
assertFalse(data.contains(","));
assertFalse(data.contains(";"));
assertFalse(data.contains("\\"));
final Map<String, String> outMap = new HashMap<String, String>(1);
decode(outMap, data);
assertEquals(1, outMap.size());
assertEquals( " \",;\\", outMap.get("a"));
}
private String oldEncoder(final Map<String, String> out) throws UnsupportedEncodingException {
StringBuilder flash = new StringBuilder();
for (String key : out.keySet()) {
if (out.get(key) == null) continue;
flash.append("\u0000");
flash.append(key);
flash.append(":");
flash.append(out.get(key));
flash.append("\u0000");
}
return URLEncoder.encode(flash.toString(), "utf-8");
}
@Test
public void decode_values_of_the_previously_supported_format() throws UnsupportedEncodingException {
final Map<String, String> inMap = new HashMap<String, String>(2);
inMap.put("a", "b");
inMap.put("c", "d");
final String data = oldEncoder(inMap);
final Map<String, String> outMap = new HashMap<String, String>(0);
decode(outMap, data);
assertEquals(0, outMap.size());
}
@Test
public void decode_values_of_the_previously_supported_format_with_the_new_delimiters_in_them() throws UnsupportedEncodingException {
final Map<String, String> inMap = new HashMap<String, String>(1);
inMap.put("a", "b&=");
final String data = oldEncoder(inMap);
final Map<String, String> outMap = new HashMap<String, String>(0);
decode(outMap, data);
assertEquals(0, outMap.size());
}
@Test
public void decode_values_with_gibberish_in_them() throws UnsupportedEncodingException {
final String data = "asfjdlkasjdflk";
final Map<String, String> outMap = new HashMap<String, String>(1);
decode(outMap, data);
assertEquals(0, outMap.size());
}
}