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/05/13 16:56:12 UTC
svn commit: r1102772 - in /incubator/wookie/trunk: WebContent/WEB-INF/web.xml
src-tests/org/apache/wookie/tests/functional/ApiKeyControllerTest.java
src/org/apache/wookie/controller/ApiKeyController.java
src/org/apache/wookie/helpers/ApiKeyHelper.java
Author: scottbw
Date: Fri May 13 14:56:12 2011
New Revision: 1102772
URL: http://svn.apache.org/viewvc?rev=1102772&view=rev
Log:
Added an admin web API for managing API keys (see WOOKIE-208). However, there are some issues to resolve around API key migration and so PUT support is not currently enabled.
Added:
incubator/wookie/trunk/src-tests/org/apache/wookie/tests/functional/ApiKeyControllerTest.java
incubator/wookie/trunk/src/org/apache/wookie/controller/ApiKeyController.java
incubator/wookie/trunk/src/org/apache/wookie/helpers/ApiKeyHelper.java
Modified:
incubator/wookie/trunk/WebContent/WEB-INF/web.xml
Modified: incubator/wookie/trunk/WebContent/WEB-INF/web.xml
URL: http://svn.apache.org/viewvc/incubator/wookie/trunk/WebContent/WEB-INF/web.xml?rev=1102772&r1=1102771&r2=1102772&view=diff
==============================================================================
--- incubator/wookie/trunk/WebContent/WEB-INF/web.xml (original)
+++ incubator/wookie/trunk/WebContent/WEB-INF/web.xml Fri May 13 14:56:12 2011
@@ -209,6 +209,20 @@
<servlet-name>WhitelistController</servlet-name>
<url-pattern>/whitelist/*</url-pattern>
</servlet-mapping>
+
+ <servlet>
+ <description></description>
+ <display-name>ApiKeys</display-name>
+ <servlet-name>ApiKeyController</servlet-name>
+ <servlet-class>
+ org.apache.wookie.controller.ApiKeyController
+ </servlet-class>
+ <load-on-startup>2</load-on-startup>
+ </servlet>
+ <servlet-mapping>
+ <servlet-name>ApiKeyController</servlet-name>
+ <url-pattern>/keys/*</url-pattern>
+ </servlet-mapping>
<servlet>
<description></description>
@@ -364,7 +378,20 @@
<role-name>widgetadmin</role-name>
</auth-constraint>
</security-constraint>
- <security-constraint>
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>ApiKeyController</web-resource-name>
+ <url-pattern>/keys/*</url-pattern>
+ <http-method>GET</http-method>
+ <http-method>DELETE</http-method>
+ <http-method>PUT</http-method>
+ <http-method>POST</http-method>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>widgetadmin</role-name>
+ </auth-constraint>
+ </security-constraint>
+ <security-constraint>
<web-resource-collection>
<web-resource-name>UpdatesController</web-resource-name>
<url-pattern>/updates/*</url-pattern>
Added: incubator/wookie/trunk/src-tests/org/apache/wookie/tests/functional/ApiKeyControllerTest.java
URL: http://svn.apache.org/viewvc/incubator/wookie/trunk/src-tests/org/apache/wookie/tests/functional/ApiKeyControllerTest.java?rev=1102772&view=auto
==============================================================================
--- incubator/wookie/trunk/src-tests/org/apache/wookie/tests/functional/ApiKeyControllerTest.java (added)
+++ incubator/wookie/trunk/src-tests/org/apache/wookie/tests/functional/ApiKeyControllerTest.java Fri May 13 14:56:12 2011
@@ -0,0 +1,328 @@
+/*
+ *
+ * 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.functional;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.methods.DeleteMethod;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.apache.commons.httpclient.methods.PutMethod;
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.input.SAXBuilder;
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * Functional tests for API key management
+ */
+public class ApiKeyControllerTest extends AbstractControllerTest {
+
+ private static final String APIKEY_SERVICE_LOCATION_VALID = TEST_SERVER_LOCATION+"keys";
+
+ @Test
+ public void getEntriesUnauthorized(){
+ try {
+ HttpClient client = new HttpClient();
+ GetMethod get = new GetMethod(APIKEY_SERVICE_LOCATION_VALID);
+ client.executeMethod(get);
+ int code = get.getStatusCode();
+ assertEquals(401, code);
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ fail("get failed");
+ }
+ }
+
+ @Test
+ public void getKeys(){
+ try {
+ HttpClient client = new HttpClient();
+ GetMethod get = new GetMethod(APIKEY_SERVICE_LOCATION_VALID);
+ setAuthenticationCredentials(client);
+ client.executeMethod(get);
+ int code = get.getStatusCode();
+ assertEquals(200, code);
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ fail("get failed");
+ }
+ }
+
+ @Test
+ public void addKey(){
+ try {
+ HttpClient client = new HttpClient();
+ PostMethod post = new PostMethod(APIKEY_SERVICE_LOCATION_VALID);
+ setAuthenticationCredentials(client);
+ post.setParameter("apikey", "TEST_KEY");
+ post.setParameter("email", "test@incubator.apache.org");
+ client.executeMethod(post);
+ int code = post.getStatusCode();
+ assertEquals(201, code);
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ fail("post failed");
+ }
+ try {
+ HttpClient client = new HttpClient();
+ GetMethod get = new GetMethod(APIKEY_SERVICE_LOCATION_VALID);
+ setAuthenticationCredentials(client);
+ client.executeMethod(get);
+ int code = get.getStatusCode();
+ assertEquals(200, code);
+ assertTrue(get.getResponseBodyAsString().contains("TEST_KEY"));
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ fail("get failed");
+ }
+ }
+
+ @Test
+ public void removeKey(){
+ String id = null;
+ try {
+ HttpClient client = new HttpClient();
+ GetMethod get = new GetMethod(APIKEY_SERVICE_LOCATION_VALID);
+ setAuthenticationCredentials(client);
+ client.executeMethod(get);
+ int code = get.getStatusCode();
+ assertEquals(200, code);
+ assertTrue(get.getResponseBodyAsString().contains("TEST_KEY"));
+ // Get the ID
+ Document doc = new SAXBuilder().build(get.getResponseBodyAsStream());
+ for (Object key: doc.getRootElement().getChildren()){
+ Element keyElement = (Element)key;
+ if (keyElement.getAttributeValue("value").equals("TEST_KEY")){
+ id = keyElement.getAttributeValue("id");
+ }
+ }
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ fail("get failed");
+ }
+ try {
+ HttpClient client = new HttpClient();
+ DeleteMethod del = new DeleteMethod(APIKEY_SERVICE_LOCATION_VALID+"/"+id);
+ setAuthenticationCredentials(client);
+ client.executeMethod(del);
+ int code = del.getStatusCode();
+ assertEquals(200, code);
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ fail("delete failed");
+ }
+ try {
+ HttpClient client = new HttpClient();
+ GetMethod get = new GetMethod(APIKEY_SERVICE_LOCATION_VALID);
+ get.setRequestHeader("Content-type", "application/json");
+ setAuthenticationCredentials(client);
+ client.executeMethod(get);
+ int code = get.getStatusCode();
+ assertEquals(200, code);
+ System.out.println(get.getResponseBodyAsString());
+ assertFalse(get.getResponseBodyAsString().contains("TEST_KEY"));
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ fail("get failed");
+ }
+ }
+
+ @Test
+ public void removeNonExistantEntry(){
+ try {
+ HttpClient client = new HttpClient();
+ DeleteMethod del = new DeleteMethod(APIKEY_SERVICE_LOCATION_VALID+"/99999999");
+ setAuthenticationCredentials(client);
+ client.executeMethod(del);
+ int code = del.getStatusCode();
+ assertEquals(404, code);
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ fail("delete failed");
+ }
+ }
+
+ @Test
+ public void addEntryNoEmailOrValue(){
+ try {
+ HttpClient client = new HttpClient();
+ PostMethod post = new PostMethod(APIKEY_SERVICE_LOCATION_VALID);
+ setAuthenticationCredentials(client);
+ client.executeMethod(post);
+ int code = post.getStatusCode();
+ assertEquals(400, code);
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ fail("post failed");
+ }
+ }
+
+ @Test
+ public void addDuplicateEntry(){
+ try {
+ HttpClient client = new HttpClient();
+ PostMethod post = new PostMethod(APIKEY_SERVICE_LOCATION_VALID);
+ setAuthenticationCredentials(client);
+ post.setParameter("apikey", "DUPLICATION_TEST");
+ post.setParameter("email", "test@127.0.0.1");
+ client.executeMethod(post);
+ int code = post.getStatusCode();
+ assertEquals(201, code);
+ client.executeMethod(post);
+ code = post.getStatusCode();
+ assertEquals(409, code);
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ fail("post failed");
+ }
+ }
+
+ @Test
+ @Ignore
+ public void migrateAPIKey(){
+
+ String keyId = null;
+ // Create a new key
+ try {
+ HttpClient client = new HttpClient();
+ PostMethod post = new PostMethod(APIKEY_SERVICE_LOCATION_VALID);
+ setAuthenticationCredentials(client);
+ post.setParameter("apikey", "MIGRATION_TEST_KEY_1");
+ post.setParameter("email", "test@incubator.apache.org");
+ client.executeMethod(post);
+ int code = post.getStatusCode();
+ assertEquals(201, code);
+
+ GetMethod get = new GetMethod(APIKEY_SERVICE_LOCATION_VALID);
+ setAuthenticationCredentials(client);
+ client.executeMethod(get);
+
+ // Get the ID
+ Document doc = new SAXBuilder().build(get.getResponseBodyAsStream());
+ for (Object key: doc.getRootElement().getChildren()){
+ Element keyElement = (Element)key;
+ if (keyElement.getAttributeValue("value").equals("MIGRATION_TEST_KEY_1")){
+ keyId = keyElement.getAttributeValue("id");
+ }
+ }
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ fail("get failed");
+ }
+
+ // Create a widget instance
+ String instance_id_key = null;
+ try {
+ HttpClient client = new HttpClient();
+ PostMethod post = new PostMethod(TEST_INSTANCES_SERVICE_URL_VALID);
+ post.setQueryString("api_key=MIGRATION_TEST_KEY_1&widgetid="+WIDGET_ID_VALID+"&userid=test&shareddatakey=migration_test");
+ client.executeMethod(post);
+ int code = post.getStatusCode();
+ String response = post.getResponseBodyAsString();
+ instance_id_key = post.getResponseBodyAsString().substring(response.indexOf("<identifier>")+12, response.indexOf("</identifier>"));
+ assertEquals(201,code);
+ post.releaseConnection();
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ fail("post failed");
+ }
+
+ // Set participant
+ try {
+ HttpClient client = new HttpClient();
+ PostMethod post = new PostMethod(TEST_PARTICIPANTS_SERVICE_URL_VALID);
+ post.setQueryString("api_key=MIGRATION_TEST_KEY_1&widgetid="+WIDGET_ID_VALID+"&userid=test&shareddatakey=migration_test&participant_id=1&participant_display_name=bob&participant_thumbnail_url=http://www.test.org");
+ client.executeMethod(post);
+ int code = post.getStatusCode();
+ assertEquals(201,code);
+ post.releaseConnection();
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ fail("post failed");
+ }
+
+ // Migrate key
+ try {
+ HttpClient client = new HttpClient();
+ PutMethod put = new PutMethod(APIKEY_SERVICE_LOCATION_VALID+"/"+keyId);
+ put.setQueryString("apikey=MIGRATION_TEST_KEY_2&email=test@127.0.0.1");
+ setAuthenticationCredentials(client);
+ client.executeMethod(put);
+ int code = put.getStatusCode();
+ assertEquals(200,code);
+ put.releaseConnection();
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ fail("post failed");
+ }
+
+ // Get instance again using the new key - should be 200 not 201
+ try {
+ HttpClient client = new HttpClient();
+ PostMethod post = new PostMethod(TEST_INSTANCES_SERVICE_URL_VALID);
+ post.setQueryString("api_key=MIGRATION_TEST_KEY_2&widgetid="+WIDGET_ID_VALID+"&userid=test&shareddatakey=migration_test");
+ client.executeMethod(post);
+ int code = post.getStatusCode();
+ assertEquals(200,code);
+ post.releaseConnection();
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ fail("post failed");
+ }
+
+ // Get participant
+ try {
+ HttpClient client = new HttpClient();
+ GetMethod get = new GetMethod(TEST_PARTICIPANTS_SERVICE_URL_VALID);
+ get.setQueryString("api_key=MIGRATION_TEST_KEY_2&id_key="+instance_id_key);
+ client.executeMethod(get);
+ int code = get.getStatusCode();
+ assertEquals(200,code);
+ String response = get.getResponseBodyAsString();
+ assertTrue(response.contains("<participant id=\"1\" display_name=\"bob\" thumbnail_url=\"http://www.test.org\" />"));
+ get.releaseConnection();
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ fail("get failed");
+ }
+
+
+ }
+}
Added: incubator/wookie/trunk/src/org/apache/wookie/controller/ApiKeyController.java
URL: http://svn.apache.org/viewvc/incubator/wookie/trunk/src/org/apache/wookie/controller/ApiKeyController.java?rev=1102772&view=auto
==============================================================================
--- incubator/wookie/trunk/src/org/apache/wookie/controller/ApiKeyController.java (added)
+++ incubator/wookie/trunk/src/org/apache/wookie/controller/ApiKeyController.java Fri May 13 14:56:12 2011
@@ -0,0 +1,155 @@
+/*
+ *
+ * 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 java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.log4j.Logger;
+import org.apache.wookie.beans.IApiKey;
+import org.apache.wookie.beans.util.IPersistenceManager;
+import org.apache.wookie.beans.util.PersistenceManagerFactory;
+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.ApiKeyHelper;
+
+/**
+ * Admin controller for creating, updating and listing API keys
+ *
+ * <ul>
+ * <li>GET /keys - index <em>requires authentication</em></li>
+ * <li>POST /keys {apikey, email} - create <em>requires authentication</em></li>
+ * <li>PUT /keys/{id} {apikey, email} - update <em>requires authentication</em></li>
+ * <li>DELETE /keys/{id} - remove <em>requires authentication</em></li>
+ * </ul>
+ *
+ * Note that PUT support is disabled until a solution is available for migrating
+ * widget instances, shared data and participants
+ */
+
+public class ApiKeyController extends Controller {
+
+ private static final long serialVersionUID = -2985087125119757793L;
+ static Logger _logger = Logger.getLogger(ApiKeyController.class.getName());
+
+ /* (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 {
+ IPersistenceManager persistenceManager = PersistenceManagerFactory.getPersistenceManager();
+ IApiKey[] apiKeys = persistenceManager.findAll(IApiKey.class);
+ returnXml(ApiKeyHelper.createXML(apiKeys),response);
+ }
+
+ /* (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 {
+ String value = request.getParameter("apikey");
+ String email = request.getParameter("email");
+ if (value == null || email == null || value.trim().length() ==0 || email.trim().length() == 0) throw new InvalidParametersException();
+ IPersistenceManager persistenceManager = PersistenceManagerFactory.getPersistenceManager();
+ Map<String, Object> values = new HashMap<String, Object>();
+ values.put("value", value);
+ values.put("email", email);
+ if (persistenceManager.findByValues(IApiKey.class, values).length > 0){
+ throw new ResourceDuplicationException();
+ }
+
+ IApiKey apiKey = persistenceManager.newInstance(IApiKey.class);
+ apiKey.setValue(value);
+ apiKey.setEmail(email);
+ persistenceManager.save(apiKey);
+ _logger.info("New API key registered for "+email);
+ return true;
+ }
+
+
+/*
+ @Override
+ protected void update(String resourceId, HttpServletRequest request)
+ throws ResourceNotFoundException, InvalidParametersException,
+ UnauthorizedAccessException {
+
+ String value = request.getParameter("apikey");
+ String email = request.getParameter("email");
+ if (value == null || email == null || value.trim().length() ==0 || email.trim().length() == 0) throw new InvalidParametersException();
+
+ IPersistenceManager persistenceManager = PersistenceManagerFactory.getPersistenceManager();
+ IApiKey apiKey = persistenceManager.findById(IApiKey.class, resourceId);
+ if (apiKey == null) throw new ResourceNotFoundException();
+ String oldValue = apiKey.getValue();
+ String oldEmail = apiKey.getEmail();
+ apiKey.setEmail(email);
+ apiKey.setValue(value);
+ persistenceManager.save(apiKey);
+ migrateWidgetInstances(apiKey, oldValue);
+ _logger.info("API key updated from "+oldEmail+" : "+oldValue + " to "+email + " : "+value);
+
+ }
+*/
+
+
+ /**
+ * Migrates any widget instances using the previous key to the new key.
+ * @param key
+ * @param oldValue
+ */
+ /*
+ private void migrateWidgetInstances(IApiKey apiKey, String oldValue){
+ IPersistenceManager persistenceManager = PersistenceManagerFactory.getPersistenceManager();
+ //
+ IWidgetInstance[] instances = persistenceManager.findByValue(IWidgetInstance.class, "apiKey", oldValue);
+ for (IWidgetInstance instance: instances){
+ //FIXME this doesn't really work right now because we can't migrate the shared data key. To do
+ // this we would need to store both the original shared data key and the internal version in the WidgetInstance or
+ // somewhere. (We then ought to rename one of them to make it clear which it is). We could then transparently
+ // update all the sharedDataKeys for instances, participants and shared data
+ instance.setApiKey(apiKey.getValue());
+ persistenceManager.save(instance);
+ }
+ }
+ */
+
+ /* (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 {
+ IPersistenceManager persistenceManager = PersistenceManagerFactory.getPersistenceManager();
+ IApiKey apiKey = persistenceManager.findById(IApiKey.class, resourceId);
+ if (apiKey == null) throw new ResourceNotFoundException();
+ persistenceManager.delete(apiKey);
+ _logger.info("API key deleted for "+apiKey.getEmail());
+ return true;
+ }
+
+}
Added: incubator/wookie/trunk/src/org/apache/wookie/helpers/ApiKeyHelper.java
URL: http://svn.apache.org/viewvc/incubator/wookie/trunk/src/org/apache/wookie/helpers/ApiKeyHelper.java?rev=1102772&view=auto
==============================================================================
--- incubator/wookie/trunk/src/org/apache/wookie/helpers/ApiKeyHelper.java (added)
+++ incubator/wookie/trunk/src/org/apache/wookie/helpers/ApiKeyHelper.java Fri May 13 14:56:12 2011
@@ -0,0 +1,50 @@
+/*
+ *
+ * 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.helpers;
+
+import org.apache.wookie.beans.IApiKey;
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.output.XMLOutputter;
+
+/**
+ * View helper for API keys
+ */
+public class ApiKeyHelper {
+
+ /**
+ * Create an XML representation of a set of API keys
+ * @param keys the keys to serialize
+ * @return a String containing the XML serialization of the API keys
+ */
+ public static String createXML(IApiKey[] keys){
+ Document document = new Document();
+ Element keysElement = new Element("keys");
+
+ for(IApiKey key: keys){
+ Element keyElement = new Element("key");
+ keyElement.setAttribute("id", String.valueOf(key.getId()));
+ keyElement.setAttribute("value", key.getValue());
+ keyElement.setAttribute("email", key.getEmail());
+ keysElement.addContent(keyElement);
+ }
+ document.setRootElement(keysElement);
+ XMLOutputter outputter = new XMLOutputter();
+ return outputter.outputString(document);
+ }
+}