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
.Dictionary
;
10 import java
.util
.List
;
11 import java
.util
.SortedMap
;
12 import java
.util
.TreeMap
;
14 import javax
.naming
.InvalidNameException
;
15 import javax
.naming
.directory
.Attribute
;
16 import javax
.naming
.directory
.Attributes
;
17 import javax
.naming
.directory
.BasicAttributes
;
18 import javax
.naming
.ldap
.LdapName
;
19 import javax
.naming
.ldap
.Rdn
;
21 import org
.argeo
.api
.cms
.CmsConstants
;
22 import org
.argeo
.api
.cms
.CmsLog
;
23 import org
.argeo
.cms
.internal
.runtime
.InitUtils
;
24 import org
.argeo
.cms
.internal
.runtime
.KernelConstants
;
25 import org
.argeo
.cms
.internal
.runtime
.KernelUtils
;
26 import org
.argeo
.osgi
.useradmin
.UserAdminConf
;
27 import org
.argeo
.util
.naming
.AttributesDictionary
;
28 import org
.argeo
.util
.naming
.LdifParser
;
29 import org
.argeo
.util
.naming
.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
;
34 import org
.osgi
.service
.cm
.ConfigurationListener
;
36 /** Manages the LDIF-based deployment configuration. */
37 public class DeployConfig
implements ConfigurationListener
{
38 private final CmsLog log
= CmsLog
.getLog(getClass());
39 // private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
41 private static Path deployConfigPath
= KernelUtils
.getOsgiInstancePath(KernelConstants
.DEPLOY_CONFIG_PATH
);
42 private SortedMap
<LdapName
, Attributes
> deployConfigs
= new TreeMap
<>();
43 // private final DataModels dataModels;
45 private boolean isFirstInit
= false;
47 private final static String ROLES
= "roles";
49 private ConfigurationAdmin configurationAdmin
;
51 public DeployConfig() {
52 // this.dataModels = dataModels;
53 // ConfigurationAdmin configurationAdmin =
54 // // bc.getService(bc.getServiceReference(ConfigurationAdmin.class));
56 // if (!isInitialized()) { // first init
57 // isFirstInit = true;
60 // this.configurationAdmin = configurationAdmin;
61 //// init(configurationAdmin, isClean, isFirstInit);
62 // } catch (IOException e) {
63 // throw new RuntimeException("Could not init deploy configs", e);
65 // FIXME check race conditions during initialization
66 // bc.registerService(ConfigurationListener.class, this, null);
69 private void firstInit() throws IOException
{
70 log
.info("## FIRST INIT ##");
71 Files
.createDirectories(deployConfigPath
.getParent());
73 // FirstInit firstInit = new FirstInit();
74 InitUtils
.prepareFirstInitInstanceArea();
76 if (!Files
.exists(deployConfigPath
))
77 deployConfigs
= new TreeMap
<>();
78 else// config file could have juste been copied by preparation
79 try (InputStream in
= Files
.newInputStream(deployConfigPath
)) {
80 deployConfigs
= new LdifParser().read(in
);
85 private void setFromFrameworkProperties(boolean isFirstInit
) {
88 List
<Dictionary
<String
, Object
>> userDirectoryConfigs
= InitUtils
.getUserDirectoryConfigs();
89 if (userDirectoryConfigs
.size() != 0) {
90 List
<String
> activeCns
= new ArrayList
<>();
91 for (int i
= 0; i
< userDirectoryConfigs
.size(); i
++) {
92 Dictionary
<String
, Object
> userDirectoryConfig
= userDirectoryConfigs
.get(i
);
93 String baseDn
= (String
) userDirectoryConfig
.get(UserAdminConf
.baseDn
.name());
95 if (CmsConstants
.ROLES_BASEDN
.equals(baseDn
))
98 cn
= UserAdminConf
.baseDnHash(userDirectoryConfig
);
100 userDirectoryConfig
.put(CmsConstants
.CN
, cn
);
101 putFactoryDeployConfig(CmsConstants
.NODE_USER_ADMIN_PID
, userDirectoryConfig
);
104 LdapName userAdminFactoryName
= serviceFactoryDn(CmsConstants
.NODE_USER_ADMIN_PID
);
105 for (LdapName name
: deployConfigs
.keySet()) {
106 if (name
.startsWith(userAdminFactoryName
) && !name
.equals(userAdminFactoryName
)) {
108 Attributes attrs
= deployConfigs
.get(name
);
109 String cn
= name
.getRdn(name
.size() - 1).getValue().toString();
110 if (!activeCns
.contains(cn
)) {
111 attrs
.put(UserAdminConf
.disabled
.name(), "true");
113 // } catch (Exception e) {
114 // throw new CmsException("Cannot disable user directory " + name, e);
121 Dictionary
<String
, Object
> webServerConfig
= InitUtils
122 .getHttpServerConfig(getProps(KernelConstants
.JETTY_FACTORY_PID
, CmsConstants
.DEFAULT
));
123 if (!webServerConfig
.isEmpty()) {
124 // TODO check for other customizers
125 // webServerConfig.put("customizer.class", "org.argeo.equinox.jetty.CmsJettyCustomizer");
126 putFactoryDeployConfig(KernelConstants
.JETTY_FACTORY_PID
, webServerConfig
);
128 // LdapName defaultHttpServiceDn = serviceDn(KernelConstants.JETTY_FACTORY_PID, CmsConstants.DEFAULT);
129 // if (deployConfigs.containsKey(defaultHttpServiceDn)) {
130 // // remove old default configs since we have now to start Jetty servlet bridge
132 // deployConfigs.remove(defaultHttpServiceDn);
139 // Dictionary<String, Object> webServerConfig = InitUtils
140 // .getHttpServerConfig(getProps(KernelConstants.JETTY_FACTORY_PID, CmsConstants.DEFAULT));
143 public void start() throws IOException
{
144 if (!isInitialized()) { // first init
151 Configuration
[] confs
= configurationAdmin
152 .listConfigurations("(service.factoryPid=" + CmsConstants
.NODE_USER_ADMIN_PID
+ ")");
153 isClean
= confs
== null || confs
.length
== 0;
154 } catch (Exception e
) {
155 throw new IllegalStateException("Cannot analyse clean state", e
);
158 try (InputStream in
= Files
.newInputStream(deployConfigPath
)) {
159 deployConfigs
= new LdifParser().read(in
);
162 if (log
.isDebugEnabled())
163 log
.debug("Clean state, loading from framework properties...");
164 setFromFrameworkProperties(isFirstInit
);
167 // TODO check consistency if not clean
174 public void loadConfigs() throws IOException
{
175 // FIXME make it more robust
176 Configuration systemRolesConf
= null;
177 LdapName systemRolesDn
;
179 // FIXME make it more robust
180 systemRolesDn
= new LdapName("cn=roles,ou=org.argeo.api.userAdmin,ou=deploy,ou=node");
181 } catch (InvalidNameException e
) {
182 throw new IllegalArgumentException(e
);
184 deployConfigs
: for (LdapName dn
: deployConfigs
.keySet()) {
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(deployConfigs
.get(dn
));
195 // service factory definition
198 Attributes config
= deployConfigs
.get(dn
);
199 Attribute disabled
= config
.get(UserAdminConf
.disabled
.name());
200 if (disabled
!= null)
201 continue deployConfigs
;
202 // service factory service
203 Rdn beforeLastRdn
= dn
.getRdn(dn
.size() - 2);
204 assert beforeLastRdn
.getType().equals(CmsConstants
.OU
);
205 String factoryPid
= beforeLastRdn
.getValue().toString();
206 Configuration conf
= configurationAdmin
.createFactoryConfiguration(factoryPid
.toString(), null);
207 if (systemRolesDn
.equals(dn
)) {
208 systemRolesConf
= configurationAdmin
.createFactoryConfiguration(factoryPid
.toString(), null);
210 AttributesDictionary dico
= new AttributesDictionary(config
);
216 // system roles must be last since it triggers node user admin publication
217 if (systemRolesConf
== null)
218 throw new IllegalStateException("System roles are not configured.");
219 systemRolesConf
.update(new AttributesDictionary(deployConfigs
.get(systemRolesDn
)));
224 public void configurationEvent(ConfigurationEvent event
) {
226 if (ConfigurationEvent
.CM_UPDATED
== event
.getType()) {
227 Configuration conf
= configurationAdmin
.getConfiguration(event
.getPid(), null);
228 LdapName serviceDn
= null;
229 String factoryPid
= conf
.getFactoryPid();
230 if (factoryPid
!= null) {
231 LdapName serviceFactoryDn
= serviceFactoryDn(factoryPid
);
232 if (deployConfigs
.containsKey(serviceFactoryDn
)) {
233 for (LdapName dn
: deployConfigs
.keySet()) {
234 if (dn
.startsWith(serviceFactoryDn
)) {
235 Rdn lastRdn
= dn
.getRdn(dn
.size() - 1);
236 assert lastRdn
.getType().equals(CmsConstants
.CN
);
237 Object value
= conf
.getProperties().get(lastRdn
.getType());
238 assert value
!= null;
239 if (value
.equals(lastRdn
.getValue())) {
246 Object cn
= conf
.getProperties().get(CmsConstants
.CN
);
248 throw new IllegalArgumentException("Properties must contain cn");
249 if (serviceDn
== null) {
250 putFactoryDeployConfig(factoryPid
, conf
.getProperties());
252 Attributes attrs
= deployConfigs
.get(serviceDn
);
253 assert attrs
!= null;
254 AttributesDictionary
.copy(conf
.getProperties(), attrs
);
257 if (log
.isDebugEnabled())
258 log
.debug("Updated deploy config " + serviceDn(factoryPid
, cn
.toString()));
260 // ignore non config-registered service factories
263 serviceDn
= serviceDn(event
.getPid());
264 if (deployConfigs
.containsKey(serviceDn
)) {
265 Attributes attrs
= deployConfigs
.get(serviceDn
);
266 assert attrs
!= null;
267 AttributesDictionary
.copy(conf
.getProperties(), attrs
);
269 if (log
.isDebugEnabled())
270 log
.debug("Updated deploy config " + serviceDn
);
272 // ignore non config-registered services
276 } catch (Exception e
) {
277 log
.error("Could not handle configuration event", e
);
281 public void putFactoryDeployConfig(String factoryPid
, Dictionary
<String
, Object
> props
) {
282 Object cn
= props
.get(CmsConstants
.CN
);
284 throw new IllegalArgumentException("cn must be set in properties");
285 LdapName serviceFactoryDn
= serviceFactoryDn(factoryPid
);
286 if (!deployConfigs
.containsKey(serviceFactoryDn
))
287 deployConfigs
.put(serviceFactoryDn
, new BasicAttributes(CmsConstants
.OU
, factoryPid
));
288 LdapName serviceDn
= serviceDn(factoryPid
, cn
.toString());
289 Attributes attrs
= new BasicAttributes();
290 AttributesDictionary
.copy(props
, attrs
);
291 deployConfigs
.put(serviceDn
, attrs
);
294 void putDeployConfig(String servicePid
, Dictionary
<String
, Object
> props
) {
295 LdapName serviceDn
= serviceDn(servicePid
);
296 Attributes attrs
= new BasicAttributes(CmsConstants
.CN
, servicePid
);
297 AttributesDictionary
.copy(props
, attrs
);
298 deployConfigs
.put(serviceDn
, attrs
);
302 try (Writer writer
= Files
.newBufferedWriter(deployConfigPath
)) {
303 new LdifWriter(writer
).write(deployConfigs
);
304 } catch (IOException e
) {
305 // throw new CmsException("Cannot save deploy configs", e);
306 log
.error("Cannot save deploy configs", e
);
310 public void setConfigurationAdmin(ConfigurationAdmin configurationAdmin
) {
311 this.configurationAdmin
= configurationAdmin
;
314 public boolean hasDomain() {
315 Configuration
[] configs
;
317 configs
= configurationAdmin
318 .listConfigurations("(service.factoryPid=" + CmsConstants
.NODE_USER_ADMIN_PID
+ ")");
319 } catch (IOException
| InvalidSyntaxException e
) {
320 throw new IllegalStateException("Cannot list user directories", e
);
323 boolean hasDomain
= false;
324 for (Configuration config
: configs
) {
325 Object realm
= config
.getProperties().get(UserAdminConf
.realm
.name());
327 log
.debug("Found realm: " + realm
);
337 private LdapName
serviceFactoryDn(String factoryPid
) {
339 return new LdapName(CmsConstants
.OU
+ "=" + factoryPid
+ "," + CmsConstants
.DEPLOY_BASEDN
);
340 } catch (InvalidNameException e
) {
341 throw new IllegalArgumentException("Cannot generate DN from " + factoryPid
, e
);
345 private LdapName
serviceDn(String servicePid
) {
347 return new LdapName(CmsConstants
.CN
+ "=" + servicePid
+ "," + CmsConstants
.DEPLOY_BASEDN
);
348 } catch (InvalidNameException e
) {
349 throw new IllegalArgumentException("Cannot generate DN from " + servicePid
, e
);
353 private LdapName
serviceDn(String factoryPid
, String cn
) {
355 return (LdapName
) serviceFactoryDn(factoryPid
).add(new Rdn(CmsConstants
.CN
, cn
));
356 } catch (InvalidNameException e
) {
357 throw new IllegalArgumentException("Cannot generate DN from " + factoryPid
+ " and " + cn
, e
);
361 public Dictionary
<String
, Object
> getProps(String factoryPid
, String cn
) {
362 Attributes attrs
= deployConfigs
.get(serviceDn(factoryPid
, cn
));
364 return new AttributesDictionary(attrs
);
369 private static boolean isInitialized() {
370 return Files
.exists(deployConfigPath
);
373 public boolean isFirstInit() {