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
.cms
.internal
.runtime
.InitUtils
;
27 import org
.argeo
.cms
.internal
.runtime
.KernelConstants
;
28 import org
.argeo
.cms
.internal
.runtime
.KernelUtils
;
29 import org
.argeo
.osgi
.useradmin
.UserAdminConf
;
30 import org
.argeo
.util
.naming
.AttributesDictionary
;
31 import org
.argeo
.util
.naming
.LdifParser
;
32 import org
.argeo
.util
.naming
.LdifWriter
;
33 import org
.osgi
.framework
.InvalidSyntaxException
;
34 import org
.osgi
.service
.cm
.Configuration
;
35 import org
.osgi
.service
.cm
.ConfigurationAdmin
;
36 import org
.osgi
.service
.cm
.ConfigurationEvent
;
38 /** Manages the LDIF-based deployment configuration. */
39 public class DeployConfig
{
40 private final static LdapName USER_ADMIN_BASE_DN
;
43 USER_ADMIN_BASE_DN
= new LdapName(
44 CmsConstants
.OU
+ "=" + CmsConstants
.NODE_USER_ADMIN_PID
+ "," + CmsConstants
.DEPLOY_BASEDN
);
45 } catch (InvalidNameException e
) {
46 throw new IllegalArgumentException(e
);
50 private final CmsLog log
= CmsLog
.getLog(getClass());
51 // private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
53 private static Path deployConfigPath
= KernelUtils
.getOsgiInstancePath(KernelConstants
.DEPLOY_CONFIG_PATH
);
54 private SortedMap
<LdapName
, Attributes
> deployConfigs
= new TreeMap
<>();
55 // private final DataModels dataModels;
57 private boolean isFirstInit
= false;
59 private final static String ROLES
= "roles";
61 private ConfigurationAdmin configurationAdmin
;
63 private void firstInit() throws IOException
{
64 log
.info("## FIRST INIT ##");
65 Files
.createDirectories(deployConfigPath
.getParent());
67 // FirstInit firstInit = new FirstInit();
68 InitUtils
.prepareFirstInitInstanceArea();
70 if (!Files
.exists(deployConfigPath
))
71 deployConfigs
= new TreeMap
<>();
72 else// config file could have juste been copied by preparation
73 try (InputStream in
= Files
.newInputStream(deployConfigPath
)) {
74 deployConfigs
= new LdifParser().read(in
);
79 private void setFromFrameworkProperties(boolean isFirstInit
) {
82 List
<Dictionary
<String
, Object
>> userDirectoryConfigs
= InitUtils
.getUserDirectoryConfigs();
83 if (userDirectoryConfigs
.size() != 0) {
84 List
<String
> activeCns
= new ArrayList
<>();
85 for (int i
= 0; i
< userDirectoryConfigs
.size(); i
++) {
86 Dictionary
<String
, Object
> userDirectoryConfig
= userDirectoryConfigs
.get(i
);
87 String baseDn
= (String
) userDirectoryConfig
.get(UserAdminConf
.baseDn
.name());
89 if (CmsConstants
.ROLES_BASEDN
.equals(baseDn
))
92 cn
= UserAdminConf
.baseDnHash(userDirectoryConfig
);
94 userDirectoryConfig
.put(CmsConstants
.CN
, cn
);
95 putFactoryDeployConfig(CmsConstants
.NODE_USER_ADMIN_PID
, userDirectoryConfig
);
98 LdapName userAdminFactoryName
= serviceFactoryDn(CmsConstants
.NODE_USER_ADMIN_PID
);
99 for (LdapName name
: deployConfigs
.keySet()) {
100 if (name
.startsWith(userAdminFactoryName
) && !name
.equals(userAdminFactoryName
)) {
102 Attributes attrs
= deployConfigs
.get(name
);
103 String cn
= name
.getRdn(name
.size() - 1).getValue().toString();
104 if (!activeCns
.contains(cn
)) {
105 attrs
.put(UserAdminConf
.disabled
.name(), "true");
107 // } catch (Exception e) {
108 // throw new CmsException("Cannot disable user directory " + name, e);
115 Dictionary
<String
, Object
> webServerConfig
= InitUtils
116 .getHttpServerConfig(getProps(KernelConstants
.JETTY_FACTORY_PID
, CmsConstants
.DEFAULT
));
117 if (!webServerConfig
.isEmpty()) {
118 // TODO check for other customizers
119 // webServerConfig.put("customizer.class", "org.argeo.equinox.jetty.CmsJettyCustomizer");
120 putFactoryDeployConfig(KernelConstants
.JETTY_FACTORY_PID
, webServerConfig
);
122 // LdapName defaultHttpServiceDn = serviceDn(KernelConstants.JETTY_FACTORY_PID, CmsConstants.DEFAULT);
123 // if (deployConfigs.containsKey(defaultHttpServiceDn)) {
124 // // remove old default configs since we have now to start Jetty servlet bridge
126 // deployConfigs.remove(defaultHttpServiceDn);
133 // Dictionary<String, Object> webServerConfig = InitUtils
134 // .getHttpServerConfig(getProps(KernelConstants.JETTY_FACTORY_PID, CmsConstants.DEFAULT));
137 public void start() {
139 if (!isInitialized()) { // first init
144 boolean isClean
= true;
145 if (configurationAdmin
!= null)
147 Configuration
[] confs
= configurationAdmin
148 .listConfigurations("(service.factoryPid=" + CmsConstants
.NODE_USER_ADMIN_PID
+ ")");
149 isClean
= confs
== null || confs
.length
== 0;
150 } catch (Exception e
) {
151 throw new IllegalStateException("Cannot analyse clean state", e
);
154 try (InputStream in
= Files
.newInputStream(deployConfigPath
)) {
155 deployConfigs
= new LdifParser().read(in
);
158 if (log
.isDebugEnabled())
159 log
.debug("Clean state, loading from framework properties...");
160 setFromFrameworkProperties(isFirstInit
);
161 if (configurationAdmin
!= null)
164 // TODO check consistency if not clean
165 } catch (IOException e
) {
166 throw new RuntimeException("Cannot load deploy configuration", e
);
174 protected void logAllConfigurations() {
175 if (!log
.isDebugEnabled())
178 Configuration
[] configurations
= configurationAdmin
.listConfigurations(null);
179 if (configurations
== null) {
180 log
.debug("No configuration available");
183 Arrays
.sort(configurations
, (o1
, o2
) -> o1
.getPid().compareTo(o2
.getPid()));
184 for (Configuration configuration
: configurations
) {
185 log
.debug(configuration
.getFactoryPid() + " - " + configuration
.getPid() + " - "
186 + configuration
.getProperties());
188 } catch (IOException
| InvalidSyntaxException e
) {
189 throw new IllegalStateException("Cannot log configurations", e
);
193 public void loadConfigs() throws IOException
{
194 // FIXME make it more robust
195 Configuration systemRolesConf
= null;
196 LdapName systemRolesDn
;
198 // FIXME make it more robust
199 systemRolesDn
= new LdapName("cn=roles,ou=org.argeo.api.userAdmin,ou=deploy,ou=node");
200 } catch (InvalidNameException e
) {
201 throw new IllegalArgumentException(e
);
203 deployConfigs
: for (LdapName dn
: deployConfigs
.keySet()) {
204 Attributes deployConfig
= deployConfigs
.get(dn
);
205 Rdn lastRdn
= dn
.getRdn(dn
.size() - 1);
206 LdapName prefix
= (LdapName
) dn
.getPrefix(dn
.size() - 1);
207 if (prefix
.toString().equals(CmsConstants
.DEPLOY_BASEDN
)) {
208 if (lastRdn
.getType().equals(CmsConstants
.CN
)) {
210 String pid
= lastRdn
.getValue().toString();
211 Configuration conf
= configurationAdmin
.getConfiguration(pid
);
212 AttributesDictionary dico
= new AttributesDictionary(deployConfig
);
215 // service factory definition
218 Attribute disabled
= deployConfig
.get(UserAdminConf
.disabled
.name());
219 if (disabled
!= null)
220 continue deployConfigs
;
221 // service factory service
222 if (!lastRdn
.getType().equals(CmsConstants
.CN
))
223 throw new IllegalStateException("Only " + CmsConstants
.CN
+ "= is supported: " + dn
);
224 Rdn beforeLastRdn
= dn
.getRdn(dn
.size() - 2);
225 assert beforeLastRdn
.getType().equals(CmsConstants
.OU
);
226 String factoryPid
= beforeLastRdn
.getValue().toString();
228 String cn
= lastRdn
.getValue().toString();
229 Configuration conf
= getSingleServiceConfiguration(factoryPid
, cn
);
231 if (systemRolesDn
.equals(dn
))
232 systemRolesConf
= conf
;
233 // TODO deal with modifications
234 // boolean modified = false;
235 // Dictionary<String, Object> currentProperties = conf.getProperties();
237 // attrs: for (NamingEnumeration<? extends Attribute> it = deployConfig.getAll(); it
238 // .hasMoreElements();) {
239 // Attribute attr = (Attribute) it.next();
240 // String key = attr.getID();
241 // Object currentValue = currentProperties.get(key);
242 // if (currentValue == null) {
248 // AttributesDictionary dico = new AttributesDictionary(deployConfig);
249 // conf.update(dico);
252 conf
= configurationAdmin
.createFactoryConfiguration(factoryPid
.toString(), null);
253 if (systemRolesDn
.equals(dn
)) {
254 systemRolesConf
= configurationAdmin
.createFactoryConfiguration(factoryPid
.toString(), null);
256 AttributesDictionary dico
= new AttributesDictionary(deployConfig
);
263 // system roles must be last since it triggers node user admin publication
264 if (systemRolesConf
== null)
265 throw new IllegalStateException("System roles are not configured.");
266 systemRolesConf
.update(new AttributesDictionary(deployConfigs
.get(systemRolesDn
)));
268 // logAllConfigurations();
271 public Set
<Dictionary
<String
, Object
>> getUserDirectoryConfigs() {
272 Set
<Dictionary
<String
, Object
>> res
= new HashSet
<>();
273 for (LdapName dn
: deployConfigs
.keySet()) {
274 if (dn
.endsWith(USER_ADMIN_BASE_DN
)) {
275 Attributes attributes
= deployConfigs
.get(dn
);
276 res
.add(new AttributesDictionary(attributes
));
283 public void configurationEvent(ConfigurationEvent event
) {
285 if (ConfigurationEvent
.CM_UPDATED
== event
.getType()) {
286 Configuration conf
= configurationAdmin
.getConfiguration(event
.getPid(), null);
287 LdapName serviceDn
= null;
288 String factoryPid
= conf
.getFactoryPid();
289 if (factoryPid
!= null) {
290 LdapName serviceFactoryDn
= serviceFactoryDn(factoryPid
);
291 if (deployConfigs
.containsKey(serviceFactoryDn
)) {
292 for (LdapName dn
: deployConfigs
.keySet()) {
293 if (dn
.startsWith(serviceFactoryDn
)) {
294 Rdn lastRdn
= dn
.getRdn(dn
.size() - 1);
295 assert lastRdn
.getType().equals(CmsConstants
.CN
);
296 Object value
= conf
.getProperties().get(lastRdn
.getType());
297 assert value
!= null;
298 if (value
.equals(lastRdn
.getValue())) {
305 Object cn
= conf
.getProperties().get(CmsConstants
.CN
);
307 throw new IllegalArgumentException("Properties must contain cn");
308 if (serviceDn
== null) {
309 putFactoryDeployConfig(factoryPid
, conf
.getProperties());
311 Attributes attrs
= deployConfigs
.get(serviceDn
);
312 assert attrs
!= null;
313 AttributesDictionary
.copy(conf
.getProperties(), attrs
);
316 if (log
.isDebugEnabled())
317 log
.debug("Updated deploy config " + serviceDn(factoryPid
, cn
.toString()));
319 // ignore non config-registered service factories
322 serviceDn
= serviceDn(event
.getPid());
323 if (deployConfigs
.containsKey(serviceDn
)) {
324 Attributes attrs
= deployConfigs
.get(serviceDn
);
325 assert attrs
!= null;
326 AttributesDictionary
.copy(conf
.getProperties(), attrs
);
328 if (log
.isDebugEnabled())
329 log
.debug("Updated deploy config " + serviceDn
);
331 // ignore non config-registered services
335 } catch (Exception e
) {
336 log
.error("Could not handle configuration event", e
);
340 public void putFactoryDeployConfig(String factoryPid
, Dictionary
<String
, Object
> props
) {
341 Object cn
= props
.get(CmsConstants
.CN
);
343 throw new IllegalArgumentException("cn must be set in properties");
344 LdapName serviceFactoryDn
= serviceFactoryDn(factoryPid
);
345 if (!deployConfigs
.containsKey(serviceFactoryDn
))
346 deployConfigs
.put(serviceFactoryDn
, new BasicAttributes(CmsConstants
.OU
, factoryPid
));
347 LdapName serviceDn
= serviceDn(factoryPid
, cn
.toString());
348 Attributes attrs
= new BasicAttributes();
349 AttributesDictionary
.copy(props
, attrs
);
350 deployConfigs
.put(serviceDn
, attrs
);
353 void putDeployConfig(String servicePid
, Dictionary
<String
, Object
> props
) {
354 LdapName serviceDn
= serviceDn(servicePid
);
355 Attributes attrs
= new BasicAttributes(CmsConstants
.CN
, servicePid
);
356 AttributesDictionary
.copy(props
, attrs
);
357 deployConfigs
.put(serviceDn
, attrs
);
361 try (Writer writer
= Files
.newBufferedWriter(deployConfigPath
)) {
362 new LdifWriter(writer
).write(deployConfigs
);
363 } catch (IOException e
) {
364 // throw new CmsException("Cannot save deploy configs", e);
365 log
.error("Cannot save deploy configs", e
);
369 public void setConfigurationAdmin(ConfigurationAdmin configurationAdmin
) {
370 this.configurationAdmin
= configurationAdmin
;
373 public boolean hasDomain() {
374 // FIXME lookup deploy configs directly
375 if (configurationAdmin
== null)
378 Configuration
[] configs
= listConfigurationsByFactory(CmsConstants
.NODE_USER_ADMIN_PID
);
380 boolean hasDomain
= false;
381 for (Configuration config
: configs
) {
382 Object realm
= config
.getProperties().get(UserAdminConf
.realm
.name());
384 log
.debug("Found realm: " + realm
);
391 private Configuration
[] listConfigurationsByFactory(String factoryPid
) {
393 Configuration
[] configs
= configurationAdmin
.listConfigurations("(service.factoryPid=" + factoryPid
+ ")");
395 configs
= new Configuration
[0];
397 } catch (IOException
| InvalidSyntaxException e
) {
398 throw new IllegalStateException("Cannot list configurations with factoryPid " + factoryPid
, e
);
403 private Configuration
getSingleServiceConfiguration(String factoryPid
, String cn
) {
404 Configuration
[] configs
= listConfigurationsByFactory(factoryPid
);
405 List
<Configuration
> res
= new ArrayList
<>();
406 for (Configuration config
: configs
) {
407 Object currentCn
= config
.getProperties().get(CmsConstants
.CN
);
408 if (currentCn
!= null && cn
.equals(currentCn
.toString()))
414 throw new IllegalStateException(
415 "More than one " + factoryPid
+ " configuration returned for " + CmsConstants
.CN
+ "=" + cn
);
422 private LdapName
serviceFactoryDn(String factoryPid
) {
424 return new LdapName(CmsConstants
.OU
+ "=" + factoryPid
+ "," + CmsConstants
.DEPLOY_BASEDN
);
425 } catch (InvalidNameException e
) {
426 throw new IllegalArgumentException("Cannot generate DN from " + factoryPid
, e
);
430 private LdapName
serviceDn(String servicePid
) {
432 return new LdapName(CmsConstants
.CN
+ "=" + servicePid
+ "," + CmsConstants
.DEPLOY_BASEDN
);
433 } catch (InvalidNameException e
) {
434 throw new IllegalArgumentException("Cannot generate DN from " + servicePid
, e
);
438 private LdapName
serviceDn(String factoryPid
, String cn
) {
440 return (LdapName
) serviceFactoryDn(factoryPid
).add(new Rdn(CmsConstants
.CN
, cn
));
441 } catch (InvalidNameException e
) {
442 throw new IllegalArgumentException("Cannot generate DN from " + factoryPid
+ " and " + cn
, e
);
446 public Dictionary
<String
, Object
> getProps(String factoryPid
, String cn
) {
447 Attributes attrs
= deployConfigs
.get(serviceDn(factoryPid
, cn
));
449 return new AttributesDictionary(attrs
);
454 private static boolean isInitialized() {
455 return Files
.exists(deployConfigPath
);
458 public boolean isFirstInit() {