By default, INTGeoServer doesn't use any form of authentication or access control: all data vended by INTGeoServer is directly accessible by all clients that can send HTTP requests. Furthemore, the default implementation is fully stateless: no HTTP session or cookies are needed.
There are applications where authentication and access control are needed before data is provided. Some applications even require an audit trail, to know who has access which data and when. Here are some hints on how you can add these behaviors. As INTGeoServer is a modular platform, you can modify the default behavior without modifying its core: you only need to add .jar files to the existing deployments.
The INTGeoServer API allows you to plug "request handler listeners" that will be executed each time any service is accessed. This listener can block access, or redirect to another service. These are ways to authenticate if the user is not already authenticated:
the listener calls your proprietary authentication API, which provides the client's credentials
the HTTP call is redirected to another HTTP service, which posts a token cookie, which is then interpreted as credentials by INTGeoServer through your proprietary authentication API
The fully qualified name of the class to extend is com.interactive.intgeoapi.server.requesthandlers.AbstractIntGeoServerRequestHandlerListener. This class has only 4 methods:
public abstract boolean canHandleRequest(AbstractServletRequestHandler handler);
public abstract void beforeHandleRequest(AbstractServletRequestHandler handler);
public abstract void afterHandleRequest(AbstractServletRequestHandler handler);
public abstract AbstractIntGeoServerRequestHandlerListener getInstance();
Most of the code that would implement authentication would be in the canHandleRequest method. If this method returns false, access is denied to all JSON services. and the AbstractIntGeoServerErrorHandler.handleServletNotAuthorized method is called. By default this method send a 401 HTTP header.
Servlet request handlers are called both by servlets and WebSocket end points.
Since INTGeoServer is stateless by default, authentication implementations that need to force HTTP sessions typically plug a "servlet listener" that is called each time the JSON servlet is called.
The fully qualified name of the class to extend is com.interactive.intgeoapi.server.servlets.AbstractIntGeoServerServletListener. This class has only 4 methods:
public abstract boolean canHandleRequest(AbstractIntGeoServerServlet servlet);
public abstract void beforeHandleRequest(AbstractIntGeoServerServlet servlet);
public abstract void afterHandleRequest(AbstractIntGeoServerServlet servlet);
public abstract AbstractIntGeoServerServletListener getInstance();
A listener implementation only forcing sessions would have this code:
public void beforeHandleRequest(AbstractIntGeoServerServlet handler) {
HttpServletRequest httpServlet = HttpContextGlobal.get().getHttpServletRequest();
httpServlet.getSession(true);
}
Servlet listeners are called only by servlets. To control sessions for web sockets end points, extend AbstractIntGeoServerEndPointListener instead.
Each time a resource is accessed, INTGeoServer operates a translation of the path passed as parameter. This operation is performed through a "files provider" This provider interprets the path passed as parameter with a data source (generally the file system) and a resource in that data source (generally a file with an absolute path).
If that provider cannot associate a resource with the specified path, then the client gets a "NOT FOUND" header response (through the AbstractIntGeoServerErrorHandler.handleNoResourceFound method).
By implementing your own files provider, you get to decide who has access to what. This provider could also log when it translates a path parameter to a path on disk. This provides your audit trail.
The fully qualified name of the class to extend is com.interactive.intgeoapi.server.filesproviders.AbstractIntGeoServerFilesProvider. As an example, here is the implementation of a files provider that doesn't operate any translation: the paths passed as parameters are absolute paths of files on disk:
public class SamePathIntGeoServerFilesProvider extends AbstractIntGeoServerFilesProvider {
@Override
public List<String> getAbsoluteFilePaths(String relativeDirPath) {
ArrayList<String> absolutePaths = new ArrayList<String>();
File f = new File(relativeDirPath);
if (f.exists()) {
File[] files = f.listFiles();
for (File file : files) {
absolutePaths.add(file.getAbsolutePath());
}
}
return absolutePaths;
}
@Override
public String getAbsoluteFilePath(String relativeFilePath) {
if (!new File(relativeFilePath).exists()) {
return null;
}
return relativeFilePath;
}
@Override
public String getRelativeFilePath(String absoluteFilePath) {
if (!new File(absoluteFilePath).exists()) {
return null;
}
return absoluteFilePath;
}
@Override
public AbstractIntGeoServerFilesProvider getInstance() {
return new SamePathIntGeoServerFilesProvider();
}
@Override
public void printServerInfo(JSONObject jsonObject) {
super.printServerInfo(jsonObject);
jsonObject.put("fileProvider", "SamePath");
}
}
Based upon this implementation, you can see that only the 3 methods in bold need to be modified to control access.
Note that the error management can also be plugged through what we call an "error handler". If you are only interested in auditing when access is denied, you would plug typically plug your own error handler.
The fully qualified name of the class to extend is com.interactive.intgeoapi.server.errorhandlers.AbstractIntGeoServerErrorHandler. Here is the abstract implementation. The methods that indicate denied access are highlighted in bold.
public static final int SC_NO_DATA = 507;
public abstract AbstractIntGeoServerErrorHandler getInstance();
protected void setStatus(int status) {
IntGeoServiceContext httpContext = IntGeoServiceContextGlobal.get();
if (httpContext == null) {
return;
}
httpContext.setResponseStatus(status);
}
// when a required parameter is missing
public void handleMissingRequestParameter(AbstractServiceRequestHandler handler, String parameterName) throws IOException {
this.setStatus(HttpServletResponse.SC_BAD_REQUEST);
}
// when the request is valid, but a required module is missing
public void handleUnsupportedRequestParameter(AbstractServiceRequestHandler handler, String msg, String parameterName, Object object) throws IOException {
this.setStatus(HttpServletResponse.SC_NOT_IMPLEMENTED);
}
// when a parameter value is invalid
public void handleInvalidRequestParameter(AbstractServiceRequestHandler handler, String parameterName, Object object) throws IOException {
this.setStatus(HttpServletResponse.SC_BAD_REQUEST);
}
// when an unexpected error occured at the request handler level
public void handleInternalHandlerError(AbstractServiceRequestHandler handler, Throwable e) throws IOException {
this.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
// when an unexpected error occured during the handling of an async process
public void handleAsyncInternalHandlerError(AbstractServiceRequestHandler handler, Throwable e) throws IOException {
this.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
// when an unexpected error occured during the start of an async process
public void handleAsyncStartHandlerError(AbstractServiceRequestHandler handler, Throwable e) throws IOException {
this.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
// when a time out occured when handling an async process
public void handleAsyncTimeOutHandlerError(AbstractServiceRequestHandler handler, Throwable e) throws IOException {
this.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
// when an unexpected error occured when completing an async process
public void handleAsyncCompleteHandlerError(AbstractServiceRequestHandler handler, Throwable e) throws IOException {
this.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
// when an unexpected error occured at the request handler level
public void handleInternalHandlerError(AbstractServiceRequestHandler handler, String msg) throws IOException {
this.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
// when a path to a resource (ex: a directory path, a CRS code, etc.) is invalid
public void handleNoResourceFound(AbstractServiceRequestHandler handler, String resourcePath) throws IOException {
this.setStatus(HttpServletResponse.SC_NOT_FOUND);
}
// when a data path is invalid or corresponds to an empty dataset
public void handleNoDataFound(AbstractServiceRequestHandler handler, String resourcePath) throws IOException {
this.setStatus(SC_NO_DATA);
}
// when a path to a resource leads to the wrong type of resource. Ex: a file instead of a directory, an unreadable file
public void handleInvalidResource(AbstractServiceRequestHandler handler, String resourcePath, String msg) throws IOException {
this.setStatus(HttpServletResponse.SC_BAD_REQUEST);
}
// when access to the handler is not authorized
public void handleHandlerNotAuthorized(AbstractServiceRequestHandler handler) throws IOException {
this.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}