View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.syncope.wa.starter;
20  
21  import com.fasterxml.jackson.databind.json.JsonMapper;
22  import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
23  import java.time.LocalDateTime;
24  import java.util.ArrayList;
25  import java.util.HashMap;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Objects;
29  import java.util.stream.Collectors;
30  import javax.ws.rs.NotFoundException;
31  import javax.ws.rs.core.Response;
32  import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
33  import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
34  import org.apache.syncope.common.keymaster.client.api.ServiceOps;
35  import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
36  import org.apache.syncope.common.lib.Attr;
37  import org.apache.syncope.common.lib.audit.AuditEntry;
38  import org.apache.syncope.common.lib.audit.EventCategory;
39  import org.apache.syncope.common.lib.to.AttrRepoTO;
40  import org.apache.syncope.common.lib.to.AuditConfTO;
41  import org.apache.syncope.common.lib.to.AuthModuleTO;
42  import org.apache.syncope.common.lib.to.PagedResult;
43  import org.apache.syncope.common.lib.types.ClientAppType;
44  import org.apache.syncope.common.lib.wa.GoogleMfaAuthToken;
45  import org.apache.syncope.common.lib.wa.ImpersonationAccount;
46  import org.apache.syncope.common.lib.wa.WAClientApp;
47  import org.apache.syncope.common.rest.api.beans.AuditQuery;
48  import org.apache.syncope.common.rest.api.service.AttrRepoService;
49  import org.apache.syncope.common.rest.api.service.AuditService;
50  import org.apache.syncope.common.rest.api.service.AuthModuleService;
51  import org.apache.syncope.common.rest.api.service.wa.GoogleMfaAuthTokenService;
52  import org.apache.syncope.common.rest.api.service.wa.ImpersonationService;
53  import org.apache.syncope.common.rest.api.service.wa.WAClientAppService;
54  import org.apache.syncope.common.rest.api.service.wa.WAConfigService;
55  import org.springframework.beans.factory.annotation.Autowired;
56  import org.springframework.context.ApplicationListener;
57  import org.springframework.context.event.ContextRefreshedEvent;
58  
59  public class SyncopeCoreTestingServer implements ApplicationListener<ContextRefreshedEvent> {
60  
61      private static final String ADDRESS = "http://localhost:9081/syncope/rest";
62  
63      public static final List<AuthModuleTO> AUTH_MODULES = new ArrayList<>();
64  
65      public static final List<AttrRepoTO> ATTR_REPOS = new ArrayList<>();
66  
67      public static final List<WAClientApp> CLIENT_APPS = new ArrayList<>();
68  
69      public static final List<Attr> CONFIG = new ArrayList<>();
70  
71      protected static class StubAuthModuleService implements AuthModuleService {
72  
73          @Override
74          public AuthModuleTO read(final String key) {
75              return AUTH_MODULES.stream().filter(m -> Objects.equals(key, m.getKey())).
76                      findFirst().orElseThrow(() -> new NotFoundException("Auth Module with key " + key));
77          }
78  
79          @Override
80          public List<AuthModuleTO> list() {
81              return AUTH_MODULES;
82          }
83  
84          @Override
85          public Response create(final AuthModuleTO authModuleTO) {
86              AUTH_MODULES.add(authModuleTO);
87              return Response.created(null).build();
88          }
89  
90          @Override
91          public void update(final AuthModuleTO authModuleTO) {
92              delete(authModuleTO.getKey());
93              create(authModuleTO);
94          }
95  
96          @Override
97          public void delete(final String key) {
98              AUTH_MODULES.removeIf(m -> Objects.equals(key, m.getKey()));
99          }
100     }
101 
102     protected static class StubAttrRepoService implements AttrRepoService {
103 
104         @Override
105         public AttrRepoTO read(final String key) {
106             return ATTR_REPOS.stream().filter(m -> Objects.equals(key, m.getKey())).
107                     findFirst().orElseThrow(() -> new NotFoundException("Attr Repo with key " + key));
108         }
109 
110         @Override
111         public List<AttrRepoTO> list() {
112             return ATTR_REPOS;
113         }
114 
115         @Override
116         public Response create(final AttrRepoTO attrRepoTO) {
117             ATTR_REPOS.add(attrRepoTO);
118             return Response.created(null).build();
119         }
120 
121         @Override
122         public void update(final AttrRepoTO attrRepoTO) {
123             delete(attrRepoTO.getKey());
124             create(attrRepoTO);
125         }
126 
127         @Override
128         public void delete(final String key) {
129             ATTR_REPOS.removeIf(m -> Objects.equals(key, m.getKey()));
130         }
131     }
132 
133     protected static class StubWAClientAppService implements WAClientAppService {
134 
135         @Override
136         public List<WAClientApp> list() {
137             return CLIENT_APPS;
138         }
139 
140         @Override
141         public WAClientApp read(final Long clientAppId, final ClientAppType type) {
142             return CLIENT_APPS.stream().
143                     filter(app -> Objects.equals(clientAppId, app.getClientAppTO().getClientAppId())).
144                     findFirst().orElseThrow(() -> new NotFoundException("ClientApp with clientId " + clientAppId));
145         }
146 
147         @Override
148         public WAClientApp read(final String name, final ClientAppType type) {
149             return CLIENT_APPS.stream().filter(app -> Objects.equals(name, app.getClientAppTO().getName())).
150                     findFirst().orElseThrow(() -> new NotFoundException("ClientApp with name " + name));
151         }
152     }
153 
154     protected static class StubWAConfigService implements WAConfigService {
155 
156         @Override
157         public List<Attr> list() {
158             return CONFIG;
159         }
160 
161         @Override
162         public Attr get(final String schema) {
163             return CONFIG.stream().filter(c -> Objects.equals(schema, c.getSchema())).
164                     findFirst().orElseThrow(() -> new NotFoundException("Config with schema " + schema));
165         }
166 
167         @Override
168         public void set(final Attr value) {
169             delete(value.getSchema());
170             CONFIG.add(value);
171         }
172 
173         @Override
174         public void delete(final String schema) {
175             CONFIG.removeIf(c -> Objects.equals(schema, c.getSchema()));
176         }
177 
178         @Override
179         public void pushToWA(final PushSubject subject, final List<String> services) {
180             // nothing to do
181         }
182     }
183 
184     protected static class StubImpersonationService implements ImpersonationService {
185 
186         private final Map<String, List<ImpersonationAccount>> accounts = new HashMap<>();
187 
188         @Override
189         public List<ImpersonationAccount> read(final String owner) {
190             return accounts.containsKey(owner) ? accounts.get(owner) : List.of();
191         }
192 
193         @Override
194         public void create(final String owner, final ImpersonationAccount account) {
195             try {
196                 if (accounts.containsKey(owner) && accounts.get(owner).stream().
197                         noneMatch(acct -> acct.getImpersonated().equalsIgnoreCase(account.getImpersonated()))) {
198 
199                     accounts.get(owner).add(account);
200                 } else {
201                     List<ImpersonationAccount> list = new ArrayList<>();
202                     list.add(account);
203                     accounts.put(owner, list);
204                 }
205             } catch (final Exception e) {
206                 throw new IllegalStateException(e);
207             }
208         }
209 
210         @Override
211         public void delete(final String owner, final String impersonated) {
212             if (accounts.containsKey(owner)) {
213                 accounts.get(owner).removeIf(acct -> acct.getImpersonated().equalsIgnoreCase(impersonated));
214             }
215         }
216     }
217 
218     protected static class StubGoogleMfaAuthTokenService implements GoogleMfaAuthTokenService {
219 
220         private final Map<String, GoogleMfaAuthToken> tokens = new HashMap<>();
221 
222         @Override
223         public void delete(final LocalDateTime expirationDate) {
224             if (expirationDate == null) {
225                 tokens.clear();
226             } else {
227                 tokens.entrySet().removeIf(token -> token.getValue().getIssueDate().compareTo(expirationDate) >= 0);
228             }
229         }
230 
231         @Override
232         public void delete(final String owner, final int otp) {
233             tokens.entrySet().
234                     removeIf(e -> e.getValue().getOtp() == otp && e.getKey().equalsIgnoreCase(owner));
235         }
236 
237         @Override
238         public void delete(final String owner) {
239             tokens.entrySet().removeIf(e -> e.getKey().equalsIgnoreCase(owner));
240         }
241 
242         @Override
243         public void delete(final int otp) {
244             tokens.entrySet().removeIf(to -> to.getValue().getOtp() == otp);
245         }
246 
247         @Override
248         public void store(final String owner, final GoogleMfaAuthToken tokenTO) {
249             tokens.put(owner, tokenTO);
250         }
251 
252         @Override
253         public GoogleMfaAuthToken read(final String owner, final int otp) {
254             return tokens.entrySet().stream().
255                     filter(to -> to.getValue().getOtp() == otp && to.getKey().equalsIgnoreCase(owner)).
256                     findFirst().get().getValue();
257         }
258 
259         @Override
260         public PagedResult<GoogleMfaAuthToken> read(final String user) {
261             PagedResult<GoogleMfaAuthToken> result = new PagedResult<>();
262             result.getResult().addAll(tokens.entrySet().stream().
263                     filter(to -> to.getKey().equalsIgnoreCase(user)).
264                     map(Map.Entry::getValue).
265                     collect(Collectors.toList()));
266             result.setSize(result.getResult().size());
267             result.setTotalCount(result.getSize());
268             return result;
269         }
270 
271         @Override
272         public PagedResult<GoogleMfaAuthToken> list() {
273             PagedResult<GoogleMfaAuthToken> result = new PagedResult<>();
274             result.setSize(tokens.size());
275             result.setTotalCount(tokens.size());
276             result.getResult().addAll(tokens.values());
277             return result;
278         }
279     }
280 
281     protected static class StubAuditService implements AuditService {
282 
283         @Override
284         public List<AuditConfTO> list() {
285             return List.of();
286         }
287 
288         @Override
289         public AuditConfTO read(final String key) {
290             throw new NotFoundException();
291         }
292 
293         @Override
294         public void set(final AuditConfTO auditTO) {
295             // nothing to do
296         }
297 
298         @Override
299         public void delete(final String key) {
300             // nothing to do
301         }
302 
303         @Override
304         public List<EventCategory> events() {
305             return List.of();
306         }
307 
308         @Override
309         public PagedResult<AuditEntry> search(final AuditQuery auditQuery) {
310             return new PagedResult<>();
311         }
312 
313         @Override
314         public void create(final AuditEntry auditEntry) {
315             // nothing to do
316         }
317     }
318 
319     @Autowired
320     private ServiceOps serviceOps;
321 
322     @Override
323     public void onApplicationEvent(final ContextRefreshedEvent event) {
324         synchronized (ADDRESS) {
325             if (serviceOps.list(NetworkService.Type.CORE).isEmpty()) {
326                 // 1. start (mocked) Core as embedded CXF
327                 JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
328                 sf.setAddress(ADDRESS);
329                 sf.setResourceClasses(
330                         AuditService.class,
331                         AuthModuleService.class,
332                         AttrRepoService.class,
333                         WAClientAppService.class,
334                         WAConfigService.class,
335                         GoogleMfaAuthTokenService.class,
336                         ImpersonationService.class);
337                 sf.setResourceProvider(
338                         AuditService.class,
339                         new SingletonResourceProvider(new StubAuditService(), true));
340                 sf.setResourceProvider(
341                         AuthModuleService.class,
342                         new SingletonResourceProvider(new StubAuthModuleService(), true));
343                 sf.setResourceProvider(
344                         AttrRepoService.class,
345                         new SingletonResourceProvider(new StubAttrRepoService(), true));
346                 sf.setResourceProvider(
347                         WAClientAppService.class,
348                         new SingletonResourceProvider(new StubWAClientAppService(), true));
349                 sf.setResourceProvider(
350                         WAConfigService.class,
351                         new SingletonResourceProvider(new StubWAConfigService(), true));
352                 sf.setResourceProvider(
353                         GoogleMfaAuthTokenService.class,
354                         new SingletonResourceProvider(new StubGoogleMfaAuthTokenService(), true));
355                 sf.setResourceProvider(
356                         ImpersonationService.class,
357                         new SingletonResourceProvider(new StubImpersonationService(), true));
358                 sf.setProviders(List.of(new JacksonJsonProvider(JsonMapper.builder().findAndAddModules().build())));
359                 sf.create();
360 
361                 // 2. register Core in Keymaster
362                 NetworkService core = new NetworkService();
363                 core.setType(NetworkService.Type.CORE);
364                 core.setAddress(ADDRESS);
365                 serviceOps.register(core);
366             }
367         }
368     }
369 }