1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.ext.scimv2.cxf;
20
21 import java.util.Map;
22 import java.util.Optional;
23 import java.util.Set;
24 import javax.validation.ValidationException;
25 import javax.ws.rs.ForbiddenException;
26 import javax.ws.rs.NotAuthorizedException;
27 import javax.ws.rs.core.Response;
28 import javax.ws.rs.core.Response.ResponseBuilder;
29 import javax.ws.rs.ext.ExceptionMapper;
30 import javax.ws.rs.ext.Provider;
31 import org.apache.commons.lang3.exception.ExceptionUtils;
32 import org.apache.syncope.common.lib.SyncopeClientException;
33 import org.apache.syncope.common.lib.types.ClientExceptionType;
34 import org.apache.syncope.common.lib.types.EntityViolationType;
35 import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException;
36 import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException;
37 import org.apache.syncope.core.persistence.api.dao.DuplicateException;
38 import org.apache.syncope.core.persistence.api.dao.MalformedPathException;
39 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
40 import org.apache.syncope.core.persistence.api.entity.PlainAttr;
41 import org.apache.syncope.core.spring.security.DelegatedAdministrationException;
42 import org.apache.syncope.core.workflow.api.WorkflowException;
43 import org.apache.syncope.ext.scimv2.api.BadRequestException;
44 import org.apache.syncope.ext.scimv2.api.data.SCIMError;
45 import org.apache.syncope.ext.scimv2.api.type.ErrorType;
46 import org.identityconnectors.framework.common.exceptions.ConfigurationException;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49 import org.springframework.dao.DataIntegrityViolationException;
50 import org.springframework.security.access.AccessDeniedException;
51 import org.springframework.transaction.TransactionSystemException;
52
53 @Provider
54 public class SCIMExceptionMapper implements ExceptionMapper<Exception> {
55
56 protected static final Logger LOG = LoggerFactory.getLogger(SCIMExceptionMapper.class);
57
58 protected static Class<?> ENTITYEXISTS_EXCLASS = null;
59
60 protected static Class<?> PERSISTENCE_EXCLASS = null;
61
62 protected static Class<?> ROLLBACK_EXCLASS = null;
63
64 protected static Class<?> JPASYSTEM_EXCLASS = null;
65
66 protected static Class<?> CONNECTOR_EXCLASS = null;
67
68 protected static Class<?> IBATISPERSISTENCE_EXCLASS = null;
69
70 static {
71 try {
72 ENTITYEXISTS_EXCLASS = Class.forName("javax.persistence.EntityExistsException");
73 PERSISTENCE_EXCLASS = Class.forName("javax.persistence.PersistenceException");
74 ROLLBACK_EXCLASS = Class.forName("javax.persistence.RollbackException");
75 JPASYSTEM_EXCLASS = Class.forName("org.springframework.orm.jpa.JpaSystemException");
76 CONNECTOR_EXCLASS = Class.forName("org.identityconnectors.framework.common.exceptions.ConnectorException");
77 IBATISPERSISTENCE_EXCLASS = Class.forName("org.apache.ibatis.exceptions.PersistenceException");
78 } catch (ClassNotFoundException e) {
79
80 }
81 }
82
83 @Override
84 public Response toResponse(final Exception ex) {
85 LOG.error("Exception thrown", ex);
86
87 ResponseBuilder builder;
88
89 if (ex instanceof AccessDeniedException
90 || ex instanceof ForbiddenException
91 || ex instanceof NotAuthorizedException) {
92
93
94 builder = null;
95 } else if (ex instanceof NotFoundException) {
96 return Response.status(Response.Status.NOT_FOUND).entity(new SCIMError(null,
97 Response.Status.NOT_FOUND.getStatusCode(), ExceptionUtils.getRootCauseMessage(ex))).
98 build();
99 } else if (ex instanceof SyncopeClientException) {
100 SyncopeClientException sce = (SyncopeClientException) ex;
101 builder = builder(sce.getType(), ExceptionUtils.getRootCauseMessage(ex));
102 } else if (ex instanceof DelegatedAdministrationException
103 || ExceptionUtils.getRootCause(ex) instanceof DelegatedAdministrationException) {
104
105 builder = builder(ClientExceptionType.DelegatedAdministration, ExceptionUtils.getRootCauseMessage(ex));
106 } else if (ENTITYEXISTS_EXCLASS.isAssignableFrom(ex.getClass())
107 || ex instanceof DuplicateException
108 || PERSISTENCE_EXCLASS.isAssignableFrom(ex.getClass())
109 && ENTITYEXISTS_EXCLASS.isAssignableFrom(ex.getCause().getClass())) {
110
111 builder = builder(ClientExceptionType.EntityExists, ExceptionUtils.getRootCauseMessage(ex));
112 } else if (ex instanceof DataIntegrityViolationException || JPASYSTEM_EXCLASS.isAssignableFrom(ex.getClass())) {
113 builder = builder(ClientExceptionType.DataIntegrityViolation, ExceptionUtils.getRootCauseMessage(ex));
114 } else if (CONNECTOR_EXCLASS.isAssignableFrom(ex.getClass())) {
115 builder = builder(ClientExceptionType.ConnectorException, ExceptionUtils.getRootCauseMessage(ex));
116 } else {
117 builder = processInvalidEntityExceptions(ex);
118 if (builder == null) {
119 builder = processBadRequestExceptions(ex);
120 }
121
122 if (builder == null && ex instanceof ValidationException) {
123 builder = builder(ClientExceptionType.RESTValidation, ExceptionUtils.getRootCauseMessage(ex));
124 }
125
126 if (builder == null && ex instanceof UnsupportedOperationException) {
127 builder = Response.status(Response.Status.INTERNAL_SERVER_ERROR).
128 entity(new SCIMError(
129 ErrorType.invalidSyntax,
130 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(),
131 ExceptionUtils.getRootCauseMessage(ex)));
132 }
133
134 if (builder == null) {
135 builder = Response.status(Response.Status.INTERNAL_SERVER_ERROR).
136 entity(ExceptionUtils.getRootCauseMessage(ex));
137 }
138 }
139
140 return Optional.ofNullable(builder).map(ResponseBuilder::build).orElse(null);
141 }
142
143 protected ResponseBuilder processInvalidEntityExceptions(final Exception ex) {
144 InvalidEntityException iee = null;
145
146 if (ex instanceof InvalidEntityException) {
147 iee = (InvalidEntityException) ex;
148 }
149 if (ex instanceof TransactionSystemException && ROLLBACK_EXCLASS.isAssignableFrom(ex.getCause().getClass())
150 && ex.getCause().getCause() instanceof InvalidEntityException) {
151
152 iee = (InvalidEntityException) ex.getCause().getCause();
153 }
154
155 if (iee != null) {
156 ClientExceptionType exType;
157 if (iee.getEntityClassSimpleName().endsWith("Policy")) {
158 exType = ClientExceptionType.InvalidPolicy;
159 } else if (iee.getEntityClassSimpleName().equals(PlainAttr.class.getSimpleName())) {
160 exType = ClientExceptionType.InvalidValues;
161 } else {
162 try {
163 exType = ClientExceptionType.valueOf("Invalid" + iee.getEntityClassSimpleName());
164 } catch (IllegalArgumentException e) {
165
166 exType = ClientExceptionType.InvalidEntity;
167 }
168 }
169
170 StringBuilder msg = new StringBuilder();
171
172 for (Map.Entry<Class<?>, Set<EntityViolationType>> violation : iee.getViolations().entrySet()) {
173 for (EntityViolationType violationType : violation.getValue()) {
174 msg.append(violationType.name()).append(": ").append(violationType.getMessage()).append('\n');
175 }
176 }
177
178 return builder(exType, msg.toString());
179 }
180
181 return null;
182 }
183
184 protected ResponseBuilder processBadRequestExceptions(final Exception ex) {
185 if (ex instanceof WorkflowException) {
186 return builder(ClientExceptionType.Workflow, ExceptionUtils.getRootCauseMessage(ex));
187 } else if (PERSISTENCE_EXCLASS.isAssignableFrom(ex.getClass())) {
188 return builder(ClientExceptionType.GenericPersistence, ExceptionUtils.getRootCauseMessage(ex));
189 } else if (IBATISPERSISTENCE_EXCLASS != null && IBATISPERSISTENCE_EXCLASS.isAssignableFrom(ex.getClass())) {
190 return builder(ClientExceptionType.Workflow, "Currently unavailable. Please try later.");
191 } else if (JPASYSTEM_EXCLASS.isAssignableFrom(ex.getClass())) {
192 return builder(ClientExceptionType.DataIntegrityViolation, ExceptionUtils.getRootCauseMessage(ex));
193 } else if (ex instanceof ConfigurationException) {
194 return builder(ClientExceptionType.InvalidConnIdConf, ExceptionUtils.getRootCauseMessage(ex));
195 } else if (ex instanceof ParsingValidationException) {
196 return builder(ClientExceptionType.InvalidValues, ExceptionUtils.getRootCauseMessage(ex));
197 } else if (ex instanceof MalformedPathException) {
198 return builder(ClientExceptionType.InvalidPath, ExceptionUtils.getRootCauseMessage(ex));
199 } else if (ex instanceof BadRequestException) {
200 return Response.status(Response.Status.BAD_REQUEST).entity(new SCIMError((BadRequestException) ex));
201 }
202
203 return null;
204 }
205
206 protected ResponseBuilder builder(final ClientExceptionType hType, final String msg) {
207 ResponseBuilder builder = Response.status(hType.getResponseStatus());
208
209 ErrorType scimType = null;
210 if (hType.name().startsWith("Invalid") || hType == ClientExceptionType.RESTValidation) {
211 scimType = ErrorType.invalidValue;
212 } else if (hType == ClientExceptionType.EntityExists) {
213 scimType = ErrorType.uniqueness;
214 }
215
216 return builder.entity(new SCIMError(scimType, hType.getResponseStatus().getStatusCode(), msg));
217 }
218 }