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.core.logic.wa;
20  
21  import java.lang.reflect.Method;
22  import java.net.URI;
23  import java.net.http.HttpClient;
24  import java.net.http.HttpRequest;
25  import java.net.http.HttpResponse;
26  import java.util.List;
27  import java.util.Optional;
28  import java.util.stream.Collectors;
29  import javax.ws.rs.core.HttpHeaders;
30  import org.apache.commons.lang3.StringUtils;
31  import org.apache.cxf.transport.http.auth.DefaultBasicAuthSupplier;
32  import org.apache.syncope.common.keymaster.client.api.KeymasterException;
33  import org.apache.syncope.common.keymaster.client.api.ServiceOps;
34  import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
35  import org.apache.syncope.common.lib.Attr;
36  import org.apache.syncope.common.lib.to.EntityTO;
37  import org.apache.syncope.common.lib.types.AMEntitlement;
38  import org.apache.syncope.common.lib.types.IdRepoEntitlement;
39  import org.apache.syncope.common.rest.api.service.wa.WAConfigService;
40  import org.apache.syncope.core.logic.AbstractTransactionalLogic;
41  import org.apache.syncope.core.logic.UnresolvedReferenceException;
42  import org.apache.syncope.core.persistence.api.dao.NotFoundException;
43  import org.apache.syncope.core.persistence.api.dao.WAConfigDAO;
44  import org.apache.syncope.core.provisioning.api.data.WAConfigDataBinder;
45  import org.apache.syncope.core.spring.security.SecurityProperties;
46  import org.springframework.security.access.prepost.PreAuthorize;
47  import org.springframework.transaction.annotation.Transactional;
48  import org.springframework.util.CollectionUtils;
49  
50  public class WAConfigLogic extends AbstractTransactionalLogic<EntityTO> {
51  
52      protected final ServiceOps serviceOps;
53  
54      protected final WAConfigDataBinder binder;
55  
56      protected final WAConfigDAO waConfigDAO;
57  
58      protected final SecurityProperties securityProperties;
59  
60      public WAConfigLogic(
61              final ServiceOps serviceOps,
62              final WAConfigDataBinder binder,
63              final WAConfigDAO waConfigDAO,
64              final SecurityProperties securityProperties) {
65  
66          this.serviceOps = serviceOps;
67          this.binder = binder;
68          this.waConfigDAO = waConfigDAO;
69          this.securityProperties = securityProperties;
70      }
71  
72      @PreAuthorize("hasRole('" + AMEntitlement.WA_CONFIG_LIST + "') or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
73      @Transactional(readOnly = true)
74      public List<Attr> list() {
75          return waConfigDAO.findAll().stream().map(binder::get).collect(Collectors.toList());
76      }
77  
78      @PreAuthorize("hasRole('" + AMEntitlement.WA_CONFIG_GET + "')")
79      @Transactional(readOnly = true)
80      public Attr get(final String schema) {
81          return Optional.ofNullable(waConfigDAO.find(schema)).
82                  map(binder::get).
83                  orElseThrow(() -> new NotFoundException("Configuration entry " + schema + " not found"));
84      }
85  
86      @PreAuthorize("hasRole('" + AMEntitlement.WA_CONFIG_SET + "')")
87      public void set(final Attr value) {
88          waConfigDAO.save(binder.set(value));
89      }
90  
91      @PreAuthorize("hasRole('" + AMEntitlement.WA_CONFIG_DELETE + "')")
92      public void delete(final String key) {
93          waConfigDAO.delete(key);
94      }
95  
96      protected void registeredServices(final HttpClient client, final String serviceAddress) {
97          String target = StringUtils.appendIfMissing(serviceAddress, "/") + "actuator/registeredServices";
98          client.sendAsync(
99                  HttpRequest.newBuilder(URI.create(target)).
100                         header(HttpHeaders.AUTHORIZATION, DefaultBasicAuthSupplier.getBasicAuthHeader(
101                                 securityProperties.getAnonymousUser(), securityProperties.getAnonymousKey())).
102                         GET().build(),
103                 HttpResponse.BodyHandlers.discarding()).
104                 thenAcceptAsync(response -> LOG.info(
105                 "Pushed to {} with HTTP status: {}", target, response.statusCode()));
106     }
107 
108     protected void refresh(final HttpClient client, final String serviceAddress) {
109         String target = StringUtils.appendIfMissing(serviceAddress, "/") + "actuator/refresh";
110         client.sendAsync(
111                 HttpRequest.newBuilder(URI.create(target)).
112                         header(HttpHeaders.AUTHORIZATION, DefaultBasicAuthSupplier.getBasicAuthHeader(
113                                 securityProperties.getAnonymousUser(), securityProperties.getAnonymousKey())).
114                         POST(HttpRequest.BodyPublishers.noBody()).build(),
115                 HttpResponse.BodyHandlers.discarding()).
116                 thenAcceptAsync(response -> LOG.info(
117                 "Pushed to {} with HTTP status: {}", target, response.statusCode()));
118     }
119 
120     @PreAuthorize("hasRole('" + AMEntitlement.WA_CONFIG_PUSH + "')")
121     public void pushToWA(final WAConfigService.PushSubject subject, final List<String> services) {
122         HttpClient client = HttpClient.newHttpClient();
123         try {
124             serviceOps.list(NetworkService.Type.WA).stream().
125                     filter(wa -> CollectionUtils.isEmpty(services) || services.contains(wa.getAddress())).
126                     forEach(wa -> {
127                         switch (subject) {
128                             case clientApps:
129                                 registeredServices(client, wa.getAddress());
130                                 break;
131 
132                             case conf:
133                             default:
134                                 refresh(client, wa.getAddress());
135                         }
136                     });
137         } catch (KeymasterException e) {
138             throw new NotFoundException("Could not find any WA instance", e);
139         }
140     }
141 
142     @Override
143     protected EntityTO resolveReference(final Method method, final Object... args)
144             throws UnresolvedReferenceException {
145 
146         throw new UnresolvedReferenceException();
147     }
148 }