1 package org
.argeo
.cms
.directory
.ldap
;
3 import java
.io
.IOException
;
4 import java
.net
.InetAddress
;
6 import java
.net
.URISyntaxException
;
7 import java
.util
.ArrayList
;
8 import java
.util
.Dictionary
;
9 import java
.util
.Hashtable
;
10 import java
.util
.List
;
11 import java
.util
.StringJoiner
;
13 import javax
.naming
.InvalidNameException
;
14 import javax
.naming
.ldap
.LdapName
;
16 import org
.argeo
.api
.acr
.ldap
.LdapAttr
;
17 import org
.argeo
.cms
.dns
.DnsBrowser
;
18 import org
.argeo
.cms
.runtime
.DirectoryConf
;
20 /** Free IPA specific conventions. */
21 public class IpaUtils
{
22 public final static String IPA_USER_BASE
= "cn=users";
23 public final static String IPA_GROUP_BASE
= "cn=groups";
24 public final static String IPA_ROLE_BASE
= "cn=roles";
25 public final static String IPA_SERVICE_BASE
= "cn=services";
27 public final static String IPA_ACCOUNTS_BASE
= "cn=accounts";
29 private final static String KRB_PRINCIPAL_NAME
= LdapAttr
.krbPrincipalName
.name().toLowerCase();
31 public final static String IPA_USER_DIRECTORY_CONFIG
= DirectoryConf
.userBase
+ "=" + IPA_USER_BASE
+ "&"
32 + DirectoryConf
.groupBase
+ "=" + IPA_GROUP_BASE
+ "&" + DirectoryConf
.systemRoleBase
+ "=" + IPA_ROLE_BASE
33 + "&" + DirectoryConf
.readOnly
+ "=true";
36 static String
domainToUserDirectoryConfigPath(String realm
) {
37 return domainToBaseDn(realm
) + "?" + IPA_USER_DIRECTORY_CONFIG
+ "&" + DirectoryConf
.realm
.name() + "=" + realm
;
40 public static void addIpaConfig(String realm
, Dictionary
<String
, Object
> properties
) {
41 properties
.put(DirectoryConf
.baseDn
.name(), domainToBaseDn(realm
));
42 properties
.put(DirectoryConf
.realm
.name(), realm
);
43 properties
.put(DirectoryConf
.userBase
.name(), IPA_USER_BASE
);
44 properties
.put(DirectoryConf
.groupBase
.name(), IPA_GROUP_BASE
);
45 properties
.put(DirectoryConf
.systemRoleBase
.name(), IPA_ROLE_BASE
);
46 properties
.put(DirectoryConf
.readOnly
.name(), Boolean
.TRUE
.toString());
49 public static String
domainToBaseDn(String domain
) {
50 String
[] dcs
= domain
.split("\\.");
51 StringJoiner sj
= new StringJoiner(",");
52 for (int i
= 0; i
< dcs
.length
; i
++) {
54 sj
.add(LdapAttr
.dc
.name() + '=' + dc
.toLowerCase());
56 return IPA_ACCOUNTS_BASE
+ ',' + sj
.toString();
59 public static LdapName
kerberosToDn(String kerberosName
) {
60 String
[] kname
= kerberosName
.split("@");
61 String username
= kname
[0];
62 String baseDn
= domainToBaseDn(kname
[1]);
64 if (!username
.contains("/"))
65 dn
= LdapAttr
.uid
+ "=" + username
+ "," + IPA_USER_BASE
+ "," + baseDn
;
67 dn
= KRB_PRINCIPAL_NAME
+ "=" + kerberosName
+ "," + IPA_SERVICE_BASE
+ "," + baseDn
;
69 return new LdapName(dn
);
70 } catch (InvalidNameException e
) {
71 throw new IllegalArgumentException("Badly formatted name for " + kerberosName
+ ": " + dn
);
79 public static String
kerberosDomainFromDns() {
80 String kerberosDomain
;
81 try (DnsBrowser dnsBrowser
= new DnsBrowser()) {
82 // TODO retrieve hostname from CMS config
83 InetAddress localhost
= InetAddress
.getLocalHost();
84 String hostname
= localhost
.getHostName();
85 int dotIndex
= hostname
.indexOf('.');
87 hostname
= localhost
.getCanonicalHostName();
88 dotIndex
= hostname
.indexOf('.');
90 throw new IllegalArgumentException(
91 "Cannot extract DNS zone from hostname " + hostname
+ " (" + localhost
+ ")");
93 String dnsZone
= hostname
.substring(dotIndex
+ 1);
94 kerberosDomain
= dnsBrowser
.getRecord("_kerberos." + dnsZone
, "TXT");
95 return kerberosDomain
;
96 } catch (IOException e
) {
97 throw new IllegalStateException("Cannot determine Kerberos domain from DNS", e
);
102 public static Dictionary
<String
, Object
> convertIpaUri(URI uri
) {
103 String path
= uri
.getPath();
104 String kerberosRealm
;
105 if (path
== null || path
.length() <= 1) {
106 kerberosRealm
= kerberosDomainFromDns();
108 kerberosRealm
= path
.substring(1);
111 if (kerberosRealm
== null)
112 throw new IllegalStateException("No Kerberos domain available for " + uri
);
113 // TODO intergrate CA certificate in truststore
114 // String schemeToUse = SCHEME_LDAPS;
115 String schemeToUse
= DirectoryConf
.SCHEME_LDAP
;
116 List
<String
> ldapHosts
;
117 String ldapHostsStr
= uri
.getHost();
118 if (ldapHostsStr
== null || ldapHostsStr
.trim().equals("")) {
119 try (DnsBrowser dnsBrowser
= new DnsBrowser()) {
120 ldapHosts
= dnsBrowser
.getSrvRecordsAsHosts("_ldap._tcp." + kerberosRealm
.toLowerCase(),
121 schemeToUse
.equals(DirectoryConf
.SCHEME_LDAP
) ?
true : false);
122 if (ldapHosts
== null || ldapHosts
.size() == 0) {
123 throw new IllegalStateException("Cannot configure LDAP for IPA " + uri
);
125 ldapHostsStr
= ldapHosts
.get(0);
127 } catch (IOException e
) {
128 throw new IllegalStateException("Cannot convert IPA uri " + uri
, e
);
131 ldapHosts
= new ArrayList
<>();
132 ldapHosts
.add(ldapHostsStr
);
135 StringBuilder uriStr
= new StringBuilder();
137 for (String host
: ldapHosts
) {
138 URI convertedUri
= new URI(schemeToUse
+ "://" + host
+ "/");
139 uriStr
.append(convertedUri
).append(' ');
141 } catch (URISyntaxException e
) {
142 throw new IllegalStateException("Cannot convert IPA uri " + uri
, e
);
145 Hashtable
<String
, Object
> res
= new Hashtable
<>();
146 res
.put(DirectoryConf
.uri
.name(), uriStr
.toString());
147 addIpaConfig(kerberosRealm
, res
);