View Javadoc
1   /*
2    * This file is part of ***  M y C o R e  ***
3    * See http://www.mycore.de/ for details.
4    *
5    * MyCoRe is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    *
10   * MyCoRe is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with MyCoRe.  If not, see <http://www.gnu.org/licenses/>.
17   */
18  
19  package org.mycore.user2.login;
20  
21  import java.util.Hashtable;
22  import java.util.Locale;
23  
24  import javax.naming.Context;
25  import javax.naming.NameNotFoundException;
26  import javax.naming.NamingEnumeration;
27  import javax.naming.NamingException;
28  import javax.naming.directory.Attribute;
29  import javax.naming.directory.Attributes;
30  import javax.naming.directory.DirContext;
31  import javax.naming.directory.InitialDirContext;
32  import javax.naming.directory.SearchControls;
33  import javax.naming.directory.SearchResult;
34  
35  import org.apache.logging.log4j.LogManager;
36  import org.apache.logging.log4j.Logger;
37  import org.jdom2.output.Format;
38  import org.jdom2.output.XMLOutputter;
39  import org.mycore.common.MCRUsageException;
40  import org.mycore.common.config.MCRConfiguration2;
41  import org.mycore.common.config.MCRConfigurationException;
42  import org.mycore.user2.MCRRole;
43  import org.mycore.user2.MCRRoleManager;
44  import org.mycore.user2.MCRUser;
45  import org.mycore.user2.MCRUserManager;
46  import org.mycore.user2.utils.MCRUserTransformer;
47  
48  /**
49   * Queries an LDAP server for the user's properties.
50   * 
51   *  # Timeout when connecting to LDAP server 
52   *  MCR.user2.LDAP.ReadTimeout=5000
53   *  
54   *  # LDAP server
55   *  MCR.user2.LDAP.ProviderURL=ldap://idp.uni-duisburg-essen.de
56   *  
57   *  # Security principal for logging in at LDAP server
58   *  MCR.user2.LDAP.SecurityPrincipal=cn=duepublico,dc=idp
59   *  
60   *  # Security credentials for logging in at LDAP server
61   *  MCR.user2.LDAP.SecurityCredentials=XXXXXX
62   *  
63   *  # Base DN
64   *  MCR.user2.LDAP.BaseDN=ou=people,dc=idp
65   *  
66   *  # Filter for user ID
67   *  MCR.user2.LDAP.UIDFilter=(uid=%s)
68   *  
69   *  # LDAP attribute mappings
70   *  
71   *  # Mapping from LDAP attribute to real name of user
72   *  MCR.user2.LDAP.Mapping.Name=cn
73   *  
74   *  # Mapping from LDAP attribute to E-Mail address of user
75   *  MCR.user2.LDAP.Mapping.E-Mail=mail
76   *  
77   *  # Mapping of any attribute.value combination to group membership of user 
78   *  MCR.user2.LDAP.Mapping.Group.eduPersonScopedAffiliation.staff@uni-duisburg-essen.de=creators
79   *  
80   *  # Default group membership (optional)
81   *  MCR.user2.LDAP.Mapping.Group.DefaultGroup=submitters
82   *
83   * @author Frank L\u00fctzenkirchen
84   */
85  public class MCRLDAPClient {
86      /** The logger */
87      private static Logger LOGGER = LogManager.getLogger(MCRLDAPClient.class);
88  
89      private static MCRLDAPClient instance = new MCRLDAPClient();
90  
91      /** Base DN */
92      private String baseDN;
93  
94      /** Filter for user ID */
95      private String uidFilter;
96  
97      /** Mapping from LDAP attribute to real name of user */
98      private String mapName;
99  
100     /** Mapping from LDAP attribute to E-Mail address of user */
101     private String mapEMail;
102 
103     /** Default group of user */
104     private MCRRole defaultGroup;
105 
106     private Hashtable<String, String> ldapEnv;
107 
108     public static MCRLDAPClient instance() {
109         return instance;
110     }
111 
112     private MCRLDAPClient() {
113         String prefix = "MCR.user2.LDAP.";
114         /* Timeout when connecting to LDAP server */
115         String readTimeout = MCRConfiguration2.getString(prefix + "ReadTimeout").orElse("10000");
116         /* LDAP server */
117         String providerURL = MCRConfiguration2.getStringOrThrow(prefix + "ProviderURL");
118         /* Security principal for logging in at LDAP server */
119         String securityPrincipal = MCRConfiguration2.getStringOrThrow(prefix + "SecurityPrincipal");
120         /* Security credentials for logging in at LDAP server */
121         String securityCredentials = MCRConfiguration2.getStringOrThrow(prefix + "SecurityCredentials");
122         baseDN = MCRConfiguration2.getStringOrThrow(prefix + "BaseDN");
123         uidFilter = MCRConfiguration2.getStringOrThrow(prefix + "UIDFilter");
124 
125         prefix += "Mapping.";
126         mapName = MCRConfiguration2.getStringOrThrow(prefix + "Name");
127         mapEMail = MCRConfiguration2.getStringOrThrow(prefix + "E-Mail");
128 
129         String group = MCRConfiguration2.getString(prefix + "Group.DefaultGroup").orElse(null);
130         if (group != null) {
131             defaultGroup = MCRRoleManager.getRole(group);
132         }
133 
134         ldapEnv = new Hashtable<>();
135         ldapEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
136         ldapEnv.put("com.sun.jndi.ldap.read.timeout", readTimeout);
137         ldapEnv.put(Context.PROVIDER_URL, providerURL);
138         ldapEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
139         ldapEnv.put(Context.SECURITY_PRINCIPAL, securityPrincipal);
140         ldapEnv.put(Context.SECURITY_CREDENTIALS, securityCredentials);
141     }
142 
143     public boolean updateUserProperties(MCRUser user) throws NamingException {
144         String userName = user.getUserName();
145         boolean userChanged = false;
146 
147         if ((defaultGroup != null) && (!user.isUserInRole((defaultGroup.getName())))) {
148             LOGGER.info("User {} add to group {}", userName, defaultGroup);
149             userChanged = true;
150             user.assignRole(defaultGroup.getName());
151         }
152 
153         // Get user properties from LDAP server
154         DirContext ctx = new InitialDirContext(ldapEnv);
155 
156         try {
157             SearchControls controls = new SearchControls();
158             controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
159             NamingEnumeration<SearchResult> results = ctx.search(baseDN,
160                 String.format(Locale.ROOT, uidFilter, userName), controls);
161 
162             while (results.hasMore()) {
163                 SearchResult searchResult = results.next();
164                 Attributes attributes = searchResult.getAttributes();
165 
166                 for (NamingEnumeration<String> attributeIDs = attributes.getIDs(); attributeIDs.hasMore();) {
167                     String attributeID = attributeIDs.next();
168                     Attribute attribute = attributes.get(attributeID);
169 
170                     for (NamingEnumeration<?> values = attribute.getAll(); values.hasMore();) {
171                         String attributeValue = values.next().toString();
172                         LOGGER.debug("{}={}", attributeID, attributeValue);
173 
174                         if (attributeID.equals(mapName) && (user.getRealName() == null)) {
175                             attributeValue = formatName(attributeValue);
176                             LOGGER.info("User {} name = {}", userName, attributeValue);
177                             user.setRealName(attributeValue);
178                             userChanged = true;
179                         }
180                         if (attributeID.equals(mapEMail) && (user.getEMailAddress() == null)) {
181                             LOGGER.info("User {} e-mail = {}", userName, attributeValue);
182                             user.setEMail(attributeValue);
183                             userChanged = true;
184                         }
185                         String groupMapping = "MCR.user2.LDAP.Mapping.Group." + attributeID + "." + attributeValue;
186                         String group = MCRConfiguration2.getString(groupMapping).orElse(null);
187                         if ((group != null) && (!user.isUserInRole((group)))) {
188                             LOGGER.info("User {} add to group {}", userName, group);
189                             user.assignRole(group);
190                             userChanged = true;
191                         }
192                     }
193                 }
194             }
195         } catch (NameNotFoundException ex) {
196             String msg = "LDAP base name not found: " + ex.getMessage() + " " + ex.getExplanation();
197             throw new MCRConfigurationException(msg, ex);
198         } catch (NamingException ex) {
199             String msg = "Exception accessing LDAP server";
200             throw new MCRUsageException(msg, ex);
201         } finally {
202             if (ctx != null) {
203                 try {
204                     ctx.close();
205                 } catch (Exception ignored) {
206                 }
207             }
208         }
209 
210         return userChanged;
211     }
212 
213     /**
214      * Formats a user name into "lastname, firstname" syntax.
215      */
216     private String formatName(String name) {
217         name = name.replaceAll("\\s+", " ").trim();
218         if (name.contains(",")) {
219             return name;
220         }
221         int pos = name.lastIndexOf(' ');
222         if (pos == -1) {
223             return name;
224         }
225         return name.substring(pos + 1) + ", " + name.substring(0, pos);
226     }
227 
228     public static void main(String[] args) throws Exception {
229         String userName = args[0];
230         String realmID = args[1];
231         MCRUser user = MCRUserManager.getUser(userName, realmID);
232         if (user == null) {
233             user = new MCRUser(userName, realmID);
234         }
235 
236         LOGGER.info("\n{}",
237             new XMLOutputter(Format.getPrettyFormat()).outputString(MCRUserTransformer.buildExportableSafeXML(user)));
238         MCRLDAPClient.instance().updateUserProperties(user);
239         LOGGER.info("\n{}",
240             new XMLOutputter(Format.getPrettyFormat()).outputString(MCRUserTransformer.buildExportableSafeXML(user)));
241     }
242 }