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.common.keymaster.client.zookeeper;
20  
21  import java.util.List;
22  import java.util.Map;
23  import java.util.concurrent.TimeUnit;
24  import java.util.regex.Pattern;
25  import javax.security.auth.login.AppConfigurationEntry;
26  import org.apache.commons.lang3.StringUtils;
27  import org.apache.curator.framework.CuratorFramework;
28  import org.apache.curator.framework.CuratorFrameworkFactory;
29  import org.apache.curator.framework.api.ACLProvider;
30  import org.apache.curator.retry.ExponentialBackoffRetry;
31  import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
32  import org.apache.syncope.common.keymaster.client.api.DomainOps;
33  import org.apache.syncope.common.keymaster.client.api.KeymasterProperties;
34  import org.apache.syncope.common.keymaster.client.api.ServiceOps;
35  import org.apache.zookeeper.ZooDefs;
36  import org.apache.zookeeper.data.ACL;
37  import org.apache.zookeeper.server.auth.DigestLoginModule;
38  import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
39  import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
40  import org.springframework.boot.context.properties.EnableConfigurationProperties;
41  import org.springframework.context.annotation.Bean;
42  import org.springframework.context.annotation.ConditionContext;
43  import org.springframework.context.annotation.Conditional;
44  import org.springframework.context.annotation.Configuration;
45  import org.springframework.core.type.AnnotatedTypeMetadata;
46  
47  @EnableConfigurationProperties(KeymasterProperties.class)
48  @Configuration(proxyBeanMethods = false)
49  public class ZookeeperKeymasterClientContext {
50  
51      private static final Pattern IPV4 = Pattern.compile(
52              "^((\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})|[a-z\\.]+):[0-9]+$");
53  
54      static class ZookeeperCondition extends SpringBootCondition {
55  
56          @Override
57          public ConditionOutcome getMatchOutcome(final ConditionContext context, final AnnotatedTypeMetadata metadata) {
58              String keymasterAddress = context.getEnvironment().getProperty("keymaster.address");
59              return new ConditionOutcome(
60                      keymasterAddress != null && IPV4.matcher(keymasterAddress).matches(),
61                      "Keymaster address not set for Zookeeper: " + keymasterAddress);
62          }
63      }
64  
65      @Conditional(ZookeeperCondition.class)
66      @Bean
67      public CuratorFramework curatorFramework(final KeymasterProperties props) throws InterruptedException {
68          if (StringUtils.isNotBlank(props.getUsername()) && StringUtils.isNotBlank(props.getPassword())) {
69              javax.security.auth.login.Configuration.setConfiguration(new javax.security.auth.login.Configuration() {
70  
71                  private final AppConfigurationEntry[] entries = {
72                      new AppConfigurationEntry(
73                      DigestLoginModule.class.getName(),
74                      AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
75                      Map.of(
76                      "username", props.getUsername(),
77                      "password", props.getPassword()
78                      ))
79                  };
80  
81                  @Override
82                  public AppConfigurationEntry[] getAppConfigurationEntry(final String name) {
83                      return entries;
84                  }
85              });
86          }
87  
88          CuratorFrameworkFactory.Builder clientBuilder = CuratorFrameworkFactory.builder().
89                  connectString(props.getAddress()).
90                  retryPolicy(new ExponentialBackoffRetry(props.getBaseSleepTimeMs(), props.getMaxRetries()));
91          if (StringUtils.isNotBlank(props.getUsername())) {
92              clientBuilder.authorization("digest", props.getUsername().getBytes()).aclProvider(new ACLProvider() {
93  
94                  @Override
95                  public List<ACL> getDefaultAcl() {
96                      return ZooDefs.Ids.CREATOR_ALL_ACL;
97                  }
98  
99                  @Override
100                 public List<ACL> getAclForPath(final String path) {
101                     return ZooDefs.Ids.CREATOR_ALL_ACL;
102                 }
103             });
104         }
105         CuratorFramework client = clientBuilder.build();
106         client.start();
107         client.blockUntilConnected(3, TimeUnit.SECONDS);
108 
109         return client;
110     }
111 
112     @Conditional(ZookeeperCondition.class)
113     @Bean
114     public ConfParamOps selfConfParamOps(final CuratorFramework client) {
115         return new ZookeeperConfParamOps(client);
116     }
117 
118     @Conditional(ZookeeperCondition.class)
119     @Bean
120     public ServiceOps serviceOps() {
121         return new ZookeeperServiceDiscoveryOps();
122         //return new ZookeeperServiceOps();
123     }
124 
125     @Conditional(ZookeeperCondition.class)
126     @Bean
127     public DomainOps domainOps() {
128         return new ZookeeperDomainOps();
129     }
130 }