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.service;
20
21 import java.time.OffsetDateTime;
22 import java.util.List;
23 import javax.ws.rs.core.Context;
24 import javax.ws.rs.core.EntityTag;
25 import javax.ws.rs.core.Response;
26 import javax.ws.rs.core.Response.ResponseBuilder;
27 import javax.ws.rs.core.UriInfo;
28 import org.apache.commons.lang3.StringUtils;
29 import org.apache.commons.lang3.tuple.Pair;
30 import org.apache.cxf.jaxrs.ext.MessageContext;
31 import org.apache.syncope.common.lib.SyncopeConstants;
32 import org.apache.syncope.common.lib.to.AnyTO;
33 import org.apache.syncope.common.lib.to.GroupTO;
34 import org.apache.syncope.common.lib.to.UserTO;
35 import org.apache.syncope.core.logic.AbstractAnyLogic;
36 import org.apache.syncope.core.logic.GroupLogic;
37 import org.apache.syncope.core.logic.SCIMDataBinder;
38 import org.apache.syncope.core.logic.UserLogic;
39 import org.apache.syncope.core.logic.scim.SCIMConfManager;
40 import org.apache.syncope.core.logic.scim.SearchCondConverter;
41 import org.apache.syncope.core.logic.scim.SearchCondVisitor;
42 import org.apache.syncope.core.persistence.api.dao.AnyDAO;
43 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
44 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
45 import org.apache.syncope.core.persistence.api.dao.UserDAO;
46 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
47 import org.apache.syncope.ext.scimv2.api.BadRequestException;
48 import org.apache.syncope.ext.scimv2.api.data.ListResponse;
49 import org.apache.syncope.ext.scimv2.api.data.SCIMResource;
50 import org.apache.syncope.ext.scimv2.api.data.SCIMSearchRequest;
51 import org.apache.syncope.ext.scimv2.api.type.ErrorType;
52 import org.apache.syncope.ext.scimv2.api.type.Resource;
53 import org.apache.syncope.ext.scimv2.api.type.SortOrder;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 abstract class AbstractSCIMService<R extends SCIMResource> {
58
59 protected static final Logger LOG = LoggerFactory.getLogger(AbstractSCIMService.class);
60
61 @Context
62 protected UriInfo uriInfo;
63
64 @Context
65 protected MessageContext messageContext;
66
67 protected final UserDAO userDAO;
68
69 protected final GroupDAO groupDAO;
70
71 protected final UserLogic userLogic;
72
73 protected final GroupLogic groupLogic;
74
75 protected final SCIMDataBinder binder;
76
77 protected final SCIMConfManager confManager;
78
79 protected AbstractSCIMService(
80 final UserDAO userDAO,
81 final GroupDAO groupDAO,
82 final UserLogic userLogic,
83 final GroupLogic groupLogic,
84 final SCIMDataBinder binder,
85 final SCIMConfManager confManager) {
86
87 this.userDAO = userDAO;
88 this.groupDAO = groupDAO;
89 this.userLogic = userLogic;
90 this.groupLogic = groupLogic;
91 this.binder = binder;
92 this.confManager = confManager;
93 }
94
95 protected AnyDAO<?> anyDAO(final Resource type) {
96 switch (type) {
97 case User:
98 return userDAO;
99
100 case Group:
101 return groupDAO;
102
103 default:
104 throw new UnsupportedOperationException();
105 }
106 }
107
108 protected AbstractAnyLogic<?, ?, ?> anyLogic(final Resource type) {
109 switch (type) {
110 case User:
111 return userLogic;
112
113 case Group:
114 return groupLogic;
115
116 default:
117 throw new UnsupportedOperationException();
118 }
119 }
120
121 protected Response createResponse(final String key, final SCIMResource resource) {
122 return Response.created(uriInfo.getAbsolutePathBuilder().path(key).build()).
123 entity(resource).
124 build();
125 }
126
127 protected Response updateResponse(final String key, final SCIMResource resource) {
128 return Response.ok(uriInfo.getAbsolutePathBuilder().path(key).build()).
129 entity(resource).
130 build();
131 }
132
133 protected ResponseBuilder checkETag(final Resource resource, final String key) {
134 OffsetDateTime lastChange = anyDAO(resource).findLastChange(key);
135 if (lastChange == null) {
136 throw new NotFoundException("Resource" + key + " not found");
137 }
138
139 return messageContext.getRequest().
140 evaluatePreconditions(new EntityTag(String.valueOf(lastChange.toInstant().toEpochMilli()), true));
141 }
142
143 @SuppressWarnings("unchecked")
144 protected ListResponse<R> doSearch(
145 final Resource type,
146 final SCIMSearchRequest request) {
147
148 if (type == null) {
149 throw new UnsupportedOperationException();
150 }
151
152 if (request.getCount() > confManager.get().getGeneralConf().getFilterMaxResults()) {
153 throw new BadRequestException(ErrorType.tooMany, "Too many results requested");
154 }
155
156 SearchCondVisitor visitor = new SearchCondVisitor(type, confManager.get());
157
158 int startIndex = request.getStartIndex() <= 1
159 ? 1
160 : (request.getStartIndex() / AnyDAO.DEFAULT_PAGE_SIZE) + 1;
161
162 int itemsPerPage = request.getCount() <= 1 ? AnyDAO.DEFAULT_PAGE_SIZE : request.getCount();
163
164 List<OrderByClause> sort;
165 if (request.getSortBy() == null) {
166 sort = List.of();
167 } else {
168 OrderByClause clause = new OrderByClause();
169 clause.setField(visitor.createAttrCond(request.getSortBy()).getSchema());
170 clause.setDirection(request.getSortOrder() == null || request.getSortOrder() == SortOrder.ascending
171 ? OrderByClause.Direction.ASC
172 : OrderByClause.Direction.DESC);
173 sort = List.of(clause);
174 }
175
176 Pair<Integer, ? extends List<? extends AnyTO>> result = anyLogic(type).search(
177 StringUtils.isBlank(request.getFilter())
178 ? null
179 : SearchCondConverter.convert(visitor, request.getFilter()),
180 startIndex,
181 itemsPerPage,
182 sort,
183 SyncopeConstants.ROOT_REALM,
184 true,
185 false);
186
187 if (result.getLeft() > confManager.get().getGeneralConf().getFilterMaxResults()) {
188 throw new BadRequestException(ErrorType.tooMany, "Too many results found");
189 }
190
191 ListResponse<R> response = new ListResponse<>(
192 result.getLeft(), startIndex == 1 ? 1 : startIndex - 1, itemsPerPage);
193
194 result.getRight().forEach(anyTO -> {
195 SCIMResource resource = null;
196 if (anyTO instanceof UserTO) {
197 resource = binder.toSCIMUser(
198 (UserTO) anyTO,
199 uriInfo.getAbsolutePathBuilder().path(anyTO.getKey()).build().toASCIIString(),
200 request.getAttributes(),
201 request.getExcludedAttributes());
202 } else if (anyTO instanceof GroupTO) {
203 resource = binder.toSCIMGroup(
204 (GroupTO) anyTO,
205 uriInfo.getAbsolutePathBuilder().path(anyTO.getKey()).build().toASCIIString(),
206 request.getAttributes(),
207 request.getExcludedAttributes());
208 }
209
210 if (resource != null) {
211 response.getResources().add((R) resource);
212 }
213 });
214
215 return response;
216 }
217 }