content: /*
   * Copyright (c) 2014 3 Round Stones Inc., Some 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
   *
   *     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 org.callimachusproject.server;
  
  import java.io.OutputStreamWriter;
  import java.io.Writer;
  import java.lang.annotation.ElementType;
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.lang.annotation.Target;
  
  import org.apache.commons.codec.digest.DigestUtils;
  import org.apache.http.HttpResponse;
  import org.apache.http.HttpVersion;
  import org.apache.http.message.BasicHttpResponse;
  import org.callimachusproject.annotations.requires;
  import org.callimachusproject.server.base.MetadataServerTestCase;
  import org.openrdf.annotations.HeaderParam;
  import org.openrdf.annotations.Iri;
  import org.openrdf.annotations.Method;
  import org.openrdf.annotations.Type;
  import org.openrdf.model.ValueFactory;
  import org.openrdf.model.vocabulary.RDF;
  import org.openrdf.repository.object.ObjectConnection;
  
  import com.sun.jersey.api.client.ClientResponse;
  import com.sun.jersey.api.client.WebResource;
  import com.sun.jersey.api.client.filter.HTTPDigestAuthFilter;
  
  public class AuthenticationTest extends MetadataServerTestCase {
  
  	@Retention(RetentionPolicy.RUNTIME)
  	@Target( { ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER })
  	public @interface reader {
  		@Iri("urn:test:reader")
  		String[] value();
  	}
  
  	@Retention(RetentionPolicy.RUNTIME)
  	@Target( { ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER })
  	public @interface writer {
  		@Iri("urn:test:writer")
  		String[] value();
  	}
  
  	@reader("urn:test:my-group")
  	@Iri("urn:test:MyProtectedResource")
  	public static class MyProtectedResource {
  		public static String body = "body";
  
  		@Method("HEAD")
  		public HttpResponse head() {
  			HttpResponse head = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
  			head.addHeader("Cache-Control", "max-age=5");
  			return head;
  		}
  
  		@Method("GET")
  		@requires("urn:test:reader")
  		@Type("text/plain")
  		public String getResponse() {
  			return body;
  		}
  	}
  
  	@reader("urn:test:my-group")
  	@writer("urn:test:my-group")
  	@Iri("urn:test:MyResource")
  	public static class MyResource {
  
  		@Method("GET")
  		@requires("urn:test:reader")
  		@Type("text/plain")
  		public String get(@HeaderParam("Authorization") String auth) {
  			return auth;
  		}
  
  		@Method("POST")
  		@requires("urn:test:writer")
  		@Type("text/plain")
  		public String post(@Type("text/plain") String input,
  				@HeaderParam("Authorization") String auth) {
  			return auth;
  		}
  	}
  
  	public void setUp() throws Exception {
  		config.addConcept(MyResource.class);
  		config.addConcept(MyProtectedResource.class);
  		super.setUp();
  		ObjectConnection con = repository.getConnection();
  		try {
  			ValueFactory vf = con.getValueFactory();
  			con.add(vf.createURI("urn:test:my-group"), vf.createURI("http://callimachusproject.org/rdf/2009/framework#member"), vf.createURI("urn:test:user:bob"));
  			con.add(vf.createURI("urn:test:my-group"), vf.createURI("http://callimachusproject.org/rdf/2009/framework#member"), vf.createURI("urn:test:user:jim"));
  			con.add(vf.createURI(getOrigin()), RDF.TYPE, vf.createURI("http://callimachusproject.org/rdf/2009/framework#Origin"));
  			con.add(vf.createURI(getOrigin()), RDF.TYPE, vf.createURI("http://callimachusproject.org/rdf/2009/framework#Realm"));
  			con.add(vf.createURI(getOrigin()), vf.createURI("http://callimachusproject.org/rdf/2009/framework#authentication"), vf.createURI("urn:test:auth"));
  			con.add(vf.createURI("urn:test:auth"), RDF.TYPE, vf.createURI("http://callimachusproject.org/rdf/2009/framework#DigestManager"));
  			con.add(vf.createURI("urn:test:auth"), vf.createURI("http://callimachusproject.org/rdf/2009/framework#authName"), vf.createLiteral("test"));
  			con.add(vf.createURI("urn:test:auth"), vf.createURI("http://callimachusproject.org/rdf/2009/framework#authNamespace"), vf.createURI("urn:test:user:"));
  			con.add(vf.createURI("urn:test:user:"), vf.createURI("http://callimachusproject.org/rdf/2009/framework#hasComponent"), vf.createURI("urn:test:user:bob"));
  			con.add(vf.createURI("urn:test:user:bob"), vf.createURI("http://callimachusproject.org/rdf/2009/framework#name"), vf.createLiteral("bob"));
  			con.add(vf.createURI("urn:test:user:bob"), vf.createURI("http://callimachusproject.org/rdf/2009/framework#passwordDigest"), vf.createURI("urn:test:passwordDigest1"));
  			Writer writer = new OutputStreamWriter(con.getBlobObject("urn:test:passwordDigest1").openOutputStream(), "UTF-8");
  			try {
  				writer.write(DigestUtils.md5Hex("bob:test:pass"));
  			} finally {
  				writer.close();
  			}
  			con.add(vf.createURI("urn:test:user:"), vf.createURI("http://callimachusproject.org/rdf/2009/framework#hasComponent"), vf.createURI("urn:test:user:jim"));
  			con.add(vf.createURI("urn:test:user:jim"), vf.createURI("http://callimachusproject.org/rdf/2009/framework#name"), vf.createLiteral("jim"));
  			con.add(vf.createURI("urn:test:user:jim"), vf.createURI("http://callimachusproject.org/rdf/2009/framework#passwordDigest"), vf.createURI("urn:test:passwordDigest2"));
  			writer = new OutputStreamWriter(con.getBlobObject("urn:test:passwordDigest2").openOutputStream(), "UTF-8");
  			try {
  				writer.write(DigestUtils.md5Hex("jim:test:pass"));
  			} finally {
  				writer.close();
  			}
  			String uri = client.path("/protected").getURI().toASCIIString();
  			con.addDesignation(con.getObject(uri), MyProtectedResource.class);
  			uri = client.path("/resource").getURI().toASCIIString();
  			con.addDesignation(con.getObject(uri), MyResource.class);
  		} finally {
  			con.close();
  		}
  	}
  
  	public void testOnce() throws Exception {
  		ClientResponse resp;
  		MyProtectedResource.body = "first";
  		resp = web("/protected", "bob").get(ClientResponse.class);
  		assertNotNull(resp.toString(), resp.getHeaders().get("ETag"));
  		assertNotNull(resp.toString(), resp.getHeaders().get("Authentication-Info").get(0));
  		assertEquals("first", resp.getEntity(String.class));
  	}
  
  	public void testTwice() throws Exception {
  		ClientResponse resp;
  		MyProtectedResource.body = "first";
  		resp = web("/protected", "bob").get(ClientResponse.class);
  		MyProtectedResource.body = "second";
  		resp = web("/protected", "jim").get(ClientResponse.class);
  		assertNotNull(resp.toString(), resp.getHeaders().get("ETag"));
  		// body should be cached
  		assertEquals("first", resp.getEntity(String.class));
  		assertNotNull(resp.toString(), resp.getHeaders().get("Authentication-Info").get(0));
  	}
  
  	public void testBadAuth() throws Exception {
  		ClientResponse resp;
  		MyProtectedResource.body = "first";
  		resp = web("/protected", "bob").get(ClientResponse.class);
  		resp = web("/protected", "nobody").get(ClientResponse.class);
  		assertFalse("first".equals(resp.getEntity(String.class)));
  		assertNull(resp.getHeaders().get("Authentication-Info"));
  	}
  
  	public void testBadAndGoddAuth() throws Exception {
  		ClientResponse resp;
  		MyProtectedResource.body = "first";
  		resp = web("/protected", "bob").get(ClientResponse.class);
  		resp = web("/protected", "nobody").get(ClientResponse.class);
  		MyProtectedResource.body = "second";
  		resp = web("/protected", "jim").get(ClientResponse.class);
  		assertNotNull(resp.toString(), resp.getHeaders().get("ETag"));
  		// body should still be cached
  		assertEquals("first", resp.getEntity(String.class));
  		assertNotNull(resp.toString(), resp.getHeaders().get("Authentication-Info").get(0));
  	}
  
  	public void testGetVaryAuth() throws Exception {
  		ClientResponse resp;
  		resp = web("/resource", "bob").get(ClientResponse.class);
  		assertTrue(resp.getEntity(String.class).contains("username=\"bob\""));
  		assertTrue(resp.getHeaders().get("Cache-Control").get(0), resp.getHeaders().get("Cache-Control").get(0).contains("private"));
  		assertNotNull(resp.toString(), resp.getHeaders().get("ETag"));
  		assertFalse(resp.toString(), String.valueOf(resp.getHeaders().get("Vary")).contains("Authorization"));
  		resp = web("/resource", "jim").get(ClientResponse.class);
  		assertTrue(resp.getEntity(String.class).contains("username=\"jim\""));
  		assertTrue(resp.toString(), resp.getHeaders().get("Cache-Control").get(0).contains("private"));
  		assertNotNull(resp.toString(), resp.getHeaders().get("ETag"));
  		assertFalse(resp.toString(), String.valueOf(resp.getHeaders().get("Vary")).contains("Authorization"));
  	}
  
  	protected void addContentEncoding(WebResource client) {
  		// its broken
  	}
  
  	private WebResource web(String path, String user) {
  		WebResource web = web(path);
  		web.addFilter(new HTTPDigestAuthFilter(user, "pass"));
  		return web;
  	}
  
  	private WebResource web(String path) {
  		return client.path(path);
  	}
  
  }
  