/* * � Copyright IBM Corp. 2012 * * 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.ibm.domino.das.service; import java.net.URI; import java.text.MessageFormat; import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; import lotus.domino.Database; import lotus.domino.NotesException; import lotus.domino.Session; import org.apache.wink.common.WinkApplication; import com.ibm.commons.util.StringUtil; import com.ibm.domino.commons.model.IGatekeeperProvider; import com.ibm.domino.commons.model.ProviderFactory; import com.ibm.domino.commons.util.UriHelper; import com.ibm.domino.das.utils.ErrorHelper; import com.ibm.domino.das.utils.ScnContext; import com.ibm.domino.das.utils.StatsContext; import com.ibm.domino.osgi.core.context.ContextInfo; import com.ibm.xsp.acl.NoAccessSignal; public class RestService extends WinkApplication { public static final String SERVICE_PATH = "/api";//$NON-NLS-1$ public static final String URL_PARAM_OWNER = "owner"; //$NON-NLS-1$ public RestService() { super(); } /** * Frees resources associated with this service. * * <p>This method is called once as the DAS servlet itself is being destroyed. * A subclass can override this method to free resources. A subclass should * free resources quickly. Otherwise, this method can block shutdown of * the HTTP server. */ public void destroy() { } public static Response createErrorResponse(String message, Response.Status status) { return (ErrorHelper.createErrorResponse(message, status)); } public static Response createErrorResponse(Exception e, Response.Status status) { return (ErrorHelper.createErrorResponse(e, status)); } public static Response createErrorResponse(Throwable e) { return (ErrorHelper.createErrorResponse(e)); } /** * Gets the database from the request context. * * @return */ public static Database getUserDatabase() { Database database = ContextInfo.getUserDatabase(); if ( database == null ) { throw new WebApplicationException(createErrorResponse("No database context.", // $NLX-RestService.Nodatabasecontext-1$ Response.Status.NOT_FOUND)); } return database; } /** * Verifies the current request has a database context. */ public static void verifyDatabaseContext() { getUserDatabase(); } /** * Verifies the current request has NO database context. */ public static void verifyNoDatabaseContext() { Database database = ContextInfo.getUserDatabase(); if ( database != null ) { throw new WebApplicationException(createErrorResponse("Database context not allowed.", // $NLX-RestService.Databasecontextnotallowed-1$ Response.Status.NOT_FOUND)); } } /** * Verifies the current request has a user context (not Anonymous). */ public static void verifyUserContext() { String user = null; Session session = ContextInfo.getUserSession(); if ( session != null ) { try { user = session.getEffectiveUserName(); } catch (NotesException e) { // Ignore this } } if ( user == null || user.equals("Anonymous")) { // $NON-NLS-1$ throw new NoAccessSignal("Need user context"); // $NLX-RestService.Needusercontext-1$ } } /** * Parses a query parameter with an integer value. * * @param paramName * @param paramValue * @param bHex * @return */ public static int getParameterInt(String paramName, String paramValue, boolean bHex) { int iParam; try { if(bHex && paramValue.toLowerCase().startsWith("0x")) // $NON-NLS-1$ iParam = Integer.parseInt(paramValue.substring(2), 16); else iParam = Integer.parseInt(paramValue); } catch (NumberFormatException nfe) { String msg = MessageFormat.format("Invalid {0} parameter: {1}", paramName, paramValue); // $NLX-RestService.Invalid0parameter1-1$ throw new WebApplicationException( createErrorResponse(new Exception(msg, nfe), Response.Status.BAD_REQUEST)); } return iParam; } /** * Parses a query parameter. The value is expected to be a list of * comma-separated strings. * * <p>Warning: This method returns a list of lower case strings. * * @param paramName * @param paramValue * @return */ public static ArrayList<String> getParameterStringList(String paramName, String paramValue) { // Check the format of parameter list Pattern pattern = Pattern.compile("(\\s*[\\w-]+\\s*,)*\\s*([\\w-]+)\\s*"); // $NON-NLS-1$ Matcher matcher = pattern.matcher(paramValue); if(!matcher.matches()){ String msg = MessageFormat.format("Invalid {0} parameter: {1} ", paramName, paramValue); // $NLX-RestService.Invalid0parameter1.1-1$ throw new WebApplicationException( createErrorResponse(new Exception(msg), Response.Status.BAD_REQUEST)); } // If match, at least have one field ArrayList<String> vaueList = new ArrayList<String>(); String[] valueArray = paramValue.split(","); for(int i = 0; i< valueArray.length; i++){ vaueList.add(valueArray[i].trim().toLowerCase()); } return vaueList; } /** * Helper method to adapt an "on-premise" URI to SCN. * * @param uri * @return */ public static URI adaptUriToScn(URI uri) { URI adapted = uri; if ( ScnContext.getCurrentInstance().isHideDbPath() ) { // Remove database from path String sUri = uri.toString(); int index = sUri.indexOf(SERVICE_PATH + "/"); if ( index != -1 ) { sUri = sUri.substring(index); if ( uri.isAbsolute() ) { // SPR# WJBJ9MZ9U9: Since SSL is terminated at the F5, we can't use // uri.getScheme() and uri.getAuthority() here. Hard code the protocol // to https and port to 443. String protocol = "https"; // $NON-NLS-1$ String authority = uri.getHost() + ":443"; // Add protocol, server & port back to the URI sUri = protocol + "://" + authority + sUri; } } // Add owner parameter (if necessary) String ownerId = ScnContext.getCurrentInstance().getOwnerId(); if ( StringUtil.isNotEmpty(ownerId) ) { sUri += "?" + URL_PARAM_OWNER + "=" + ownerId; } adapted = UriHelper.create(sUri, false); } return adapted; } /** * Common bookkeeping before handling a request. * * <p>A resource class can call this method before doing request-specific processing. * Currently, this method sets the statistics category and/or checks gatekeeper. * In the future, other pre-processing may be added. * * @param feature * @param category */ public static void beforeRequest(int feature, String category) { if ( category != null ) { StatsContext.getCurrentInstance().setRequestCategory(category); } if ( feature != 0 ) { ScnContext ctx = ScnContext.getCurrentInstance(); String customerId = ctx.getCustomerId(); String userId = ctx.getUserId(); IGatekeeperProvider provider = ProviderFactory.getGatekeeperProvider(); if ( ! provider.isFeatureEnabled(feature, customerId, userId) ) { String msg = MessageFormat.format("Feature {0} is disabled.", feature); // $NLX-RestService.Feature0isdisabled-1$ throw new WebApplicationException(ErrorHelper.createErrorResponse(msg, Response.Status.FORBIDDEN)); } } } }