org.apache.shindig.auth.BlobCrypterSecurityTokenCodec Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of shindig-common Show documentation
Show all versions of shindig-common Show documentation
Common java code for Shindig
The newest version!
/*
* 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 java.io.IOException;
import java.util.Collection;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.lang3.StringUtils;
import org.apache.shindig.common.crypto.BasicBlobCrypter;
import org.apache.shindig.common.crypto.BlobCrypter;
import org.apache.shindig.common.crypto.BlobCrypterException;
import org.apache.shindig.config.ContainerConfig;
import com.google.common.collect.Maps;
import com.google.inject.Inject;
import com.google.inject.Singleton;
/**
* 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>"
*
* @since 2.0.0
*/
@Singleton
public class BlobCrypterSecurityTokenCodec implements SecurityTokenCodec, ContainerConfig.ConfigObserver {
private static final Logger LOG = Logger.getLogger(BlobCrypterSecurityTokenCodec.class.getName());
public static final String SECURITY_TOKEN_KEY = "gadgets.securityTokenKey";
public static final String SIGNED_FETCH_DOMAIN = "gadgets.signedFetchDomain";
/**
* Keys are container ids, values are crypters
*/
protected Map crypters = Maps.newHashMap();
/**
* Keys are container ids, values are domains used for signed fetch.
*/
protected Map domains = Maps.newHashMap();
@Inject
public BlobCrypterSecurityTokenCodec(ContainerConfig config) {
try {
config.addConfigObserver(this, false);
loadContainers(config, config.getContainers(), crypters, domains);
} catch (IOException e) {
// Someone specified securityTokenKeyFile, but we couldn't load the key. That merits killing
// the server.
LOG.log(Level.SEVERE, "Error while initializing BlobCrypterSecurityTokenCodec", e);
throw new RuntimeException(e);
}
}
public void containersChanged(
ContainerConfig config, Collection changed, Collection removed) {
Map newCrypters = Maps.newHashMap(crypters);
Map newDomains = Maps.newHashMap(domains);
try {
loadContainers(config, changed, newCrypters, newDomains);
for (String container : removed) {
newCrypters.remove(container);
newDomains.remove(container);
}
} catch (IOException e) {
// Someone specified securityTokenKeyFile, but we couldn't load the key.
// Keep the old configuration.
LOG.log(Level.WARNING, "There was an error loading an updated container configuration. "
+ "Keeping old configuration.", e);
return;
}
crypters = newCrypters;
domains = newDomains;
}
private void loadContainers(ContainerConfig config, Collection containers,
Map crypters, Map domains) throws IOException {
for (String container : containers) {
String key = config.getString(container, SECURITY_TOKEN_KEY);
if (key != null) {
BlobCrypter crypter = loadCrypter(key);
crypters.put(container, crypter);
}
String domain = config.getString(container, SIGNED_FETCH_DOMAIN);
domains.put(container, domain);
}
}
/**
* Load a BlobCrypter using the specified key. Override this if you have your own BlobCrypter
* implementation.
*
* @param key The security token key.
* @return The BlobCrypter.
*/
protected BlobCrypter loadCrypter(String key) {
return new BasicBlobCrypter(key);
}
/**
* Decrypt and verify the provided security token.
*/
public SecurityToken createToken(Map tokenParameters)
throws SecurityTokenException {
String token = tokenParameters.get(SecurityTokenCodec.SECURITY_TOKEN_NAME);
if (StringUtils.isBlank(token)) {
// No token is present, assume anonymous access
return new AnonymousSecurityToken();
}
String[] fields = StringUtils.split(token, ':');
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 activeUrl = tokenParameters.get(SecurityTokenCodec.ACTIVE_URL_NAME);
String crypted = fields[1];
try {
BlobCrypterSecurityToken st = new BlobCrypterSecurityToken(container, domain, activeUrl,
crypter.unwrap(crypted));
return st.enforceNotExpired();
} catch (BlobCrypterException e) {
throw new SecurityTokenException(e);
}
}
/**
* 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 encodeToken(SecurityToken token) throws SecurityTokenException {
if (!token.getAuthenticationMode().equals(
AuthenticationMode.SECURITY_TOKEN_URL_PARAMETER.name())) {
throw new SecurityTokenException("Can only encode BlogCrypterSecurityTokens");
}
// Test code sends in real AbstractTokens, they have modified time sources in them so
// that we can test token expiration, production tokens are proxied via the SecurityToken interface.
AbstractSecurityToken aToken = token instanceof AbstractSecurityToken ?
(AbstractSecurityToken)token : BlobCrypterSecurityToken.fromToken(token);
BlobCrypter crypter = crypters.get(aToken.getContainer());
if (crypter == null) {
throw new SecurityTokenException("Unknown container " + aToken.getContainer());
}
try {
aToken.setExpires();
return aToken.getContainer() + ':' + crypter.wrap(aToken.toMap());
} catch (BlobCrypterException e) {
throw new SecurityTokenException(e);
}
}
public int getTokenTimeToLive() {
return AbstractSecurityToken.MAX_TOKEN_TTL;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy