summaryrefslogtreecommitdiff
path: root/test/java
diff options
context:
space:
mode:
Diffstat (limited to 'test/java')
-rw-r--r--test/java/net/yacy/cora/protocol/RequestHeaderTest.java58
-rw-r--r--test/java/net/yacy/http/AdminSecurityTest.java109
2 files changed, 167 insertions, 0 deletions
diff --git a/test/java/net/yacy/cora/protocol/RequestHeaderTest.java b/test/java/net/yacy/cora/protocol/RequestHeaderTest.java
index 3cdb23cb9..0931d8e0c 100644
--- a/test/java/net/yacy/cora/protocol/RequestHeaderTest.java
+++ b/test/java/net/yacy/cora/protocol/RequestHeaderTest.java
@@ -19,6 +19,12 @@
*/
package net.yacy.cora.protocol;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import javax.servlet.http.HttpServletRequest;
+
import org.junit.Test;
import static org.junit.Assert.*;
@@ -30,6 +36,58 @@ import static org.junit.Assert.*;
public class RequestHeaderTest {
/**
+ * Build a minimal HttpServletRequest stub answering getRemoteAddr() with the
+ * given socket peer address and returning the given X-Real-IP header value.
+ */
+ private static HttpServletRequest stubRequest(final String socketPeer, final String xRealIP) {
+ final InvocationHandler h = new InvocationHandler() {
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) {
+ switch (method.getName()) {
+ case "getRemoteAddr":
+ return socketPeer;
+ case "getRemoteHost":
+ return socketPeer;
+ case "getHeader":
+ return RequestHeader.X_Real_IP.equals(args[0]) ? xRealIP : null;
+ default:
+ return null;
+ }
+ }
+ };
+ return (HttpServletRequest) Proxy.newProxyInstance(
+ RequestHeaderTest.class.getClassLoader(),
+ new Class<?>[]{HttpServletRequest.class}, h);
+ }
+
+ /**
+ * Authentication must rely on the true socket peer, never on the spoofable
+ * X-Real-IP header. A remote client sending "X-Real-IP: 127.0.0.1" must not
+ * be treated as localhost.
+ */
+ @Test
+ public void testXRealIpDoesNotAffectAuthentication() {
+ final String remoteClient = "203.0.113.7"; // a non-local address (TEST-NET-3)
+
+ // spoofing attempt: remote socket peer, but X-Real-IP claims localhost
+ final RequestHeader spoofed = new RequestHeader(stubRequest(remoteClient, "127.0.0.1"));
+ // routing accessor honors the header (kept for peer routing behind a trusted proxy) ...
+ assertEquals("127.0.0.1", spoofed.getRemoteAddr());
+ // ... but the authentication accessor must return the true socket peer
+ assertEquals(remoteClient, spoofed.getRemoteSocketAddr());
+ assertFalse("spoofed X-Real-IP must not grant localhost access", spoofed.accessFromLocalhost());
+
+ // genuine localhost access still works
+ final RequestHeader local = new RequestHeader(stubRequest("127.0.0.1", null));
+ assertEquals("127.0.0.1", local.getRemoteSocketAddr());
+ assertTrue(local.accessFromLocalhost());
+
+ // remote client without spoofing stays remote
+ final RequestHeader remote = new RequestHeader(stubRequest(remoteClient, null));
+ assertFalse(remote.accessFromLocalhost());
+ }
+
+ /**
* Test of getServerPort method, of class RequestHeader.
*/
@Test
diff --git a/test/java/net/yacy/http/AdminSecurityTest.java b/test/java/net/yacy/http/AdminSecurityTest.java
new file mode 100644
index 000000000..4fd8abb58
--- /dev/null
+++ b/test/java/net/yacy/http/AdminSecurityTest.java
@@ -0,0 +1,109 @@
+package net.yacy.http;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import net.yacy.cora.order.Base64Order;
+import net.yacy.cora.order.Digest;
+
+/**
+ * Unit tests for the servlet container neutral admin security decision logic,
+ * especially the supported admin password hash formats.
+ */
+public class AdminSecurityTest {
+
+ /**
+ * Test which paths demand admin rights depending on configuration.
+ */
+ @Test
+ public void testIsProtectedPath() {
+ // pages suffixed with "_p" are always protected
+ Assert.assertTrue(AdminSecurity.isProtectedPath("/Settings_p.html", false, false, true));
+ Assert.assertTrue(AdminSecurity.isProtectedPath("/api/table_p.xml", false, false, true));
+ // normal pages are public by default
+ Assert.assertFalse(AdminSecurity.isProtectedPath("/index.html", false, false, true));
+ Assert.assertFalse(AdminSecurity.isProtectedPath("/yacysearch.html", false, false, true));
+
+ // adminForAllPages protects everything ...
+ Assert.assertTrue(AdminSecurity.isProtectedPath("/index.html", true, false, true));
+ // ... except the p2p and remote search interfaces ...
+ Assert.assertFalse(AdminSecurity.isProtectedPath("/yacy/hello.html", true, false, true));
+ Assert.assertFalse(AdminSecurity.isProtectedPath("/solr/select", true, false, true));
+ // ... which are protected too in private robinson mode
+ Assert.assertTrue(AdminSecurity.isProtectedPath("/yacy/hello.html", true, true, true));
+ Assert.assertTrue(AdminSecurity.isProtectedPath("/solr/select", true, true, true));
+
+ // without public search page the solr and gsa interfaces are protected
+ Assert.assertTrue(AdminSecurity.isProtectedPath("/solr/select", false, false, false));
+ Assert.assertTrue(AdminSecurity.isProtectedPath("/gsa/search", false, false, false));
+ Assert.assertFalse(AdminSecurity.isProtectedPath("/index.html", false, false, false));
+ }
+
+ /**
+ * Test the Base64 based admin password hash format MD5Hex(Base64(user:password)).
+ */
+ @Test
+ public void testCheckAdminPasswordBase64Format() {
+ final String user = "admin";
+ final String pw = "secret";
+ final String configHash = AdminSecurity.calcHash(user + ":" + pw);
+
+ Assert.assertTrue(AdminSecurity.checkAdminPassword(user, configHash, "YaCy", "admin", false, pw));
+ Assert.assertFalse(AdminSecurity.checkAdminPassword(user, configHash, "YaCy", "admin", false, "wrong"));
+ Assert.assertFalse(AdminSecurity.checkAdminPassword(user, configHash, "YaCy", "admin", false, ""));
+
+ // the hash itself is accepted as password, but only on recent localhost access (bin/apicall.sh)
+ Assert.assertTrue(AdminSecurity.checkAdminPassword(user, configHash, "YaCy", "admin", true, configHash));
+ Assert.assertFalse(AdminSecurity.checkAdminPassword(user, configHash, "YaCy", "admin", false, configHash));
+ }
+
+ /**
+ * Test the "MD5:" prefixed admin password hash format MD5Hex(user:realm:password).
+ */
+ @Test
+ public void testCheckAdminPasswordDigestFormat() {
+ final String user = "admin";
+ final String pw = "secret";
+ final String realm = "YaCy";
+ final String configHash = "MD5:" + Digest.encodeMD5Hex(user + ":" + realm + ":" + pw);
+
+ Assert.assertTrue(AdminSecurity.checkAdminPassword(user, configHash, realm, "admin", false, pw));
+ Assert.assertFalse(AdminSecurity.checkAdminPassword(user, configHash, realm, "admin", false, "wrong"));
+ // the realm is part of the hash: a different realm must not verify
+ Assert.assertFalse(AdminSecurity.checkAdminPassword(user, configHash, "OtherRealm", "admin", false, pw));
+
+ // the full config hash is accepted as password for the admin user on recent localhost access (bin/apicall.sh)
+ Assert.assertTrue(AdminSecurity.checkAdminPassword(user, configHash, realm, "admin", true, configHash));
+ Assert.assertFalse(AdminSecurity.checkAdminPassword(user, configHash, realm, "admin", false, configHash));
+ // but not for another user name
+ Assert.assertFalse(AdminSecurity.checkAdminPassword("other", configHash, realm, "admin", true, configHash));
+ }
+
+ /**
+ * Test the lazy localhost authorization with the config hash as Basic credential.
+ */
+ @Test
+ public void testCheckLocalhostLazyAuth() {
+ final String adminUser = "admin";
+ final String configHash = "MD5:0cef3f723bbf6ec22bbb0ca4d4dfd001";
+ final String validHeader = "Basic " + Base64Order.standardCoder.encodeString(adminUser + ":" + configHash);
+
+ Assert.assertTrue(AdminSecurity.checkLocalhostLazyAuth(validHeader, adminUser, configHash));
+ Assert.assertFalse(AdminSecurity.checkLocalhostLazyAuth(null, adminUser, configHash));
+ Assert.assertFalse(AdminSecurity.checkLocalhostLazyAuth("Basic d3Jvbmc=", adminUser, configHash));
+ Assert.assertFalse(AdminSecurity.checkLocalhostLazyAuth("Digest something", adminUser, configHash));
+ }
+
+ /**
+ * Test the localhost access check including the referer host condition.
+ */
+ @Test
+ public void testIsLocalhostAccess() {
+ Assert.assertTrue(AdminSecurity.isLocalhostAccess("127.0.0.1", null));
+ Assert.assertTrue(AdminSecurity.isLocalhostAccess("127.0.0.1", ""));
+ Assert.assertTrue(AdminSecurity.isLocalhostAccess("127.0.0.1", "localhost"));
+ Assert.assertFalse(AdminSecurity.isLocalhostAccess("192.0.2.17", null));
+ // a request from localhost referred by a remote page is not a localhost access
+ Assert.assertFalse(AdminSecurity.isLocalhostAccess("127.0.0.1", "example.org"));
+ }
+}