/*
* Copyright 2015 Bounce Storage, Inc. <info@bouncestorage.com>
*
* 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 com.bouncestorage.swiftproxy;
import static com.google.common.base.Throwables.propagate;
import java.net.URI;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import javax.ws.rs.core.MediaType;
import com.bouncestorage.swiftproxy.v1.InfoResource;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Module;
import org.apache.commons.lang3.RandomStringUtils;
import org.glassfish.jersey.server.ResourceConfig;
import org.jclouds.Constants;
import org.jclouds.ContextBuilder;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.BlobStoreContext;
import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class BounceResourceConfig extends ResourceConfig {
private static final Map<String, MediaType> swiftFormatToMediaType = ImmutableMap.of(
"json", MediaType.APPLICATION_JSON_TYPE,
"application/json", MediaType.APPLICATION_JSON_TYPE,
"xml", MediaType.APPLICATION_XML_TYPE,
"plain", MediaType.TEXT_PLAIN_TYPE
);
private final Logger logger = LoggerFactory.getLogger(getClass());
private final Properties properties;
private URI endPoint;
private BlobStoreLocator locator;
private Cache<String, String> tokensToIdentities = CacheBuilder.newBuilder()
.expireAfterWrite(InfoResource.CONFIG.tempauth.token_life, TimeUnit.SECONDS)
.build();
private Cache<String, AuthenticatedBlobStore> identitiesToBlobStore = CacheBuilder.newBuilder()
.expireAfterWrite(InfoResource.CONFIG.tempauth.token_life, TimeUnit.SECONDS)
.build();
public interface AuthenticatedBlobStore {
BlobStore get(String container, String key);
default BlobStore get(String container) {
return get(container, null);
}
default BlobStore get() {
return get(null, null);
}
}
BounceResourceConfig(Properties properties, BlobStoreLocator locator) {
if (properties == null && locator == null) {
throw new NullPointerException("One of properties or locator must be set");
}
this.properties = properties;
this.locator = locator;
packages(getClass().getPackage().getName());
}
public String authenticate(String identity, String credential) {
AuthenticatedBlobStore blobStore = tryAuthenticate(identity, credential);
if (blobStore != null) {
String token = "AUTH_tk" + RandomStringUtils.randomAlphanumeric(32);
tokensToIdentities.put(token, identity);
identitiesToBlobStore.put(identity, blobStore);
return token;
}
return null;
}
private AuthenticatedBlobStore tryAuthenticate(String identity, String credential) {
if (locator != null) {
Map.Entry<String, BlobStore> entry = locator.locateBlobStore(identity, null, null);
if (entry != null && entry.getKey().equals(credential)) {
logger.debug("blob store for {} found", identity);
return (container, key) -> locator.locateBlobStore(identity, container, key).getValue();
} else {
logger.debug("blob store for {} not found", identity);
}
} else {
logger.debug("fallback to authenticate with configured provider");
String provider = properties.getProperty(Constants.PROPERTY_PROVIDER);
if (provider.equals("transient")) {
/* there's no authentication for transient blobstores, so simply re-use
the previous blobstore so that multiple authentication will reuse the
same namespace */
AuthenticatedBlobStore blobStore = identitiesToBlobStore.getIfPresent(identity);
if (blobStore != null) {
return blobStore;
}
}
try {
BlobStoreContext context = ContextBuilder
.newBuilder(provider)
.overrides(properties)
.credentials(identity, credential)
.modules(ImmutableSet.<Module>of(new SLF4JLoggingModule()))
.build(BlobStoreContext.class);
return (container, key) -> context.getBlobStore();
} catch (Throwable e) {
throw propagate(e);
}
}
return null;
}
public AuthenticatedBlobStore getBlobStore(String authToken) {
String identity = tokensToIdentities.getIfPresent(authToken);
return identitiesToBlobStore.getIfPresent(identity);
}
public static MediaType getMediaType(String format) {
return swiftFormatToMediaType.get(format);
}
public void setEndPoint(URI endPoint) {
this.endPoint = endPoint;
}
public URI getEndPoint() {
return endPoint;
}
public boolean isLocatorSet() {
return locator != null;
}
public void setBlobStoreLocator(BlobStoreLocator newLocator) {
locator = newLocator;
}
}