/* * 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.servlet; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableMap; import com.google.inject.Inject; import org.apache.shindig.auth.SecurityToken; import org.apache.shindig.auth.SecurityTokenCodec; import org.apache.shindig.auth.SecurityTokenException; import org.apache.shindig.common.uri.Uri; import org.apache.shindig.gadgets.Gadget; import org.apache.shindig.gadgets.GadgetContext; import org.apache.shindig.gadgets.RenderingContext; import org.apache.shindig.gadgets.http.HttpResponse; import org.apache.shindig.gadgets.process.ProcessingException; import org.apache.shindig.gadgets.process.Processor; import org.apache.shindig.gadgets.spec.Feature; import org.apache.shindig.gadgets.spec.GadgetSpec; import org.apache.shindig.gadgets.spec.LinkSpec; import org.apache.shindig.gadgets.spec.ModulePrefs; 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.uri.IframeUriManager; import org.apache.shindig.protocol.conversion.BeanDelegator; import org.apache.shindig.protocol.conversion.BeanFilter; import java.util.Locale; import java.util.Map; import java.util.Set; /** * Service that interfaces with the system to provide information about gadgets. * * @since 2.0.0 */ public class GadgetsHandlerService { // Map shindig data class to API interfaces @VisibleForTesting static final Map<Class<?>, Class<?>> apiClasses = new ImmutableMap.Builder<Class<?>, Class<?>>() .put(View.class, GadgetsHandlerApi.View.class) .put(UserPref.class, GadgetsHandlerApi.UserPref.class) .put(EnumValuePair.class, GadgetsHandlerApi.EnumValuePair.class) .put(ModulePrefs.class, GadgetsHandlerApi.ModulePrefs.class) .put(Feature.class, GadgetsHandlerApi.Feature.class) .put(LinkSpec.class, GadgetsHandlerApi.LinkSpec.class) // Enums .put(View.ContentType.class, GadgetsHandlerApi.ViewContentType.class) .put(UserPref.DataType.class, GadgetsHandlerApi.UserPrefDataType.class) .build(); // Provide mapping for internal enums to api enums @VisibleForTesting static final Map<Enum<?>, Enum<?>> enumConversionMap = new ImmutableMap.Builder<Enum<?>, Enum<?>>() // View.ContentType mapping .putAll(BeanDelegator.createDefaultEnumMap(View.ContentType.class, GadgetsHandlerApi.ViewContentType.class)) // UserPref.DataType mapping .putAll(BeanDelegator.createDefaultEnumMap(UserPref.DataType.class, GadgetsHandlerApi.UserPrefDataType.class)) .build(); protected final Processor processor; protected final IframeUriManager iframeUriManager; protected final SecurityTokenCodec securityTokenCodec; protected final BeanDelegator beanDelegator; protected final BeanFilter beanFilter; @Inject public GadgetsHandlerService(Processor processor, IframeUriManager iframeUriManager, SecurityTokenCodec securityTokenCodec, BeanFilter beanFilter) { this.processor = processor; this.iframeUriManager = iframeUriManager; this.securityTokenCodec = securityTokenCodec; this.beanFilter = beanFilter; this.beanDelegator = new BeanDelegator(apiClasses, enumConversionMap); } /** * Get gadget metadata information and iframe url. Support filtering of fields * @param request request parameters * @return gadget metadata nd iframe url * @throws ProcessingException */ public GadgetsHandlerApi.MetadataResponse getMetadata(GadgetsHandlerApi.MetadataRequest request) throws ProcessingException { if (request.getUrl() == null) { throw new ProcessingException("Missing url paramater", HttpResponse.SC_BAD_REQUEST); } if (request.getContainer() == null) { throw new ProcessingException("Missing container paramater", HttpResponse.SC_BAD_REQUEST); } if (request.getFields() == null) { throw new ProcessingException("Missing fields paramater", HttpResponse.SC_BAD_REQUEST); } Set<String> fields = beanFilter.processBeanFields(request.getFields()); GadgetContext context = new MetadataGadgetContext(request); Gadget gadget = processor.process(context); String iframeUrl = (fields.contains("iframeurl") || fields.contains(BeanFilter.ALL_FIELDS)) ? iframeUriManager.makeRenderingUri(gadget).toString() : null; Boolean needsTokenRefresh = (fields.contains("needstokenrefresh") || fields.contains(BeanFilter.ALL_FIELDS)) ? gadget.getAllFeatures().contains("auth-refresh") : null; return createMetadataResponse(context.getUrl(), gadget.getSpec(), iframeUrl, needsTokenRefresh, fields); } /** * Create security token * @param request token paramaters (gadget, owner and viewer) * @return Security token * @throws SecurityTokenException */ public GadgetsHandlerApi.TokenResponse getToken(GadgetsHandlerApi.TokenRequest request) throws SecurityTokenException, ProcessingException { if (request.getUrl() == null) { throw new ProcessingException("Missing url paramater", HttpResponse.SC_BAD_REQUEST); } if (request.getContainer() == null) { throw new ProcessingException("Missing container paramater", HttpResponse.SC_BAD_REQUEST); } if (request.getFields() == null) { throw new ProcessingException("Missing fields paramater", HttpResponse.SC_BAD_REQUEST); } Set<String> fields = beanFilter.processBeanFields(request.getFields()); SecurityToken tokenData = convertToken(request.getToken(), request.getContainer(), request.getUrl().toString()); String token = securityTokenCodec.encodeToken(tokenData); return createTokenResponse(request.getUrl(), token, fields); } /** * GadgetContext for metadata request. Used by the gadget processor */ protected class MetadataGadgetContext extends GadgetContext { private final GadgetsHandlerApi.MetadataRequest request; private final SecurityToken token; public MetadataGadgetContext(GadgetsHandlerApi.MetadataRequest request) { this.request = request; this.token = convertToken( request.getToken(), request.getContainer(), request.getUrl().toString()); } @Override public Uri getUrl() { return request.getUrl(); } @Override public String getContainer() { return request.getContainer(); } @Override public RenderingContext getRenderingContext() { return RenderingContext.METADATA; } @Override public int getModuleId() { return 1; } @Override public Locale getLocale() { return request.getLocale(); } @Override public boolean getIgnoreCache() { return request.getIgnoreCache(); } @Override public boolean getDebug() { return request.getDebug(); } @Override public String getView() { return request.getView(); } @Override public SecurityToken getToken() { return token; } } private SecurityToken convertToken(GadgetsHandlerApi.TokenData token, String container, String url) { if (token == null) { return null; } return beanDelegator.createDelegator(token, SecurityToken.class, ImmutableMap.<String, Object>of("container", container, "appid", url, "appurl", url)); } public GadgetsHandlerApi.BaseResponse createBaseResponse(Uri url, String error) { return beanDelegator.createDelegator(error, GadgetsHandlerApi.BaseResponse.class, ImmutableMap.<String, Object>of("url", url, "error", error)); } private GadgetsHandlerApi.MetadataResponse createMetadataResponse( Uri url, GadgetSpec spec, String iframeUrl, Boolean needsTokenRefresh, Set<String> fields) { return (GadgetsHandlerApi.MetadataResponse) beanFilter.createFilteredBean( beanDelegator.createDelegator(spec, GadgetsHandlerApi.MetadataResponse.class, ImmutableMap.<String, Object>of( "url", url, "error", BeanDelegator.NULL, "iframeurl", BeanDelegator.nullable(iframeUrl), "needstokenrefresh", BeanDelegator.nullable(needsTokenRefresh))), fields); } private GadgetsHandlerApi.TokenResponse createTokenResponse( Uri url, String token, Set<String> fields) { return (GadgetsHandlerApi.TokenResponse) beanFilter.createFilteredBean( beanDelegator.createDelegator("empty", GadgetsHandlerApi.TokenResponse.class, ImmutableMap.<String, Object>of("url", url, "error", BeanDelegator.NULL, "token", BeanDelegator.nullable(token))), fields); } }