1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.core.rest.cxf.service;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.time.OffsetDateTime;
24 import java.util.List;
25 import java.util.Optional;
26 import javax.ws.rs.InternalServerErrorException;
27 import javax.ws.rs.NotFoundException;
28 import javax.ws.rs.core.HttpHeaders;
29 import javax.ws.rs.core.MediaType;
30 import javax.ws.rs.core.Response;
31 import javax.ws.rs.core.StreamingOutput;
32 import org.apache.commons.lang3.StringUtils;
33 import org.apache.commons.lang3.tuple.Pair;
34 import org.apache.cxf.Bus;
35 import org.apache.cxf.transport.DestinationFactoryManager;
36 import org.apache.cxf.transport.http.DestinationRegistry;
37 import org.apache.cxf.transport.http.HTTPTransportFactory;
38 import org.apache.syncope.common.lib.SyncopeClientException;
39 import org.apache.syncope.common.lib.SyncopeConstants;
40 import org.apache.syncope.common.lib.to.GroupTO;
41 import org.apache.syncope.common.lib.to.PagedResult;
42 import org.apache.syncope.common.lib.to.TypeExtensionTO;
43 import org.apache.syncope.common.lib.types.ClientExceptionType;
44 import org.apache.syncope.common.rest.api.Preference;
45 import org.apache.syncope.common.rest.api.RESTHeaders;
46 import org.apache.syncope.common.rest.api.batch.BatchPayloadParser;
47 import org.apache.syncope.common.rest.api.batch.BatchRequestItem;
48 import org.apache.syncope.common.rest.api.service.SyncopeService;
49 import org.apache.syncope.core.logic.SyncopeLogic;
50 import org.apache.syncope.core.persistence.api.dao.BatchDAO;
51 import org.apache.syncope.core.persistence.api.entity.Batch;
52 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
53 import org.apache.syncope.core.rest.cxf.batch.BatchProcess;
54 import org.apache.syncope.core.spring.ApplicationContextProvider;
55 import org.apache.syncope.core.spring.security.AuthContextUtils;
56 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
57 import org.springframework.security.core.context.SecurityContextHolder;
58 import org.springframework.stereotype.Service;
59
60 @Service
61 public class SyncopeServiceImpl extends AbstractService implements SyncopeService {
62
63 private static final String CONTENT_XML = "Content.xml";
64
65 protected final SyncopeLogic logic;
66
67 protected final ThreadPoolTaskExecutor batchExecutor;
68
69 protected final Bus bus;
70
71 protected final BatchDAO batchDAO;
72
73 protected final EntityFactory entityFactory;
74
75 public SyncopeServiceImpl(
76 final SyncopeLogic logic,
77 final ThreadPoolTaskExecutor batchExecutor,
78 final Bus bus,
79 final BatchDAO batchDAO,
80 final EntityFactory entityFactory) {
81
82 this.logic = logic;
83 this.batchExecutor = batchExecutor;
84 this.bus = bus;
85 this.batchDAO = batchDAO;
86 this.entityFactory = entityFactory;
87 }
88
89 @Override
90 public PagedResult<GroupTO> searchAssignableGroups(
91 final String realm, final String term, final int page, final int size) {
92
93 Pair<Integer, List<GroupTO>> result = logic.searchAssignableGroups(
94 StringUtils.prependIfMissing(realm, SyncopeConstants.ROOT_REALM), term, page, size);
95 return buildPagedResult(result.getRight(), page, size, result.getLeft());
96 }
97
98 @Override
99 public TypeExtensionTO readUserTypeExtension(final String groupName) {
100 return logic.readTypeExtension(groupName);
101 }
102
103 private DestinationRegistry getDestinationRegistryFromBusOrDefault() {
104 DestinationFactoryManager dfm = bus.getExtension(DestinationFactoryManager.class);
105 try {
106 HTTPTransportFactory df = (HTTPTransportFactory) dfm.
107 getDestinationFactory("http://cxf.apache.org/transports/http/configuration");
108 return df.getRegistry();
109 } catch (Exception e) {
110 throw new InternalServerErrorException("Could not find CXF's DestinationRegistry", e);
111 }
112 }
113
114 @Override
115 public Response batch(final InputStream input) {
116
117 MediaType mediaType = MediaType.valueOf(messageContext.getHttpServletRequest().getContentType());
118 String boundary = mediaType.getParameters().get(RESTHeaders.BOUNDARY_PARAMETER);
119
120 if (batchDAO.find(boundary) != null) {
121 SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.EntityExists);
122 sce.getElements().add("Batch with boundary " + boundary + " already processing");
123 throw sce;
124 }
125
126
127 List<BatchRequestItem> batchRequestItems;
128 try {
129 batchRequestItems = BatchPayloadParser.parse(input, mediaType, new BatchRequestItem());
130 } catch (IOException e) {
131 LOG.error("Could not parse batch request with boundary {}", boundary, e);
132
133 SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidEntity);
134 sce.getElements().add("Batch request with boundary " + boundary);
135 throw sce;
136 }
137
138
139 Batch batch = entityFactory.newEntity(Batch.class);
140 batch.setKey(boundary);
141 batch.setExpiryTime(OffsetDateTime.now().plusMinutes(5));
142 batchDAO.save(batch);
143
144 BatchProcess batchProcess = ApplicationContextProvider.getBeanFactory().createBean(BatchProcess.class);
145 batchProcess.setBoundary(boundary);
146 batchProcess.setBasePath(uriInfo.getBaseUri().toASCIIString());
147 batchProcess.setBatchRequestItems(batchRequestItems);
148 batchProcess.setDestinationRegistry(getDestinationRegistryFromBusOrDefault());
149 batchProcess.setServletConfig(messageContext.getServletConfig());
150 batchProcess.setServletRequest(messageContext.getHttpServletRequest());
151 batchProcess.setAuthentication(SecurityContextHolder.getContext().getAuthentication());
152
153
154 if (getPreference() == Preference.RESPOND_ASYNC) {
155 batchExecutor.execute(batchProcess);
156
157 return Response.accepted().
158 header(RESTHeaders.PREFERENCE_APPLIED, getPreference().toString()).
159 header(HttpHeaders.LOCATION, uriInfo.getAbsolutePathBuilder().build()).
160 type(RESTHeaders.multipartMixedWith(boundary)).
161 build();
162 } else {
163 batchProcess.run();
164 return batch();
165 }
166 }
167
168 @Override
169 public Response batch() {
170 MediaType mediaType = MediaType.valueOf(messageContext.getHttpServletRequest().getContentType());
171 String boundary = mediaType.getParameters().get(RESTHeaders.BOUNDARY_PARAMETER);
172
173 Batch batch = Optional.ofNullable(batchDAO.find(boundary)).
174 orElseThrow(() -> new NotFoundException("Batch " + boundary));
175
176 if (batch.getResults() == null) {
177 return Response.accepted().
178 type(RESTHeaders.multipartMixedWith(boundary)).
179 header(HttpHeaders.RETRY_AFTER, 5).
180 header(HttpHeaders.LOCATION, uriInfo.getAbsolutePathBuilder().build()).
181 build();
182 }
183
184 Response response = Response.ok(batch.getResults()).
185 type(RESTHeaders.multipartMixedWith(boundary)).
186 build();
187
188 batchDAO.delete(boundary);
189
190 return response;
191 }
192
193 @Override
194 public Response exportInternalStorageContent(final int tableThreshold) {
195 StreamingOutput sout = os -> logic.exportInternalStorageContent(tableThreshold, os);
196
197 return Response.ok(sout).
198 type(MediaType.TEXT_XML).
199 header(HttpHeaders.CONTENT_DISPOSITION,
200 "attachment; filename=" + AuthContextUtils.getDomain() + CONTENT_XML).
201 build();
202 }
203 }