1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
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 }