You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@wookie.apache.org by sc...@apache.org on 2011/11/18 18:38:54 UTC

svn commit: r1203763 - in /incubator/wookie/trunk: src-tests/org/apache/wookie/tests/proxy/ src/ src/org/apache/wookie/controller/ src/org/apache/wookie/proxy/

Author: scottbw
Date: Fri Nov 18 17:38:53 2011
New Revision: 1203763

URL: http://svn.apache.org/viewvc?rev=1203763&view=rev
Log:
First part of committing update to policy management; See WOOKIE-292. This commit adds the new policy framework.

Added:
    incubator/wookie/trunk/src-tests/org/apache/wookie/tests/proxy/
    incubator/wookie/trunk/src-tests/org/apache/wookie/tests/proxy/PoliciesTest.java
    incubator/wookie/trunk/src-tests/org/apache/wookie/tests/proxy/PolicyTest.java
    incubator/wookie/trunk/src/org/apache/wookie/controller/PoliciesController.java
    incubator/wookie/trunk/src/org/apache/wookie/proxy/Policies.java
    incubator/wookie/trunk/src/org/apache/wookie/proxy/Policy.java
    incubator/wookie/trunk/src/org/apache/wookie/proxy/PolicyFormatException.java
    incubator/wookie/trunk/src/policies
Removed:
    incubator/wookie/trunk/src/org/apache/wookie/controller/WhiteListController.java
    incubator/wookie/trunk/src/org/apache/wookie/controller/WidgetAccessRequestPolicyController.java
Modified:
    incubator/wookie/trunk/src/org/apache/wookie/proxy/ProxyServlet.java

Added: incubator/wookie/trunk/src-tests/org/apache/wookie/tests/proxy/PoliciesTest.java
URL: http://svn.apache.org/viewvc/incubator/wookie/trunk/src-tests/org/apache/wookie/tests/proxy/PoliciesTest.java?rev=1203763&view=auto
==============================================================================
--- incubator/wookie/trunk/src-tests/org/apache/wookie/tests/proxy/PoliciesTest.java (added)
+++ incubator/wookie/trunk/src-tests/org/apache/wookie/tests/proxy/PoliciesTest.java Fri Nov 18 17:38:53 2011
@@ -0,0 +1,170 @@
+/*
+ * 
+ * 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.apache.wookie.tests.proxy;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.net.URISyntaxException;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.wookie.proxy.Policies;
+import org.apache.wookie.proxy.PolicyFormatException;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Tests Policies
+ */
+public class PoliciesTest {
+  
+  @BeforeClass
+  public static void setUp() throws ConfigurationException, PolicyFormatException{
+    //
+    // Clear existing
+    //
+    Policies.getInstance().clear();
+    assertEquals(0, Policies.getInstance().getPolicies().length);
+
+    //
+    // Add a couple of other policies to test with
+    //
+    Policies.getInstance().addPolicy("http://incubator.apache.org/wookie/widgets/test2 http://allowed.com DENY");
+    Policies.getInstance().addPolicy("http://incubator.apache.org/wookie/widgets/test http://*.apache.org ALLOW");
+    Policies.getInstance().addPolicy("http://incubator.apache.org/wookie/widgets/test2 http://test.com:9000 ALLOW");
+    Policies.getInstance().addPolicy("http://incubator.apache.org/wookie/widgets/test2 http://test2.com DENY");
+    Policies.getInstance().addPolicy("http://incubator.apache.org/wookie/widgets/test2 http://*.test3.com DENY");
+    Policies.getInstance().addPolicy("* http://allowed.com ALLOW");
+    Policies.getInstance().addPolicy("* http://notallowed.com DENY");
+    Policies.getInstance().addPolicy("http://www.getwookie.org/widgets/weather http://newsrss.bbc.co.uk:80 ALLOW");
+    
+    assertEquals(8, Policies.getInstance().getPolicies().length);
+  }
+
+  @Test
+  public void validUrl() throws URISyntaxException, ConfigurationException{
+    assertTrue(Policies.getInstance().validate("http://abc.apache.org", "http://incubator.apache.org/wookie/widgets/test"));
+  }
+  
+  @Test
+  public void invalidUrl() throws URISyntaxException, ConfigurationException{
+    assertFalse(Policies.getInstance().validate("http://abc.invalid.org", "http://incubator.apache.org/wookie/widgets/test"));
+  }
+  
+  @Test
+  public void invalidPort() throws URISyntaxException, ConfigurationException{
+    assertFalse(Policies.getInstance().validate("http://test.com:9001", "http://incubator.apache.org/wookie/widgets/test2"));
+  }
+  
+  @Test
+  public void validPort() throws URISyntaxException, ConfigurationException{
+    assertTrue(Policies.getInstance().validate("http://test.com:9000", "http://incubator.apache.org/wookie/widgets/test2"));
+  }
+  
+  @Test
+  public void globals() throws URISyntaxException, ConfigurationException{
+    assertTrue(Policies.getInstance().validate("http://allowed.com", "http://incubator.apache.org/wookie/widgets/test"));
+  }
+  
+  @Test
+  public void globals2() throws URISyntaxException, ConfigurationException{
+    assertFalse(Policies.getInstance().validate("http://notallowed.com", "http://incubator.apache.org/wookie/widgets/test"));
+  }
+  
+  @Test
+  public void override() throws URISyntaxException, ConfigurationException{
+    assertFalse(Policies.getInstance().validate("http://allowed.com", "http://incubator.apache.org/wookie/widgets/test2"));
+  }
+  
+  @Test
+  public void validSubdomain() throws URISyntaxException, ConfigurationException{
+    assertTrue(Policies.getInstance().validate("http://zzz.apache.org", "http://incubator.apache.org/wookie/widgets/test"));
+  }
+  
+  @Test
+  public void invalidSubdomain() throws URISyntaxException, ConfigurationException{
+    assertFalse(Policies.getInstance().validate("http://zzz.allowed.com", "http://incubator.apache.org/wookie/widgets/test"));
+  }
+
+  
+  @Test
+  public void invalidSubdomain2() throws URISyntaxException, ConfigurationException{
+    assertFalse(Policies.getInstance().validate("http://abc.test3.com", "http://incubator.apache.org/wookie/widgets/test2"));
+  }
+  
+  @Test(expected = URISyntaxException.class)
+  public void badURI() throws URISyntaxException, ConfigurationException{
+      Policies.getInstance().validate("zzzzz", "http://incubator.apache.org/wookie/widgets/test");
+  }
+  @Test(expected = URISyntaxException.class)
+  public void badURI2() throws URISyntaxException, ConfigurationException{
+      Policies.getInstance().validate("test:", "http://incubator.apache.org/wookie/widgets/test");
+  }
+  @Test(expected = URISyntaxException.class)
+  public void badURI3() throws URISyntaxException, ConfigurationException{
+      Policies.getInstance().validate("not:really:valid", "http://incubator.apache.org/wookie/widgets/test");
+  }
+  @Test(expected = URISyntaxException.class)
+  public void badURI4() throws URISyntaxException, ConfigurationException{
+      Policies.getInstance().validate("/test", "http://incubator.apache.org/wookie/widgets/test");
+  }
+  
+  @Test
+  public void addAndDeletePolicy() throws ConfigurationException, URISyntaxException, PolicyFormatException{
+    //
+    // Add a new policy and test
+    //
+    Policies.getInstance().addPolicy("* http://temp.org ALLOW");
+    assertTrue(Policies.getInstance().validate("http://temp.org", "http://incubator.apache.org/wookie/widgets/test"));
+    //
+    // Delete the policy and test again
+    //
+    Policies.getInstance().removePolicy("* http://temp.org ALLOW");
+    assertFalse(Policies.getInstance().validate("http://temp.org", "http://incubator.apache.org/wookie/widgets/test"));      
+  }
+  
+  @Test
+  public void addWildcardPolicy() throws ConfigurationException, URISyntaxException, PolicyFormatException{
+    Policies.getInstance().addPolicy("http://incubator.apache.org/wookie/widgets/test3 * ALLOW");
+    assertTrue(Policies.getInstance().validate("http://xxx.yyy.zzz", "http://incubator.apache.org/wookie/widgets/test3"));
+    
+  }
+  
+  @Test
+  public void clearPolicies() throws URISyntaxException, ConfigurationException, PolicyFormatException{
+    assertTrue(Policies.getInstance().validate("http://zzz.apache.org", "http://incubator.apache.org/wookie/widgets/test"));
+    Policies.getInstance().clearPolicies("http://incubator.apache.org/wookie/widgets/test");
+    assertFalse(Policies.getInstance().validate("http://zzz.apache.org", "http://incubator.apache.org/wookie/widgets/test"));
+    Policies.getInstance().addPolicy("http://incubator.apache.org/wookie/widgets/test http://*.apache.org ALLOW");
+  }
+  
+  @Test
+  public void weather() throws ConfigurationException, URISyntaxException{
+    assertTrue(Policies.getInstance().validate("http://newsrss.bbc.co.uk/weather/forecast/9/Next3DaysRSS.xml", "http://www.getwookie.org/widgets/weather"));
+  }
+  
+  @Test
+  public void duplicates() throws ConfigurationException, PolicyFormatException{
+    Policies.getInstance().addPolicy("http://temp.org http://temp.org ALLOW");
+    Policies.getInstance().addPolicy("http://temp.org http://temp.org ALLOW");
+    assertEquals(1, Policies.getInstance().getPolicies("http://temp.org").length);
+    Policies.getInstance().clearPolicies("http://temp.org");
+  }
+ 
+}

Added: incubator/wookie/trunk/src-tests/org/apache/wookie/tests/proxy/PolicyTest.java
URL: http://svn.apache.org/viewvc/incubator/wookie/trunk/src-tests/org/apache/wookie/tests/proxy/PolicyTest.java?rev=1203763&view=auto
==============================================================================
--- incubator/wookie/trunk/src-tests/org/apache/wookie/tests/proxy/PolicyTest.java (added)
+++ incubator/wookie/trunk/src-tests/org/apache/wookie/tests/proxy/PolicyTest.java Fri Nov 18 17:38:53 2011
@@ -0,0 +1,109 @@
+/*
+ * 
+ * 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.apache.wookie.tests.proxy;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.apache.wookie.proxy.Policy;
+import org.apache.wookie.proxy.PolicyFormatException;
+import org.junit.Test;
+
+/**
+ * Tests Policy
+ */
+public class PolicyTest {
+  
+  
+  @Test
+  public void createPolicy() throws PolicyFormatException{
+    Policy policy = new Policy();
+    policy.setScope("*");
+    policy.setOrigin("*");
+    policy.setDirective("ALLOW");
+  }
+
+  @Test
+  public void createPolicyFromString() throws PolicyFormatException{
+    Policy policy = new Policy("* http://localhost ALLOW");
+  }
+  
+  @Test(expected = PolicyFormatException.class)
+  public void createPolicyFromInvalidString() throws PolicyFormatException{
+    Policy policy = new Policy("http://localhost ALLOW");
+  }
+  
+  @Test
+  public void policyToString() throws PolicyFormatException{
+    Policy policy = new Policy("* http://localhost ALLOW");
+    assertEquals("* http://localhost ALLOW",policy.toString());
+  }
+  
+  @Test(expected = PolicyFormatException.class)
+  public void createPolicyFromInvalidUriString() throws PolicyFormatException{
+    Policy policy = new Policy("* isnotauri! ALLOW");
+  }
+  
+  @Test(expected = PolicyFormatException.class)
+  public void createPolicyFromInvalidUriString2() throws PolicyFormatException{
+    Policy policy = new Policy("* nohost: ALLOW");
+  }
+
+  @Test(expected = PolicyFormatException.class)
+  public void createPolicyFromInvalidUriString3() throws PolicyFormatException{
+    Policy policy = new Policy("* test:test@http://x.y.z ALLOW");
+  }
+
+  @Test(expected = PolicyFormatException.class)
+  public void createPolicyFromInvalidUriString4() throws PolicyFormatException{
+    Policy policy = new Policy("* http://test@www.apache.org ALLOW");
+  }
+ 
+  @Test
+  public void checkInvalid() throws URISyntaxException{
+    Policy policy = new Policy();
+    assertEquals(0,policy.allows(new URI("http://test.apache.org")));  
+  }
+
+  @Test
+  public void checkInvalid2() throws URISyntaxException, PolicyFormatException{
+    Policy policy = new Policy("* http://test.apache.org ALLOW");
+    assertEquals(0,policy.allows(new URI("ftp://test.apache.org")));  
+  }
+  
+  @Test
+  public void equalsTest() throws PolicyFormatException{
+    Policy policy = new Policy("* http://test.apache.org ALLOW");
+    Policy policy2 = new Policy("* http://test.apache.org ALLOW");
+    assertEquals(policy,policy2);
+    assertTrue(policy.equals(policy2));
+    
+  }
+
+  @Test
+  public void unequalTest() throws PolicyFormatException{
+    Policy policy = new Policy("* http://test.apache.org ALLOW");
+    Object policy2 = new String("* http://test.apache.org ALLOW");
+    assertFalse(policy.equals(policy2));
+  }
+}
+

Added: incubator/wookie/trunk/src/org/apache/wookie/controller/PoliciesController.java
URL: http://svn.apache.org/viewvc/incubator/wookie/trunk/src/org/apache/wookie/controller/PoliciesController.java?rev=1203763&view=auto
==============================================================================
--- incubator/wookie/trunk/src/org/apache/wookie/controller/PoliciesController.java (added)
+++ incubator/wookie/trunk/src/org/apache/wookie/controller/PoliciesController.java Fri Nov 18 17:38:53 2011
@@ -0,0 +1,177 @@
+/*
+ * 
+ * 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.apache.wookie.controller;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+import org.apache.wookie.exceptions.InvalidParametersException;
+import org.apache.wookie.exceptions.ResourceDuplicationException;
+import org.apache.wookie.exceptions.ResourceNotFoundException;
+import org.apache.wookie.exceptions.UnauthorizedAccessException;
+import org.apache.wookie.helpers.PoliciesHelper;
+import org.apache.wookie.proxy.Policies;
+import org.apache.wookie.proxy.Policy;
+import org.apache.wookie.proxy.PolicyFormatException;
+
+/**
+ *
+ */
+public class PoliciesController extends Controller {
+
+  private static final long serialVersionUID = -5779464009961150201L;
+  static Logger _logger = Logger.getLogger(PoliciesController.class.getName());   
+
+  /* (non-Javadoc)
+   * @see org.apache.wookie.controller.Controller#show(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+   */
+  @Override
+  protected void show(String resourceId, HttpServletRequest request,
+      HttpServletResponse response) throws ResourceNotFoundException,
+      UnauthorizedAccessException, IOException {
+    
+    Policy[] policies;
+    
+    //
+    // The resource id is the policy scope. E.g., calling
+    // policies/* would get the global policies
+    //
+    try {
+      policies = Policies.getInstance().getPolicies(resourceId);         
+    } catch (ConfigurationException e) {
+      _logger.error("Problem with policies configuration", e);
+      throw new IOException();
+    }
+    
+    switch (format(request)) {
+    case XML: returnXml(PoliciesHelper.toXml(policies),response);break;
+    case HTML: returnHtml(PoliciesHelper.toHtml(policies),response);break;
+    case JSON: returnJson(PoliciesHelper.toJson(policies),response);break;
+    }
+    
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.wookie.controller.Controller#index(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+   */
+  @Override
+  protected void index(HttpServletRequest request, HttpServletResponse response)
+  throws UnauthorizedAccessException, IOException {
+    Policy[] policies;
+    try {
+      policies = Policies.getInstance().getPolicies();
+    } catch (ConfigurationException e) {
+      _logger.error("Problem with policies configuration", e);
+      throw new IOException();
+    }
+
+    switch (format(request)) {
+    case XML: returnXml(PoliciesHelper.toXml(policies),response);break;
+    case HTML: returnHtml(PoliciesHelper.toHtml(policies),response);break;
+    case JSON: returnJson(PoliciesHelper.toJson(policies),response);break;
+    }
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.wookie.controller.Controller#create(java.lang.String, javax.servlet.http.HttpServletRequest)
+   */
+  @Override
+  protected boolean create(String resourceId, HttpServletRequest request)
+      throws ResourceDuplicationException, InvalidParametersException,
+      UnauthorizedAccessException {
+    try {
+      
+      //
+      // Check the request body
+      //
+      String policy = request.getReader().readLine();
+      return Policies.getInstance().addPolicy(policy);
+      
+    } catch (ConfigurationException e) {
+      
+      //
+      // Problem with the configuration
+      //
+      _logger.error("Problem with policies configuration", e);
+      throw new InvalidParametersException();
+      
+    } catch (PolicyFormatException e) {
+      
+      //
+      // Format of the policy is incorrect
+      //
+      throw new InvalidParametersException();
+      
+    } catch (IOException e) {
+      
+      //
+      // No body 
+      //
+      throw new InvalidParametersException();
+    }
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.wookie.controller.Controller#remove(java.lang.String, javax.servlet.http.HttpServletRequest)
+   */
+  @Override
+  protected boolean remove(String resourceId, HttpServletRequest request)
+  throws ResourceNotFoundException, UnauthorizedAccessException,
+  InvalidParametersException {
+
+    //
+    // For some reason the main controller resourceId method isn't parsing
+    // the resource part correctly
+    //
+    resourceId = request.getPathInfo().trim();
+    if (resourceId != null) resourceId = StringUtils.stripStart(resourceId, "/");
+
+    try {
+
+      //
+      // Obtain a policy from the resource identifier
+      //
+      Policy policy;
+      policy = new Policy(resourceId);
+      Policies.getInstance().removePolicy(policy);
+      return true;
+      
+    } catch (PolicyFormatException e) {
+
+      //
+      // The resource isn't formatted correctly
+      //
+      throw new InvalidParametersException();
+
+    } catch (ConfigurationException e) {
+
+      //
+      // Problem with the configuration
+      //
+      _logger.error("Problem with policies configuration", e);
+      throw new InvalidParametersException();
+    }
+
+  }
+
+}

Added: incubator/wookie/trunk/src/org/apache/wookie/proxy/Policies.java
URL: http://svn.apache.org/viewvc/incubator/wookie/trunk/src/org/apache/wookie/proxy/Policies.java?rev=1203763&view=auto
==============================================================================
--- incubator/wookie/trunk/src/org/apache/wookie/proxy/Policies.java (added)
+++ incubator/wookie/trunk/src/org/apache/wookie/proxy/Policies.java Fri Nov 18 17:38:53 2011
@@ -0,0 +1,303 @@
+/*
+ * 
+ * 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.apache.wookie.proxy;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.commons.collections.map.MultiValueMap;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.PropertiesConfiguration;
+import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
+import org.apache.commons.lang.ArrayUtils;
+
+/**
+ * Policies management class
+ * 
+ * <p>This class provides access to policies for the proxy service, 
+ * including both global "whitelist" policies and widget-specific
+ * policies derived from the &lt;access&gt; element in config.xml</p>
+ * 
+ * <p>The Policies class is a singleton and maps directly onto the
+ * <code>policies</code> configuration file in the server.</p>
+ * 
+ * <p>Note that policies are loaded dynamically from the <code>
+ * policies</code> file whenever it changes on the file system</p>
+ */
+public class Policies {
+  
+  private  MultiValueMap policies;
+  private  PropertiesConfiguration properties;
+  private  FileChangedReloadingStrategy reloader = new FileChangedReloadingStrategy();
+  
+  private static Policies instance;
+  
+  private Policies() throws ConfigurationException{
+    policies = new MultiValueMap();
+    properties = new PropertiesConfiguration("policies");
+    properties.setReloadingStrategy(reloader);
+    properties.getLayout().setGlobalSeparator(" ");
+    loadPolicies();
+  }
+  
+  public static Policies getInstance() throws ConfigurationException{
+    if (instance == null)
+    instance = new Policies();
+    return instance;
+  }
+  
+  /**
+   * Refresh policies, loading from file if the file
+   * has been modified since it was last reloaded.
+   * @throws ConfigurationException
+   */
+  private void refresh() throws ConfigurationException{
+    if (reloader.reloadingRequired()){
+      policies.clear();
+      loadPolicies();
+      reloader.reloadingPerformed();
+    }
+  }
+  
+  /**
+   * Load policies from the policies file
+   * @throws ConfigurationException
+   */
+  private void loadPolicies() throws ConfigurationException{
+    properties.clear();
+    properties.load();
+       
+    @SuppressWarnings("rawtypes")
+    Iterator keys = properties.getKeys();
+    while(keys.hasNext()){
+      String key = (String)keys.next();
+      String[] entries = properties.getStringArray(key);
+      for(int i=0;i<entries.length;i++){
+        try {
+          addPolicyToCollection(key+" "+entries[i]);
+        } catch (Exception e) {
+          // TODO LOG AN ERROR
+        }
+      }
+    }
+  }
+  
+  /**
+   * Validate a request for a resource with a particular scope
+   * @param location the location requested
+   * @param scope the scope of the request
+   * @return true if the location is allowed in the scope, otherwise false
+   * @throws URISyntaxException if the request is not a valid URI
+   * @throws ConfigurationException
+   */
+  public boolean validate(String location, String scope) throws URISyntaxException, ConfigurationException{
+    URI uri = new URI(location);
+    return validate(uri, scope);
+  }
+  
+  /**
+   * Validate a request for a resource with a particular scope
+   * @param uri the URI requested
+   * @param scope the scope of the request
+   * @return true if the location is allowed in the scope, otherwise false
+   * @throws URISyntaxException if the request is not a valid URI
+   * @throws ConfigurationException
+   */
+  public boolean validate(URI uri, String scope) throws URISyntaxException, ConfigurationException{
+    
+    refresh();
+    
+    //
+    // Obtain matching policies
+    //
+    Policy[] matching = getPolicies(scope);
+    
+    //
+    // Mix in globals
+    //
+    if (!scope.equals("*")){
+        matching = (Policy[]) ArrayUtils.addAll(getPolicies("*"), matching);
+    }    
+    
+    //
+    // Check if allowed
+    // 
+    boolean allowed = false;
+    boolean denied = false;
+    for (Policy policy:matching){
+      if (policy.allows(uri) == 1) allowed = true;
+      if (policy.allows(uri) == -1) denied = true;      
+    }
+    if (allowed && !denied) return true;
+    return false;
+  }
+
+  /**
+   * Add a new policy formatted using a policy string
+   * @param policyString
+   * @throws PolicyFormatException 
+   * @throws ConfigurationException 
+   * @throws Exception 
+   */
+  public boolean addPolicy(String policyString) throws PolicyFormatException, ConfigurationException{
+    Policy policy = new Policy(policyString);
+    return addPolicy(policy);
+  }
+  
+  /**
+   * Add a new policy object
+   * @param policy
+   * @throws ConfigurationException 
+   */
+  public boolean addPolicy(Policy policy) throws ConfigurationException{
+    boolean added = addPolicyToCollection(policy);
+    if(added){
+      properties.addProperty(policy.getScope(), policy.getOrigin()+" "+policy.getDirective());
+      properties.save();
+    }
+    return added;
+  }
+  
+  /**
+   * Private method for adding a policy to the internal policy collection
+   * @param policyString
+   * @throws PolicyFormatException 
+   * @throws Exception 
+   */
+  private void addPolicyToCollection(String policyString) throws ConfigurationException, PolicyFormatException{
+    Policy policy = new Policy(policyString);
+    addPolicyToCollection(policy);
+  }
+  
+  /**
+   * Private method for adding a policy to the internal policy collection
+   * @param policy
+   */
+  private boolean addPolicyToCollection(Policy policy){
+    @SuppressWarnings("unchecked")
+    Collection<Policy> existingPolicies = (Collection<Policy>) policies.get(policy.getScope());
+    if (existingPolicies == null || !existingPolicies.contains(policy)){  
+      
+      //
+      // Add the policy
+      //
+      policies.put(policy.getScope(), policy);
+      return true;
+      
+    } else {
+      
+      //
+      // Duplicate of existing policy, so don't add it
+      //
+      return false;
+    }
+  }
+  
+  /**
+   * Remove a policy
+   * @param policyString
+   * @throws PolicyFormatException 
+   * @throws Exception 
+   */
+  public void removePolicy(String policyString) throws ConfigurationException, PolicyFormatException{
+    Policy policy = new Policy(policyString);
+    removePolicy(policy);
+  }
+  
+  /**
+   * Remove a policy
+   * @param policy
+   * @throws ConfigurationException
+   */
+  public void removePolicy(Policy policy) throws ConfigurationException{
+
+    //
+    // Remove from local collection
+    //
+    @SuppressWarnings("unchecked")
+    Collection<Policy> matchingPolicies = (Collection<Policy>) policies.get(policy.getScope());
+    matchingPolicies.remove(policy);
+    policies.remove(policy.getScope());
+    for (Policy match: matchingPolicies) policies.put(match.getScope(), match);
+    
+    //
+    // Remove from properties file
+    //
+    properties.clearProperty(policy.getScope());
+    for (Policy match: getPolicies(policy.getScope())){
+      properties.addProperty(match.getScope(), match.getOrigin()+" "+match.getDirective());
+    }
+    properties.save();
+
+  }
+  
+  /**
+   * Clear all policies relating to the scope provided (typically a widget uri)
+   * @param scope
+   * @throws ConfigurationException 
+   */
+  public void clearPolicies(String scope) throws ConfigurationException{
+    refresh();
+    policies.remove(scope);
+    properties.clearProperty(scope);
+    properties.save();
+  }
+  
+  /**
+   * Clear all policies
+   * @throws ConfigurationException
+   */
+  public void clear() throws ConfigurationException{
+    refresh();
+    policies.clear();
+    properties.clear();
+    properties.save();
+  }
+  
+  /**
+   * Get all policies within a specific scope
+   * @param scope
+   * @return an array of Policy objects
+   * @throws ConfigurationException
+   */
+  public Policy[] getPolicies(String scope) throws ConfigurationException{
+    refresh();
+    @SuppressWarnings("unchecked")
+    Collection<Policy> matchedPolicies = (Collection<Policy>) policies.get(scope);
+    if (matchedPolicies == null) return new Policy[0];
+    return matchedPolicies.toArray(new Policy[matchedPolicies.size()]);
+  }
+  
+  /**
+   * Get all policies
+   * @return an array of Policy objects
+   * @throws ConfigurationException
+   */
+  @SuppressWarnings("unchecked")
+  public Policy[] getPolicies() throws ConfigurationException{
+    refresh();
+    ArrayList<Policy> allPolicies = new ArrayList<Policy>();
+    for (Object key:policies.keySet()){
+      allPolicies.addAll(policies.getCollection(key));
+    }
+    return allPolicies.toArray(new Policy[allPolicies.size()]);
+  }
+}

Added: incubator/wookie/trunk/src/org/apache/wookie/proxy/Policy.java
URL: http://svn.apache.org/viewvc/incubator/wookie/trunk/src/org/apache/wookie/proxy/Policy.java?rev=1203763&view=auto
==============================================================================
--- incubator/wookie/trunk/src/org/apache/wookie/proxy/Policy.java (added)
+++ incubator/wookie/trunk/src/org/apache/wookie/proxy/Policy.java Fri Nov 18 17:38:53 2011
@@ -0,0 +1,230 @@
+/*
+ * 
+ * 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.apache.wookie.proxy;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.apache.wookie.w3c.util.IRIValidator;
+
+/**
+ * A class representing a single Policy, equivalent to a single
+ * entry in the <code>policies</code> configuration file
+ */
+public class Policy {
+
+  private String scope;
+  private String origin;
+  private String directive;
+
+  public Policy(){
+
+  }
+
+  /**
+   * Construct a Policy instance from a policy string comprising
+   * the scope, origin and directive separated by spaces
+   * @param policy
+   * @throws Exception 
+   */
+  public Policy(String policy) throws PolicyFormatException{
+    String[] elements = policy.split(" ");
+    if (elements.length == 3){
+      setScope(elements[0]);
+      setOrigin(elements[1]);
+      setDirective(elements[2]);
+    } else {
+      throw new PolicyFormatException("Policy incorrectly formatted");
+    }
+  }
+
+  /* (non-Javadoc)
+   * @see java.lang.Object#toString()
+   */
+  public String toString(){
+    String policy = scope + " " + origin + " " + directive;
+    return policy;
+  }
+
+  /**
+   * @return the scope
+   */
+  public String getScope() {
+    return scope;
+  }
+  /**
+   * @param scope the scope to set
+   */
+  public void setScope(String scope) {
+    this.scope = scope;
+  }
+  /**
+   * @return the origin
+   */
+  public String getOrigin() {
+    return origin;
+  }
+  /**
+   * @param origin the origin to set
+   * @throws Exception 
+   */
+  public void setOrigin(String origin) throws PolicyFormatException {
+    this.origin = this.checkOrigin(origin);
+  }
+  /**
+   * @return the directive
+   */
+  public String getDirective() {
+    return directive;
+  }
+  /**
+   * @param directive the directive to set
+   */
+  public void setDirective(String directive) {
+    this.directive = directive;
+  }
+
+  /* (non-Javadoc)
+   * @see java.lang.Object#equals(java.lang.Object)
+   */
+  @Override
+  public boolean equals(Object obj) {
+    if (!obj.getClass().equals(this.getClass())) return false;
+
+    Policy otherPolicy = (Policy)obj;
+
+    if(otherPolicy.getScope().equalsIgnoreCase(getScope()) && (otherPolicy.getDirective().equalsIgnoreCase(getDirective())) && (otherPolicy.getOrigin().equalsIgnoreCase(getOrigin()))){
+      return true;
+    }
+
+    return false;
+  }
+
+  /**
+   * Checks whether the policy grants or denies access to a given URI.
+   * @param uri
+   * @return returns 1 if the policy grants access to the URI, -1 if it denies access, and 0 if it doesn't match
+   * @throws URISyntaxException
+   */
+  public int allows(URI uri) throws URISyntaxException{
+    
+    //
+    // Policy is being checked which has no origin set
+    //
+    if(this.getOrigin() == null) return 0;
+
+    //
+    // Check valid URI
+    //
+    if(uri.getScheme() == null) throw new URISyntaxException(uri.toString(), "no valid scheme");
+    if(uri.getAuthority() == null) throw new URISyntaxException(uri.toString(), "no valid authority");
+
+    //
+    // Wildcard match
+    //
+    if (getOrigin().equals("*")) return 1;
+
+    //
+    // Get the match origin, and check they match
+    //
+    URI match = new URI(getOrigin());
+
+    //
+    // Check schemes match
+    //
+    if(!uri.getScheme().equalsIgnoreCase(match.getScheme())) return 0;
+
+    //
+    // Check ports match
+    // Note that where the Policy has no port specified, we treat
+    // this as being "any port"
+    //
+    int requestedPort = uri.getPort();
+    if (requestedPort == -1) requestedPort = 80;
+    if( match.getPort() != -1 && requestedPort !=  match.getPort()) return 0;
+
+
+    if(match.getAuthority().startsWith(("*"))){
+      
+      //
+      // Subdomain match
+      //
+      String subdomain = uri.getAuthority().split("\\.")[0];
+      if (subdomain != null){
+        String matchHost = match.getAuthority().replace("*", subdomain);
+        if(uri.getHost().equalsIgnoreCase(matchHost))
+          if (getDirective().equalsIgnoreCase("ALLOW")){
+            return 1;
+          } else {
+            return -1;
+          }
+      }
+    } else {
+
+      //
+      // Simple hosts match
+      //
+      if(uri.getHost().equalsIgnoreCase(match.getHost()))
+        if (getDirective().equalsIgnoreCase("ALLOW")){
+          return 1;
+        } else {
+          return -1;
+        }
+    }
+    return 0;
+  }
+
+  /**
+   * Checks whether a supplied origin parameter is valid, and returns the processed result
+   * @param origin
+   * @return a processed origin with extraneous elements removed
+   * @throws PolicyFormatException if the origin is not valid
+   */
+  private String checkOrigin(String origin) throws PolicyFormatException{
+    origin = origin.trim();
+    if (origin.equals("*")) return origin;
+    
+    if (!IRIValidator.isValidIRI(origin)) throw new PolicyFormatException("origin is not a valid IRI");
+    
+
+    URI uri;
+    try {
+      uri = new URI(origin);
+      if ((uri.getHost() == null && (uri.getAuthority() == null || !uri.getAuthority().startsWith("*.")))) throw new PolicyFormatException("origin has no host");
+      if (uri.getUserInfo()!=null) throw new PolicyFormatException("origin has userinfo");
+      
+      URI processedURI;
+      //
+      // If the origin contains a *, we construct it using an authority
+      //
+      if (uri.getAuthority().startsWith("*.")){
+        processedURI = new URI(uri.getScheme(),uri.getAuthority(),null,null,null);
+      } else {
+        //
+        // Remove query and path
+        //        
+        processedURI = new URI(uri.getScheme(),null,uri.getHost(),uri.getPort(),null,null,null);
+      }
+      
+      return processedURI.toString();
+    } catch (URISyntaxException e1) {
+      throw new PolicyFormatException("origin is not a valid URI");
+    }
+  }
+
+}

Added: incubator/wookie/trunk/src/org/apache/wookie/proxy/PolicyFormatException.java
URL: http://svn.apache.org/viewvc/incubator/wookie/trunk/src/org/apache/wookie/proxy/PolicyFormatException.java?rev=1203763&view=auto
==============================================================================
--- incubator/wookie/trunk/src/org/apache/wookie/proxy/PolicyFormatException.java (added)
+++ incubator/wookie/trunk/src/org/apache/wookie/proxy/PolicyFormatException.java Fri Nov 18 17:38:53 2011
@@ -0,0 +1,31 @@
+/*
+ * 
+ * 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.apache.wookie.proxy;
+
+/**
+ * An Exception thrown when a Policy is incorrectly formatted
+ */
+public class PolicyFormatException extends Exception {
+
+  private static final long serialVersionUID = 1273238260784592379L;
+  
+  public PolicyFormatException(String reason){
+    super(reason);
+  }
+
+}

Modified: incubator/wookie/trunk/src/org/apache/wookie/proxy/ProxyServlet.java
URL: http://svn.apache.org/viewvc/incubator/wookie/trunk/src/org/apache/wookie/proxy/ProxyServlet.java?rev=1203763&r1=1203762&r2=1203763&view=diff
==============================================================================
--- incubator/wookie/trunk/src/org/apache/wookie/proxy/ProxyServlet.java (original)
+++ incubator/wookie/trunk/src/org/apache/wookie/proxy/ProxyServlet.java Fri Nov 18 17:38:53 2011
@@ -18,6 +18,7 @@ import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.net.MalformedURLException;
 import java.net.URI;
+import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URLDecoder;
 
@@ -28,13 +29,11 @@ import javax.servlet.http.HttpServletReq
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationException;
 import org.apache.commons.httpclient.Header;
 import org.apache.commons.httpclient.auth.AuthenticationException;
 import org.apache.log4j.Logger;
-import org.apache.wookie.beans.IAccessRequest;
 import org.apache.wookie.beans.IWidgetInstance;
-import org.apache.wookie.beans.IWhitelist;
-import org.apache.wookie.beans.IWidget;
 import org.apache.wookie.beans.util.IPersistenceManager;
 import org.apache.wookie.beans.util.PersistenceManagerFactory;
 
@@ -102,7 +101,7 @@ public class ProxyServlet extends HttpSe
 			//
 			if (properties.getBoolean("widget.proxy.usewhitelist") && !isAllowed(bean.getNewUrl().toURI(), instance)){
 				response.sendError(HttpServletResponse.SC_FORBIDDEN,"<error>URL Blocked</error>");
-				fLogger.warn("URL " + bean.getNewUrl().toExternalForm() + " Blocked");
+				fLogger.warn("URL " + bean.getNewUrl().toExternalForm() + " Blocked for scope "+instance.getWidget().getGuid());
 				return;
 			}	
 
@@ -186,32 +185,14 @@ public class ProxyServlet extends HttpSe
 	 * @return
 	 */
 	public boolean isAllowed(URI requestedUri, IWidgetInstance instance){
-		// Check global whitelist
-	    IPersistenceManager persistenceManager = PersistenceManagerFactory.getPersistenceManager();
-		for (IWhitelist whiteList : persistenceManager.findAll(IWhitelist.class)){
-			// TODO - make this better then just comparing the beginning...
-			if(requestedUri.toString().toLowerCase().startsWith(whiteList.getfUrl().toLowerCase()))			
-				return true;
-		}
-
-		// Check widget-specific policies using W3C WARP
-		if (instance != null && isAllowedByPolicy(requestedUri, instance.getWidget())) return true;
-
-		return false;		
-	}
-
-	/**
-	 * Check widget-specific policies using W3C WARP
-	 * @param requestedUri the URI requested
-	 * @param widget the Widget requesting access to the URI
-	 * @return true if a policy grants access to the requested URI
-	 */
-	private boolean isAllowedByPolicy(URI requestedUri, IWidget widget){
-	    IPersistenceManager persistenceManager = PersistenceManagerFactory.getPersistenceManager();
-	    for (IAccessRequest policy: persistenceManager.findApplicableAccessRequests(widget))
-			if (policy.isAllowed(requestedUri)) return true;
-		fLogger.warn("No policy grants widget "+widget.getWidgetTitle("en")+" access to: "+requestedUri.toString());
-		return false;
+	  try {
+      return Policies.getInstance().validate(requestedUri, instance.getWidget().getGuid());
+    } catch (ConfigurationException e) {
+      fLogger.error("Problem with policies configuration", e);
+      return false;
+    } catch (URISyntaxException e) {
+      return false;
+    }
 	}
 
 	/**

Added: incubator/wookie/trunk/src/policies
URL: http://svn.apache.org/viewvc/incubator/wookie/trunk/src/policies?rev=1203763&view=auto
==============================================================================
--- incubator/wookie/trunk/src/policies (added)
+++ incubator/wookie/trunk/src/policies Fri Nov 18 17:38:53 2011
@@ -0,0 +1,36 @@
+##
+## Access Policies File
+##
+## This file is dynamically loaded by Wookie and configures
+## access policies for the proxy service, including both
+## whitelist (global) and widget-specific access policies
+##
+## Note that when new widgets are added to Wookie their
+## access policy requests are automatically added to this
+## file.
+## 
+##
+## The format of entries is widget-uri origin directive
+## The widget-uri and origin may be a wildcard (*). To enable
+## subdomains, use a wildcard as the first element of the
+## origin host, e.g. http://*.apache.org
+##
+## The default (implicit) policy is to deny all origins for all widgets
+##
+## Example:
+##
+## * http://127.0.0.1 ALLOW
+## http\://www.getwookie.org/widgets/weather http://newsrss.bbc.co.uk ALLOW
+## http\://www.getwookie.org/widgets/rss * ALLOW
+##
+## This set of policies would allow access to 127.0.0.1 for all 
+## widgets, while the "weather" widget would be allowed to access 
+## URLs from  http://newsrss.bbc.co.uk, and the RSS widget could 
+## access any URL from any origin.
+##
+## Note that it is necessary to escape the ":" character with a "\"
+##
+
+* http://127.0.0.1 ALLOW
+* http://localhost:8080 ALLOW
+* http://*.apache.org ALLOW
\ No newline at end of file