1 package org
.argeo
.cms
.internal
.osgi
;
3 import java
.io
.IOException
;
4 import java
.io
.InputStream
;
6 import java
.nio
.file
.Files
;
7 import java
.nio
.file
.Path
;
8 import java
.util
.ArrayList
;
9 import java
.util
.Arrays
;
10 import java
.util
.Dictionary
;
11 import java
.util
.HashSet
;
12 import java
.util
.List
;
14 import java
.util
.SortedMap
;
15 import java
.util
.TreeMap
;
17 import javax
.naming
.InvalidNameException
;
18 import javax
.naming
.directory
.Attribute
;
19 import javax
.naming
.directory
.Attributes
;
20 import javax
.naming
.directory
.BasicAttributes
;
21 import javax
.naming
.ldap
.LdapName
;
22 import javax
.naming
.ldap
.Rdn
;
24 import org
.argeo
.api
.cms
.CmsConstants
;
25 import org
.argeo
.api
.cms
.CmsLog
;
26 import org
.argeo
.util
.directory
.DirectoryConf
;
27 import org
.argeo
.util
.directory
.ldap
.AttributesDictionary
;
28 import org
.argeo
.util
.directory
.ldap
.LdifParser
;
29 import org
.argeo
.util
.directory
.ldap
.LdifWriter
;
30 import org
.osgi
.framework
.InvalidSyntaxException
;
31 import org
.osgi
.service
.cm
.Configuration
;
32 import org
.osgi
.service
.cm
.ConfigurationAdmin
;
33 import org
.osgi
.service
.cm
.ConfigurationEvent
;
35 /** Manages the LDIF-based deployment configuration. */
37 public class DeployConfig
{
39 private final CmsLog log
= CmsLog
.getLog(getClass());
40 // private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
42 private static Path deployConfigPath
;// = KernelUtils.getOsgiInstancePath(KernelConstants.DEPLOY_CONFIG_PATH);
43 private SortedMap
<LdapName
, Attributes
> deployConfigs
= new TreeMap
<>();
44 // private final DataModels dataModels;
46 private boolean isFirstInit
= false;
48 private final static String ROLES
= "roles";
50 private ConfigurationAdmin configurationAdmin
;
52 private void firstInit() throws IOException
{
53 log
.info("## FIRST INIT ##");
54 Files
.createDirectories(deployConfigPath
.getParent());
56 // FirstInit firstInit = new FirstInit();
57 //InitUtils.prepareFirstInitInstanceArea();
59 if (!Files
.exists(deployConfigPath
))
60 deployConfigs
= new TreeMap
<>();
61 else// config file could have juste been copied by preparation
62 try (InputStream in
= Files
.newInputStream(deployConfigPath
)) {
63 deployConfigs
= new LdifParser().read(in
);
68 private void setFromFrameworkProperties(boolean isFirstInit
) {
71 // List<Dictionary<String, Object>> userDirectoryConfigs = InitUtils.getUserDirectoryConfigs();
72 // if (userDirectoryConfigs.size() != 0) {
73 // List<String> activeCns = new ArrayList<>();
74 // for (int i = 0; i < userDirectoryConfigs.size(); i++) {
75 // Dictionary<String, Object> userDirectoryConfig = userDirectoryConfigs.get(i);
76 // String baseDn = (String) userDirectoryConfig.get(DirectoryConf.baseDn.name());
78 // if (CmsConstants.ROLES_BASEDN.equals(baseDn))
81 // cn = DirectoryConf.baseDnHash(userDirectoryConfig);
83 // userDirectoryConfig.put(CmsConstants.CN, cn);
84 // putFactoryDeployConfig(CmsConstants.NODE_USER_ADMIN_PID, userDirectoryConfig);
87 // LdapName userAdminFactoryName = serviceFactoryDn(CmsConstants.NODE_USER_ADMIN_PID);
88 // for (LdapName name : deployConfigs.keySet()) {
89 // if (name.startsWith(userAdminFactoryName) && !name.equals(userAdminFactoryName)) {
91 // Attributes attrs = deployConfigs.get(name);
92 // String cn = name.getRdn(name.size() - 1).getValue().toString();
93 // if (!activeCns.contains(cn)) {
94 // attrs.put(DirectoryConf.disabled.name(), "true");
96 //// } catch (Exception e) {
97 //// throw new CmsException("Cannot disable user directory " + name, e);
104 // Dictionary<String, Object> webServerConfig = InitUtils
105 // .getHttpServerConfig(getProps(KernelConstants.JETTY_FACTORY_PID, CmsConstants.DEFAULT));
106 // if (!webServerConfig.isEmpty()) {
107 // // TODO check for other customizers
108 // putFactoryDeployConfig(KernelConstants.JETTY_FACTORY_PID, webServerConfig);
117 public void start() {
119 if (!isInitialized()) { // first init
124 boolean isClean
= true;
125 if (configurationAdmin
!= null)
127 Configuration
[] confs
= configurationAdmin
128 .listConfigurations("(service.factoryPid=" + CmsConstants
.NODE_USER_ADMIN_PID
+ ")");
129 isClean
= confs
== null || confs
.length
== 0;
130 } catch (Exception e
) {
131 throw new IllegalStateException("Cannot analyse clean state", e
);
134 try (InputStream in
= Files
.newInputStream(deployConfigPath
)) {
135 deployConfigs
= new LdifParser().read(in
);
138 if (log
.isDebugEnabled())
139 log
.debug("Clean state, loading from framework properties...");
140 setFromFrameworkProperties(isFirstInit
);
141 if (configurationAdmin
!= null)
144 // TODO check consistency if not clean
145 } catch (IOException e
) {
146 throw new RuntimeException("Cannot load deploy configuration", e
);
154 protected void logAllConfigurations() {
155 if (!log
.isDebugEnabled())
158 Configuration
[] configurations
= configurationAdmin
.listConfigurations(null);
159 if (configurations
== null) {
160 log
.debug("No configuration available");
163 Arrays
.sort(configurations
, (o1
, o2
) -> o1
.getPid().compareTo(o2
.getPid()));
164 for (Configuration configuration
: configurations
) {
165 log
.debug(configuration
.getFactoryPid() + " - " + configuration
.getPid() + " - "
166 + configuration
.getProperties());
168 } catch (IOException
| InvalidSyntaxException e
) {
169 throw new IllegalStateException("Cannot log configurations", e
);
173 public void loadConfigs() throws IOException
{
174 // FIXME make it more robust
175 Configuration systemRolesConf
= null;
176 LdapName systemRolesDn
;
178 // FIXME make it more robust
179 systemRolesDn
= new LdapName("cn=roles,ou=org.argeo.api.userAdmin,ou=deploy,ou=node");
180 } catch (InvalidNameException e
) {
181 throw new IllegalArgumentException(e
);
183 deployConfigs
: for (LdapName dn
: deployConfigs
.keySet()) {
184 Attributes deployConfig
= deployConfigs
.get(dn
);
185 Rdn lastRdn
= dn
.getRdn(dn
.size() - 1);
186 LdapName prefix
= (LdapName
) dn
.getPrefix(dn
.size() - 1);
187 if (prefix
.toString().equals(CmsConstants
.DEPLOY_BASEDN
)) {
188 if (lastRdn
.getType().equals(CmsConstants
.CN
)) {
190 String pid
= lastRdn
.getValue().toString();
191 Configuration conf
= configurationAdmin
.getConfiguration(pid
);
192 AttributesDictionary dico
= new AttributesDictionary(deployConfig
);
195 // service factory definition
198 Attribute disabled
= deployConfig
.get(DirectoryConf
.disabled
.name());
199 if (disabled
!= null)
200 continue deployConfigs
;
201 // service factory service
202 if (!lastRdn
.getType().equals(CmsConstants
.CN
))
203 throw new IllegalStateException("Only " + CmsConstants
.CN
+ "= is supported: " + dn
);
204 Rdn beforeLastRdn
= dn
.getRdn(dn
.size() - 2);
205 assert beforeLastRdn
.getType().equals(CmsConstants
.OU
);
206 String factoryPid
= beforeLastRdn
.getValue().toString();
208 String cn
= lastRdn
.getValue().toString();
209 Configuration conf
= getSingleServiceConfiguration(factoryPid
, cn
);
211 if (systemRolesDn
.equals(dn
))
212 systemRolesConf
= conf
;
213 // TODO deal with modifications
214 // boolean modified = false;
215 // Dictionary<String, Object> currentProperties = conf.getProperties();
217 // attrs: for (NamingEnumeration<? extends Attribute> it = deployConfig.getAll(); it
218 // .hasMoreElements();) {
219 // Attribute attr = (Attribute) it.next();
220 // String key = attr.getID();
221 // Object currentValue = currentProperties.get(key);
222 // if (currentValue == null) {
228 // AttributesDictionary dico = new AttributesDictionary(deployConfig);
229 // conf.update(dico);
232 conf
= configurationAdmin
.createFactoryConfiguration(factoryPid
.toString(), null);
233 if (systemRolesDn
.equals(dn
)) {
234 systemRolesConf
= configurationAdmin
.createFactoryConfiguration(factoryPid
.toString(), null);
236 AttributesDictionary dico
= new AttributesDictionary(deployConfig
);
243 // system roles must be last since it triggers node user admin publication
244 if (systemRolesConf
== null)
245 throw new IllegalStateException("System roles are not configured.");
246 systemRolesConf
.update(new AttributesDictionary(deployConfigs
.get(systemRolesDn
)));
248 // logAllConfigurations();
251 public Set
<Dictionary
<String
, Object
>> getUserDirectoryConfigs() {
252 // not static because class is not supported by Android
253 final LdapName USER_ADMIN_BASE_DN
;
255 USER_ADMIN_BASE_DN
= new LdapName(
256 CmsConstants
.OU
+ "=" + CmsConstants
.NODE_USER_ADMIN_PID
+ "," + CmsConstants
.DEPLOY_BASEDN
);
257 } catch (InvalidNameException e
) {
258 throw new IllegalArgumentException(e
);
260 Set
<Dictionary
<String
, Object
>> res
= new HashSet
<>();
261 for (LdapName dn
: deployConfigs
.keySet()) {
262 if (dn
.endsWith(USER_ADMIN_BASE_DN
)) {
263 Attributes attributes
= deployConfigs
.get(dn
);
264 res
.add(new AttributesDictionary(attributes
));
271 public void configurationEvent(ConfigurationEvent event
) {
273 if (ConfigurationEvent
.CM_UPDATED
== event
.getType()) {
274 Configuration conf
= configurationAdmin
.getConfiguration(event
.getPid(), null);
275 LdapName serviceDn
= null;
276 String factoryPid
= conf
.getFactoryPid();
277 if (factoryPid
!= null) {
278 LdapName serviceFactoryDn
= serviceFactoryDn(factoryPid
);
279 if (deployConfigs
.containsKey(serviceFactoryDn
)) {
280 for (LdapName dn
: deployConfigs
.keySet()) {
281 if (dn
.startsWith(serviceFactoryDn
)) {
282 Rdn lastRdn
= dn
.getRdn(dn
.size() - 1);
283 assert lastRdn
.getType().equals(CmsConstants
.CN
);
284 Object value
= conf
.getProperties().get(lastRdn
.getType());
285 assert value
!= null;
286 if (value
.equals(lastRdn
.getValue())) {
293 Object cn
= conf
.getProperties().get(CmsConstants
.CN
);
295 throw new IllegalArgumentException("Properties must contain cn");
296 if (serviceDn
== null) {
297 putFactoryDeployConfig(factoryPid
, conf
.getProperties());
299 Attributes attrs
= deployConfigs
.get(serviceDn
);
300 assert attrs
!= null;
301 AttributesDictionary
.copy(conf
.getProperties(), attrs
);
304 if (log
.isDebugEnabled())
305 log
.debug("Updated deploy config " + serviceDn(factoryPid
, cn
.toString()));
307 // ignore non config-registered service factories
310 serviceDn
= serviceDn(event
.getPid());
311 if (deployConfigs
.containsKey(serviceDn
)) {
312 Attributes attrs
= deployConfigs
.get(serviceDn
);
313 assert attrs
!= null;
314 AttributesDictionary
.copy(conf
.getProperties(), attrs
);
316 if (log
.isDebugEnabled())
317 log
.debug("Updated deploy config " + serviceDn
);
319 // ignore non config-registered services
323 } catch (Exception e
) {
324 log
.error("Could not handle configuration event", e
);
328 public void putFactoryDeployConfig(String factoryPid
, Dictionary
<String
, Object
> props
) {
329 Object cn
= props
.get(CmsConstants
.CN
);
331 throw new IllegalArgumentException("cn must be set in properties");
332 LdapName serviceFactoryDn
= serviceFactoryDn(factoryPid
);
333 if (!deployConfigs
.containsKey(serviceFactoryDn
))
334 deployConfigs
.put(serviceFactoryDn
, new BasicAttributes(CmsConstants
.OU
, factoryPid
));
335 LdapName serviceDn
= serviceDn(factoryPid
, cn
.toString());
336 Attributes attrs
= new BasicAttributes();
337 AttributesDictionary
.copy(props
, attrs
);
338 deployConfigs
.put(serviceDn
, attrs
);
341 void putDeployConfig(String servicePid
, Dictionary
<String
, Object
> props
) {
342 LdapName serviceDn
= serviceDn(servicePid
);
343 Attributes attrs
= new BasicAttributes(CmsConstants
.CN
, servicePid
);
344 AttributesDictionary
.copy(props
, attrs
);
345 deployConfigs
.put(serviceDn
, attrs
);
349 try (Writer writer
= Files
.newBufferedWriter(deployConfigPath
)) {
350 new LdifWriter(writer
).write(deployConfigs
);
351 } catch (IOException e
) {
352 // throw new CmsException("Cannot save deploy configs", e);
353 log
.error("Cannot save deploy configs", e
);
357 public void setConfigurationAdmin(ConfigurationAdmin configurationAdmin
) {
358 this.configurationAdmin
= configurationAdmin
;
361 public boolean hasDomain() {
362 // FIXME lookup deploy configs directly
363 if (configurationAdmin
== null)
366 Configuration
[] configs
= listConfigurationsByFactory(CmsConstants
.NODE_USER_ADMIN_PID
);
368 boolean hasDomain
= false;
369 for (Configuration config
: configs
) {
370 Object realm
= config
.getProperties().get(DirectoryConf
.realm
.name());
372 log
.debug("Found realm: " + realm
);
379 private Configuration
[] listConfigurationsByFactory(String factoryPid
) {
381 Configuration
[] configs
= configurationAdmin
.listConfigurations("(service.factoryPid=" + factoryPid
+ ")");
383 configs
= new Configuration
[0];
385 } catch (IOException
| InvalidSyntaxException e
) {
386 throw new IllegalStateException("Cannot list configurations with factoryPid " + factoryPid
, e
);
391 private Configuration
getSingleServiceConfiguration(String factoryPid
, String cn
) {
392 Configuration
[] configs
= listConfigurationsByFactory(factoryPid
);
393 List
<Configuration
> res
= new ArrayList
<>();
394 for (Configuration config
: configs
) {
395 Object currentCn
= config
.getProperties().get(CmsConstants
.CN
);
396 if (currentCn
!= null && cn
.equals(currentCn
.toString()))
402 throw new IllegalStateException(
403 "More than one " + factoryPid
+ " configuration returned for " + CmsConstants
.CN
+ "=" + cn
);
410 private LdapName
serviceFactoryDn(String factoryPid
) {
412 return new LdapName(CmsConstants
.OU
+ "=" + factoryPid
+ "," + CmsConstants
.DEPLOY_BASEDN
);
413 } catch (InvalidNameException e
) {
414 throw new IllegalArgumentException("Cannot generate DN from " + factoryPid
, e
);
418 private LdapName
serviceDn(String servicePid
) {
420 return new LdapName(CmsConstants
.CN
+ "=" + servicePid
+ "," + CmsConstants
.DEPLOY_BASEDN
);
421 } catch (InvalidNameException e
) {
422 throw new IllegalArgumentException("Cannot generate DN from " + servicePid
, e
);
426 private LdapName
serviceDn(String factoryPid
, String cn
) {
428 return (LdapName
) serviceFactoryDn(factoryPid
).add(new Rdn(CmsConstants
.CN
, cn
));
429 } catch (InvalidNameException e
) {
430 throw new IllegalArgumentException("Cannot generate DN from " + factoryPid
+ " and " + cn
, e
);
434 public Dictionary
<String
, Object
> getProps(String factoryPid
, String cn
) {
435 Attributes attrs
= deployConfigs
.get(serviceDn(factoryPid
, cn
));
437 return new AttributesDictionary(attrs
);
442 private static boolean isInitialized() {
443 return Files
.exists(deployConfigPath
);
446 public boolean isFirstInit() {