/*
* 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.usergrid.rest.management.organizations.applications;
import com.fasterxml.jackson.jaxrs.json.annotation.JSONP;
import com.google.common.base.Preconditions;
import org.apache.amber.oauth2.common.exception.OAuthSystemException;
import org.apache.amber.oauth2.common.message.OAuthResponse;
import org.apache.commons.lang.NullArgumentException;
import org.apache.commons.lang.StringUtils;
import org.apache.usergrid.management.ApplicationInfo;
import org.apache.usergrid.management.OrganizationInfo;
import org.apache.usergrid.management.export.ExportService;
import org.apache.usergrid.persistence.EntityManager;
import org.apache.usergrid.persistence.core.util.Health;
import org.apache.usergrid.persistence.queue.impl.UsergridAwsCredentials;
import org.apache.usergrid.rest.AbstractContextResource;
import org.apache.usergrid.rest.ApiResponse;
import org.apache.usergrid.rest.applications.ServiceResource;
import org.apache.usergrid.rest.management.organizations.applications.imports.ImportsResource;
import org.apache.usergrid.rest.security.annotations.RequireOrganizationAccess;
import org.apache.usergrid.rest.utils.JSONPUtils;
import org.apache.usergrid.security.oauth.ClientCredentialsInfo;
import org.apache.usergrid.security.providers.SignInAsProvider;
import org.apache.usergrid.security.providers.SignInProviderFactory;
import org.apache.usergrid.services.ServiceManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import static javax.servlet.http.HttpServletResponse.*;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
@Component("org.apache.usergrid.rest.management.organizations.applications.ApplicationResource")
@Scope("prototype")
@Produces({
MediaType.APPLICATION_JSON,
"application/javascript",
"application/x-javascript",
"text/ecmascript",
"application/ecmascript",
"text/jscript"
})
public class ApplicationResource extends AbstractContextResource {
private static final Logger logger = LoggerFactory.getLogger(ApplicationResource.class);
public static final String CONFIRM_APPLICATION_IDENTIFIER = "confirm_application_identifier";
//@Autowired
//protected ExportService exportService;
OrganizationInfo organization;
UUID applicationId;
ApplicationInfo application;
@Autowired
private SignInProviderFactory signInProviderFactory;
public ApplicationResource() {
}
public ApplicationResource init( OrganizationInfo organization, UUID applicationId ) {
this.organization = organization;
this.applicationId = applicationId;
return this;
}
public ApplicationResource init( OrganizationInfo organization, ApplicationInfo application ) {
this.organization = organization;
applicationId = application.getId();
this.application = application;
return this;
}
@RequireOrganizationAccess
@GET
@JSONP
@Produces({MediaType.APPLICATION_JSON, "application/javascript"})
public ApiResponse getApplication(
@Context UriInfo ui, @QueryParam("callback") @DefaultValue("callback") String callback )
throws Exception {
ApiResponse response = createApiResponse();
ServiceManager sm = smf.getServiceManager( applicationId );
response.setAction( "get" );
response.setApplication( sm.getApplication() );
response.setParams( ui.getQueryParameters() );
response.setResults( management.getApplicationMetadata( applicationId ) );
return response;
}
@RequireOrganizationAccess
@GET
@Path("credentials")
@JSONP
@Produces({MediaType.APPLICATION_JSON, "application/javascript"})
public ApiResponse getCredentials(
@Context UriInfo ui, @QueryParam("callback") @DefaultValue("callback") String callback )
throws Exception {
ApiResponse response = createApiResponse();
response.setAction("get application client credentials");
ClientCredentialsInfo credentials =
new ClientCredentialsInfo( management.getClientIdForApplication( applicationId ),
management.getClientSecretForApplication( applicationId ) );
response.setCredentials( credentials );
return response;
}
@RequireOrganizationAccess
@POST
@Path("credentials")
@JSONP
@Produces({MediaType.APPLICATION_JSON, "application/javascript"})
public ApiResponse generateCredentials( @Context UriInfo ui,
@QueryParam("callback") @DefaultValue("callback") String callback )
throws Exception {
ApiResponse response = createApiResponse();
response.setAction( "generate application client credentials" );
ClientCredentialsInfo credentials =
new ClientCredentialsInfo( management.getClientIdForApplication( applicationId ),
management.newClientSecretForApplication(applicationId) );
response.setCredentials( credentials );
return response;
}
@RequireOrganizationAccess
@GET
@JSONP
@Path("_size")
public ApiResponse getApplicationSize(
@Context UriInfo ui, @QueryParam("callback") @DefaultValue("callback") String callback )
throws Exception {
ApiResponse response = createApiResponse();
response.setAction( "get application size for all entities" );
long size = management.getApplicationSize(this.applicationId);
Map<String,Object> map = new HashMap<>();
Map<String,Object> innerMap = new HashMap<>();
Map<String,Object> sumMap = new HashMap<>();
innerMap.put("application",size);
sumMap.put("size",innerMap);
map.put("aggregation", sumMap);
response.setMetadata(map);
return response;
}
@RequireOrganizationAccess
@GET
@JSONP
@Path("{collection_name}/_size")
public ApiResponse getCollectionSize(
@Context UriInfo ui,
@PathParam( "collection_name" ) String collection_name,
@QueryParam("callback") @DefaultValue("callback") String callback )
throws Exception {
ApiResponse response = createApiResponse();
response.setAction("get collection size for all entities");
long size = management.getCollectionSize(this.applicationId, collection_name);
Map<String,Object> map = new HashMap<>();
Map<String,Object> sumMap = new HashMap<>();
Map<String,Object> innerMap = new HashMap<>();
innerMap.put(collection_name,size);
sumMap.put("size",innerMap);
map.put("aggregation",sumMap);
response.setMetadata(map);
return response;
}
@RequireOrganizationAccess
@GET
@JSONP
@Path("collections/_size")
public ApiResponse getEachCollectionSize(
@Context UriInfo ui,
@QueryParam("callback") @DefaultValue("callback") String callback )
throws Exception {
ApiResponse response = createApiResponse();
response.setAction("get collection size for all entities");
Map<String,Long> sizes = management.getEachCollectionSize(this.applicationId);
Map<String,Object> map = new HashMap<>();
Map<String,Object> sumMap = new HashMap<>();
sumMap.put("size",sizes);
map.put("aggregation",sumMap);
response.setMetadata(map);
return response;
}
@POST
@Path("sia-provider")
@Consumes(APPLICATION_JSON)
@RequireOrganizationAccess
@JSONP
@Produces({MediaType.APPLICATION_JSON, "application/javascript"})
public ApiResponse configureProvider(
@Context UriInfo ui,
@QueryParam("provider_key") String siaProvider,
Map<String, Object> json,
@QueryParam("callback")
@DefaultValue("") String callback )
throws Exception {
ApiResponse response = createApiResponse();
response.setAction( "post signin provider configuration" );
Preconditions.checkArgument( siaProvider != null, "Sign in provider required" );
SignInAsProvider signInAsProvider = null;
if ( StringUtils.equalsIgnoreCase( siaProvider, "facebook" ) ) {
signInAsProvider = signInProviderFactory.facebook(
smf.getServiceManager( applicationId ).getApplication() );
}
else if ( StringUtils.equalsIgnoreCase( siaProvider, "pingident" ) ) {
signInAsProvider = signInProviderFactory.pingident(
smf.getServiceManager( applicationId ).getApplication() );
}
else if ( StringUtils.equalsIgnoreCase( siaProvider, "foursquare" ) ) {
signInAsProvider = signInProviderFactory.foursquare(
smf.getServiceManager( applicationId ).getApplication() );
}
Preconditions.checkArgument( signInAsProvider != null,
"No signin provider found by that name: " + siaProvider );
signInAsProvider.saveToConfiguration( json );
return response;
}
// @POST
// @Path("export")
// @Consumes(APPLICATION_JSON)
// @RequireOrganizationAccess
// public Response exportPostJson( @Context UriInfo ui,Map<String, Object> json,
// @QueryParam("callback") @DefaultValue("") String callback )
// throws OAuthSystemException {
//
// UsergridAwsCredentials uac = new UsergridAwsCredentials();
//
// UUID jobUUID = null;
// Map<String, String> uuidRet = new HashMap<String, String>();
//
// Map<String,Object> properties;
// Map<String, Object> storage_info;
//
// try {
// if((properties = ( Map<String, Object> ) json.get( "properties" )) == null){
// throw new NullArgumentException("Could not find 'properties'");
// }
// storage_info = ( Map<String, Object> ) properties.get( "storage_info" );
// String storage_provider = ( String ) properties.get( "storage_provider" );
// if(storage_provider == null) {
// throw new NullArgumentException( "Could not find field 'storage_provider'" );
// }
// if(storage_info == null) {
// throw new NullArgumentException( "Could not find field 'storage_info'" );
// }
//
//
// String bucketName = ( String ) storage_info.get( "bucket_location" );
// String accessId = ( String ) storage_info.get( "s3_access_id" );
// String secretKey = ( String ) storage_info.get( "s3_key" );
//
// if ( bucketName == null ) {
// throw new NullArgumentException( "Could not find field 'bucketName'" );
// }
// if ( accessId == null ) {
// throw new NullArgumentException( "Could not find field 's3_access_id'" );
// }
// if ( secretKey == null ) {
//
// throw new NullArgumentException( "Could not find field 's3_key'" );
// }
//
// json.put("organizationId", organization.getUuid());
// json.put( "applicationId",applicationId);
//
// jobUUID = exportService.schedule( json );
// uuidRet.put( "Export Entity", jobUUID.toString() );
// }
// catch ( NullArgumentException e ) {
// return Response.status( SC_BAD_REQUEST )
// .type( JSONPUtils.jsonMediaType( callback ) )
// .entity( ServiceResource.wrapWithCallback( e.getMessage(), callback ) ).build();
// }
// catch ( Exception e ) {
// // TODO: throw descriptive error message and or include on in the response
// // TODO: fix below, it doesn't work if there is an exception.
// // Make it look like the OauthResponse.
// return Response.status( SC_INTERNAL_SERVER_ERROR )
// .type( JSONPUtils.jsonMediaType( callback ) )
// .entity( ServiceResource.wrapWithCallback( e.getMessage(), callback ) ).build();
// }
//
// return Response.status( SC_ACCEPTED ).entity( uuidRet ).build();
// }
//
// @POST
// @Path("collection/{collection_name}/export")
// @Consumes(APPLICATION_JSON)
// @RequireOrganizationAccess
// public Response exportPostJson( @Context UriInfo ui,
// @PathParam( "collection_name" ) String collection_name ,Map<String, Object> json,
// @QueryParam("callback") @DefaultValue("") String callback )
// throws OAuthSystemException {
//
// UsergridAwsCredentials uac = new UsergridAwsCredentials();
// UUID jobUUID = null;
// String colExport = collection_name;
// Map<String, String> uuidRet = new HashMap<String, String>();
//
// Map<String,Object> properties;
// Map<String, Object> storage_info;
//
// try {
// //checkJsonExportProperties(json);
// if((properties = ( Map<String, Object> ) json.get( "properties" )) == null){
// throw new NullArgumentException("Could not find 'properties'");
// }
// storage_info = ( Map<String, Object> ) properties.get( "storage_info" );
// String storage_provider = ( String ) properties.get( "storage_provider" );
// if(storage_provider == null) {
// throw new NullArgumentException( "Could not find field 'storage_provider'" );
// }
// if(storage_info == null) {
// throw new NullArgumentException( "Could not find field 'storage_info'" );
// }
//
// String bucketName = ( String ) storage_info.get( "bucket_location" );
// String accessId = ( String ) storage_info.get( "s3_access_id" );
// String secretKey = ( String ) storage_info.get( "s3_key" );
//
// if ( accessId == null ) {
// throw new NullArgumentException( "Could not find field 's3_access_id'" );
// }
// if ( secretKey == null ) {
// throw new NullArgumentException( "Could not find field 's3_key'" );
// }
//
// if(bucketName == null) {
// throw new NullArgumentException( "Could not find field 'bucketName'" );
// }
//
// json.put( "organizationId",organization.getUuid() );
// json.put( "applicationId", applicationId);
// json.put( "collectionName", colExport);
//
// jobUUID = exportService.schedule( json );
// uuidRet.put( "Export Entity", jobUUID.toString() );
// }
// catch ( NullArgumentException e ) {
// return Response.status( SC_BAD_REQUEST )
// .type( JSONPUtils.jsonMediaType( callback ) )
// .entity( ServiceResource.wrapWithCallback( e.getMessage(), callback ) )
// .build();
// }
// catch ( Exception e ) {
//
// // TODO: throw descriptive error message and or include on in the response
// // TODO: fix below, it doesn't work if there is an exception.
// // Make it look like the OauthResponse.
//
// OAuthResponse errorMsg = OAuthResponse.errorResponse( SC_INTERNAL_SERVER_ERROR )
// .setErrorDescription( e.getMessage() )
// .buildJSONMessage();
//
// return Response.status( errorMsg.getResponseStatus() )
// .type( JSONPUtils.jsonMediaType( callback ) )
// .entity( ServiceResource.wrapWithCallback( errorMsg.getBody(), callback ) )
// .build();
// }
//
// return Response.status( SC_ACCEPTED ).entity( uuidRet ).build();
// }
//
//
// @Path( "imports" )
// public ImportsResource importGetJson( @Context UriInfo ui,
// @QueryParam( "callback" ) @DefaultValue( "" ) String callback )
// throws Exception {
//
//
// return getSubResource( ImportsResource.class ).init( organization, application );
// }
@GET
@Path("/status")
public Response getStatus() {
Map<String, Object> statusMap = new HashMap<String, Object>();
EntityManager em = emf.getEntityManager( applicationId );
if ( !emf.getIndexHealth().equals( Health.RED ) ) {
statusMap.put("message", "Index Health Status RED for application " + applicationId );
return Response.status( SC_INTERNAL_SERVER_ERROR ).entity( statusMap ).build();
}
try {
if ( em.getApplication() == null ) {
statusMap.put("message", "Application " + applicationId + " not found");
return Response.status( SC_NOT_FOUND ).entity( statusMap ).build();
}
} catch (Exception ex) {
statusMap.put("message", "Error looking up application " + applicationId );
return Response.status( SC_INTERNAL_SERVER_ERROR ).entity( statusMap ).build();
}
return Response.status( SC_OK ).entity( null ).build();
}
/**
* Put on application URL will restore application if it was deleted.
*/
@PUT
@RequireOrganizationAccess
@JSONP
@Produces({MediaType.APPLICATION_JSON, "application/javascript"})
public ApiResponse executePut( @Context UriInfo ui, String body,
@QueryParam("callback") @DefaultValue("callback") String callback ) throws Exception {
if ( applicationId == null ) {
throw new IllegalArgumentException("Application ID not specified in request");
}
management.restoreApplication( applicationId );
ApiResponse response = createApiResponse();
response.setAction( "restore" );
response.setApplication( emf.getEntityManager( applicationId ).getApplication() );
response.setParams( ui.getQueryParameters() );
return response;
}
/**
* Caller MUST pass confirm_application_identifier that is either the UUID or the
* name of the application to be deleted. Yes, this is redundant and intended to
* be a protection measure to force caller to confirm that they want to do a delete.
*/
@DELETE
@RequireOrganizationAccess
@JSONP
@Produces({MediaType.APPLICATION_JSON, "application/javascript"})
public ApiResponse executeDelete( @Context UriInfo ui,
@QueryParam("callback") @DefaultValue("callback") String callback,
@QueryParam(CONFIRM_APPLICATION_IDENTIFIER) String confirmApplicationIdentifier) throws Exception {
if ( application == null && applicationId == null ) {
throw new IllegalArgumentException("Application ID not specified in request");
}
// If the path uses name then expect name, otherwise if they use uuid then expect uuid.
if (application == null) {
if (!applicationId.toString().equals( confirmApplicationIdentifier )) {
throw new IllegalArgumentException(
"Cannot delete application without supplying correct application id.");
}
} else if (!application.getName().split( "/" )[1].equals( confirmApplicationIdentifier ) ) {
throw new IllegalArgumentException(
"Cannot delete application without supplying correct application name");
}
management.deleteApplication( applicationId );
if (logger.isTraceEnabled()) {
logger.trace("ApplicationResource.delete() deleted appId = {}", applicationId);
}
ApiResponse response = createApiResponse();
response.setAction( "delete" );
response.setApplication(emf.getEntityManager( applicationId ).getApplication());
response.setParams(ui.getQueryParameters());
if (logger.isTraceEnabled()) {
logger.trace("ApplicationResource.delete() sending response ");
}
return response;
}
}