You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@shindig.apache.org by be...@apache.org on 2008/09/24 03:10:30 UTC

svn commit: r698410 - in /incubator/shindig/trunk/java/common/src: main/java/org/apache/shindig/auth/ test/java/org/apache/shindig/auth/

Author: beaton
Date: Tue Sep 23 18:10:30 2008
New Revision: 698410

URL: http://svn.apache.org/viewvc?rev=698410&view=rev
Log:
Provide real security tokens.  There is no way to enable these without writing
Guice modules, yet.

Also remove the toSerialForm method from SecurityToken, since Shindig never
uses it.

Added:
    incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/BlobCrypterSecurityToken.java   (with props)
    incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/BlobCrypterSecurityTokenDecoder.java   (with props)
    incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/auth/BlobCrypterSecurityTokenDecoderTest.java   (with props)
    incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/auth/BlobCrypterSecurityTokenTest.java   (with props)
Modified:
    incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/SecurityToken.java

Added: incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/BlobCrypterSecurityToken.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/BlobCrypterSecurityToken.java?rev=698410&view=auto
==============================================================================
--- incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/BlobCrypterSecurityToken.java (added)
+++ incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/BlobCrypterSecurityToken.java Tue Sep 23 18:10:30 2008
@@ -0,0 +1,177 @@
+/*
+ * 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.auth;
+
+import com.google.common.collect.Maps;
+
+import org.apache.shindig.common.crypto.BlobCrypter;
+import org.apache.shindig.common.crypto.BlobCrypterException;
+
+import java.util.Map;
+
+/**
+ * Authentication based on a provided BlobCrypter.
+ * 
+ * Wire format is "<container>:<encrypted-and-signed-token>"
+ * 
+ * Container is included so different containers can use different security tokens if necessary.
+ */
+public class BlobCrypterSecurityToken implements SecurityToken {
+  
+  private static final int MAX_TOKEN_LIFETIME_SECS = 3600;
+  
+  private static final String OWNER_KEY = "o";
+  private static final String VIEWER_KEY = "v";
+  private static final String GADGET_KEY = "g";
+  private static final String GADGET_INSTANCE_KEY = "i";
+  private static final String TRUSTED_JSON_KEY = "j";
+  
+  private final BlobCrypter crypter;
+  private final String container;
+  private final String domain;
+  
+  private String ownerId;
+  private String viewerId;
+  private String appUrl;
+  private long moduleId;
+  private String trustedJson;
+  
+  /**
+   * Create a new security token.
+   * 
+   * @param crypter used for encryption and signing
+   * @param container container that is issuing the token
+   * @param domain domain to use for signed fetch with default signed fetch key.
+   */
+  public BlobCrypterSecurityToken(BlobCrypter crypter, String container, String domain) {
+    this.crypter = crypter;
+    this.container = container;
+    this.domain = domain;
+  }
+  
+  /**
+   * Decrypt and verify a token.  Note this is not public, use BlobCrypterSecurityTokenDecoder
+   * instead.
+   * 
+   * @param crypter crypter to use for decryption
+   * @param container container that minted the token
+   * @param domain oauth_consumer_key to use for signed fetch with default key
+   * @param token the encrypted token (just the portion after the first ":")
+   * @return the decrypted, verified token.
+   * 
+   * @throws BlobCrypterException
+   */
+  static BlobCrypterSecurityToken decrypt(BlobCrypter crypter, String container, String domain,
+        String token) throws BlobCrypterException {
+    Map<String, String> values = crypter.unwrap(token, MAX_TOKEN_LIFETIME_SECS);
+    BlobCrypterSecurityToken t = new BlobCrypterSecurityToken(crypter, container, domain);
+    t.setOwnerId(values.get(OWNER_KEY));
+    t.setViewerId(values.get(VIEWER_KEY));
+    t.setAppUrl(values.get(GADGET_KEY));
+    String moduleId = values.get(GADGET_INSTANCE_KEY);
+    if (moduleId != null) {
+      t.setModuleId(Long.parseLong(moduleId));
+    }
+    t.setTrustedJson(values.get(TRUSTED_JSON_KEY));
+    return t;
+  }
+  
+  /**
+   * Encrypt and sign the token.  The returned value is *not* web safe, it should be URL
+   * encoded before being used as a form parameter.
+   */
+  public String encrypt() throws BlobCrypterException {
+    Map<String, String> values = Maps.newHashMap();
+    if (ownerId != null) {
+      values.put(OWNER_KEY, ownerId);
+    }
+    if (viewerId != null) {
+      values.put(VIEWER_KEY, viewerId);
+    }
+    if (appUrl != null) {
+      values.put(GADGET_KEY, appUrl);
+    }
+    if (moduleId != 0) {
+      values.put(GADGET_INSTANCE_KEY, Long.toString(moduleId));
+    }
+    if (trustedJson != null) {
+      values.put(TRUSTED_JSON_KEY, trustedJson);
+    }
+    return container + ":" + crypter.wrap(values);
+  }
+  
+  // Legacy value for signed fetch, opensocial 0.8 prefers opensocial_app_url
+  public String getAppId() {
+    return appUrl;
+  }
+  
+  public String getAppUrl() {
+    return appUrl;
+  }
+  
+  public void setAppUrl(String appUrl) {
+    this.appUrl = appUrl;
+  }
+  
+  // Used for oauth_consumer_key for signed fetch with default key.  This is a weird spot for this.
+  public String getDomain() {
+    return domain;
+  }
+  
+  public long getModuleId() {
+    return moduleId;
+  }
+  
+  public void setModuleId(long moduleId) {
+    this.moduleId = moduleId;
+  }
+  
+  public String getOwnerId() {
+    return ownerId;
+  }
+  
+  public void setOwnerId(String ownerId) {
+    this.ownerId = ownerId;
+  }
+  
+  public String getTrustedJson() {
+    return trustedJson;
+  }
+  
+  public void setTrustedJson(String trustedJson) {
+    this.trustedJson = trustedJson;
+  }
+   
+  // Our tokens are static, we could change this to periodically update the token.
+  public String getUpdatedToken() {
+    return null;
+  }
+  
+  public String getViewerId() {
+    return viewerId;
+  }
+  
+  public void setViewerId(String viewerId) {
+    this.viewerId = viewerId;
+  }
+  
+  public boolean isAnonymous() {
+    return false;
+  }
+}

Propchange: incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/BlobCrypterSecurityToken.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/BlobCrypterSecurityTokenDecoder.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/BlobCrypterSecurityTokenDecoder.java?rev=698410&view=auto
==============================================================================
--- incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/BlobCrypterSecurityTokenDecoder.java (added)
+++ incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/BlobCrypterSecurityTokenDecoder.java Tue Sep 23 18:10:30 2008
@@ -0,0 +1,118 @@
+/*
+ * 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.auth;
+
+import com.google.common.collect.Maps;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import org.apache.shindig.common.ContainerConfig;
+import org.apache.shindig.common.crypto.BasicBlobCrypter;
+import org.apache.shindig.common.crypto.BlobCrypter;
+import org.apache.shindig.common.crypto.BlobCrypterException;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Provides security token decoding services.  Configuration is via containers.js.  Each container
+ * should specify (or inherit)
+ * 
+ * securityTokenKeyFile: path to file containing a key to use for verifying tokens.
+ * signedFetchDomain: oauth_consumer_key value to use for signed fetch using default key.
+ * 
+ * Creating a key is best done with a command line like this:
+ * 
+ *     dd if=/dev/random bs=32 count=1  | openssl base64 > /tmp/key.txt
+ * 
+ * Wire format is "<container>:<encrypted-and-signed-token>"
+ */
+@Singleton
+public class BlobCrypterSecurityTokenDecoder implements SecurityTokenDecoder {
+
+  public static final String SECURITY_TOKEN_KEY_FILE = "securityTokenKeyFile";
+  
+  public static final String SIGNED_FETCH_DOMAIN = "signedFetchDomain";
+  
+  /**
+   * Keys are container ids, values are crypters
+   */
+  private Map<String, BlobCrypter> crypters = Maps.newHashMap();
+  
+  /**
+   * Keys are container ids, values are domains used for signed fetch.
+   */
+  private Map<String, String> domains = Maps.newHashMap();
+
+  @Inject
+  public BlobCrypterSecurityTokenDecoder(ContainerConfig config) {
+    try {
+      for (String container : config.getContainers()) {
+        String keyFile = config.get(container, SECURITY_TOKEN_KEY_FILE);
+        if (keyFile != null) {
+          BlobCrypter crypter = loadCrypterFromFile(new File(keyFile));
+          crypters.put(container, crypter);
+        }
+        String domain = config.get(container, SIGNED_FETCH_DOMAIN);
+        domains.put(container, domain);
+      }
+    } catch (IOException e) {
+      // Someone specified securityTokenKeyFile, but we couldn't load the key.  That merits killing
+      // the server.
+      throw new RuntimeException(e);
+    }
+  }
+  
+  /**
+   * Load a BlobCrypter from the specified file.  Override this if you have your own
+   * BlobCrypter implementation.
+   */
+  protected BlobCrypter loadCrypterFromFile(File file) throws IOException {
+    return new BasicBlobCrypter(file);
+  }
+  
+  /**
+   * Decrypt and verify the provided security token.
+   */
+  public SecurityToken createToken(Map<String, String> tokenParameters)
+      throws SecurityTokenException {
+    String token = tokenParameters.get(SecurityTokenDecoder.SECURITY_TOKEN_NAME);
+    if (token == null || token.trim().length() == 0) {
+      // No token is present, assume anonymous access
+      return new AnonymousSecurityToken();
+    }
+    String[] fields = token.split(":");
+    if (fields.length != 2) {
+      throw new SecurityTokenException("Invalid security token " + token);
+    }
+    String container = fields[0];
+    BlobCrypter crypter = crypters.get(container);
+    if (crypter == null) {
+      throw new SecurityTokenException("Unknown container " + token);
+    }
+    String domain = domains.get(container);
+    String crypted = fields[1];
+    try {
+      return BlobCrypterSecurityToken.decrypt(crypter, container, domain, crypted);
+    } catch (BlobCrypterException e) {
+      throw new SecurityTokenException(e);
+    }
+  }
+}

Propchange: incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/BlobCrypterSecurityTokenDecoder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/SecurityToken.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/SecurityToken.java?rev=698410&r1=698409&r2=698410&view=diff
==============================================================================
--- incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/SecurityToken.java (original)
+++ incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/auth/SecurityToken.java Tue Sep 23 18:10:30 2008
@@ -23,16 +23,7 @@
  * Use in conjunction with @code SecurityTokenDecoder.
  */
 public interface SecurityToken {
-
-  /**
-   * Serializes the token into a string. This can be the exact same as
-   * toString; using a different name here is only to force interface
-   * compliance.
-   *
-   * @return A string representation of the token.
-   */
-  public String toSerialForm();
-
+  
   /**
    * @return the owner from the token, or null if there is none.
    */

Added: incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/auth/BlobCrypterSecurityTokenDecoderTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/auth/BlobCrypterSecurityTokenDecoderTest.java?rev=698410&view=auto
==============================================================================
--- incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/auth/BlobCrypterSecurityTokenDecoderTest.java (added)
+++ incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/auth/BlobCrypterSecurityTokenDecoderTest.java Tue Sep 23 18:10:30 2008
@@ -0,0 +1,233 @@
+/*
+ * 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.auth;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import org.apache.shindig.common.ContainerConfig;
+import org.apache.shindig.common.crypto.BasicBlobCrypter;
+import org.apache.shindig.common.crypto.BlobCrypter;
+import org.apache.shindig.common.util.CharsetUtil;
+import org.apache.shindig.common.util.FakeTimeSource;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Tests for BlobCrypterSecurityTokenDecoder
+ */
+public class BlobCrypterSecurityTokenDecoderTest {
+
+  private BlobCrypterSecurityTokenDecoder decoder;
+  private FakeTimeSource timeSource = new FakeTimeSource();
+
+  @Before
+  public void setUp() throws Exception {
+    ContainerConfig config = new ContainerConfig(null) {
+      @Override
+      public String get(String container, String name) {
+        if (BlobCrypterSecurityTokenDecoder.SECURITY_TOKEN_KEY_FILE.equals(name)) {
+          return getContainerKey(container);
+        }
+        if (BlobCrypterSecurityTokenDecoder.SIGNED_FETCH_DOMAIN.equals(name)) {
+          return container + ".com";
+        }
+        throw new RuntimeException("Mock not smart enough, unknown name " + name);
+      }
+      
+      @Override
+      public Collection<String> getContainers() {
+        return Lists.newArrayList("container", "example");
+      }
+    };
+    decoder = new DecoderWithLoadStubbedOut(config);
+  }
+  
+  private String getContainerKey(String container) {
+    return "KEY FOR CONTAINER " + container;
+  }
+  
+  private BlobCrypter getBlobCrypter(String fileName) {
+    BasicBlobCrypter c = new BasicBlobCrypter(CharsetUtil.getUtf8Bytes(fileName));
+    c.timeSource = timeSource;
+    return c;
+  }
+  
+  /**
+   * Stubs out loading the key file.
+   */
+  private class DecoderWithLoadStubbedOut extends BlobCrypterSecurityTokenDecoder {
+
+    public DecoderWithLoadStubbedOut(ContainerConfig config) {
+      super(config);
+    }
+    
+    /**
+     * @return a crypter based on the name of the file passed in, rather than the contents
+     */
+    @Override
+    protected BlobCrypter loadCrypterFromFile(File file) throws IOException {
+      if (file.getPath().contains("fail")) {
+        throw new IOException("Load failed: " + file);
+      }
+      return getBlobCrypter(file.getPath());
+    }
+  }
+  
+  @Test
+  public void testCreateToken() throws Exception {
+    BlobCrypterSecurityToken t = new BlobCrypterSecurityToken(
+        getBlobCrypter(getContainerKey("container")), "container", null);
+    t.setAppUrl("http://www.example.com/gadget.xml");
+    t.setModuleId(12345L);
+    t.setOwnerId("owner");
+    t.setViewerId("viewer");
+    t.setTrustedJson("trusted");
+    String encrypted = t.encrypt();
+    
+    SecurityToken t2 = decoder.createToken(
+        Maps.immutableMap(SecurityTokenDecoder.SECURITY_TOKEN_NAME, encrypted));
+    
+    assertEquals("http://www.example.com/gadget.xml", t2.getAppId());
+    assertEquals("http://www.example.com/gadget.xml", t2.getAppUrl());
+    assertEquals("container.com", t2.getDomain());
+    assertEquals(12345L, t2.getModuleId());
+    assertEquals("owner", t2.getOwnerId());
+    assertEquals("viewer", t2.getViewerId());
+    assertEquals("trusted", t2.getTrustedJson());
+  }
+  
+  @Test
+  public void testUnknownContainer() throws Exception {
+    BlobCrypterSecurityToken t = new BlobCrypterSecurityToken(
+        getBlobCrypter(getContainerKey("container")), "container", null);
+    t.setAppUrl("http://www.example.com/gadget.xml");
+    t.setModuleId(12345L);
+    t.setOwnerId("owner");
+    t.setViewerId("viewer");
+    t.setTrustedJson("trusted");
+    String encrypted = t.encrypt();
+    encrypted = encrypted.replace("container:", "other:");
+    
+    try {
+      decoder.createToken(Maps.immutableMap(SecurityTokenDecoder.SECURITY_TOKEN_NAME, encrypted));
+      fail("should have reported that container was unknown");
+    } catch (SecurityTokenException e) {
+      assertTrue(e.getMessage(), e.getMessage().contains("Unknown container"));
+    }
+  }
+  
+  @Test
+  public void testWrongContainer() throws Exception {
+    BlobCrypterSecurityToken t = new BlobCrypterSecurityToken(
+        getBlobCrypter(getContainerKey("container")), "container", null);
+    t.setAppUrl("http://www.example.com/gadget.xml");
+    t.setModuleId(12345L);
+    t.setOwnerId("owner");
+    t.setViewerId("viewer");
+    t.setTrustedJson("trusted");
+    String encrypted = t.encrypt();
+    encrypted = encrypted.replace("container:", "example:");
+    
+    try {
+      decoder.createToken(Maps.immutableMap(SecurityTokenDecoder.SECURITY_TOKEN_NAME, encrypted));
+      fail("should have tried to decrypt with wrong key");
+    } catch (SecurityTokenException e) {
+      assertTrue(e.getMessage(), e.getMessage().contains("Invalid token signature"));
+    }
+  }
+  
+  @Test
+  public void testExpired() throws Exception {
+    BlobCrypterSecurityToken t = new BlobCrypterSecurityToken(
+        getBlobCrypter(getContainerKey("container")), "container", null);
+    t.setAppUrl("http://www.example.com/gadget.xml");
+    t.setModuleId(12345L);
+    t.setOwnerId("owner");
+    t.setViewerId("viewer");
+    t.setTrustedJson("trusted");
+    String encrypted = t.encrypt();
+    
+    timeSource.incrementSeconds(3600 + 181); // one hour plus clock skew
+    try {
+      decoder.createToken(Maps.immutableMap(SecurityTokenDecoder.SECURITY_TOKEN_NAME, encrypted));
+      fail("should have expired");
+    } catch (SecurityTokenException e) {
+      assertTrue(e.getMessage(), e.getMessage().contains("Blob expired"));
+    }
+  }
+  
+  @Test
+  public void testMalformed() throws Exception {
+    try {
+      decoder.createToken(Maps.immutableMap(SecurityTokenDecoder.SECURITY_TOKEN_NAME, "foo"));
+      fail("should have tried to decrypt with wrong key");
+    } catch (SecurityTokenException e) {
+      assertTrue(e.getMessage(), e.getMessage().contains("Invalid security token foo"));
+    }
+  }
+  
+  @Test
+  public void testAnonymous() throws Exception {
+    SecurityToken t = decoder.createToken(
+        Maps.immutableMap(SecurityTokenDecoder.SECURITY_TOKEN_NAME, "   "));
+    assertTrue(t.isAnonymous());
+    
+    Map<String, String> empty = Maps.immutableMap();
+    t = decoder.createToken(empty);
+    assertTrue(t.isAnonymous());
+  }
+    
+  @Test
+  public void testLoadFailure() throws Exception {
+    ContainerConfig config = new ContainerConfig(null) {
+      @Override
+      public String get(String container, String name) {
+        if (BlobCrypterSecurityTokenDecoder.SECURITY_TOKEN_KEY_FILE.equals(name)) {
+          return getContainerKey(container);
+        }
+        if (BlobCrypterSecurityTokenDecoder.SIGNED_FETCH_DOMAIN.equals(name)) {
+          return container + ".com";
+        }
+        throw new RuntimeException("Mock not smart enough, unknown name " + name);
+      }
+      
+      @Override
+      public Collection<String> getContainers() {
+        return Lists.newArrayList("container", "example", "failure");
+      }
+    };
+    
+    try {
+      new DecoderWithLoadStubbedOut(config);
+      fail("Should have failed to load crypter");
+    } catch (RuntimeException e) {
+      assertTrue(e.getMessage(), e.getMessage().contains("Load failed"));
+    }
+  }
+}

Propchange: incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/auth/BlobCrypterSecurityTokenDecoderTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/auth/BlobCrypterSecurityTokenTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/auth/BlobCrypterSecurityTokenTest.java?rev=698410&view=auto
==============================================================================
--- incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/auth/BlobCrypterSecurityTokenTest.java (added)
+++ incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/auth/BlobCrypterSecurityTokenTest.java Tue Sep 23 18:10:30 2008
@@ -0,0 +1,104 @@
+/*
+ * 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.auth;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.apache.shindig.common.crypto.BasicBlobCrypter;
+import org.apache.shindig.common.crypto.BlobExpiredException;
+import org.apache.shindig.common.crypto.Crypto;
+import org.apache.shindig.common.util.FakeTimeSource;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests BlobCrypterSecurityToken
+ */
+public class BlobCrypterSecurityTokenTest {
+
+  private static final String CONTAINER = "container";
+  private static final String DOMAIN = "example.com";
+  
+  private FakeTimeSource timeSource = new FakeTimeSource();
+  private BasicBlobCrypter crypter;
+  
+  @Before
+  public void setUp() {
+    crypter = new BasicBlobCrypter(Crypto.getRandomBytes(20));
+    crypter.timeSource = timeSource;
+  }
+  
+  @Test
+  public void testNullValues() throws Exception {
+    BlobCrypterSecurityToken t = new BlobCrypterSecurityToken(crypter, CONTAINER, DOMAIN);
+    String token = t.encrypt();
+    assertTrue("should start with container: " + token, token.startsWith("container:"));
+    String[] fields = token.split(":");
+    BlobCrypterSecurityToken t2 =
+        BlobCrypterSecurityToken.decrypt(crypter, CONTAINER, DOMAIN, fields[1]);
+    assertNull(t2.getAppId(), t2.getAppId());
+    assertNull(t2.getAppUrl(), t2.getAppUrl());
+    assertEquals(DOMAIN, t2.getDomain());
+    assertEquals(0, t2.getModuleId());
+    assertNull(t2.getOwnerId(), t2.getOwnerId());
+    assertNull(t2.getViewerId(), t2.getViewerId());
+    assertNull(t2.getTrustedJson(), t2.getTrustedJson());
+    assertNull(t2.getUpdatedToken(), t2.getUpdatedToken());
+  }
+  
+  @Test
+  public void testRealValues() throws Exception {
+    BlobCrypterSecurityToken t = new BlobCrypterSecurityToken(crypter, CONTAINER, DOMAIN);
+    t.setAppUrl("http://www.example.com/gadget.xml");
+    t.setModuleId(12345L);
+    t.setOwnerId("owner");
+    t.setViewerId("viewer");
+    t.setTrustedJson("trusted");
+    String token = t.encrypt();
+    assertTrue("should start with container: " + token, token.startsWith("container:"));
+    String[] fields = token.split(":");
+    BlobCrypterSecurityToken t2 =
+        BlobCrypterSecurityToken.decrypt(crypter, CONTAINER, DOMAIN, fields[1]);
+    assertEquals("http://www.example.com/gadget.xml", t2.getAppId());
+    assertEquals("http://www.example.com/gadget.xml", t2.getAppUrl());
+    assertEquals(DOMAIN, t2.getDomain());
+    assertEquals(12345L, t2.getModuleId());
+    assertEquals("owner", t2.getOwnerId());
+    assertEquals("viewer", t2.getViewerId());
+    assertEquals("trusted", t2.getTrustedJson());
+  }
+  
+  @Test
+  public void testExpired() throws Exception {
+    BlobCrypterSecurityToken t = new BlobCrypterSecurityToken(crypter, CONTAINER, DOMAIN);
+    String token = t.encrypt();
+    // one hour plus clock skew
+    timeSource.incrementSeconds(3600 + 181);
+    String[] fields = token.split(":");
+    try {
+      BlobCrypterSecurityToken.decrypt(crypter, CONTAINER, DOMAIN, fields[1]);
+      fail("Token should have expired");
+    } catch (BlobExpiredException e) {
+      // good
+    }
+  }
+}

Propchange: incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/auth/BlobCrypterSecurityTokenTest.java
------------------------------------------------------------------------------
    svn:eol-style = native