You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by rb...@apache.org on 2011/10/02 21:44:15 UTC

svn commit: r1178236 [1/4] - in /shindig/trunk: config/ content/samplecontainer/examples/commoncontainer/ features/src/main/javascript/features/container.util/ features/src/main/javascript/features/container/ features/src/main/javascript/features/rpc/ ...

Author: rbaxter85
Date: Sun Oct  2 19:44:13 2011
New Revision: 1178236

URL: http://svn.apache.org/viewvc?rev=1178236&view=rev
Log:
SHINDIG-1601
Adds some gadget administration features to Shindig.  Also enables arbitration of RPC calls.

Added:
    shindig/trunk/config/gadget-admin.json
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/BasicGadgetAdminStore.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/ContainerAdminData.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/FeatureAdminData.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/GadgetAdminData.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/GadgetAdminModule.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/GadgetAdminStore.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/ServerAdminData.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/admin/
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/admin/BasicGadgetAdminStoreTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/admin/ContainerAdminDataTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/admin/FeatureAdminDataTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/admin/GadgetAdminDataTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/admin/ServerAdminDataTest.java
Removed:
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicGadgetBlacklist.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetBlacklist.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/BasicGadgetBlacklistTest.java
Modified:
    shindig/trunk/config/container.js
    shindig/trunk/content/samplecontainer/examples/commoncontainer/assembler.js
    shindig/trunk/content/samplecontainer/examples/commoncontainer/viewController.js
    shindig/trunk/features/src/main/javascript/features/container.util/util.js
    shindig/trunk/features/src/main/javascript/features/container/container.js
    shindig/trunk/features/src/main/javascript/features/rpc/rpc.js
    shindig/trunk/java/common/conf/shindig.properties
    shindig/trunk/java/common/src/main/java/org/apache/shindig/common/logging/i18n/MessageKeys.java
    shindig/trunk/java/common/src/main/resources/org/apache/shindig/common/logging/i18n/resource.properties
    shindig/trunk/java/common/src/main/resources/org/apache/shindig/common/logging/i18n/resource_en_US.properties
    shindig/trunk/java/gadgets/pom.xml
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetException.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RenderingContext.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/config/CoreUtilConfigContributor.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/features/FeatureRegistry.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/process/Processor.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderingGadgetRewriter.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetsHandlerApi.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetsHandlerService.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/MakeRequestHandler.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ProxyHandler.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/process/ProcessorTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/RenderingGadgetRewriterTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/RewriteModuleTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/StyleTagProxyEmbeddedUrlsVisitorTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/FakeProcessor.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/GadgetsHandlerServiceTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/GadgetsHandlerTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/MakeRequestHandlerTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/MakeRequestServletTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/ProxyHandlerTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/tags/TemplateBasedTagHandlerTest.java
    shindig/trunk/java/samples/src/test/java/org/apache/shindig/social/opensocial/jpa/spi/integration/JpaTestGuiceModule.java
    shindig/trunk/java/server/src/main/webapp/WEB-INF/web.xml
    shindig/trunk/java/server/src/test/java/org/apache/shindig/server/endtoend/EndToEndServer.java

Modified: shindig/trunk/config/container.js
URL: http://svn.apache.org/viewvc/shindig/trunk/config/container.js?rev=1178236&r1=1178235&r2=1178236&view=diff
==============================================================================
--- shindig/trunk/config/container.js (original)
+++ shindig/trunk/config/container.js Sun Oct  2 19:44:13 2011
@@ -131,6 +131,11 @@
 "gadgets.uri.proxy.host" : "${Cur['defaultShindigProxyConcatAuthority']}",
 "gadgets.uri.proxy.path" : "${CONTEXT_ROOT}/gadgets/proxy",
 
+//Enables/Disables feature administration
+"gadgets.admin.enableFeatureAdministration" : "false",
+
+//Enables whitelist checks
+"gadgets.admin.enableGadgetWhitelist" : "false",
 
 // This config data will be passed down to javascript. Please
 // configure your object using the feature name rather than
@@ -301,6 +306,9 @@
     "serverBase": "${CONTEXT_ROOT}/gadgets/"
   },
   "container" : {
-    "relayPath": "${CONTEXT_ROOT}/gadgets/files/container/rpc_relay.html"
+    "relayPath": "${CONTEXT_ROOT}/gadgets/files/container/rpc_relay.html",
+
+    //Enables/Disables the RPC arbitrator functionality in the common container
+    "enableRpcArbitration": false
   }
 }}

Added: shindig/trunk/config/gadget-admin.json
URL: http://svn.apache.org/viewvc/shindig/trunk/config/gadget-admin.json?rev=1178236&view=auto
==============================================================================
--- shindig/trunk/config/gadget-admin.json (added)
+++ shindig/trunk/config/gadget-admin.json Sun Oct  2 19:44:13 2011
@@ -0,0 +1,22 @@
+{
+  "default" : {
+    "gadgets" : {
+      "http://www.google.com/ig/modules/horoscope.xml" : {
+        "features" : ["views", "tabs", "setprefs", "dynamic-height"],
+        "type" : "blacklist"
+      },
+      "http://www.labpixies.com/campaigns/todo/todo.xml" : {
+        "features" : ["setprefs", "dynamic-height", "views"],
+        "type" : "whitelist"
+      },
+      "http://localhost:8080/samplecontainer/examples/media-openGadgets/Media.xml" : {
+        "features" : [],
+        "type" : "blacklist"
+      },
+      "http://localhost:8080/*" : {
+        "features" : [],
+        "type" : "whitelist"
+      }
+    }
+  }
+}
\ No newline at end of file

Modified: shindig/trunk/content/samplecontainer/examples/commoncontainer/assembler.js
URL: http://svn.apache.org/viewvc/shindig/trunk/content/samplecontainer/examples/commoncontainer/assembler.js?rev=1178236&r1=1178235&r2=1178236&view=diff
==============================================================================
--- shindig/trunk/content/samplecontainer/examples/commoncontainer/assembler.js (original)
+++ shindig/trunk/content/samplecontainer/examples/commoncontainer/assembler.js Sun Oct  2 19:44:13 2011
@@ -28,6 +28,9 @@ testConfig[osapi.container.ContainerConf
 //  Create the new CommonContainer
 var CommonContainer = new osapi.container.Container(testConfig);
 
+//Gadget site to title id map
+var siteToTitleMap = {};
+
 // Default the security token for the container. Using this example security token requires enabling
 // the DefaultSecurityTokenCodec to let UrlParameterAuthenticationHandler create valid security token.
 shindig.auth.updateSecurityToken('john.doe:john.doe:appid:cont:url:0:default');
@@ -55,6 +58,8 @@ CommonContainer.init = function() {
     hub: CommonContainer.managedHub
   });
 
+  CommonContainer.rpcRegister('set_title', setTitleHandler);
+
   try {
 
     // Connect to the ManagedHub

Modified: shindig/trunk/content/samplecontainer/examples/commoncontainer/viewController.js
URL: http://svn.apache.org/viewvc/shindig/trunk/content/samplecontainer/examples/commoncontainer/viewController.js?rev=1178236&r1=1178235&r2=1178236&view=diff
==============================================================================
--- shindig/trunk/content/samplecontainer/examples/commoncontainer/viewController.js (original)
+++ shindig/trunk/content/samplecontainer/examples/commoncontainer/viewController.js Sun Oct  2 19:44:13 2011
@@ -79,6 +79,7 @@ $(function() {
 		 if (actionId === 'remove') {
 			if (confirm('This gadget will be removed, ok?')) {
 			  portlet.remove();
+			  delete siteToTitleMap[gadgetSite.getId()];
 		    }
 		 }else if (actionId === 'expand') {
 			//navigate to currentView prior to colapse gadget
@@ -88,6 +89,12 @@ $(function() {
 		 }
     };
 
+    //RPC handler for the set-title feature
+    setTitleHandler = function(rpcArgs, title) {
+      var titleId = siteToTitleMap[rpcArgs.gs.id_];
+      $('#' + titleId).text(title);
+    };
+
     //create a gadget with navigation tool bar header enabling gadget collapse, expand, remove, navigate to view actions.
     buildGadget = function(result,gadgetURL) {
       var gadgetSiteString = "$(this).closest(\'.portlet\').find(\'.portlet-content\').data(\'gadgetSite\')";
@@ -98,11 +105,13 @@ $(function() {
 	    }
       var newGadgetSite = gadgetTemplate;
       newGadgetSite = newGadgetSite.replace(/(gadget-site)/g, '$1-' + curId);
+      siteToTitleMap['gadget-site-' + curId] = 'gadget-title-' + curId;
       $(newGadgetSite).appendTo($('#gadgetArea')).addClass('ui-widget ui-widget-content ui-helper-clearfix ui-corner-all')
       .find('.portlet-header')
       .addClass('ui-widget-header ui-corner-all')
-      .text(result[gadgetURL]['modulePrefs'].title)
-      .append('<ul id="viewsDropdown">' +
+      .text('')
+      .append('<span id="gadget-title-' + curId + '">' + result[gadgetURL]['modulePrefs'].title + '</span>' +
+              '<ul id="viewsDropdown">' +
              '<li class="li-header">' +
                '<a href="#" class="hidden"><span id="dropdownIcon" class="ui-icon ui-icon-triangle-1-s"></span></a>' +
              '<ul>' +
@@ -142,8 +151,10 @@ $(function() {
 	$('#addGadget').click(function() {
 		CommonContainer.preloadGadget(newGadgetUrl.val(), function(result) {
 		  for (var gadgetURL in result) {
-			 buildGadget(result, gadgetURL);
-			 curId++;
+		    if(!result[gadgetURL].error) {
+			    buildGadget(result, gadgetURL);
+			    curId++;
+		    }
 		  }
 
 	      //Clear Values
@@ -161,8 +172,10 @@ $(function() {
 		var testGadgets = $('#gadgetCollection').val().split(',');
 		CommonContainer.preloadGadgets(testGadgets, function(result) {
 		  for (var gadgetURL in result) {
-			buildGadget(result, gadgetURL);
-		    curId++;
+		    if(!result[gadgetURL].error) {
+		      buildGadget(result, gadgetURL);
+		      curId++;
+		    }
 		  }
 
 		});

Modified: shindig/trunk/features/src/main/javascript/features/container.util/util.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/container.util/util.js?rev=1178236&r1=1178235&r2=1178236&view=diff
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/container.util/util.js (original)
+++ shindig/trunk/features/src/main/javascript/features/container.util/util.js Sun Oct  2 19:44:13 2011
@@ -80,7 +80,8 @@ osapi.container.util.newMetadataRequest 
       'views.preferredHeight',
       'views.preferredWidth',
       'expireTimeMs',
-      'responseTimeMs'
+      'responseTimeMs',
+      'rpcServiceIds'
     ]
   };
 };

Modified: shindig/trunk/features/src/main/javascript/features/container/container.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/container/container.js?rev=1178236&r1=1178235&r2=1178236&view=diff
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/container/container.js (original)
+++ shindig/trunk/features/src/main/javascript/features/container/container.js Sun Oct  2 19:44:13 2011
@@ -134,6 +134,8 @@ osapi.container.Container = function(opt
 
   this.initializeMixins_();
 
+  this.setupRpcArbitrator_(config);
+
   this.preloadCaches(config);
 
   this.registerRpcServices_();
@@ -476,6 +478,11 @@ osapi.container.ContainerConfig.GET_PREF
  */
 osapi.container.ContainerConfig.SET_PREFERENCES = 'SET_PREFERENCES';
 /**
+ * Used to arbitrate RPC calls.
+ * @type {function}
+ */
+osapi.container.ContainerConfig.RPC_ARBITRATOR = 'rpcArbitrator';
+/**
  * Used to retrieve security tokens for gadgets.
  * @type {function}
  */
@@ -662,6 +669,42 @@ osapi.container.Container.prototype.regi
   });
 };
 
+/**
+ * Sets up the RPC arbitrator if enabled in the container js.  If
+ * a function is provided in the containers config the container will use
+ * that, if not it will use the default arbitrator.
+ * @private
+ */
+osapi.container.Container.prototype.setupRpcArbitrator_ = function(config) {
+  var container = gadgets.config.get('container');
+  if(typeof container.enableRpcArbitration !== 'undefined' &&
+          container.enableRpcArbitration) {
+    var arbitrate = osapi.container.util.getSafeJsonValue(
+            config, osapi.container.ContainerConfig.RPC_ARBITRATOR, null);
+    if(!arbitrate) {
+      var self = this;
+      //This implementation uses the metadata cache to check to allowed rpc service ids
+      arbitrate = function(serviceId, from) {
+        var site = self.getGadgetSiteByIframeId_(from);
+        if(site && site.getActiveGadgetHolder()) {
+          var cachedResponse = self.service_.getCachedGadgetMetadata(
+                  site.getActiveGadgetHolder().getUrl());
+          if(!cachedResponse.error && cachedResponse.rpcServiceIds) {
+            for(var i = 0, rpcServiceId; rpcServiceId = cachedResponse.rpcServiceIds[i]; i++) {
+              if(rpcServiceId == serviceId) {
+                return true;
+              }
+            }
+          }
+        }
+        gadgets.warn('RPC call to ' + serviceId + ' was not allowed.');
+        return false;
+      };
+    }
+    gadgets.rpc.config({'arbitrator' : arbitrate});
+  }
+};
+
 
 /**
  * Keep track of preloaded gadget URLs. These gadgets will have their tokens

Modified: shindig/trunk/features/src/main/javascript/features/rpc/rpc.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/rpc/rpc.js?rev=1178236&r1=1178235&r2=1178236&view=diff
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/rpc/rpc.js (original)
+++ shindig/trunk/features/src/main/javascript/features/rpc/rpc.js Sun Oct  2 19:44:13 2011
@@ -133,6 +133,7 @@ if (!window['gadgets']['rpc']) { // make
     var rpcId = window.name;
 
     var securityCallback = function() {};
+    var arbitrator = null;
     var LOAD_TIMEOUT = 0;
     var FRAME_PHISH = 1;
     var FORGED_MSG = 2;
@@ -285,6 +286,10 @@ if (!window['gadgets']['rpc']) { // make
       if (rpc && typeof rpc['s'] === 'string' && typeof rpc['f'] === 'string' &&
           rpc['a'] instanceof Array) {
 
+        if (typeof arbitrate === 'function' && !arbitrate(rpc['s'], rpc['f'])) {
+          return;
+        }
+
         // Validate auth token.
         if (authToken[rpc['f']]) {
           // We don't do type coercion here because all entries in the authToken
@@ -802,6 +807,9 @@ if (!window['gadgets']['rpc']) { // make
         if (typeof config.securityCallback === 'function') {
           securityCallback = config.securityCallback;
         }
+        if (typeof config.arbitrator === 'function') {
+          arbitrate = config.arbitrator;
+        }
       },
 
       /**

Modified: shindig/trunk/java/common/conf/shindig.properties
URL: http://svn.apache.org/viewvc/shindig/trunk/java/common/conf/shindig.properties?rev=1178236&r1=1178235&r2=1178236&view=diff
==============================================================================
--- shindig/trunk/java/common/conf/shindig.properties (original)
+++ shindig/trunk/java/common/conf/shindig.properties Sun Oct  2 19:44:13 2011
@@ -21,9 +21,6 @@ shindig.features.default=res://features/
 # Location of container configurations (comma separated)
 shindig.containers.default=res://containers/default/container.js
 
-# A file containing blacklisted gadgets.
-shindig.blacklist.file=
-
 ### Inbound OAuth support
 # The URL base to use for full OAuth support (three-legged)
 shindig.oauth.base-url=/oauth

Modified: shindig/trunk/java/common/src/main/java/org/apache/shindig/common/logging/i18n/MessageKeys.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/logging/i18n/MessageKeys.java?rev=1178236&r1=1178235&r2=1178236&view=diff
==============================================================================
--- shindig/trunk/java/common/src/main/java/org/apache/shindig/common/logging/i18n/MessageKeys.java (original)
+++ shindig/trunk/java/common/src/main/java/org/apache/shindig/common/logging/i18n/MessageKeys.java Sun Oct  2 19:44:13 2011
@@ -83,7 +83,7 @@ public interface MessageKeys {
 	//PipelineExecutor
 	public static final String ERROR_PRELOADING="errorPreloading";
 	//Processor
-	public static final String RENDER_BLACKLISTED_GADGET="renderBlacklistedGadget";
+	public static final String RENDER_NON_WHITELISTED_GADGET="renderNonWhitelistedGadget";
 	//CajaResponseRewriter
 	public static final String FAILED_TO_RETRIEVE="failedToRetrieve";
 	public static final String FAILED_TO_READ="failedToRead";

Modified: shindig/trunk/java/common/src/main/resources/org/apache/shindig/common/logging/i18n/resource.properties
URL: http://svn.apache.org/viewvc/shindig/trunk/java/common/src/main/resources/org/apache/shindig/common/logging/i18n/resource.properties?rev=1178236&r1=1178235&r2=1178236&view=diff
==============================================================================
--- shindig/trunk/java/common/src/main/resources/org/apache/shindig/common/logging/i18n/resource.properties (original)
+++ shindig/trunk/java/common/src/main/resources/org/apache/shindig/common/logging/i18n/resource.properties Sun Oct  2 19:44:13 2011
@@ -87,7 +87,7 @@ unableToConvertScript=Unable to convert 
 errorPreloading=Unexpected error when preloading
 
 ##Processor
-renderBlacklistedGadget=Attempted to render blacklisted gadget: {0}
+renderNonWhitelistedGadget=Attempted to render a gadget not on the whitelist: {0}
 
 ##CajaResponseRewriter, CajaContentRewriter
 failedToRetrieve=Failed to retrieve {0}

Modified: shindig/trunk/java/common/src/main/resources/org/apache/shindig/common/logging/i18n/resource_en_US.properties
URL: http://svn.apache.org/viewvc/shindig/trunk/java/common/src/main/resources/org/apache/shindig/common/logging/i18n/resource_en_US.properties?rev=1178236&r1=1178235&r2=1178236&view=diff
==============================================================================
--- shindig/trunk/java/common/src/main/resources/org/apache/shindig/common/logging/i18n/resource_en_US.properties (original)
+++ shindig/trunk/java/common/src/main/resources/org/apache/shindig/common/logging/i18n/resource_en_US.properties Sun Oct  2 19:44:13 2011
@@ -87,7 +87,7 @@ unableToConvertScript=Unable to convert 
 errorPreloading=Unexpected error when preloading
 
 ##Processor
-renderBlacklistedGadget=Attempted to render blacklisted gadget: {0}
+renderNonWhitelistedGadget=Attempted to render a gadget not on the whitelist: {0}
 
 ##CajaResponseRewriter, CajaContentRewriter
 failedToRetrieve=Failed to retrieve {0}

Modified: shindig/trunk/java/gadgets/pom.xml
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/pom.xml?rev=1178236&r1=1178235&r2=1178236&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/pom.xml (original)
+++ shindig/trunk/java/gadgets/pom.xml Sun Oct  2 19:44:13 2011
@@ -53,6 +53,7 @@
         <includes>
           <include>oauth.json</include>
           <include>OSML_library.xml</include>
+          <include>gadget-admin.json</include>
         </includes>
       </resource>
 

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetException.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetException.java?rev=1178236&r1=1178235&r2=1178236&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetException.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetException.java Sun Oct  2 19:44:13 2011
@@ -71,7 +71,7 @@ public class GadgetException extends Exc
     UNKNOWN_VIEW_SPECIFIED,
 
     // Blacklisting
-    BLACKLISTED_GADGET,
+    NON_WHITELISTED_GADGET,
 
     // OAuth
     OAUTH_STORAGE_ERROR,
@@ -79,14 +79,18 @@ public class GadgetException extends Exc
 
     // Signed fetch
     REQUEST_SIGNING_FAILURE,
-    
+
     // Error in the JavaScript processing pipeline
     JS_PROCESSING_ERROR,
+
+    //Gadget Admin Error
+    GADGET_ADMIN_STORAGE_ERROR,
+    GADGET_ADMIN_FEATURE_NOT_ALLOWED
   }
 
   private final Code code;
   private final int httpStatusCode;
-  
+
   public GadgetException(Code code, int httpStatusCode) {
     this.code = code;
     this.httpStatusCode = httpStatusCode;

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RenderingContext.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RenderingContext.java?rev=1178236&r1=1178235&r2=1178236&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RenderingContext.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RenderingContext.java Sun Oct  2 19:44:13 2011
@@ -37,7 +37,7 @@ public enum RenderingContext {
   // Used when retrieving metadata about a gadget. Processing is generally
   // identical to processing under GADGET, but some operations may be safely
   // skipped, such as preload processing.
-  METADATA(null, null, null),
+  METADATA("gadget", null, null),
 
   // Allows specification of feature JS with an <all> tag. Specially handled in
   // FeatureRegistry: content specified in an <all> tag is chosen if there are

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/BasicGadgetAdminStore.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/BasicGadgetAdminStore.java?rev=1178236&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/BasicGadgetAdminStore.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/BasicGadgetAdminStore.java Sun Oct  2 19:44:13 2011
@@ -0,0 +1,407 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.shindig.gadgets.admin;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.config.ContainerConfig;
+import org.apache.shindig.gadgets.Gadget;
+import org.apache.shindig.gadgets.GadgetContext;
+import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.admin.FeatureAdminData.Type;
+import org.apache.shindig.gadgets.features.FeatureRegistry;
+import org.apache.shindig.gadgets.features.FeatureRegistryProvider;
+import org.apache.shindig.gadgets.spec.Feature;
+import org.apache.shindig.gadgets.spec.GadgetSpec;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import com.google.caja.util.Sets;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+/**
+ * A simple implementation of a gadget administration store.
+ *
+ * @version $Id: $
+ */
+@Singleton
+public class BasicGadgetAdminStore implements GadgetAdminStore {
+
+  // Key in the container config which indicated whether white-listing is turned on.
+  private static final String WHITELIST_KEY = "gadgets.admin.enableGadgetWhitelist";
+
+  // Key in the container config which indicates whether feature administration is turned on.
+  private static final String ENABLE_FEATURE_ADMIN = "gadgets.admin.enableFeatureAdministration";
+
+  private static final Logger LOG = Logger.getLogger(BasicGadgetAdminStore.class.getName());
+
+  private static final String GADGETS = "gadgets";
+  private static final String FEATURES = "features";
+  private static final String TYPE = "type";
+  private static final String BLACKLIST = "blacklist";
+  private static final String CORE_FEATURE = "core";
+
+  private ServerAdminData serverAdminData;
+  private FeatureRegistryProvider featureRegistryProvider;
+  private ContainerConfig config;
+
+  /**
+   * Constructor.
+   */
+  @Inject
+  public BasicGadgetAdminStore(FeatureRegistryProvider featureRegistryProvider,
+          ContainerConfig config) {
+    this.serverAdminData = new ServerAdminData();
+    this.featureRegistryProvider = featureRegistryProvider;
+    this.config = config;
+  }
+
+  /**
+   * Inits the store from a JSON String representing the gadget administration information.
+   *
+   * @param store
+   *          a JSON String representing the gadget administration information.
+   * @throws GadgetException thrown when the store cannot be initiated.
+   */
+  public void init(String store) throws GadgetException {
+    try {
+      JSONObject json = new JSONObject(store);
+      Iterator<?> iter = json.keys();
+      String container;
+      while (iter.hasNext()) {
+        container = (String) iter.next();
+        serverAdminData.addContainerAdminData(container,
+                createContainerData(container, json.getJSONObject(container)));
+      }
+    } catch (JSONException e) {
+      throw new GadgetException(GadgetException.Code.GADGET_ADMIN_STORAGE_ERROR, e);
+    }
+  }
+
+  /**
+   * Creates container security information from a JSON object.
+   *
+   * @param container
+   *          the container the security information is for.
+   * @param containerJson
+   *          the JSON object representing the information.
+   * @return container admin data
+   * @throws JSONException
+   *           thrown when we cannot get the information from the JSON object
+   */
+  private ContainerAdminData createContainerData(String container, JSONObject containerJson)
+          throws JSONException, GadgetException {
+    ContainerAdminData containerData = new ContainerAdminData();
+    if (containerJson.has(GADGETS)) {
+      containerData = new ContainerAdminData(
+              createGadgetAdminDataMap(containerJson.getJSONObject(GADGETS)));
+    }
+    return containerData;
+  }
+
+  /**
+   * Creates a map of gadget administration data.
+   *
+   * @param gadgetsJson
+   *          the JSON object representing the admin data.
+   * @return a map of gadget administration data.
+   * @throws JSONException
+   *           thrown when the map cannot be created.
+   */
+  private Map<String, GadgetAdminData> createGadgetAdminDataMap(JSONObject gadgetsJson)
+          throws JSONException {
+    Map<String, GadgetAdminData> map = Maps.newHashMap();
+    Iterator<?> keys = gadgetsJson.keys();
+    String gadgetUrl;
+    JSONObject gadgetJson;
+    while (keys.hasNext()) {
+      gadgetUrl = (String) keys.next();
+      gadgetJson = gadgetsJson.getJSONObject(gadgetUrl);
+      map.put(gadgetUrl, createGadgetAdminData(gadgetJson));
+    }
+    return map;
+  }
+
+  /**
+   * Creates a gadget administration data.
+   *
+   * @param gadgetJson
+   *          the gadget JSON object.
+   * @return gadget administration data.
+   * @throws JSONException
+   *           thrown when the information cannot found in the JSON object.
+   */
+  private GadgetAdminData createGadgetAdminData(JSONObject gadgetJson) throws JSONException {
+    FeatureAdminData data = new FeatureAdminData();
+    if (gadgetJson.has(FEATURES)) {
+      JSONArray features = gadgetJson.getJSONArray(FEATURES);
+      for (int i = 0; i < features.length(); i++) {
+        data.addFeature(features.getString(i));
+      }
+    }
+
+    data.setType(Type.WHITELIST);
+    if (!data.getFeatures().contains(CORE_FEATURE)) {
+      // Add the core feature since every gadget needs this and it can't be disabled
+      data.addFeature(CORE_FEATURE);
+    }
+    if (gadgetJson.has(TYPE)) {
+      String type = gadgetJson.getString(TYPE);
+      if (type.equalsIgnoreCase(BLACKLIST)) {
+        data.setType(Type.BLACKLIST);
+        //We need core for everything so remove it if it is blacklisted
+        data.removeFeature(CORE_FEATURE);
+      }
+    }
+    return new GadgetAdminData(data);
+  }
+
+  public GadgetAdminData getGadgetAdminData(String container, String gadgetUrl) {
+    GadgetAdminData data = null;
+    if (serverAdminData.hasContainerAdminData(container)) {
+      ContainerAdminData containerData = serverAdminData.getContainerAdminData(container);
+      if (containerData.hasGadgetAdminData(gadgetUrl)) {
+        data = containerData.getGadgetAdminData(gadgetUrl);
+      }
+    }
+    return data;
+  }
+
+  public void setGadgetAdminData(String container, String gadgetUrl, GadgetAdminData adminData) {
+    if (serverAdminData.hasContainerAdminData(container)) {
+      ContainerAdminData containerData = serverAdminData.getContainerAdminData(container);
+      containerData.addGadgetAdminData(gadgetUrl, adminData);
+    }
+  }
+
+  public ContainerAdminData getContainerAdminData(String container) {
+    ContainerAdminData data = null;
+    if (serverAdminData.hasContainerAdminData(container)) {
+      data = serverAdminData.getContainerAdminData(container);
+    }
+    return data;
+  }
+
+  public void setContainerAdminData(String container, ContainerAdminData containerAdminData) {
+    serverAdminData.addContainerAdminData(container, containerAdminData);
+  }
+
+  public ServerAdminData getServerAdminData() {
+    return serverAdminData;
+  }
+
+  /**
+   * Safely gets the container from the gadget by doing null checks.
+   *
+   * @param gadget
+   *          The gadget to get the container from.
+   * @return The container.
+   */
+  private String getSafeContainerFromGadget(Gadget gadget) {
+    GadgetContext context = gadget.getContext();
+    if (context != null) {
+      return context.getContainer();
+    }
+    return null;
+  }
+
+  /**
+   * Safely gets the gadget's URL from the gadget by doing null checks.
+   *
+   * @param gadget
+   *          The gadget to get the URL from.
+   * @return The gadget's URL.
+   */
+  private String getSafeGadgetUrlFromGadget(Gadget gadget) {
+    GadgetSpec spec = gadget.getSpec();
+    if (spec != null) {
+      Uri gadgetUri = spec.getUrl();
+      if (gadgetUri != null) {
+        return gadgetUri.toString();
+      }
+    }
+    return null;
+  }
+
+  public boolean checkFeatureAdminInfo(Gadget gadget) {
+    String container = getSafeContainerFromGadget(gadget);
+    String gadgetUrl = getSafeGadgetUrlFromGadget(gadget);
+    if (container == null || gadgetUrl == null) {
+      return false;
+    }
+
+    if (!isFeatureAdminEnabled(container)) {
+      return true;
+    }
+
+    GadgetContext context = gadget.getContext();
+    try {
+      FeatureRegistry featureRegistry = featureRegistryProvider.get(context.getRepository());
+      if (!hasGadgetAdminData(container, gadgetUrl)) {
+        return false;
+      }
+
+      FeatureAdminData featureAdminData = this.getGadgetAdminData(container, gadgetUrl)
+              .getFeatureAdminData();
+
+      Set<String> features = featureAdminData.getFeatures();
+      if(featureAdminData.getType() == Type.WHITELIST) {
+        //If the admin has specified a whitelist get all the dependencies for the features the admin
+        //has whitelisted and add them as well.  Blacklists need to be more specific.
+        features = Sets.newHashSet(featureRegistry.getFeatures(features));
+      }
+
+      List<String> gadgetFeatures = featureRegistry.getFeatures(getRequiredGadgetFeatures(gadget));
+
+      return areAllFeaturesAllowed(Sets.immutableSet(features),
+              gadgetFeatures, featureAdminData);
+    } catch (GadgetException e) {
+      LOG.log(Level.WARNING, "Exception while getting the FeatureRegistry.");
+      return false;
+    }
+
+  }
+
+  /**
+   * Gets all required gadget features.
+   *
+   * @param gadget
+   *          The gadget to get the gadget features for.
+   * @return The required gadget features.
+   */
+  private List<String> getRequiredGadgetFeatures(Gadget gadget) {
+    List<String> featureNames = Lists.newArrayList();
+    List<Feature> features = gadget.getSpec().getModulePrefs().getAllFeatures();
+    for (Feature feature : features) {
+      if (feature.getRequired()) {
+        featureNames.add(feature.getName());
+      }
+    }
+    return featureNames;
+  }
+
+  /**
+   * Checks the features for a gadget to see if they are allowed.
+   *
+   * @param featuresForGadget
+   *          a set of features that the admin has either whitelist or blacklisted.
+   * @param gadgetFeatures
+   *          a list of features required by the gadget.
+   * @param featureAdminData
+   *          the feature admin data for the gadget.
+   * @return true if all the features for the gadget are allowed, false otherwise.
+   */
+  private boolean areAllFeaturesAllowed(Set<String> featuresForGadget, List<String> gadgetFeatures,
+          FeatureAdminData featureAdminData) {
+    switch (featureAdminData.getType()) {
+    case BLACKLIST:
+      for (String feature : gadgetFeatures) {
+        if (featuresForGadget.contains(feature)) {
+          return false;
+        }
+      }
+
+      break;
+    case WHITELIST:
+    default:
+      return featuresForGadget.containsAll(gadgetFeatures);
+    }
+    return true;
+  }
+
+  public boolean isAllowedFeature(Feature feature, Gadget gadget) {
+    String container = getSafeContainerFromGadget(gadget);
+    String gadgetUrl = getSafeGadgetUrlFromGadget(gadget);
+    if (container == null || gadgetUrl == null) {
+      return false;
+    }
+    if (!isFeatureAdminEnabled(container)) {
+      return true;
+    }
+    if (!hasGadgetAdminData(container, gadgetUrl)) {
+      // If feature administration is not enabled assume the feature is allowed
+      return false;
+    }
+    GadgetAdminData gadgetAdminData = getGadgetAdminData(container, gadgetUrl);
+
+    FeatureAdminData featureAdminData = gadgetAdminData.getFeatureAdminData();
+    String featureName = feature.getName();
+    switch (featureAdminData.getType()) {
+    case BLACKLIST:
+      return !featureAdminData.getFeatures().contains(featureName);
+    case WHITELIST:
+    default:
+      return featureAdminData.getFeatures().contains(featureName);
+    }
+  }
+
+  /**
+   * Determines whether we have gadget administration data for a gadget.
+   *
+   * @param container
+   *          The container the gadget is in.
+   * @param gadgetUrl
+   *          The gadget to check.
+   * @return true if we do have gadget administration data false otherwise.
+   */
+  private boolean hasGadgetAdminData(String container, String gadgetUrl) {
+    return this.getGadgetAdminData(container, gadgetUrl) != null;
+  }
+
+  public boolean isWhitelisted(String container, String gadgetUrl) {
+    if (isWhitelistingEnabled(container)) {
+      return hasGadgetAdminData(container, gadgetUrl);
+    } else {
+      // If the white list checking is not enabled just assume it is there
+      return true;
+    }
+  }
+
+  /**
+   * Determines whether whitelisting is enabled for a container.
+   *
+   * @param container
+   *          The container to check.
+   * @return true if whitelisting is enabled for the container false otherwise.
+   */
+  private boolean isWhitelistingEnabled(String container) {
+    return config.getBool(container, WHITELIST_KEY);
+  }
+
+  /**
+   * Determines whether feature administration is enabled for a container.
+   *
+   * @param container
+   *          The container to check.
+   * @return true if feature administration is enabled for the container false otherwise.
+   */
+  private boolean isFeatureAdminEnabled(String container) {
+    return config.getBool(container, ENABLE_FEATURE_ADMIN);
+  }
+}

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/ContainerAdminData.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/ContainerAdminData.java?rev=1178236&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/ContainerAdminData.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/ContainerAdminData.java Sun Oct  2 19:44:13 2011
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.shindig.gadgets.admin;
+
+import java.util.Map;
+import java.util.Set;
+
+import com.google.caja.util.Maps;
+import com.google.common.base.Objects;
+
+/**
+ * Container's administration data.
+ *
+ * @version $Id: $
+ */
+public class ContainerAdminData {
+  private Map<String, GadgetAdminData> gadgetAdminMap;
+
+  /**
+   * Constructor
+   */
+  public ContainerAdminData() {
+    this(null);
+  }
+
+  /**
+   * Constructor
+   *
+   * @param gadgetAdminMap
+   *          map of gadget URLs to gadget admin data.
+   */
+  public ContainerAdminData(Map<String, GadgetAdminData> gadgetAdminMap) {
+    if (gadgetAdminMap == null) {
+      gadgetAdminMap = Maps.newHashMap();
+    }
+    this.gadgetAdminMap = gadgetAdminMap;
+  }
+
+  /**
+   * Adds gadget administration information for this container.
+   *
+   * @param gadgetUrl
+   *          the URL of the gadget the admin data is for.
+   * @param toAdd
+   *          the administration data for the gadget.
+   */
+  public void addGadgetAdminData(String gadgetUrl, GadgetAdminData toAdd) {
+    if (gadgetUrl != null) {
+      if (toAdd == null) {
+        toAdd = new GadgetAdminData();
+      }
+      this.gadgetAdminMap.put(gadgetUrl, toAdd);
+    }
+  }
+
+  /**
+   * Removes the gadget administration data.
+   *
+   * @param gadgetUrl
+   *          the gadget URL.
+   *
+   * @return The gadget administration data that was removed, or null if there was not gadget
+   *         administration data associated with that gadget URL.
+   */
+  public GadgetAdminData removeGadgetAdminData(String gadgetUrl) {
+    return this.gadgetAdminMap.remove(gadgetUrl);
+  }
+
+  /**
+   * Gets the gadget admin data for a given gadget.
+   *
+   * @param gadgetUrl
+   *          the URL to the gadget to get the administration data for.
+   * @return the gadget admin data.
+   */
+  public GadgetAdminData getGadgetAdminData(String gadgetUrl) {
+    GadgetAdminData match = this.gadgetAdminMap.get(gadgetUrl);
+    if(match != null) {
+      return match;
+    }
+
+    String key = gadgetUrl != null ? getGadgetAdminDataKey(gadgetUrl) : null;
+    return this.gadgetAdminMap.get(key);
+  }
+
+  /**
+   * Gets the gadget admin map.
+   *
+   * @return the gadget admin map.
+   */
+  public Map<String, GadgetAdminData> getGadgetAdminMap() {
+    return this.gadgetAdminMap;
+  }
+
+  /**
+   * Clears the gadget administration data.
+   */
+  public void clearGadgetAdminData() {
+    this.gadgetAdminMap.clear();
+  }
+
+  /**
+   * Determines whether there is administration data for a gadget.
+   *
+   * @param gadgetUrl
+   *          the gadget URL to check.
+   * @return true if there is administration data for a gadget false otherwise.
+   */
+  public boolean hasGadgetAdminData(String gadgetUrl) {
+    if (this.gadgetAdminMap.keySet().contains(gadgetUrl)) {
+      return true;
+    }
+
+    return gadgetUrl != null ? getGadgetAdminDataKey(gadgetUrl) != null : false;
+  }
+
+  /**
+   * Gets the key in the map for the gadget URL.
+   *
+   * @param gadgetUrl
+   *          The gadget URL.
+   * @return The key in the map for the gadget URL.
+   */
+  private String getGadgetAdminDataKey(String gadgetUrl) {
+    Set<String> gadgetUrls = this.gadgetAdminMap.keySet();
+    String key = null;
+    for (String url : gadgetUrls) {
+      if (url.endsWith("*") && gadgetUrl.startsWith(url.substring(0, url.length() - 1))) {
+        if (key == null || (key != null && key.length() < url.length())) {
+          key = url;
+        }
+      }
+    }
+    return key;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof ContainerAdminData) {
+      ContainerAdminData test = (ContainerAdminData) obj;
+      Map<String, GadgetAdminData> testGadgetAdminMap = test.getGadgetAdminMap();
+      return testGadgetAdminMap.equals(this.getGadgetAdminMap());
+
+    }
+    return false;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hashCode(this.gadgetAdminMap);
+  }
+}

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/FeatureAdminData.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/FeatureAdminData.java?rev=1178236&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/FeatureAdminData.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/FeatureAdminData.java Sun Oct  2 19:44:13 2011
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.shindig.gadgets.admin;
+
+import java.util.Set;
+
+import com.google.caja.util.Sets;
+import com.google.common.base.Objects;
+
+/**
+ * Feature administration data for a gadget.
+ *
+ * @version $Id: $
+ */
+public class FeatureAdminData {
+
+  /**
+   * Enumerates the type of feature list.
+   *
+   * @version $Id: $
+   */
+  public enum Type {
+    WHITELIST, BLACKLIST
+  }
+
+  private Set<String> features;
+  private Type type;
+
+  /**
+   * Constructor
+   */
+  public FeatureAdminData() {
+    this(null, null);
+  }
+
+  /**
+   * Constructor
+   *
+   * @param features
+   *          a set of features
+   * @param type
+   *          determines which set takes priority over the other
+   */
+  public FeatureAdminData(Set<String> features, Type type) {
+    if (features == null) {
+      features = Sets.newHashSet();
+    }
+    if (type == null) {
+      type = Type.WHITELIST;
+    }
+    this.features = features;
+    this.type = type;
+  }
+
+  private void addFeatures(Set<String> toAdd, Set<String> features) {
+    for (String feature : toAdd) {
+      if (feature != null) {
+        features.add(feature);
+      }
+    }
+  }
+
+  /**
+   * Adds features for this gadget.
+   *
+   * @param toAdd
+   *          the features for this gadget.
+   */
+  public void addFeatures(Set<String> toAdd) {
+    addFeatures(toAdd, this.features);
+  }
+
+  private Set<String> createSingleFeatureSet(String feature) {
+    Set<String> features = Sets.newHashSet();
+    if (feature != null) {
+      features.add(feature);
+    }
+    return features;
+  }
+
+  /**
+   * Adds an feature for a gadget.
+   *
+   * @param toAdd
+   *          the feature to add.
+   */
+  public void addFeature(String toAdd) {
+    Set<String> features = createSingleFeatureSet(toAdd);
+    addFeatures(features);
+  }
+
+  /**
+   * Clears the set of features.
+   */
+  public void clearFeatures() {
+    this.features.clear();
+  }
+
+  private void removeFeatures(Set<String> toRemove, Set<String> features) {
+    if (toRemove != null && features != null) {
+      for (String feature : toRemove) {
+        features.remove(feature);
+      }
+    }
+  }
+
+  /**
+   * Removes the list of features for a gadget.
+   *
+   * @param toRemove
+   *          the features to remove.
+   */
+  public void removeFeatures(Set<String> toRemove) {
+    removeFeatures(toRemove, this.features);
+  }
+
+  /**
+   * Removes an feature for a gadget.
+   *
+   * @param toRemove
+   *          the feature to remove.
+   */
+  public void removeFeature(String toRemove) {
+    Set<String> features = createSingleFeatureSet(toRemove);
+    removeFeatures(features, this.features);
+  }
+
+  /**
+   * Gets the features.
+   *
+   * @return the features.
+   */
+  public Set<String> getFeatures() {
+    return this.features;
+  }
+
+  /**
+   * Gets the type of features list.
+   *
+   * @return the type of features list.
+   */
+  public Type getType() {
+    return this.type;
+  }
+
+  /**
+   * Sets the type. If this method is passed null than it will default to WHITELIST.
+   *
+   * @param type
+   *          the type to set.
+   */
+  public void setType(Type type) {
+    if (type == null) {
+      type = Type.WHITELIST;
+    }
+    this.type = type;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof FeatureAdminData) {
+      FeatureAdminData test = (FeatureAdminData) obj;
+      return this.getFeatures().equals(test.getFeatures())
+              && this.getType().equals(test.getType());
+    }
+    return false;
+
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hashCode(this.features, this.type);
+  }
+}

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/GadgetAdminData.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/GadgetAdminData.java?rev=1178236&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/GadgetAdminData.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/GadgetAdminData.java Sun Oct  2 19:44:13 2011
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.shindig.gadgets.admin;
+
+import com.google.common.base.Objects;
+
+/**
+ * Information about the container's administration data.
+ *
+ * @version 3.0.0
+ */
+public class GadgetAdminData {
+  // In the future as more gadget admin data is created we
+  // should add it here.
+  private FeatureAdminData featureAdminData;
+
+  /**
+   * Constructor
+   */
+  public GadgetAdminData() {
+    this.featureAdminData = new FeatureAdminData();
+  }
+
+  /**
+   * Constructor
+   *
+   * @param featureAdminData
+   *          Feature administration data for this gadget
+   */
+  public GadgetAdminData(FeatureAdminData featureAdminData) {
+    if (featureAdminData == null) {
+      featureAdminData = new FeatureAdminData();
+    }
+    this.featureAdminData = featureAdminData;
+  }
+
+  /**
+   * Gets the feature administration data for this gadget.
+   *
+   * @return
+   */
+  public FeatureAdminData getFeatureAdminData() {
+    return this.featureAdminData;
+  }
+
+  /**
+   * Sets the feature admin data.
+   *
+   * @param featureAdminData
+   *          the feature admin data to set.
+   */
+  public void setFeatureAdminData(FeatureAdminData featureAdminData) {
+    this.featureAdminData = featureAdminData;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof GadgetAdminData) {
+      GadgetAdminData test = (GadgetAdminData) obj;
+      return this.getFeatureAdminData().equals(test.getFeatureAdminData());
+    }
+    return false;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hashCode(this.featureAdminData);
+  }
+}

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/GadgetAdminModule.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/GadgetAdminModule.java?rev=1178236&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/GadgetAdminModule.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/GadgetAdminModule.java Sun Oct  2 19:44:13 2011
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.shindig.gadgets.admin;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.shindig.common.logging.i18n.MessageKeys;
+import org.apache.shindig.common.util.ResourceLoader;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+/**
+ * Module to load the gadget administration information.
+ *
+ * @version $Id: $
+ */
+public class GadgetAdminModule extends AbstractModule {
+
+  private static final String GADGET_ADMIN_CONFIG = "config/gadget-admin.json";
+  private static final String classname = GadgetAdminModule.class.getName();
+  private static final Logger LOG = Logger.getLogger(classname, MessageKeys.MESSAGES);
+
+  @Override
+  protected void configure() {
+    bind(GadgetAdminStore.class).toProvider(GadgetAdminStoreProvider.class);
+  }
+
+  @Singleton
+  public static class GadgetAdminStoreProvider implements Provider<GadgetAdminStore> {
+    private BasicGadgetAdminStore store;
+
+    @Inject
+    public GadgetAdminStoreProvider(BasicGadgetAdminStore store) {
+      this.store = store;
+      loadStore();
+    }
+
+    private void loadStore() {
+      try {
+        String gadgetAdminString = ResourceLoader.getContent(GADGET_ADMIN_CONFIG);
+        this.store.init(gadgetAdminString);
+      } catch (Throwable t) {
+        if (LOG.isLoggable(Level.WARNING)) {
+          LOG.logp(Level.WARNING, classname, "loadStore", MessageKeys.FAILED_TO_INIT,
+                  new Object[] { GADGET_ADMIN_CONFIG });
+          LOG.log(Level.WARNING, "", t);
+        }
+      }
+    }
+
+    @Override
+    public GadgetAdminStore get() {
+      return store;
+    }
+  }
+}

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/GadgetAdminStore.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/GadgetAdminStore.java?rev=1178236&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/GadgetAdminStore.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/GadgetAdminStore.java Sun Oct  2 19:44:13 2011
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.shindig.gadgets.admin;
+
+import org.apache.shindig.gadgets.Gadget;
+import org.apache.shindig.gadgets.spec.Feature;
+
+/**
+ * Interface for working with the store of gadget administration data.
+ *
+ * @version 3.0.0
+ */
+public interface GadgetAdminStore {
+
+  /**
+   * Gets the administration data for a gadget in a container.
+   *
+   * @param container
+   *          the container id.
+   * @param gadgetUrl
+   *          the gadget URL.
+   * @return the administration data for a gadget in a container
+   */
+  public GadgetAdminData getGadgetAdminData(String container, String gadgetUrl);
+
+  /**
+   * Sets gadget administration data for a gadget in a container.
+   *
+   * @param container
+   *          the container id.
+   * @param gadgetUrl
+   *          the gadget URL.
+   * @param adminData
+   *          administration data.
+   */
+  public void setGadgetAdminData(String container, String gadgetUrl, GadgetAdminData adminData);
+
+  /**
+   * Gets container administration data.
+   *
+   * @param container
+   *          the container to get the administration data for.
+   * @return container administration data.
+   */
+  public ContainerAdminData getContainerAdminData(String container);
+
+  /**
+   * Sets the container administration data..
+   *
+   * @param container
+   *          the container to set the administration data for.
+   * @param containerAdminData
+   *          the container administration data.
+   */
+  public void setContainerAdminData(String container, ContainerAdminData containerAdminData);
+
+  /**
+   * Gets the administration data for the server.
+   *
+   * @return the administration data for the server.
+   */
+  public ServerAdminData getServerAdminData();
+
+  /**
+   * Checks the feature administration data for a gadget.
+   *
+   * @param gadget
+   *          The gadget to check.
+   * @return true if the gadget is allowed to use all the features it requires false otherwise.
+   */
+  public boolean checkFeatureAdminInfo(Gadget gadget);
+
+  /**
+   * If feature administration is enabled for the given container then check to see if the feature
+   * is allowed. If it is not allowed the feature code will not be loaded in the container so we
+   * should not put it in the config.
+   *
+   * @param feature
+   *          The feature to check.
+   * @param gadget
+   *          The gadget to check.
+   * @return true if the feature is allowed to be used by the gadget in the given container.
+   */
+  public boolean isAllowedFeature(Feature feature, Gadget gadget);
+
+  /**
+   * Determines whether a gadget is on the whitelist of trusted gadgets set by the admin.
+   *
+   * @param container
+   *          The container id.
+   * @param gadgetUrl
+   *          The gadget URL.
+   * @return true if the gadget is on the whitelist, false otherwise.
+   */
+  public boolean isWhitelisted(String container, String gadgetUrl);
+
+}

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/ServerAdminData.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/ServerAdminData.java?rev=1178236&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/ServerAdminData.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/admin/ServerAdminData.java Sun Oct  2 19:44:13 2011
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.shindig.gadgets.admin;
+
+import java.util.Map;
+
+import com.google.caja.util.Maps;
+import com.google.common.base.Objects;
+
+/**
+ * Administration data for the server.
+ *
+ * @version $Id: $
+ */
+public class ServerAdminData {
+  private Map<String, ContainerAdminData> containerAdminDataMap;
+
+  /**
+   * Constructor.
+   */
+  public ServerAdminData() {
+    this(null);
+  }
+
+  /**
+   * Constructor.
+   *
+   * @param containerAdminMap
+   *          a map of container IDs to container.
+   */
+  public ServerAdminData(Map<String, ContainerAdminData> containerAdminMap) {
+    if (containerAdminMap == null) {
+      containerAdminMap = Maps.newHashMap();
+    }
+    this.containerAdminDataMap = containerAdminMap;
+  }
+
+  /**
+   * Gets the given containers administration data.
+   *
+   * @param container
+   *          the id of the container.
+   * @return the administration data for the container.
+   */
+  public ContainerAdminData getContainerAdminData(String container) {
+    container = container != null ? container.toLowerCase() : container;
+    return this.containerAdminDataMap.get(container);
+  }
+
+  /**
+   * Removes container administration data.
+   *
+   * @param container
+   *          the container id.
+   */
+  public void removeContainerAdminData(String container) {
+    this.containerAdminDataMap.remove(container);
+  }
+
+  /**
+   * Adds administration data for a container.
+   *
+   * @param container
+   *          the container id the admin data is for.
+   * @param toAdd
+   *          the admin data to add.
+   */
+  public void addContainerAdminData(String container, ContainerAdminData toAdd) {
+    if (container != null) {
+      if (toAdd == null) {
+        toAdd = new ContainerAdminData();
+      }
+      this.containerAdminDataMap.put(container.toLowerCase(), toAdd);
+    }
+  }
+
+  /**
+   * Gets the map of container IDs to container admin data.
+   *
+   * @return the map of container IDs to container admin data.
+   */
+  public Map<String, ContainerAdminData> getContainerAdminDataMap() {
+    return this.containerAdminDataMap;
+  }
+
+  /**
+   * Clears all the container administration data.
+   */
+  public void clearContainerAdminData() {
+    this.containerAdminDataMap.clear();
+  }
+
+  /**
+   * Determines whether there is administration data for the container.
+   *
+   * @param container
+   *          the container to check.
+   * @return true if there is administration data false otherwise.
+   */
+  public boolean hasContainerAdminData(String container) {
+    container = container != null ? container.toLowerCase() : container;
+    return this.containerAdminDataMap.keySet().contains(container);
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof ServerAdminData) {
+      ServerAdminData test = (ServerAdminData) obj;
+      return test.getContainerAdminDataMap().equals(this.containerAdminDataMap);
+    }
+    return false;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hashCode(this.containerAdminDataMap);
+  }
+}

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/config/CoreUtilConfigContributor.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/config/CoreUtilConfigContributor.java?rev=1178236&r1=1178235&r2=1178236&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/config/CoreUtilConfigContributor.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/config/CoreUtilConfigContributor.java Sun Oct  2 19:44:13 2011
@@ -18,16 +18,18 @@
  */
 package org.apache.shindig.gadgets.config;
 
-import com.google.common.collect.Maps;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
 import org.apache.shindig.gadgets.Gadget;
+import org.apache.shindig.gadgets.admin.GadgetAdminStore;
 import org.apache.shindig.gadgets.features.FeatureRegistry;
 import org.apache.shindig.gadgets.spec.Feature;
 
-import java.util.Collection;
-import java.util.Map;
-import java.util.Set;
+import com.google.common.collect.Maps;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
 
 /**
  * Populates the core.util configuration, which at present includes the list
@@ -37,11 +39,15 @@ import java.util.Set;
  */
 @Singleton
 public class CoreUtilConfigContributor implements ConfigContributor {
+
   private final FeatureRegistry registry;
+  private final GadgetAdminStore gadgetAdminStore;
 
   @Inject
-  public CoreUtilConfigContributor(final FeatureRegistry registry) {
+  public CoreUtilConfigContributor(final FeatureRegistry registry,
+          GadgetAdminStore gadgetAdminStore) {
     this.registry = registry;
+    this.gadgetAdminStore = gadgetAdminStore;
   }
 
 
@@ -54,7 +60,8 @@ public class CoreUtilConfigContributor i
 
     for (Feature feature : features) {
       // Skip unregistered features
-      if (!allFeatureNames.contains(feature.getName())) {
+      if ((!allFeatureNames.contains(feature.getName())) ||
+              (!gadgetAdminStore.isAllowedFeature(feature, gadget))) {
         continue;
       }
       // Flatten out the multimap a bit for backwards compatibility:  map keys

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/features/FeatureRegistry.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/features/FeatureRegistry.java?rev=1178236&r1=1178235&r2=1178236&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/features/FeatureRegistry.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/features/FeatureRegistry.java Sun Oct  2 19:44:13 2011
@@ -635,7 +635,7 @@ public class FeatureRegistry {
       String tagMatch = null;
       String directTag = rctx.getFeatureBundleTag();
       for (FeatureBundle bundle : bundles) {
-        if (directTag.equalsIgnoreCase(bundle.getType())) {
+        if (directTag != null && directTag.equalsIgnoreCase(bundle.getType())) {
           tagMatch = directTag;
         }
       }

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/process/Processor.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/process/Processor.java?rev=1178236&r1=1178235&r2=1178236&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/process/Processor.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/process/Processor.java Sun Oct  2 19:44:13 2011
@@ -17,14 +17,19 @@
  */
 package org.apache.shindig.gadgets.process;
 
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.servlet.http.HttpServletResponse;
+
 import org.apache.shindig.common.logging.i18n.MessageKeys;
 import org.apache.shindig.common.uri.Uri;
 import org.apache.shindig.config.ContainerConfig;
 import org.apache.shindig.gadgets.Gadget;
-import org.apache.shindig.gadgets.GadgetBlacklist;
 import org.apache.shindig.gadgets.GadgetContext;
 import org.apache.shindig.gadgets.GadgetException;
 import org.apache.shindig.gadgets.GadgetSpecFactory;
+import org.apache.shindig.gadgets.admin.GadgetAdminStore;
 import org.apache.shindig.gadgets.features.FeatureRegistry;
 import org.apache.shindig.gadgets.features.FeatureRegistryProvider;
 import org.apache.shindig.gadgets.spec.GadgetSpec;
@@ -34,11 +39,6 @@ import org.apache.shindig.gadgets.variab
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.servlet.http.HttpServletResponse;
-
 /**
  * Converts an input Context into an output Gadget.
  */
@@ -50,18 +50,18 @@ public class Processor {
   private final GadgetSpecFactory gadgetSpecFactory;
   private final VariableSubstituter substituter;
   private final ContainerConfig containerConfig;
-  private final GadgetBlacklist blacklist;
+  private final GadgetAdminStore gadgetAdminStore;
   private final FeatureRegistryProvider featureRegistryProvider;
 
   @Inject
   public Processor(GadgetSpecFactory gadgetSpecFactory,
                    VariableSubstituter substituter,
                    ContainerConfig containerConfig,
-                   GadgetBlacklist blacklist,
+                   GadgetAdminStore gadgetAdminStore,
                    FeatureRegistryProvider featureRegistryProvider) {
     this.gadgetSpecFactory = gadgetSpecFactory;
     this.substituter = substituter;
-    this.blacklist = blacklist;
+    this.gadgetAdminStore = gadgetAdminStore;
     this.containerConfig = containerConfig;
     this.featureRegistryProvider = featureRegistryProvider;
   }
@@ -104,13 +104,12 @@ public class Processor {
     }
 
     validateGadgetUrl(url);
-
-    if (blacklist.isBlacklisted(url)) {
+    if (!gadgetAdminStore.isWhitelisted(context.getContainer(), url.toString())) {
       if (LOG.isLoggable(Level.INFO)) {
-        LOG.logp(Level.INFO, classname, "process", MessageKeys.RENDER_BLACKLISTED_GADGET, new Object[] {url});
+        LOG.logp(Level.INFO, classname, "process", MessageKeys.RENDER_NON_WHITELISTED_GADGET, new Object[] {url});
       }
       throw new ProcessingException("The requested gadget is unavailable",
-          HttpServletResponse.SC_FORBIDDEN);
+              HttpServletResponse.SC_FORBIDDEN);
     }
 
     return new Gadget()

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderingGadgetRewriter.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderingGadgetRewriter.java?rev=1178236&r1=1178235&r2=1178236&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderingGadgetRewriter.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderingGadgetRewriter.java Sun Oct  2 19:44:13 2011
@@ -18,6 +18,13 @@
  */
 package org.apache.shindig.gadgets.render;
 
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
 import org.apache.commons.lang.StringUtils;
 import org.apache.shindig.common.JsonSerializer;
 import org.apache.shindig.common.logging.i18n.MessageKeys;
@@ -32,6 +39,7 @@ import org.apache.shindig.gadgets.Gadget
 import org.apache.shindig.gadgets.MessageBundleFactory;
 import org.apache.shindig.gadgets.RenderingContext;
 import org.apache.shindig.gadgets.UnsupportedFeatureException;
+import org.apache.shindig.gadgets.admin.GadgetAdminStore;
 import org.apache.shindig.gadgets.config.ConfigProcessor;
 import org.apache.shindig.gadgets.features.FeatureRegistry;
 import org.apache.shindig.gadgets.features.FeatureRegistryProvider;
@@ -58,13 +66,6 @@ import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 import org.w3c.dom.Text;
 
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
 import com.google.common.base.Splitter;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSortedSet;
@@ -112,6 +113,7 @@ public class RenderingGadgetRewriter imp
   protected final JsServingPipeline jsServingPipeline;
   protected final JsUriManager jsUriManager;
   protected final ConfigProcessor configProcessor;
+  protected final GadgetAdminStore gadgetAdminStore;
 
   protected Set<String> defaultExternLibs = ImmutableSet.of();
 
@@ -131,13 +133,15 @@ public class RenderingGadgetRewriter imp
                                  FeatureRegistryProvider featureRegistryProvider,
                                  JsServingPipeline jsServingPipeline,
                                  JsUriManager jsUriManager,
-                                 ConfigProcessor configProcessor) {
+                                 ConfigProcessor configProcessor,
+                                 GadgetAdminStore gadgetAdminStore) {
     this.messageBundleFactory = messageBundleFactory;
     this.containerConfig = containerConfig;
     this.featureRegistryProvider = featureRegistryProvider;
     this.jsServingPipeline = jsServingPipeline;
     this.jsUriManager = jsUriManager;
     this.configProcessor = configProcessor;
+    this.gadgetAdminStore = gadgetAdminStore;
   }
 
   public void setDefaultDoctypeQName(String qname) {
@@ -297,6 +301,10 @@ public class RenderingGadgetRewriter imp
     FeatureRegistry featureRegistry = featureRegistryProvider.get(repository);
 
     checkRequiredFeatures(gadget, featureRegistry);
+    //Check to make sure all the required features that are about to be injected are allowed
+    if(!gadgetAdminStore.checkFeatureAdminInfo(gadget)) {
+      throw new GadgetException(Code.GADGET_ADMIN_FEATURE_NOT_ALLOWED);
+    }
 
     // Set of extern libraries requested by the container
     Set<String> externForcedLibs = defaultExternLibs;
@@ -312,7 +320,15 @@ public class RenderingGadgetRewriter imp
       injectScript(externForcedLibs, null, false, gadget, headTag, firstHeadChild, "");
     }
 
-    Collection<String> gadgetLibs = gadget.getDirectFeatureDeps();
+    Collection<String> gadgetLibs = Lists.newArrayList(gadget.getDirectFeatureDeps());
+    List<Feature> gadgetFeatures = gadget.getSpec().getModulePrefs().getAllFeatures();
+    for(Feature feature : gadgetFeatures) {
+      if(!feature.getRequired() &&
+              !gadgetAdminStore.isAllowedFeature(feature, gadget)) {
+        //If the feature is optional and the admin has not allowed it don't include it
+        gadgetLibs.remove(feature.getName());
+      }
+    }
 
     // Get config for all features
     Set<String> allLibs = ImmutableSet.<String>builder()

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetsHandlerApi.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetsHandlerApi.java?rev=1178236&r1=1178235&r2=1178236&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetsHandlerApi.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetsHandlerApi.java Sun Oct  2 19:44:13 2011
@@ -18,19 +18,15 @@
  */
 package org.apache.shindig.gadgets.servlet;
 
-import com.google.common.collect.Multimap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
 
 import org.apache.shindig.common.uri.Uri;
-import org.apache.shindig.gadgets.spec.OAuthService.EndPoint;
-import org.apache.shindig.gadgets.spec.OAuthService.Location;
-import org.apache.shindig.gadgets.spec.OAuthService.Method;
 import org.apache.shindig.protocol.conversion.BeanFilter.Unfiltered;
 
-// Keep imports clean, so it is clear what is used by API
-
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
+import com.google.common.collect.Multimap;
 
 /**
  * Gadget Handler Interface data.
@@ -100,6 +96,7 @@ public class GadgetsHandlerApi {
     public Map<String, UserPref> getUserPrefs();
     public Map<String, View> getViews();
     public Boolean getNeedsTokenRefresh();
+    public Set<String> getRpcServiceIds();
   }
 
   public enum ViewContentType {
@@ -251,9 +248,9 @@ public class GadgetsHandlerApi {
   }
 
   public enum Location {
-	    HEADER("auth-header"),
-	    URL("uri-query"),
-	    BODY("post-body");
+    HEADER("auth-header"),
+    URL("uri-query"),
+    BODY("post-body");
 
     private String locationString;
     private Location(String locationString) {

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetsHandlerService.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetsHandlerService.java?rev=1178236&r1=1178235&r2=1178236&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetsHandlerService.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetsHandlerService.java Sun Oct  2 19:44:13 2011
@@ -25,12 +25,6 @@ import java.util.Map;
 import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Lists;
-import com.google.inject.Inject;
-import com.google.inject.name.Named;
 
 import javax.servlet.http.HttpServletResponse;
 
@@ -46,6 +40,12 @@ import org.apache.shindig.gadgets.Gadget
 import org.apache.shindig.gadgets.GadgetContext;
 import org.apache.shindig.gadgets.GadgetException;
 import org.apache.shindig.gadgets.RenderingContext;
+import org.apache.shindig.gadgets.admin.GadgetAdminStore;
+import org.apache.shindig.gadgets.features.ApiDirective;
+import org.apache.shindig.gadgets.features.FeatureRegistry;
+import org.apache.shindig.gadgets.features.FeatureRegistry.FeatureBundle;
+import org.apache.shindig.gadgets.features.FeatureRegistry.LookupResult;
+import org.apache.shindig.gadgets.features.FeatureRegistryProvider;
 import org.apache.shindig.gadgets.http.HttpResponse;
 import org.apache.shindig.gadgets.js.JsException;
 import org.apache.shindig.gadgets.js.JsRequestBuilder;
@@ -61,12 +61,12 @@ import org.apache.shindig.gadgets.spec.M
 import org.apache.shindig.gadgets.spec.OAuthService;
 import org.apache.shindig.gadgets.spec.OAuthSpec;
 import org.apache.shindig.gadgets.spec.UserPref;
-import org.apache.shindig.gadgets.spec.View;
 import org.apache.shindig.gadgets.spec.UserPref.EnumValuePair;
+import org.apache.shindig.gadgets.spec.View;
 import org.apache.shindig.gadgets.uri.IframeUriManager;
 import org.apache.shindig.gadgets.uri.JsUriManager;
-import org.apache.shindig.gadgets.uri.ProxyUriManager;
 import org.apache.shindig.gadgets.uri.JsUriManager.JsUri;
+import org.apache.shindig.gadgets.uri.ProxyUriManager;
 import org.apache.shindig.gadgets.uri.ProxyUriManager.ProxyUri;
 import org.apache.shindig.protocol.conversion.BeanDelegator;
 import org.apache.shindig.protocol.conversion.BeanFilter;
@@ -78,6 +78,13 @@ import com.google.caja.render.JsMinimalP
 import com.google.caja.render.JsPrettyPrinter;
 import com.google.caja.reporting.MessageContext;
 import com.google.caja.reporting.RenderContext;
+import com.google.caja.util.Sets;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
 
 /**
  * Service that interfaces with the system to provide information about gadgets.
@@ -142,6 +149,8 @@ public class GadgetsHandlerService {
   protected final long specRefreshInterval;
   protected final BeanFilter beanFilter;
   protected final CajaContentRewriter cajaContentRewriter;
+  protected final GadgetAdminStore gadgetAdminStore;
+  protected final FeatureRegistryProvider featureRegistryProvider;
 
   @Inject
   public GadgetsHandlerService(TimeSource timeSource, Processor processor,
@@ -149,7 +158,9 @@ public class GadgetsHandlerService {
       ProxyUriManager proxyUriManager, JsUriManager jsUriManager, ProxyHandler proxyHandler,
       JsServingPipeline jsPipeline, JsRequestBuilder jsRequestBuilder,
       @Named("shindig.cache.xml.refreshInterval") long specRefreshInterval,
-      BeanFilter beanFilter, CajaContentRewriter cajaContentRewriter) {
+      BeanFilter beanFilter, CajaContentRewriter cajaContentRewriter,
+      GadgetAdminStore gadgetAdminStore,
+      FeatureRegistryProvider featureRegistryProvider) {
     this.timeSource = timeSource;
     this.processor = processor;
     this.iframeUriManager = iframeUriManager;
@@ -162,6 +173,8 @@ public class GadgetsHandlerService {
     this.specRefreshInterval = specRefreshInterval;
     this.beanFilter = beanFilter;
     this.cajaContentRewriter = cajaContentRewriter;
+    this.gadgetAdminStore = gadgetAdminStore;
+    this.featureRegistryProvider = featureRegistryProvider;
 
     this.beanDelegator = new BeanDelegator(API_CLASSES, ENUM_CONVERSION_MAP);
   }
@@ -179,17 +192,59 @@ public class GadgetsHandlerService {
 
     GadgetContext context = new MetadataGadgetContext(request);
     Gadget gadget = processor.process(context);
+
     boolean needIfrUrl = isFieldIncluded(fields, "iframeurl");
-    if (needIfrUrl && gadget.getCurrentView() == null) {
-      throw new ProcessingException("View " + request.getView() + " does not exist",
-          HttpResponse.SC_BAD_REQUEST);
+    if (needIfrUrl) {
+      if(!gadgetAdminStore.checkFeatureAdminInfo(gadget)) {
+        throw new ProcessingException("Gadget is not trusted to render in this container.",
+              HttpResponse.SC_BAD_REQUEST);
+      }
+
+      if(gadget.getCurrentView() == null) {
+        throw new ProcessingException("View " + request.getView() + " does not exist",
+                HttpResponse.SC_BAD_REQUEST);
+      }
     }
     String iframeUrl = needIfrUrl ? iframeUriManager.makeRenderingUri(gadget).toString() : null;
     Boolean needsTokenRefresh =
         isFieldIncluded(fields, "needstokenrefresh") ?
             gadget.getAllFeatures().contains("auth-refresh") : null;
+
+    Set<String> rpcServiceIds = getRpcServiceIds(gadget);
     return createMetadataResponse(context.getUrl(), gadget.getSpec(), iframeUrl,
-        needsTokenRefresh, fields, timeSource.currentTimeMillis() + specRefreshInterval);
+        needsTokenRefresh, fields, timeSource.currentTimeMillis() + specRefreshInterval,
+        rpcServiceIds);
+  }
+
+  /**
+   * Gets the set of allowed RPC service ids.
+   *
+   * @param gadget
+   *          the gadget to get the service ids for.
+   * @return the set of allowed RPC service ids.
+   */
+  private Set<String> getRpcServiceIds(Gadget gadget) {
+    GadgetContext context = gadget.getContext();
+    Set<String> rpcEndpoints = Sets.newHashSet();
+    List<Feature> modulePrefFeatures = gadget.getSpec().getModulePrefs().getAllFeatures();
+    List<String> featureNames = Lists.newArrayList();
+    for(Feature feature : modulePrefFeatures) {
+      if(gadgetAdminStore.isAllowedFeature(feature, gadget)) {
+        featureNames.add(feature.getName());
+      }
+    }
+    try {
+      FeatureRegistry featureRegistry = featureRegistryProvider.get(context.getRepository());
+      LookupResult result = featureRegistry.getFeatureResources(context, featureRegistry.getFeatures(featureNames),
+              null);
+      List<FeatureBundle> bundles = result.getBundles();
+      for (FeatureBundle bundle : bundles) {
+        rpcEndpoints.addAll(bundle.getApis(ApiDirective.Type.RPC, false));
+      }
+    } catch (GadgetException e) {
+      LOG.log(Level.WARNING, "Error getting features from feature registry", e);
+    }
+    return rpcEndpoints;
   }
 
   private boolean isFieldIncluded(Set<String> fields, String name) {
@@ -471,7 +526,7 @@ public class GadgetsHandlerService {
   @VisibleForTesting
   GadgetsHandlerApi.MetadataResponse createMetadataResponse(
       Uri url, GadgetSpec spec, String iframeUrl, Boolean needsTokenRefresh,
-      Set<String> fields, Long expireTime) {
+      Set<String> fields, Long expireTime, Set<String> rpcServiceIds) {
     return (GadgetsHandlerApi.MetadataResponse) beanFilter.createFilteredBean(
         beanDelegator.createDelegator(spec, GadgetsHandlerApi.MetadataResponse.class,
             ImmutableMap.<String, Object>builder()
@@ -480,7 +535,8 @@ public class GadgetsHandlerService {
                 .put("iframeurl", BeanDelegator.nullable(iframeUrl))
                 .put("needstokenrefresh", BeanDelegator.nullable(needsTokenRefresh))
                 .put("responsetimems", timeSource.currentTimeMillis())
-                .put("expiretimems", BeanDelegator.nullable(expireTime)).build()),
+                .put("expiretimems", BeanDelegator.nullable(expireTime))
+                .put("rpcserviceids", BeanDelegator.nullable(rpcServiceIds)).build()),
         fields);
   }