|  |  |  | # Authors: Rob Crittenden <rcritten@redhat.com> | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Copyright (C) 2007  Red Hat | 
					
						
							|  |  |  | # see file 'COPYING' for use and warranty information | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # This program is free software; you can redistribute it and/or | 
					
						
							|  |  |  | # modify it under the terms of the GNU General Public License as | 
					
						
							|  |  |  | # published by the Free Software Foundation; version 2 only | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  | # GNU General Public License for more details. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # You should have received a copy of the GNU General Public License | 
					
						
							|  |  |  | # along with this program; if not, write to the Free Software | 
					
						
							|  |  |  | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import sys | 
					
						
							|  |  |  | sys.path.append("/usr/share/ipa") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ldap | 
					
						
							|  |  |  | import ipaserver.dsinstance | 
					
						
							|  |  |  | import ipaserver.ipaldap | 
					
						
							| 
									
										
										
										
											2007-09-04 16:13:15 -04:00
										 |  |  | import ipa.ipautil | 
					
						
							|  |  |  | import xmlrpclib | 
					
						
							|  |  |  | import ipa.config | 
					
						
							| 
									
										
										
										
											2007-08-24 15:42:56 -04:00
										 |  |  | import copy | 
					
						
							| 
									
										
										
										
											2007-08-22 10:30:51 -07:00
										 |  |  | from ipa import ipaerror | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import string | 
					
						
							|  |  |  | from types import * | 
					
						
							| 
									
										
										
										
											2007-08-13 16:41:38 -04:00
										 |  |  | import os | 
					
						
							| 
									
										
										
										
											2007-08-21 14:26:36 -07:00
										 |  |  | import re | 
					
						
							| 
									
										
										
										
											2007-08-13 16:41:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | # Need a global to store this between requests | 
					
						
							|  |  |  | _LDAPPool = None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-29 18:07:05 -04:00
										 |  |  | DefaultUserContainer = "cn=users,cn=accounts" | 
					
						
							|  |  |  | DefaultGroupContainer = "cn=groups,cn=accounts" | 
					
						
							| 
									
										
										
										
											2007-08-23 09:44:00 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-13 16:41:38 -04:00
										 |  |  | # | 
					
						
							|  |  |  | # Apache runs in multi-process mode so each process will have its own | 
					
						
							|  |  |  | # connection. This could theoretically drive the total number of connections | 
					
						
							|  |  |  | # very high but since this represents just the administrative interface | 
					
						
							|  |  |  | # this is not anticipated. | 
					
						
							|  |  |  | class IPAConnPool: | 
					
						
							|  |  |  |     def __init__(self): | 
					
						
							|  |  |  |         self.freelist = [] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |     def getConn(self, host, port, bindca, bindcert, bindkey, proxydn=None, keytab=None): | 
					
						
							|  |  |  |         conn = None | 
					
						
							| 
									
										
										
										
											2007-08-13 16:41:38 -04:00
										 |  |  |         if len(self.freelist) > 0: | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |             for i in range(len(self.freelist)): | 
					
						
							|  |  |  |                 c = self.freelist[i] | 
					
						
							|  |  |  |                 if ((c.host == host) and (c.port == port)): | 
					
						
							|  |  |  |                     conn = self.freelist.pop(i) | 
					
						
							|  |  |  |                     break | 
					
						
							|  |  |  |         if conn is None: | 
					
						
							| 
									
										
										
										
											2007-08-13 16:41:38 -04:00
										 |  |  |             conn = ipaserver.ipaldap.IPAdmin(host,port,bindca,bindcert,bindkey) | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |         if proxydn is not None: | 
					
						
							|  |  |  |             conn.set_proxydn(proxydn) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             conn.set_keytab(keytab) | 
					
						
							| 
									
										
										
										
											2007-08-13 16:41:38 -04:00
										 |  |  |         return conn | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def releaseConn(self, conn): | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |         # We can't re-use SASL connections. If proxydn is None it means | 
					
						
							|  |  |  |         # we have a keytab set. See ipaldap.set_keytab | 
					
						
							|  |  |  |         if conn.proxydn is None: | 
					
						
							|  |  |  |             conn.unbind_s() | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.freelist.append(conn) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-06 10:05:53 -04:00
										 |  |  | class IPAServer: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self): | 
					
						
							| 
									
										
										
										
											2007-08-13 16:41:38 -04:00
										 |  |  |         global _LDAPPool | 
					
						
							| 
									
										
										
										
											2007-08-06 10:05:53 -04:00
										 |  |  |         # FIXME, this needs to be auto-discovered | 
					
						
							|  |  |  |         self.host = 'localhost' | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |         self.port = 389 | 
					
						
							|  |  |  |         self.sslport = 636 | 
					
						
							| 
									
										
										
										
											2007-08-06 10:05:53 -04:00
										 |  |  |         self.bindcert = "/usr/share/ipa/cert.pem" | 
					
						
							|  |  |  |         self.bindkey = "/usr/share/ipa/key.pem" | 
					
						
							|  |  |  |         self.bindca = "/usr/share/ipa/cacert.asc" | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2007-08-13 16:41:38 -04:00
										 |  |  |         if _LDAPPool is None: | 
					
						
							|  |  |  |             _LDAPPool = IPAConnPool() | 
					
						
							| 
									
										
										
										
											2007-08-06 10:05:53 -04:00
										 |  |  |         ipa.config.init_config() | 
					
						
							| 
									
										
										
										
											2007-09-04 16:13:15 -04:00
										 |  |  |         self.basedn = ipa.ipautil.realm_to_suffix(ipa.config.config.get_realm()) | 
					
						
							| 
									
										
										
										
											2007-08-06 10:05:53 -04:00
										 |  |  |         self.scope = ldap.SCOPE_SUBTREE | 
					
						
							|  |  |  |         self.princ = None | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |         self.keytab = None | 
					
						
							| 
									
										
										
										
											2007-08-06 10:05:53 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def set_principal(self, princ): | 
					
						
							|  |  |  |         self.princ = princ | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def set_keytab(self, keytab): | 
					
						
							|  |  |  |         self.keytab = keytab | 
					
						
							| 
									
										
										
										
											2007-08-06 10:05:53 -04:00
										 |  |  |      | 
					
						
							|  |  |  |     def get_dn_from_principal(self, princ): | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |         """Given a kerberos principal get the LDAP uid""" | 
					
						
							| 
									
										
										
										
											2007-08-13 16:41:38 -04:00
										 |  |  |         global _LDAPPool | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-06 10:05:53 -04:00
										 |  |  |         filter = "(krbPrincipalName=" + princ + ")" | 
					
						
							| 
									
										
										
										
											2007-08-22 10:30:51 -07:00
										 |  |  |         # The only anonymous search we should have | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |         conn = _LDAPPool.getConn(self.host,self.sslport,self.bindca,self.bindcert,self.bindkey,None,None) | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |             ent = conn.getEntry(self.basedn, self.scope, filter, ['dn']) | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  |         finally: | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |             _LDAPPool.releaseConn(conn) | 
					
						
							| 
									
										
										
										
											2007-08-06 10:05:53 -04:00
										 |  |  |      | 
					
						
							|  |  |  |         return "dn:" + ent.dn | 
					
						
							| 
									
										
										
										
											2007-08-24 15:42:56 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |     def __setup_connection(self, opts): | 
					
						
							|  |  |  |         """Set up common things done in the connection.
 | 
					
						
							|  |  |  |            If there is a keytab then return None as the proxy dn and the keytab | 
					
						
							|  |  |  |            otherwise return the proxy dn and None as the keytab. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |            We only want one or the other used at one time and we prefer | 
					
						
							|  |  |  |            the keytab. So if there is a keytab, return that and None for | 
					
						
							|  |  |  |            proxy dn to make calling getConn() easier. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if opts: | 
					
						
							|  |  |  |             if opts.get('keytab'): | 
					
						
							|  |  |  |                 self.set_keytab(opts['keytab']) | 
					
						
							|  |  |  |                 self.set_principal(None) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 self.set_keytab(None) | 
					
						
							|  |  |  |                 self.set_principal(opts['remoteuser']) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.set_keytab(None) | 
					
						
							|  |  |  |             # The caller should have already set the principal | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if self.princ is not None: | 
					
						
							|  |  |  |             return self.get_dn_from_principal(self.princ), None | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return None, self.keytab | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getConnection(self, opts): | 
					
						
							|  |  |  |         """Wrapper around IPAConnPool.getConn() so we don't have to pass
 | 
					
						
							|  |  |  |            around self.* every time a connection is needed. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |            For SASL connections (where we have a keytab) we can't set | 
					
						
							|  |  |  |            the SSL variables for certificates. It confuses the ldap | 
					
						
							|  |  |  |            module. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         global _LDAPPool | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         (proxy_dn, keytab) = self.__setup_connection(opts) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if keytab is not None: | 
					
						
							|  |  |  |             bindca = None | 
					
						
							|  |  |  |             bindcert = None | 
					
						
							|  |  |  |             bindkey = None | 
					
						
							|  |  |  |             port = self.port | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             bindca = self.bindca | 
					
						
							|  |  |  |             bindcert = self.bindcert  | 
					
						
							|  |  |  |             bindkey = self.bindkey | 
					
						
							|  |  |  |             port = self.sslport | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return _LDAPPool.getConn(self.host,port,bindca,bindcert,bindkey,proxy_dn,keytab) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def releaseConnection(self, conn): | 
					
						
							|  |  |  |         global _LDAPPool | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         _LDAPPool.releaseConn(conn) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-06 10:05:53 -04:00
										 |  |  |     def convert_entry(self, ent): | 
					
						
							|  |  |  |         entry = dict(ent.data) | 
					
						
							|  |  |  |         entry['dn'] = ent.dn | 
					
						
							|  |  |  |         # For now convert single entry lists to a string for the ui. | 
					
						
							|  |  |  |         # TODO: we need to deal with multi-values better | 
					
						
							|  |  |  |         for key,value in entry.iteritems(): | 
					
						
							|  |  |  |             if isinstance(value,list) or isinstance(value,tuple): | 
					
						
							|  |  |  |                 if len(value) == 0: | 
					
						
							|  |  |  |                     entry[key] = '' | 
					
						
							|  |  |  |                 elif len(value) == 1: | 
					
						
							|  |  |  |                     entry[key] = value[0] | 
					
						
							|  |  |  |         return entry | 
					
						
							| 
									
										
										
										
											2007-08-23 09:44:00 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-24 15:42:56 -04:00
										 |  |  |     def __get_entry (self, base, filter, sattrs=None, opts=None): | 
					
						
							|  |  |  |         """Get a specific entry. Return as a dict of values.
 | 
					
						
							| 
									
										
										
										
											2007-08-06 10:05:53 -04:00
										 |  |  |            Multi-valued fields are represented as lists. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         ent="" | 
					
						
							| 
									
										
										
										
											2007-08-17 10:03:33 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |         conn = self.getConnection(opts) | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |             ent = conn.getEntry(base, self.scope, filter, sattrs) | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  |         finally: | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |             self.releaseConnection(conn) | 
					
						
							| 
									
										
										
										
											2007-08-06 10:05:53 -04:00
										 |  |  |      | 
					
						
							|  |  |  |         return self.convert_entry(ent) | 
					
						
							| 
									
										
										
										
											2007-08-24 15:42:56 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __update_entry (self, oldentry, newentry, opts=None): | 
					
						
							|  |  |  |         """Update an LDAP entry
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |            oldentry is a dict | 
					
						
							|  |  |  |            newentry is a dict | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         oldentry = self.convert_scalar_values(oldentry) | 
					
						
							|  |  |  |         newentry = self.convert_scalar_values(newentry) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Should be able to get this from either the old or new entry | 
					
						
							|  |  |  |         # but just in case someone has decided to try changing it, use the | 
					
						
							|  |  |  |         # original | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             moddn = oldentry['dn'] | 
					
						
							|  |  |  |         except KeyError, e: | 
					
						
							|  |  |  |             raise ipaerror.gen_exception(ipaerror.LDAP_MISSING_DN) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |         conn = self.getConnection(opts) | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |             res = conn.updateEntry(moddn, oldentry, newentry) | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  |         finally: | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |             self.releaseConnection(conn) | 
					
						
							| 
									
										
										
										
											2007-08-24 15:42:56 -04:00
										 |  |  |         return res | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __safe_filter(self, criteria): | 
					
						
							|  |  |  |         """Make sure any arguments used when creating a filter are safe.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # TODO: this escaper assumes the python-ldap library will error out | 
					
						
							|  |  |  |         #       on invalid codepoints.  we need to check malformed utf-8 input | 
					
						
							|  |  |  |         #       where the second byte in a multi-byte character | 
					
						
							|  |  |  |         #       is (illegally) ')' and make sure python-ldap | 
					
						
							|  |  |  |         #       bombs out. | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  |         criteria = re.sub(r'[\(\)\\\*]', ldap_search_escape, criteria) | 
					
						
							| 
									
										
										
										
											2007-08-24 15:42:56 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return criteria | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __generate_match_filters(self, search_fields, criteria_words): | 
					
						
							|  |  |  |         """Generates a search filter based on a list of words and a list
 | 
					
						
							|  |  |  |            of fields to search against. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |            Returns a tuple of two filters: (exact_match, partial_match)"""
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # construct search pattern for a single word | 
					
						
							|  |  |  |         # (|(f1=word)(f2=word)...) | 
					
						
							|  |  |  |         search_pattern = "(|" | 
					
						
							|  |  |  |         for field in search_fields: | 
					
						
							|  |  |  |             search_pattern += "(" + field + "=%(match)s)" | 
					
						
							|  |  |  |         search_pattern += ")" | 
					
						
							|  |  |  |         gen_search_pattern = lambda word: search_pattern % {'match':word} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # construct the giant match for all words | 
					
						
							|  |  |  |         exact_match_filter = "(&" | 
					
						
							|  |  |  |         partial_match_filter = "(&" | 
					
						
							|  |  |  |         for word in criteria_words: | 
					
						
							|  |  |  |             exact_match_filter += gen_search_pattern(word) | 
					
						
							|  |  |  |             partial_match_filter += gen_search_pattern("*%s*" % word) | 
					
						
							|  |  |  |         exact_match_filter += ")" | 
					
						
							|  |  |  |         partial_match_filter += ")" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return (exact_match_filter, partial_match_filter) | 
					
						
							| 
									
										
										
										
											2007-08-23 09:44:00 -04:00
										 |  |  |   | 
					
						
							| 
									
										
										
										
											2007-08-24 15:42:56 -04:00
										 |  |  | # User support | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __is_user_unique(self, uid, opts): | 
					
						
							|  |  |  |         """Return 1 if the uid is unique in the tree, 0 otherwise.""" | 
					
						
							|  |  |  |         uid = self.__safe_filter(uid) | 
					
						
							|  |  |  |         filter = "(&(uid=%s)(objectclass=posixAccount))" % uid | 
					
						
							|  |  |  |   | 
					
						
							| 
									
										
										
										
											2007-08-27 13:45:28 -04:00
										 |  |  |         try: | 
					
						
							|  |  |  |             entry = self.__get_entry(self.basedn, filter, ['dn','uid'], opts) | 
					
						
							| 
									
										
										
										
											2007-08-24 15:42:56 -04:00
										 |  |  |             return 0 | 
					
						
							| 
									
										
										
										
											2007-08-27 13:45:28 -04:00
										 |  |  |         except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND): | 
					
						
							| 
									
										
										
										
											2007-08-24 15:42:56 -04:00
										 |  |  |             return 1 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-23 09:44:00 -04:00
										 |  |  |     def get_user_by_uid (self, uid, sattrs=None, opts=None): | 
					
						
							|  |  |  |         """Get a specific user's entry. Return as a dict of values.
 | 
					
						
							|  |  |  |            Multi-valued fields are represented as lists. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-24 15:42:56 -04:00
										 |  |  |         uid = self.__safe_filter(uid) | 
					
						
							| 
									
										
										
										
											2007-08-23 09:44:00 -04:00
										 |  |  |         filter = "(uid=" + uid + ")" | 
					
						
							| 
									
										
										
										
											2007-08-24 15:42:56 -04:00
										 |  |  |         return self.__get_entry(self.basedn, filter, sattrs, opts) | 
					
						
							| 
									
										
										
										
											2007-08-06 10:05:53 -04:00
										 |  |  |      | 
					
						
							| 
									
										
										
										
											2007-08-23 09:44:00 -04:00
										 |  |  |     def get_user_by_dn (self, dn, sattrs=None, opts=None): | 
					
						
							|  |  |  |         """Get a specific user's entry. Return as a dict of values.
 | 
					
						
							|  |  |  |            Multi-valued fields are represented as lists. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         filter = "(objectClass=*)" | 
					
						
							| 
									
										
										
										
											2007-08-24 15:42:56 -04:00
										 |  |  |         return self.__get_entry(dn, filter, sattrs, opts) | 
					
						
							| 
									
										
										
										
											2007-08-23 09:44:00 -04:00
										 |  |  |      | 
					
						
							|  |  |  |     def add_user (self, user, user_container=None, opts=None): | 
					
						
							| 
									
										
										
										
											2007-08-17 10:03:33 -04:00
										 |  |  |         """Add a user in LDAP. Takes as input a dict where the key is the
 | 
					
						
							|  |  |  |            attribute name and the value is either a string or in the case | 
					
						
							|  |  |  |            of a multi-valued field a list of values. user_container sets | 
					
						
							|  |  |  |            where in the tree the user is placed."""
 | 
					
						
							| 
									
										
										
										
											2007-08-23 09:44:00 -04:00
										 |  |  |         if user_container is None: | 
					
						
							| 
									
										
										
										
											2007-08-24 15:42:56 -04:00
										 |  |  |             user_container = DefaultUserContainer | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if self.__is_user_unique(user['uid'], opts) == 0: | 
					
						
							|  |  |  |             raise ipaerror.gen_exception(ipaerror.LDAP_DUPLICATE) | 
					
						
							| 
									
										
										
										
											2007-08-17 14:27:54 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-10 10:30:15 -04:00
										 |  |  |         dn="uid=%s,%s,%s" % (user['uid'], user_container,self.basedn) | 
					
						
							| 
									
										
										
										
											2007-08-17 15:32:05 -07:00
										 |  |  |         entry = ipaserver.ipaldap.Entry(dn) | 
					
						
							| 
									
										
										
										
											2007-08-06 10:05:53 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-23 11:57:25 -04:00
										 |  |  |         # FIXME: This should be dynamic and can include just about anything | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Let us add in some missing attributes | 
					
						
							|  |  |  |         if user.get('homedirectory') is None: | 
					
						
							|  |  |  |                 user['homedirectory'] = '/home/%s' % user.get('uid') | 
					
						
							|  |  |  |         if not user.get('gecos') is None: | 
					
						
							|  |  |  |                 user['gecos'] = user['uid'] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # FIXME: This can be removed once the DS plugin is installed | 
					
						
							|  |  |  |         user['uidnumber'] = '501' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # FIXME: What is the default group for users? | 
					
						
							|  |  |  |         user['gidnumber'] = '501' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         realm = ipa.config.config.get_realm() | 
					
						
							|  |  |  |         user['krbprincipalname'] = "%s@%s" % (user.get('uid'), realm) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # FIXME. This is a hack so we can request separate First and Last | 
					
						
							|  |  |  |         # name in the GUI. | 
					
						
							|  |  |  |         if user.get('cn') is None: | 
					
						
							|  |  |  |             user['cn'] = "%s %s" % (user.get('givenname'), | 
					
						
							|  |  |  |                                            user.get('sn')) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if user.get('gn'): | 
					
						
							|  |  |  |             del user['gn'] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-06 10:05:53 -04:00
										 |  |  |         # some required objectclasses | 
					
						
							|  |  |  |         entry.setValues('objectClass', 'top', 'posixAccount', 'shadowAccount', 'account', 'person', 'inetOrgPerson', 'organizationalPerson', 'krbPrincipalAux', 'krbTicketPolicyAux') | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |         # Fill in shadow fields | 
					
						
							|  |  |  |         entry.setValue('shadowMin', '0') | 
					
						
							|  |  |  |         entry.setValue('shadowMax', '99999') | 
					
						
							|  |  |  |         entry.setValue('shadowWarning', '7') | 
					
						
							|  |  |  |         entry.setValue('shadowExpire', '-1') | 
					
						
							|  |  |  |         entry.setValue('shadowInactive', '-1') | 
					
						
							|  |  |  |         entry.setValue('shadowFlag', '-1') | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |         # FIXME: calculate shadowLastChange | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |         # fill in our new entry with everything sent by the user | 
					
						
							|  |  |  |         for u in user: | 
					
						
							| 
									
										
										
										
											2007-08-17 15:32:05 -07:00
										 |  |  |             entry.setValues(u, user[u]) | 
					
						
							| 
									
										
										
										
											2007-08-06 10:05:53 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |         conn = self.getConnection(opts) | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |             res = conn.addEntry(entry) | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  |         finally: | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |             self.releaseConnection(conn) | 
					
						
							| 
									
										
										
										
											2007-08-22 10:30:51 -07:00
										 |  |  |         return res | 
					
						
							| 
									
										
										
										
											2007-08-06 10:05:53 -04:00
										 |  |  |      | 
					
						
							|  |  |  |     def get_add_schema (self): | 
					
						
							|  |  |  |         """Get the list of fields to be used when adding users in the GUI.""" | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |         # FIXME: this needs to be pulled from LDAP | 
					
						
							|  |  |  |         fields = [] | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |         field1 = { | 
					
						
							|  |  |  |             "name":       "uid" , | 
					
						
							|  |  |  |             "label":      "Login:", | 
					
						
							|  |  |  |             "type":       "text", | 
					
						
							|  |  |  |             "validator":  "text", | 
					
						
							|  |  |  |             "required":   "true" | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         fields.append(field1) | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |         field1 = { | 
					
						
							|  |  |  |             "name":       "givenName" , | 
					
						
							|  |  |  |             "label":      "First name:", | 
					
						
							|  |  |  |             "type":       "text", | 
					
						
							|  |  |  |             "validator":  "string", | 
					
						
							|  |  |  |             "required":   "true" | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         fields.append(field1) | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |         field1 = { | 
					
						
							|  |  |  |             "name":       "sn" , | 
					
						
							|  |  |  |             "label":      "Last name:", | 
					
						
							|  |  |  |             "type":       "text", | 
					
						
							|  |  |  |             "validator":  "string", | 
					
						
							|  |  |  |             "required":   "true" | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         fields.append(field1) | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |         field1 = { | 
					
						
							|  |  |  |             "name":       "mail" , | 
					
						
							|  |  |  |             "label":      "E-mail address:", | 
					
						
							|  |  |  |             "type":       "text", | 
					
						
							|  |  |  |             "validator":  "email", | 
					
						
							|  |  |  |             "required":   "true" | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         fields.append(field1) | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |         return fields | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2007-08-16 18:59:58 -04:00
										 |  |  |     def get_all_users (self, args=None, opts=None): | 
					
						
							| 
									
										
										
										
											2007-08-06 10:05:53 -04:00
										 |  |  |         """Return a list containing a User object for each
 | 
					
						
							|  |  |  |         existing user. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         filter = "(objectclass=posixAccount)" | 
					
						
							| 
									
										
										
										
											2007-08-22 10:30:51 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |         conn = self.getConnection(opts) | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |             all_users = conn.getList(self.basedn, self.scope, filter, None) | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  |         finally: | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |             self.releaseConnection(conn) | 
					
						
							| 
									
										
										
										
											2007-08-06 10:05:53 -04:00
										 |  |  |      | 
					
						
							|  |  |  |         users = [] | 
					
						
							|  |  |  |         for u in all_users: | 
					
						
							|  |  |  |             users.append(self.convert_entry(u)) | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |         return users | 
					
						
							| 
									
										
										
										
											2007-08-13 16:41:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-24 15:42:56 -04:00
										 |  |  |     def find_users (self, criteria, sattrs=None, opts=None): | 
					
						
							| 
									
										
										
										
											2007-08-28 16:01:07 -07:00
										 |  |  |         """Returns a list: counter followed by the results.
 | 
					
						
							|  |  |  |            If the results are truncated, counter will be set to -1."""
 | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  |         # Assume the list of fields to search will come from a central | 
					
						
							|  |  |  |         # configuration repository.  A good format for that would be | 
					
						
							|  |  |  |         # a comma-separated list of fields | 
					
						
							| 
									
										
										
										
											2007-08-28 09:20:12 -07:00
										 |  |  |         search_fields_conf_str = "uid,givenName,sn,telephoneNumber,ou,carLicense,title" | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  |         search_fields = string.split(search_fields_conf_str, ",") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-24 15:42:56 -04:00
										 |  |  |         criteria = self.__safe_filter(criteria) | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  |         criteria_words = re.split(r'\s+', criteria) | 
					
						
							|  |  |  |         criteria_words = filter(lambda value:value!="", criteria_words) | 
					
						
							|  |  |  |         if len(criteria_words) == 0: | 
					
						
							| 
									
										
										
										
											2007-09-07 11:07:59 -07:00
										 |  |  |             return [0] | 
					
						
							| 
									
										
										
										
											2007-08-21 14:26:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  |         (exact_match_filter, partial_match_filter) = self.__generate_match_filters( | 
					
						
							|  |  |  |                 search_fields, criteria_words) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |         conn = self.getConnection(opts) | 
					
						
							| 
									
										
										
										
											2007-08-13 16:41:38 -04:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  |             try: | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |                 exact_results = conn.getListAsync(self.basedn, self.scope, | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  |                         exact_match_filter, sattrs) | 
					
						
							|  |  |  |             except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND): | 
					
						
							| 
									
										
										
										
											2007-08-28 16:01:07 -07:00
										 |  |  |                 exact_results = [0] | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |             try: | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |                 partial_results = conn.getListAsync(self.basedn, self.scope, | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  |                         partial_match_filter, sattrs) | 
					
						
							|  |  |  |             except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND): | 
					
						
							| 
									
										
										
										
											2007-08-28 16:01:07 -07:00
										 |  |  |                 partial_results = [0] | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  |         finally: | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |             self.releaseConnection(conn) | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-28 16:01:07 -07:00
										 |  |  |         exact_counter = exact_results[0] | 
					
						
							|  |  |  |         partial_counter = partial_results[0] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         exact_results = exact_results[1:] | 
					
						
							|  |  |  |         partial_results = partial_results[1:] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  |         # Remove exact matches from the partial_match list | 
					
						
							|  |  |  |         exact_dns = set(map(lambda e: e.dn, exact_results)) | 
					
						
							|  |  |  |         partial_results = filter(lambda e: e.dn not in exact_dns, | 
					
						
							|  |  |  |                                  partial_results) | 
					
						
							| 
									
										
										
										
											2007-08-22 10:30:51 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-28 16:01:07 -07:00
										 |  |  |         if (exact_counter == -1) or (partial_counter == -1): | 
					
						
							|  |  |  |             counter = -1 | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             counter = len(exact_results) + len(partial_results) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         users = [counter] | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  |         for u in exact_results + partial_results: | 
					
						
							| 
									
										
										
										
											2007-08-13 16:41:38 -04:00
										 |  |  |             users.append(self.convert_entry(u)) | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-13 16:41:38 -04:00
										 |  |  |         return users | 
					
						
							| 
									
										
										
										
											2007-08-14 17:22:05 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-20 10:50:11 -07:00
										 |  |  |     def convert_scalar_values(self, orig_dict): | 
					
						
							|  |  |  |         """LDAP update dicts expect all values to be a list (except for dn).
 | 
					
						
							|  |  |  |            This method converts single entries to a list."""
 | 
					
						
							|  |  |  |         new_dict={} | 
					
						
							|  |  |  |         for (k,v) in orig_dict.iteritems(): | 
					
						
							|  |  |  |             if not isinstance(v, list) and k != 'dn': | 
					
						
							|  |  |  |                 v = [v] | 
					
						
							|  |  |  |             new_dict[k] = v | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return new_dict | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-23 09:44:00 -04:00
										 |  |  |     def update_user (self, olduser, newuser, opts=None): | 
					
						
							| 
									
										
										
										
											2007-08-14 17:22:05 -04:00
										 |  |  |         """Update a user in LDAP""" | 
					
						
							| 
									
										
										
										
											2007-08-24 15:42:56 -04:00
										 |  |  |         return self.__update_entry(olduser, newuser, opts) | 
					
						
							| 
									
										
										
										
											2007-08-17 10:03:33 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-23 09:44:00 -04:00
										 |  |  |     def mark_user_deleted (self, uid, opts=None): | 
					
						
							| 
									
										
										
										
											2007-08-17 10:03:33 -04:00
										 |  |  |         """Mark a user as inactive in LDAP. We aren't actually deleting
 | 
					
						
							|  |  |  |            users here, just making it so they can't log in, etc.""" | 
					
						
							| 
									
										
										
										
											2007-08-23 09:44:00 -04:00
										 |  |  |         user = self.get_user_by_uid(uid, ['dn', 'uid', 'nsAccountlock'], opts) | 
					
						
							| 
									
										
										
										
											2007-08-17 10:03:33 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Are we doing an add or replace operation? | 
					
						
							|  |  |  |         if user.has_key('nsaccountlock'): | 
					
						
							| 
									
										
										
										
											2007-08-23 09:44:00 -04:00
										 |  |  |             if user['nsaccountlock'] == "true": | 
					
						
							|  |  |  |                 return "already marked as deleted" | 
					
						
							| 
									
										
										
										
											2007-08-17 10:03:33 -04:00
										 |  |  |             has_key = True | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             has_key = False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |         conn = self.getConnection(opts) | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |             res = conn.inactivateEntry(user['dn'], has_key) | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  |         finally: | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |             self.releaseConnection(conn) | 
					
						
							| 
									
										
										
										
											2007-08-22 10:30:51 -07:00
										 |  |  |         return res | 
					
						
							| 
									
										
										
										
											2007-08-21 14:26:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-28 13:52:08 -04:00
										 |  |  |     def delete_user (self, uid, opts=None): | 
					
						
							|  |  |  |         """Delete a user. Not to be confused with inactivate_user. This
 | 
					
						
							|  |  |  |            makes the entry go away completely. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |            uid is the uid of the user to delete | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |            The memberOf plugin handles removing the user from any other | 
					
						
							|  |  |  |            groups. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         user_dn = self.get_user_by_uid(uid, ['dn', 'uid', 'objectclass'], opts) | 
					
						
							|  |  |  |         if user_dn is None: | 
					
						
							|  |  |  |             raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |         conn = self.getConnection(opts) | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             res = conn.deleteEntry(user_dn['dn']) | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             self.releaseConnection(conn) | 
					
						
							| 
									
										
										
										
											2007-08-28 13:52:08 -04:00
										 |  |  |         return res | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-24 15:42:56 -04:00
										 |  |  | # Group support | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __is_group_unique(self, cn, opts): | 
					
						
							|  |  |  |         """Return 1 if the cn is unique in the tree, 0 otherwise.""" | 
					
						
							|  |  |  |         cn = self.__safe_filter(cn) | 
					
						
							|  |  |  |         filter = "(&(cn=%s)(objectclass=posixGroup))" % cn | 
					
						
							|  |  |  |   | 
					
						
							| 
									
										
										
										
											2007-08-28 13:52:08 -04:00
										 |  |  |         try: | 
					
						
							|  |  |  |             entry = self.__get_entry(self.basedn, filter, ['dn','cn'], opts) | 
					
						
							| 
									
										
										
										
											2007-08-24 15:42:56 -04:00
										 |  |  |             return 0 | 
					
						
							| 
									
										
										
										
											2007-08-28 13:52:08 -04:00
										 |  |  |         except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND): | 
					
						
							| 
									
										
										
										
											2007-08-24 15:42:56 -04:00
										 |  |  |             return 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_group_by_cn (self, cn, sattrs=None, opts=None): | 
					
						
							|  |  |  |         """Get a specific group's entry. Return as a dict of values.
 | 
					
						
							|  |  |  |            Multi-valued fields are represented as lists. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         cn = self.__safe_filter(cn) | 
					
						
							|  |  |  |         filter = "(cn=" + cn + ")" | 
					
						
							|  |  |  |         return self.__get_entry(self.basedn, filter, sattrs, opts) | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     def get_group_by_dn (self, dn, sattrs=None, opts=None): | 
					
						
							|  |  |  |         """Get a specific group's entry. Return as a dict of values.
 | 
					
						
							|  |  |  |            Multi-valued fields are represented as lists. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         filter = "(objectClass=*)" | 
					
						
							|  |  |  |         return self.__get_entry(dn, filter, sattrs, opts) | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     def add_group (self, group, group_container=None, opts=None): | 
					
						
							|  |  |  |         """Add a group in LDAP. Takes as input a dict where the key is the
 | 
					
						
							|  |  |  |            attribute name and the value is either a string or in the case | 
					
						
							|  |  |  |            of a multi-valued field a list of values. group_container sets | 
					
						
							|  |  |  |            where in the tree the group is placed."""
 | 
					
						
							|  |  |  |         if group_container is None: | 
					
						
							|  |  |  |             group_container = DefaultGroupContainer | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if self.__is_group_unique(group['cn'], opts) == 0: | 
					
						
							|  |  |  |             raise ipaerror.gen_exception(ipaerror.LDAP_DUPLICATE) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         dn="cn=%s,%s,%s" % (group['cn'], group_container,self.basedn) | 
					
						
							|  |  |  |         entry = ipaserver.ipaldap.Entry(dn) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # some required objectclasses | 
					
						
							|  |  |  |         entry.setValues('objectClass', 'top', 'groupofuniquenames', 'posixGroup') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # FIXME, need a gidNumber generator | 
					
						
							|  |  |  |         if group.get('gidnumber') is None: | 
					
						
							|  |  |  |             entry.setValues('gidNumber', '501') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # fill in our new entry with everything sent by the user | 
					
						
							|  |  |  |         for g in group: | 
					
						
							|  |  |  |             entry.setValues(g, group[g]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |         conn = self.getConnection(opts) | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |             res = conn.addEntry(entry) | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  |         finally: | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |             self.releaseConnection(conn) | 
					
						
							| 
									
										
										
										
											2007-08-24 15:42:56 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def find_groups (self, criteria, sattrs=None, opts=None): | 
					
						
							|  |  |  |         """Return a list containing a User object for each
 | 
					
						
							|  |  |  |         existing group that matches the criteria. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         criteria = self.__safe_filter(criteria) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         filter = "(&(cn=%s)(objectClass=posixGroup))" % criteria | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |         conn = self.getConnection(opts) | 
					
						
							| 
									
										
										
										
											2007-08-24 15:42:56 -04:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |             results = conn.getList(self.basedn, self.scope, filter, sattrs) | 
					
						
							| 
									
										
										
										
											2007-08-24 15:42:56 -04:00
										 |  |  |         except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND): | 
					
						
							|  |  |  |             results = [] | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  |         finally: | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |             self.releaseConnection(conn) | 
					
						
							| 
									
										
										
										
											2007-08-24 15:42:56 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         groups = [] | 
					
						
							|  |  |  |         for u in results: | 
					
						
							|  |  |  |             groups.append(self.convert_entry(u)) | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |         return groups | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def add_user_to_group(self, user, group, opts=None): | 
					
						
							|  |  |  |         """Add a user to an existing group.
 | 
					
						
							|  |  |  |            user is a uid of the user to add | 
					
						
							|  |  |  |            group is the cn of the group to be added to | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         old_group = self.get_group_by_cn(group, None, opts) | 
					
						
							|  |  |  |         if old_group is None: | 
					
						
							|  |  |  |             raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND) | 
					
						
							|  |  |  |         new_group = copy.deepcopy(old_group) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         user_dn = self.get_user_by_uid(user, ['dn', 'uid', 'objectclass'], opts) | 
					
						
							|  |  |  |         if user_dn is None: | 
					
						
							|  |  |  |             raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if new_group.get('uniquemember') is not None: | 
					
						
							|  |  |  |             if ((isinstance(new_group.get('uniquemember'), str)) or (isinstance(new_group.get('uniquemember'), unicode))): | 
					
						
							|  |  |  |                 new_group['uniquemember'] = [new_group['uniquemember']] | 
					
						
							|  |  |  |             new_group['uniquemember'].append(user_dn['dn']) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             new_group['uniquemember'] = user_dn['dn'] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             ret = self.__update_entry(old_group, new_group, opts) | 
					
						
							|  |  |  |         except ipaerror.exception_for(ipaerror.LDAP_EMPTY_MODLIST): | 
					
						
							|  |  |  |             raise | 
					
						
							|  |  |  |         return ret | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def add_users_to_group(self, users, group, opts=None): | 
					
						
							|  |  |  |         """Given a list of user uid's add them to the group cn denoted by group
 | 
					
						
							|  |  |  |            Returns a list of the users were not added to the group. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         failed = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (isinstance(users, str)): | 
					
						
							|  |  |  |             users = [users] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for user in users: | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 self.add_user_to_group(user, group, opts) | 
					
						
							|  |  |  |             except ipaerror.exception_for(ipaerror.LDAP_EMPTY_MODLIST): | 
					
						
							|  |  |  |                 # User is already in the group | 
					
						
							|  |  |  |                 failed.append(user) | 
					
						
							|  |  |  |             except ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND): | 
					
						
							|  |  |  |                 # User or the group does not exist | 
					
						
							|  |  |  |                 failed.append(user) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return failed | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def remove_user_from_group(self, user, group, opts=None): | 
					
						
							|  |  |  |         """Remove a user from an existing group.
 | 
					
						
							|  |  |  |            user is a uid of the user to remove | 
					
						
							|  |  |  |            group is the cn of the group to be removed from | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         old_group = self.get_group_by_cn(group, None, opts) | 
					
						
							|  |  |  |         if old_group is None: | 
					
						
							|  |  |  |             raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND) | 
					
						
							|  |  |  |         new_group = copy.deepcopy(old_group) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         user_dn = self.get_user_by_uid(user, ['dn', 'uid', 'objectclass'], opts) | 
					
						
							|  |  |  |         if user_dn is None: | 
					
						
							|  |  |  |             raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if new_group.get('uniquemember') is not None: | 
					
						
							|  |  |  |             if ((isinstance(new_group.get('uniquemember'), str)) or (isinstance(new_group.get('uniquemember'), unicode))): | 
					
						
							|  |  |  |                 new_group['uniquemember'] = [new_group['uniquemember']] | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 new_group['uniquemember'].remove(user_dn['dn']) | 
					
						
							|  |  |  |             except ValueError: | 
					
						
							|  |  |  |                 # User is not in the group | 
					
						
							|  |  |  |                 # FIXME: raise more specific error? | 
					
						
							|  |  |  |                 raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             # Nothing to do if the group has no members | 
					
						
							|  |  |  |             # FIXME raise SOMETHING? | 
					
						
							|  |  |  |             return "Success" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             ret = self.__update_entry(old_group, new_group, opts) | 
					
						
							|  |  |  |         except ipaerror.exception_for(ipaerror.LDAP_EMPTY_MODLIST): | 
					
						
							|  |  |  |             raise | 
					
						
							|  |  |  |         return ret | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def remove_users_from_group(self, users, group, opts=None): | 
					
						
							|  |  |  |         """Given a list of user uid's remove them from the group cn denoted
 | 
					
						
							|  |  |  |            by group | 
					
						
							|  |  |  |            Returns a list of the users were not removed from the group. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         failed = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (isinstance(users, str)): | 
					
						
							|  |  |  |             users = [users] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for user in users: | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 self.remove_user_from_group(user, group, opts) | 
					
						
							|  |  |  |             except ipaerror.exception_for(ipaerror.LDAP_EMPTY_MODLIST): | 
					
						
							|  |  |  |                 # User is not in the group | 
					
						
							|  |  |  |                 failed.append(user) | 
					
						
							|  |  |  |             except ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND): | 
					
						
							|  |  |  |                 # User or the group does not exist | 
					
						
							|  |  |  |                 failed.append(user) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return failed | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def update_group (self, oldgroup, newgroup, opts=None): | 
					
						
							|  |  |  |         """Update a group in LDAP""" | 
					
						
							|  |  |  |         return self.__update_entry(oldgroup, newgroup, opts) | 
					
						
							| 
									
										
										
										
											2007-08-21 14:26:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-28 13:52:08 -04:00
										 |  |  |     def delete_group (self, group_cn, opts=None): | 
					
						
							|  |  |  |         """Delete a group
 | 
					
						
							|  |  |  |            group_cn is the cn of the group to delete | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |            The memberOf plugin handles removing the group from any other | 
					
						
							|  |  |  |            groups. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         group = self.get_group_by_cn(group_cn, ['dn', 'cn'], opts) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if len(group) != 1: | 
					
						
							|  |  |  |             raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-09-05 13:14:23 -04:00
										 |  |  |         conn = self.getConnection(opts) | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             res = conn.deleteEntry(group[0]['dn']) | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             self.releaseConnection(conn) | 
					
						
							| 
									
										
										
										
											2007-08-28 13:52:08 -04:00
										 |  |  |         return res | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def add_group_to_group(self, group, tgroup, opts=None): | 
					
						
							|  |  |  |         """Add a user to an existing group.
 | 
					
						
							|  |  |  |            group is a cn of the group to add | 
					
						
							|  |  |  |            tgroup is the cn of the group to be added to | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         old_group = self.get_group_by_cn(tgroup, None, opts) | 
					
						
							|  |  |  |         if old_group is None: | 
					
						
							|  |  |  |             raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND) | 
					
						
							|  |  |  |         new_group = copy.deepcopy(old_group) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         group_dn = self.get_group_by_cn(group, ['dn', 'cn', 'objectclass'], opts) | 
					
						
							|  |  |  |         if group_dn is None: | 
					
						
							|  |  |  |             raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if new_group.get('uniquemember') is not None: | 
					
						
							|  |  |  |             if ((isinstance(new_group.get('uniquemember'), str)) or (isinstance(new_group.get('uniquemember'), unicode))): | 
					
						
							|  |  |  |                 new_group['uniquemember'] = [new_group['uniquemember']] | 
					
						
							|  |  |  |             new_group['uniquemember'].append(group_dn['dn']) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             new_group['uniquemember'] = group_dn['dn'] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             ret = self.__update_entry(old_group, new_group, opts) | 
					
						
							|  |  |  |         except ipaerror.exception_for(ipaerror.LDAP_EMPTY_MODLIST): | 
					
						
							|  |  |  |             raise | 
					
						
							|  |  |  |         return ret | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-21 14:26:36 -07:00
										 |  |  | def ldap_search_escape(match): | 
					
						
							|  |  |  |     """Escapes out nasty characters from the ldap search.
 | 
					
						
							|  |  |  |     See RFC 2254."""
 | 
					
						
							|  |  |  |     value = match.group() | 
					
						
							|  |  |  |     if (len(value) != 1): | 
					
						
							|  |  |  |         return "" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if value == "(": | 
					
						
							|  |  |  |         return "\\28" | 
					
						
							|  |  |  |     elif value == ")": | 
					
						
							|  |  |  |         return "\\29" | 
					
						
							|  |  |  |     elif value == "\\": | 
					
						
							|  |  |  |         return "\\5c" | 
					
						
							| 
									
										
										
										
											2007-08-27 11:30:26 -07:00
										 |  |  |     elif value == "*": | 
					
						
							|  |  |  |         # drop '*' from input.  search performs its own wildcarding | 
					
						
							|  |  |  |         return "" | 
					
						
							| 
									
										
										
										
											2007-08-21 14:26:36 -07:00
										 |  |  |     else: | 
					
						
							|  |  |  |         return value |