diff --git a/li.strolch.agent/src/main/java/li/strolch/persistence/api/AbstractTransaction.java b/li.strolch.agent/src/main/java/li/strolch/persistence/api/AbstractTransaction.java index 84ed70181..6436b3cb5 100644 --- a/li.strolch.agent/src/main/java/li/strolch/persistence/api/AbstractTransaction.java +++ b/li.strolch.agent/src/main/java/li/strolch/persistence/api/AbstractTransaction.java @@ -32,6 +32,7 @@ import li.strolch.agent.impl.AuditingAuditMapFacade; import li.strolch.agent.impl.AuditingOrderMap; import li.strolch.agent.impl.AuditingResourceMap; import li.strolch.agent.impl.InternalStrolchRealm; +import li.strolch.exception.StrolchAccessDeniedException; import li.strolch.exception.StrolchException; import li.strolch.model.GroupedParameterizedElement; import li.strolch.model.Locator; @@ -65,6 +66,7 @@ import li.strolch.service.api.Command; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ch.eitchnet.privilege.base.PrivilegeException; import ch.eitchnet.privilege.model.Certificate; import ch.eitchnet.privilege.model.PrivilegeContext; import ch.eitchnet.utils.dbc.DBC; @@ -237,8 +239,12 @@ public abstract class AbstractTransaction implements StrolchTransaction { } private void assertQueryAllowed(StrolchQuery query) { - PrivilegeContext privilegeContext = this.privilegeHandler.getPrivilegeContext(this.certificate); - privilegeContext.validateAction(query); + try { + PrivilegeContext privilegeContext = this.privilegeHandler.getPrivilegeContext(this.certificate); + privilegeContext.validateAction(query); + } catch (PrivilegeException e) { + throw new StrolchAccessDeniedException(this.certificate, query, e.getMessage(), e); + } } @Override @@ -563,7 +569,6 @@ public abstract class AbstractTransaction implements StrolchTransaction { if (closeDuration >= 100000000L) { sb.append(", close="); //$NON-NLS-1$ sb.append(StringHelper.formatNanoDuration(closeDuration)); - logger.info(sb.toString()); } logger.error(sb.toString()); } @@ -594,7 +599,6 @@ public abstract class AbstractTransaction implements StrolchTransaction { if (closeDuration >= 100000000L) { sb.append(", close="); //$NON-NLS-1$ sb.append(StringHelper.formatNanoDuration(closeDuration)); - logger.info(sb.toString()); } String msg = "Strolch Transaction for realm {0} failed due to {1}\n{2}"; //$NON-NLS-1$ diff --git a/li.strolch.agent/src/main/java/li/strolch/service/api/AbstractService.java b/li.strolch.agent/src/main/java/li/strolch/service/api/AbstractService.java index bea0da28d..91a118a4d 100644 --- a/li.strolch.agent/src/main/java/li/strolch/service/api/AbstractService.java +++ b/li.strolch.agent/src/main/java/li/strolch/service/api/AbstractService.java @@ -146,14 +146,9 @@ public abstract class AbstractService abstractService = (AbstractService) service; @SuppressWarnings("unchecked") U arg = (U) abstractService.getResultInstance(); @@ -93,7 +99,7 @@ public class DefaultServiceHandler extends StrolchComponent implements ServiceHa arg.setThrowable(e); return arg; } else { - throw new StrolchException(e.getMessage()); + throw new StrolchAccessDeniedException(certificate, service, e.getMessage(), e); } } @@ -113,16 +119,7 @@ public class DefaultServiceHandler extends StrolchComponent implements ServiceHa } // log the result - long end = System.nanoTime(); - String msg = "User {0}: Service {1} took {2}"; //$NON-NLS-1$ - msg = MessageFormat.format(msg, username, service.getClass().getName(), - StringHelper.formatNanoDuration(end - start)); - if (serviceResult.getState() == ServiceResultState.SUCCESS) - logger.info(msg); - else if (serviceResult.getState() == ServiceResultState.WARNING) - logger.warn(msg); - else if (serviceResult.getState() == ServiceResultState.FAILED) - logger.error(msg); + logResult(service, start, username, serviceResult); return serviceResult; @@ -131,8 +128,46 @@ public class DefaultServiceHandler extends StrolchComponent implements ServiceHa String msg = "User {0}: Service failed {1} after {2} due to {3}"; //$NON-NLS-1$ msg = MessageFormat.format(msg, username, service.getClass().getName(), StringHelper.formatNanoDuration(end - start), e.getMessage()); - logger.error(msg, e); + logger.error(msg); throw new StrolchException(msg, e); } } + + private void logResult(Service service, long start, String username, ServiceResult serviceResult) { + + long end = System.nanoTime(); + + String msg = "User {0}: Service {1} took {2}"; //$NON-NLS-1$ + msg = MessageFormat.format(msg, username, service.getClass().getName(), + StringHelper.formatNanoDuration(end - start)); + + if (serviceResult.getState() == ServiceResultState.SUCCESS) { + logger.info(msg); + } else if (serviceResult.getState() == ServiceResultState.WARNING) { + + msg = ServiceResultState.WARNING + " " + msg; + logger.warn(msg); + + if (StringHelper.isNotEmpty(serviceResult.getMessage()) && serviceResult.getThrowable() != null) { + logger.warn("Reason: " + serviceResult.getMessage(), serviceResult.getThrowable()); + } else if (StringHelper.isNotEmpty(serviceResult.getMessage())) { + logger.warn("Reason: " + serviceResult.getMessage()); + } else if (serviceResult.getThrowable() != null) { + logger.warn("Reason: " + serviceResult.getThrowable().getMessage(), serviceResult.getThrowable()); + } + + } else if (serviceResult.getState() == ServiceResultState.FAILED) { + + msg = ServiceResultState.FAILED + " " + msg; + logger.error(msg); + + if (StringHelper.isNotEmpty(serviceResult.getMessage()) && serviceResult.getThrowable() != null) { + logger.error("Reason: " + serviceResult.getMessage(), serviceResult.getThrowable()); + } else if (StringHelper.isNotEmpty(serviceResult.getMessage())) { + logger.error("Reason: " + serviceResult.getMessage()); + } else if (serviceResult.getThrowable() != null) { + logger.error("Reason: " + serviceResult.getThrowable().getMessage(), serviceResult.getThrowable()); + } + } + } } diff --git a/li.strolch.model/src/main/java/li/strolch/exception/StrolchAccessDeniedException.java b/li.strolch.model/src/main/java/li/strolch/exception/StrolchAccessDeniedException.java new file mode 100644 index 000000000..6f882c4c0 --- /dev/null +++ b/li.strolch.model/src/main/java/li/strolch/exception/StrolchAccessDeniedException.java @@ -0,0 +1,64 @@ +/* + * Copyright 2013 Robert von Burg + * + * 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 li.strolch.exception; + +import ch.eitchnet.privilege.model.Certificate; +import ch.eitchnet.privilege.model.Restrictable; + +/** + * @author Robert von Burg + */ +public class StrolchAccessDeniedException extends StrolchException { + + private static final long serialVersionUID = 1L; + + private Certificate certificate; + private Restrictable restrictable; + + /** + * + * @param certificate + * @param restrictable + * @param message + * @param cause + */ + public StrolchAccessDeniedException(Certificate certificate, Restrictable restrictable, String message, + Throwable cause) { + super(message, cause); + this.certificate = certificate; + this.restrictable = restrictable; + } + + /** + * + * @param certificate + * @param restrictable + * @param message + */ + public StrolchAccessDeniedException(Certificate certificate, Restrictable restrictable, String message) { + super(message); + this.certificate = certificate; + this.restrictable = restrictable; + } + + public Certificate getCertificate() { + return certificate; + } + + public Restrictable getRestrictable() { + return restrictable; + } +} diff --git a/li.strolch.model/src/main/java/li/strolch/model/parameter/AbstractParameter.java b/li.strolch.model/src/main/java/li/strolch/model/parameter/AbstractParameter.java index d749fc55f..04110f754 100644 --- a/li.strolch.model/src/main/java/li/strolch/model/parameter/AbstractParameter.java +++ b/li.strolch.model/src/main/java/li/strolch/model/parameter/AbstractParameter.java @@ -227,8 +227,8 @@ public abstract class AbstractParameter extends AbstractStrolchElement implem */ protected void validateValue(T value) throws StrolchException { if (value == null) { - String msg = "{0} Parameter {1} may not have a null value!"; //$NON-NLS-1$ - msg = MessageFormat.format(msg, getType(), getId()); + String msg = "Can not set null value on Parameter {0}"; //$NON-NLS-1$ + msg = MessageFormat.format(msg, getLocator()); throw new StrolchException(msg); } } diff --git a/li.strolch.rest/src/main/java/li/strolch/rest/RestfulStrolchComponent.java b/li.strolch.rest/src/main/java/li/strolch/rest/RestfulStrolchComponent.java index d994503dd..65f0115b6 100644 --- a/li.strolch.rest/src/main/java/li/strolch/rest/RestfulStrolchComponent.java +++ b/li.strolch.rest/src/main/java/li/strolch/rest/RestfulStrolchComponent.java @@ -20,6 +20,7 @@ import java.text.MessageFormat; import li.strolch.agent.api.ComponentContainer; import li.strolch.agent.api.StrolchComponent; import li.strolch.rest.filters.AccessControlResponseFilter; +import li.strolch.rest.filters.HttpCacheResponseFilter; import li.strolch.runtime.configuration.ComponentConfiguration; import org.glassfish.jersey.server.ServerProperties; @@ -35,6 +36,7 @@ public class RestfulStrolchComponent extends StrolchComponent { private static final String PARAM_CORS_ORIGIN = "corsOrigin"; //$NON-NLS-1$ private static final String PARAM_REST_LOGGING = "restLogging"; //$NON-NLS-1$ private static final String PARAM_REST_LOGGING_ENTITY = "restLoggingEntity"; //$NON-NLS-1$ + private static final String PARAM_HTTP_CACHE_MODE = "httpCacheMode"; //$NON-NLS-1$ /** * Allowed values: @@ -69,6 +71,7 @@ public class RestfulStrolchComponent extends StrolchComponent { private String corsOrigin; private boolean restLogging; private boolean restLoggingEntity; + private String cacheMode; /** * @param container @@ -140,6 +143,10 @@ public class RestfulStrolchComponent extends StrolchComponent { String msg = "Set restLogging={0} with logEntities={1} restTracing={2} with threshold={3}"; //$NON-NLS-1$ logger.info(MessageFormat.format(msg, this.restLogging, this.restLoggingEntity, this.restTracing, this.restTracingThreshold)); + + // set http cache mode + this.cacheMode = configuration.getString(PARAM_HTTP_CACHE_MODE, HttpCacheResponseFilter.NO_CACHE); + logger.info("HTTP header cache mode is set to {}",cacheMode); super.initialize(configuration); } diff --git a/li.strolch.rest/src/main/java/li/strolch/rest/StrolchRestfulExceptionMapper.java b/li.strolch.rest/src/main/java/li/strolch/rest/StrolchRestfulExceptionMapper.java index 02c4b9e17..f4f52a07b 100644 --- a/li.strolch.rest/src/main/java/li/strolch/rest/StrolchRestfulExceptionMapper.java +++ b/li.strolch.rest/src/main/java/li/strolch/rest/StrolchRestfulExceptionMapper.java @@ -4,12 +4,16 @@ import java.text.MessageFormat; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; import javax.ws.rs.ext.ExceptionMapper; import javax.ws.rs.ext.Provider; +import li.strolch.exception.StrolchAccessDeniedException; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ch.eitchnet.privilege.model.Restrictable; import ch.eitchnet.utils.helper.StringHelper; @Provider @@ -19,7 +23,29 @@ public class StrolchRestfulExceptionMapper implements ExceptionMapper @Override public Response toResponse(Exception ex) { + logger.error(MessageFormat.format("Handling exception {0}", ex.getClass()), ex); //$NON-NLS-1$ - return Response.status(500).entity(StringHelper.formatExceptionMessage(ex)).type(MediaType.TEXT_PLAIN).build(); + + if (ex instanceof StrolchAccessDeniedException) { + StrolchAccessDeniedException e = (StrolchAccessDeniedException) ex; + StringBuilder sb = new StringBuilder(); + sb.append("User "); + sb.append(e.getCertificate().getUsername()); + sb.append(" does not have access to "); + Restrictable restrictable = e.getRestrictable(); + if (restrictable == null) { + sb.append(StringHelper.NULL); + } else { + sb.append(restrictable.getPrivilegeName()); + sb.append(" - "); + sb.append(restrictable.getPrivilegeValue()); + } + + return Response.status(Status.FORBIDDEN).entity(sb.toString()).type(MediaType.TEXT_PLAIN).build(); + } + + String exceptionMessage = StringHelper.formatExceptionMessage(ex); + return Response.status(Status.INTERNAL_SERVER_ERROR).entity(exceptionMessage).type(MediaType.TEXT_PLAIN) + .build(); } } \ No newline at end of file diff --git a/li.strolch.rest/src/main/java/li/strolch/rest/filters/HttpCacheResponseFilter.java b/li.strolch.rest/src/main/java/li/strolch/rest/filters/HttpCacheResponseFilter.java new file mode 100644 index 000000000..65acbb67d --- /dev/null +++ b/li.strolch.rest/src/main/java/li/strolch/rest/filters/HttpCacheResponseFilter.java @@ -0,0 +1,44 @@ +package li.strolch.rest.filters; + +import java.io.IOException; + +import javax.annotation.Priority; +import javax.ws.rs.Priorities; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.ContainerResponseFilter; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.Provider; + +@Provider +@Priority(Priorities.HEADER_DECORATOR) +public class HttpCacheResponseFilter implements ContainerResponseFilter { + + public static final String NO_CACHE = "no-cache"; //$NON-NLS-1$ + + private static String cacheMode = NO_CACHE; + + @Override + public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) + throws IOException { + + MultivaluedMap headers = responseContext.getHeaders(); + headers.add(HttpHeaders.CACHE_CONTROL, cacheMode); + } + + /** + * @return the cacheMode + */ + public static String getCacheMode() { + return cacheMode; + } + + /** + * @param cacheMode + * the cacheMode to set + */ + public static void setCacheMode(String cacheMode) { + HttpCacheResponseFilter.cacheMode = cacheMode; + } +} \ No newline at end of file