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.starter;
20  
21  import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
22  import io.swagger.v3.oas.integration.api.OpenAPIConfiguration;
23  import io.swagger.v3.oas.models.ExternalDocumentation;
24  import io.swagger.v3.oas.models.media.Schema;
25  import io.swagger.v3.oas.models.parameters.HeaderParameter;
26  import io.swagger.v3.oas.models.parameters.Parameter;
27  import io.swagger.v3.oas.models.security.SecurityScheme;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Optional;
31  import java.util.Set;
32  import java.util.regex.Pattern;
33  import java.util.stream.Collectors;
34  import org.apache.commons.lang3.StringUtils;
35  import org.apache.cxf.Bus;
36  import org.apache.cxf.endpoint.Server;
37  import org.apache.cxf.jaxrs.ext.MessageContext;
38  import org.apache.cxf.jaxrs.model.doc.JavaDocProvider;
39  import org.apache.cxf.jaxrs.openapi.OpenApiCustomizer;
40  import org.apache.cxf.jaxrs.openapi.OpenApiFeature;
41  import org.apache.cxf.jaxrs.spring.JAXRSServerFactoryBeanDefinitionParser.SpringJAXRSServerFactoryBean;
42  import org.apache.cxf.jaxrs.utils.JAXRSUtils;
43  import org.apache.cxf.jaxrs.validation.JAXRSBeanValidationInInterceptor;
44  import org.apache.cxf.transport.common.gzip.GZIPInInterceptor;
45  import org.apache.cxf.transport.common.gzip.GZIPOutInterceptor;
46  import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
47  import org.apache.syncope.common.keymaster.client.api.DomainOps;
48  import org.apache.syncope.common.keymaster.client.api.DomainWatcher;
49  import org.apache.syncope.common.keymaster.client.api.KeymasterProperties;
50  import org.apache.syncope.common.keymaster.client.api.ServiceOps;
51  import org.apache.syncope.common.keymaster.rest.api.service.ConfParamService;
52  import org.apache.syncope.common.keymaster.rest.api.service.DomainService;
53  import org.apache.syncope.common.keymaster.rest.api.service.NetworkServiceService;
54  import org.apache.syncope.common.lib.SyncopeConstants;
55  import org.apache.syncope.common.rest.api.RESTHeaders;
56  import org.apache.syncope.core.keymaster.internal.InternalConfParamHelper;
57  import org.apache.syncope.core.keymaster.internal.SelfKeymasterInternalConfParamOps;
58  import org.apache.syncope.core.keymaster.internal.SelfKeymasterInternalDomainOps;
59  import org.apache.syncope.core.keymaster.internal.SelfKeymasterInternalServiceOps;
60  import org.apache.syncope.core.keymaster.rest.cxf.service.ConfParamServiceImpl;
61  import org.apache.syncope.core.keymaster.rest.cxf.service.DomainServiceImpl;
62  import org.apache.syncope.core.keymaster.rest.cxf.service.NetworkServiceServiceImpl;
63  import org.apache.syncope.core.keymaster.rest.security.SelfKeymasterUsernamePasswordAuthenticationProvider;
64  import org.apache.syncope.core.logic.ConfParamLogic;
65  import org.apache.syncope.core.logic.DomainLogic;
66  import org.apache.syncope.core.logic.NetworkServiceLogic;
67  import org.apache.syncope.core.persistence.api.DomainHolder;
68  import org.apache.syncope.core.persistence.api.dao.ConfParamDAO;
69  import org.apache.syncope.core.persistence.api.dao.DomainDAO;
70  import org.apache.syncope.core.persistence.api.dao.NetworkServiceDAO;
71  import org.apache.syncope.core.persistence.api.entity.SelfKeymasterEntityFactory;
72  import org.apache.syncope.core.persistence.jpa.dao.JPAConfParamDAO;
73  import org.apache.syncope.core.persistence.jpa.dao.JPADomainDAO;
74  import org.apache.syncope.core.persistence.jpa.dao.JPANetworkServiceDAO;
75  import org.apache.syncope.core.persistence.jpa.entity.JPASelfKeymasterEntityFactory;
76  import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
77  import org.apache.syncope.core.rest.cxf.JavaDocUtils;
78  import org.apache.syncope.core.rest.cxf.RestServiceExceptionMapper;
79  import org.apache.syncope.core.spring.security.AuthDataAccessor;
80  import org.apache.syncope.core.spring.security.DefaultCredentialChecker;
81  import org.apache.syncope.core.spring.security.SecurityProperties;
82  import org.apache.syncope.core.spring.security.UsernamePasswordAuthenticationProvider;
83  import org.apache.syncope.core.spring.security.WebSecurityContext;
84  import org.apache.syncope.core.starter.SelfKeymasterContext.SelfKeymasterCondition;
85  import org.slf4j.Logger;
86  import org.slf4j.LoggerFactory;
87  import org.springframework.boot.autoconfigure.AutoConfigureBefore;
88  import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
89  import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
90  import org.springframework.boot.context.properties.EnableConfigurationProperties;
91  import org.springframework.context.annotation.Bean;
92  import org.springframework.context.annotation.ConditionContext;
93  import org.springframework.context.annotation.Conditional;
94  import org.springframework.context.annotation.Configuration;
95  import org.springframework.core.env.Environment;
96  import org.springframework.core.type.AnnotatedTypeMetadata;
97  
98  @EnableConfigurationProperties(KeymasterProperties.class)
99  @Configuration(proxyBeanMethods = false)
100 @AutoConfigureBefore(WebSecurityContext.class)
101 @Conditional(SelfKeymasterCondition.class)
102 public class SelfKeymasterContext {
103 
104     private static final Logger LOG = LoggerFactory.getLogger(SelfKeymasterContext.class);
105 
106     private static final Pattern HTTP = Pattern.compile("^http.+");
107 
108     static class SelfKeymasterCondition extends SpringBootCondition {
109 
110         @Override
111         public ConditionOutcome getMatchOutcome(final ConditionContext context, final AnnotatedTypeMetadata metadata) {
112             String keymasterAddress = context.getEnvironment().getProperty("keymaster.address");
113             return new ConditionOutcome(
114                     keymasterAddress != null && HTTP.matcher(keymasterAddress).matches(),
115                     "Keymaster address not set for Self: " + keymasterAddress);
116         }
117     }
118 
119     @Bean
120     public Server selfKeymasterContainer(
121             final DomainHolder domainHolder,
122             final ConfParamService confParamService,
123             final NetworkServiceService networkServiceService,
124             final DomainService domainService,
125             final JacksonJsonProvider jsonProvider,
126             final GZIPInInterceptor gzipInInterceptor,
127             final GZIPOutInterceptor gzipOutInterceptor,
128             final JAXRSBeanValidationInInterceptor validationInInterceptor,
129             final RestServiceExceptionMapper restServiceExceptionMapper,
130             final Bus bus,
131             final Environment env) {
132 
133         SpringJAXRSServerFactoryBean selfKeymasterContainer = new SpringJAXRSServerFactoryBean();
134         selfKeymasterContainer.setBus(bus);
135         selfKeymasterContainer.setAddress("/keymaster");
136         selfKeymasterContainer.setStaticSubresourceResolution(true);
137 
138         selfKeymasterContainer.setProperties(Map.of("convert.wadl.resources.to.dom", "false"));
139 
140         selfKeymasterContainer.setServiceBeans(List.of(confParamService, networkServiceService, domainService));
141 
142         selfKeymasterContainer.setInInterceptors(List.of(gzipInInterceptor, validationInInterceptor));
143 
144         selfKeymasterContainer.setOutInterceptors(List.of(gzipOutInterceptor));
145 
146         selfKeymasterContainer.setProviders(List.of(restServiceExceptionMapper, jsonProvider));
147 
148         // OpenAPI
149         JavaDocProvider javaDocProvider = JavaDocUtils.getJavaDocURLs().
150                 map(JavaDocProvider::new).
151                 orElseGet(() -> JavaDocUtils.getJavaDocPaths(env).
152                 map(javaDocPaths -> {
153                     try {
154                         return new JavaDocProvider(javaDocPaths);
155                     } catch (Exception e) {
156                         LOG.error("Could not set javadoc paths from {}", List.of(javaDocPaths), e);
157                         return null;
158                     }
159                 }).
160                 orElse(null));
161         OpenApiCustomizer openApiCustomizer = new OpenApiCustomizer() {
162 
163             @Override
164             public OpenAPIConfiguration customize(final OpenAPIConfiguration configuration) {
165                 super.customize(configuration);
166 
167                 MessageContext ctx = JAXRSUtils.createContextValue(
168                         JAXRSUtils.getCurrentMessage(), null, MessageContext.class);
169 
170                 String url = StringUtils.substringBeforeLast(ctx.getUriInfo().getRequestUri().getRawPath(), "/");
171                 configuration.getOpenAPI().setServers(List.of(new io.swagger.v3.oas.models.servers.Server().url(url)));
172 
173                 return configuration;
174             }
175 
176             @Override
177             protected void addParameters(final List<Parameter> parameters) {
178                 Optional<Parameter> domainHeaderParameter = parameters.stream().
179                         filter(p -> p instanceof HeaderParameter && RESTHeaders.DOMAIN.equals(p.getName())).findFirst();
180                 if (domainHeaderParameter.isEmpty()) {
181                     HeaderParameter parameter = new HeaderParameter();
182                     parameter.setName(RESTHeaders.DOMAIN);
183                     parameter.setRequired(true);
184 
185                     ExternalDocumentation extDoc = new ExternalDocumentation();
186                     extDoc.setDescription("Apache Syncope Reference Guide");
187                     extDoc.setUrl("https://syncope.apache.org/docs/3.0/reference-guide.html#domains");
188 
189                     Schema<String> schema = new Schema<>();
190                     schema.setDescription("Domains are built to facilitate multitenancy.");
191                     schema.setExternalDocs(extDoc);
192                     schema.setEnum(domainHolder.getDomains().keySet().stream().sorted().collect(Collectors.toList()));
193                     schema.setDefault(SyncopeConstants.MASTER_DOMAIN);
194                     parameter.setSchema(schema);
195 
196                     parameters.add(parameter);
197                 }
198             }
199         };
200         openApiCustomizer.setDynamicBasePath(false);
201         openApiCustomizer.setReplaceTags(false);
202         openApiCustomizer.setJavadocProvider(javaDocProvider);
203 
204         String version = env.getProperty("version");
205         OpenApiFeature openapiFeature = new OpenApiFeature();
206         openapiFeature.setUseContextBasedConfig(true);
207         openapiFeature.setTitle("Apache Syncope Self Keymaster");
208         openapiFeature.setVersion(version);
209         openapiFeature.setDescription("Apache Syncope Self Keymaster" + version);
210         openapiFeature.setContactName("The Apache Syncope community");
211         openapiFeature.setContactEmail("dev@syncope.apache.org");
212         openapiFeature.setContactUrl("https://syncope.apache.org");
213         openapiFeature.setScan(false);
214         openapiFeature.setResourcePackages(Set.of("org.apache.syncope.common.keymaster.rest.api.service"));
215         openapiFeature.setSecurityDefinitions(
216                 Map.of("BasicAuthentication", new SecurityScheme().type(SecurityScheme.Type.HTTP).scheme("basic")));
217         openapiFeature.setCustomizer(openApiCustomizer);
218         selfKeymasterContainer.setFeatures(List.of(openapiFeature));
219 
220         return selfKeymasterContainer.create();
221     }
222 
223     @Bean
224     public UsernamePasswordAuthenticationProvider usernamePasswordAuthenticationProvider(
225             final DomainOps domainOps,
226             final AuthDataAccessor dataAccessor,
227             final UserProvisioningManager provisioningManager,
228             final DefaultCredentialChecker credentialChecker,
229             final SecurityProperties securityProperties,
230             final KeymasterProperties keymasterProperties) {
231 
232         return new SelfKeymasterUsernamePasswordAuthenticationProvider(
233                 domainOps,
234                 dataAccessor,
235                 provisioningManager,
236                 credentialChecker,
237                 securityProperties,
238                 keymasterProperties);
239     }
240 
241     @Bean
242     public InternalConfParamHelper internalConfParamHelper(
243             final ConfParamDAO confParamDAO,
244             final SelfKeymasterEntityFactory entityFactory) {
245 
246         return new InternalConfParamHelper(confParamDAO, entityFactory);
247     }
248 
249     @Bean
250     public ConfParamOps internalConfParamOps(final InternalConfParamHelper helper) {
251         return new SelfKeymasterInternalConfParamOps(helper);
252     }
253 
254     @Bean
255     public ServiceOps internalServiceOps(
256             final NetworkServiceLogic networkServiceLogic,
257             final KeymasterProperties props) {
258 
259         return new SelfKeymasterInternalServiceOps(networkServiceLogic, props);
260     }
261 
262     @Bean
263     public DomainOps domainOps(final DomainLogic domainLogic, final KeymasterProperties props) {
264         return new SelfKeymasterInternalDomainOps(domainLogic, props);
265     }
266 
267     @Bean
268     public ConfParamLogic confParamLogic(final InternalConfParamHelper helper) {
269         return new ConfParamLogic(helper);
270     }
271 
272     @Bean
273     public DomainLogic domainLogic(
274             final DomainDAO domainDAO,
275             final SelfKeymasterEntityFactory selfKeymasterEntityFactory,
276             final DomainWatcher domainWatcher) {
277 
278         return new DomainLogic(domainDAO, selfKeymasterEntityFactory, domainWatcher);
279     }
280 
281     @Bean
282     public NetworkServiceLogic networkServiceLogic(
283             final NetworkServiceDAO serviceDAO,
284             final SelfKeymasterEntityFactory selfKeymasterEntityFactory) {
285 
286         return new NetworkServiceLogic(serviceDAO, selfKeymasterEntityFactory);
287     }
288 
289     @Bean
290     public SelfKeymasterEntityFactory selfKeymasterEntityFactory() {
291         return new JPASelfKeymasterEntityFactory();
292     }
293 
294     @Bean
295     public ConfParamDAO confParamDAO() {
296         return new JPAConfParamDAO();
297     }
298 
299     @Bean
300     public DomainDAO domainDAO() {
301         return new JPADomainDAO();
302     }
303 
304     @Bean
305     public NetworkServiceDAO networkServiceDAO() {
306         return new JPANetworkServiceDAO();
307     }
308 
309     @Bean
310     public ConfParamService confParamService(final ConfParamLogic confParamLogic) {
311         return new ConfParamServiceImpl(confParamLogic);
312     }
313 
314     @Bean
315     public DomainService domainService(final DomainLogic domainLogic) {
316         return new DomainServiceImpl(domainLogic);
317     }
318 
319     @Bean
320     public NetworkServiceService networkServiceService(final NetworkServiceLogic networkServiceLogic) {
321         return new NetworkServiceServiceImpl(networkServiceLogic);
322     }
323 }