001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2013-2015 ForgeRock AS. 015 */ 016 017package org.forgerock.json.resource; 018 019import org.forgerock.services.context.Context; 020import org.forgerock.util.promise.Promise; 021 022/** 023 * An interface for implementing request handler filters. Filters are linked 024 * together using a {@link FilterChain}. 025 * <p> 026 * On receipt of a request a filter implementation may either: 027 * <ul> 028 * <li><i>stop processing</i> the request and return a result or error 029 * immediately. This is achieved by returning a completed {@code Promise} with 030 * a {@link QueryResponse} or a {@link ResourceException} methods and returning 031 * <li><i>continue processing</i> the request using the next filter in the 032 * filter chain. This is achieved by invoking the appropriate {@code handlerXXX} 033 * method on the passed in request handler. Implementations are permitted to 034 * modify the context or request before forwarding. They may also chain the 035 * promise, returned from the downstream handler, in order to be notified when 036 * a response is returned, allowing a filter to interact with responses before 037 * they are sent to the client. 038 * </ul> 039 * <p> 040 * Implementations are allowed to invoke arbitrary {@code handleXXX} methods on 041 * the request handler if needed before deciding to stop or continue processing. 042 * However, implementations should take care to ensure that the passed in result 043 * handler is invoked at most once per request. This is useful in the case where 044 * a filter implements some functionality as a composite of other operations 045 * (e.g. theoretically, a password modify action could be intercepted within a 046 * filter and converted into a read + update). 047 * <p> 048 * Filter chains are fully asynchronous: filters and request handlers may 049 * delegate work to separate threads either directly (i.e. new Thread() ...) or 050 * indirectly (e.g. via NIO completion handlers). 051 * <p> 052 * The following example illustrates how an authorization filter could be 053 * implemented: 054 * 055 * <pre> 056 * public class AuthzFilter implements Filter { 057 * 058 * public Promise<Resource, ResourceException> filterRead(final Context context, 059 * final ReadRequest request, final RequestHandler next) { 060 * /* 061 * * Only forward the request if the request is allowed. 062 * */ 063 * if (isAuthorized(context, request)) { 064 * /* 065 * * Continue processing the request since it is allowed. Chain the 066 * * promise so that we can filter the returned resource. 067 * */ 068 * return next.handleRead(context, request) 069 * .thenAsync(new AsyncFunction<Resource, Resource, ResourceException>() { 070 * @Override 071 * public Promise<Resource, ResourceException> apply(Resource result) { 072 * /* 073 * * Filter the resource and its attributes. 074 * */ 075 * if (isAuthorized(context, result)) { 076 * return Promises.newResultPromise(filterResource(context, result)); 077 * } else { 078 * return newExceptionPromise(ResourceException.newNotFoundException()); 079 * } 080 * } 081 * }, new AsyncFunction<ResourceException, Resource, ResourceException>() { 082 * @Override 083 * public Promise<Resource, ResourceException> apply(ResourceException error) { 084 * // Forward - assumes no authorization is required. 085 * return newExceptionPromise(error); 086 * } 087 * }); 088 * } else { 089 * /* 090 * * Stop processing the request since it is not allowed. 091 * */ 092 * ResourceException exception = new ForbiddenException(); 093 * return newExceptionPromise(exception); 094 * } 095 * } 096 * 097 * // Remaining filterXXX methods... 098 * } 099 * </pre> 100 * 101 * @see Filters 102 */ 103public interface Filter { 104 105 /** 106 * Filters an action request. 107 * 108 * @param context 109 * The filter chain context. 110 * @param request 111 * The action request. 112 * @param next 113 * A request handler representing the remainder of the filter 114 * chain. 115 * @return A {@code Promise} containing the result of the operation. 116 */ 117 Promise<ActionResponse, ResourceException> filterAction(Context context, ActionRequest request, 118 RequestHandler next); 119 120 /** 121 * Filters a create request. 122 * 123 * @param context 124 * The filter chain context. 125 * @param request 126 * The create request. 127 * @param next 128 * A request handler representing the remainder of the filter 129 * chain. 130 * @return A {@code Promise} containing the result of the operation. 131 */ 132 Promise<ResourceResponse, ResourceException> filterCreate(Context context, CreateRequest request, 133 RequestHandler next); 134 135 /** 136 * Filters a delete request. 137 * 138 * @param context 139 * The filter chain context. 140 * @param request 141 * The delete request. 142 * @param next 143 * A request handler representing the remainder of the filter 144 * chain. 145 * @return A {@code Promise} containing the result of the operation. 146 */ 147 Promise<ResourceResponse, ResourceException> filterDelete(Context context, DeleteRequest request, 148 RequestHandler next); 149 150 /** 151 * Filters a patch request. 152 * 153 * @param context 154 * The filter chain context. 155 * @param request 156 * The patch request. 157 * @param next 158 * A request handler representing the remainder of the filter 159 * chain. 160 * @return A {@code Promise} containing the result of the operation. 161 */ 162 Promise<ResourceResponse, ResourceException> filterPatch(Context context, PatchRequest request, 163 RequestHandler next); 164 165 /** 166 * Filters a query request. 167 * <p> 168 * Implementations which return results directly rather than forwarding the 169 * request should invoke {@link QueryResourceHandler#handleResource(ResourceResponse)} 170 * for each resource which matches the query criteria. Once all matching 171 * resources have been returned implementations are required to return 172 * either a {@link QueryResponse} if the query has completed successfully, or 173 * {@link ResourceException} if the query did not complete successfully 174 * (even if some matching resources were returned). 175 * 176 * @param context 177 * The filter chain context. 178 * @param request 179 * The query request. 180 * @param handler 181 * The resource handler. 182 * @param next 183 * A request handler representing the remainder of the filter 184 * chain. 185 * @return A {@code Promise} containing the result of the operation. 186 */ 187 Promise<QueryResponse, ResourceException> filterQuery(Context context, QueryRequest request, 188 QueryResourceHandler handler, RequestHandler next); 189 190 /** 191 * Filters a read request. 192 * 193 * @param context 194 * The filter chain context. 195 * @param request 196 * The read request. 197 * @param next 198 * A request handler representing the remainder of the filter 199 * chain. 200 * @return A {@code Promise} containing the result of the operation. 201 */ 202 Promise<ResourceResponse, ResourceException> filterRead(Context context, ReadRequest request, 203 RequestHandler next); 204 205 /** 206 * Filters an update request. 207 * 208 * @param context 209 * The filter chain context. 210 * @param request 211 * The update request. 212 * @param next 213 * A request handler representing the remainder of the filter 214 * chain. 215 * @return A {@code Promise} containing the result of the operation. 216 */ 217 Promise<ResourceResponse, ResourceException> filterUpdate(Context context, UpdateRequest request, 218 RequestHandler next); 219}