root / pykota / trunk / pykota / storages / ldapstorage.py @ 1141

Revision 1141, 35.9 kB (checked in by jalet, 21 years ago)

LDAP group access will be slower when cache is disabled, but at least code
is consistent with the rest of the caching mechanis, but at least code
is consistent with the rest of the caching mechanism

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1# PyKota
2#
3# PyKota : Print Quotas for CUPS and LPRng
4#
5# (c) 2003 Jerome Alet <alet@librelogiciel.com>
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 2 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
19#
20# $Id$
21#
22# $Log$
23# Revision 1.30  2003/10/06 14:42:36  jalet
24# LDAP group access will be slower when cache is disabled, but at least code
25# is consistent with the rest of the caching mechanis, but at least code
26# is consistent with the rest of the caching mechanism
27#
28# Revision 1.29  2003/10/06 13:12:28  jalet
29# More work on caching
30#
31# Revision 1.28  2003/10/03 12:27:02  jalet
32# Several optimizations, especially with LDAP backend
33#
34# Revision 1.27  2003/10/03 08:57:55  jalet
35# Caching mechanism now caches all that's cacheable.
36#
37# Revision 1.26  2003/10/02 20:23:18  jalet
38# Storage caching mechanism added.
39#
40# Revision 1.25  2003/08/20 15:56:24  jalet
41# Better user and group deletion
42#
43# Revision 1.24  2003/07/29 20:55:17  jalet
44# 1.14 is out !
45#
46# Revision 1.23  2003/07/29 19:52:32  jalet
47# Forgot to read the email field from LDAP
48#
49# Revision 1.22  2003/07/29 09:54:03  jalet
50# Added configurable LDAP mail attribute support
51#
52# Revision 1.21  2003/07/28 09:11:12  jalet
53# PyKota now tries to add its attributes intelligently in existing LDAP
54# directories.
55#
56# Revision 1.20  2003/07/25 10:41:30  jalet
57# Better documentation.
58# pykotme now displays the current user's account balance.
59# Some test changed in ldap module.
60#
61# Revision 1.19  2003/07/14 14:18:16  jalet
62# Wrong documentation strings
63#
64# Revision 1.18  2003/07/11 14:23:13  jalet
65# When adding an user only adds one object containing both the user and
66# its account balance instead of two objects.
67#
68# Revision 1.17  2003/07/07 12:51:07  jalet
69# Small fix
70#
71# Revision 1.16  2003/07/07 12:11:13  jalet
72# Small fix
73#
74# Revision 1.15  2003/07/07 11:49:24  jalet
75# Lots of small fixes with the help of PyChecker
76#
77# Revision 1.14  2003/07/07 08:33:18  jalet
78# Bug fix due to a typo in LDAP code
79#
80# Revision 1.13  2003/07/05 07:46:50  jalet
81# The previous bug fix was incomplete.
82#
83# Revision 1.12  2003/06/30 13:54:21  jalet
84# Sorts by user / group name
85#
86# Revision 1.11  2003/06/25 14:10:01  jalet
87# Hey, it may work (edpykota --reset excepted) !
88#
89# Revision 1.10  2003/06/16 21:55:15  jalet
90# More work on LDAP, again. Problem detected.
91#
92# Revision 1.9  2003/06/16 11:59:09  jalet
93# More work on LDAP
94#
95# Revision 1.8  2003/06/15 22:26:52  jalet
96# More work on LDAP
97#
98# Revision 1.7  2003/06/14 22:44:21  jalet
99# More work on LDAP storage backend.
100#
101# Revision 1.6  2003/06/13 19:07:57  jalet
102# Two big bugs fixed, time to release something ;-)
103#
104# Revision 1.5  2003/06/10 16:37:54  jalet
105# Deletion of the second user which is not needed anymore.
106# Added a debug configuration field in /etc/pykota.conf
107# All queries can now be sent to the logger in debug mode, this will
108# greatly help improve performance when time for this will come.
109#
110# Revision 1.4  2003/06/10 10:45:32  jalet
111# Not implemented methods now raise an exception when called.
112#
113# Revision 1.3  2003/06/06 20:49:15  jalet
114# Very latest schema. UNTESTED.
115#
116# Revision 1.2  2003/06/06 14:21:08  jalet
117# New LDAP schema.
118# Small bug fixes.
119#
120# Revision 1.1  2003/06/05 11:19:13  jalet
121# More good work on LDAP storage.
122#
123#
124#
125
126#
127# My IANA assigned number, for
128# "Conseil Internet & Logiciels Libres, J�me Alet"
129# is 16868. Use this as a base to create the LDAP schema.
130#
131
132import time
133import md5
134
135from pykota.storage import PyKotaStorageError,BaseStorage,StorageObject,StorageUser,StorageGroup,StoragePrinter,StorageLastJob,StorageUserPQuota,StorageGroupPQuota
136
137try :
138    import ldap
139    from ldap import modlist
140except ImportError :   
141    import sys
142    # TODO : to translate or not to translate ?
143    raise PyKotaStorageError, "This python version (%s) doesn't seem to have the python-ldap module installed correctly." % sys.version.split()[0]
144   
145class Storage(BaseStorage) :
146    def __init__(self, pykotatool, host, dbname, user, passwd) :
147        """Opens the LDAP connection."""
148        # raise PyKotaStorageError, "Sorry, the LDAP backend for PyKota is not yet implemented !"
149        BaseStorage.__init__(self, pykotatool)
150        self.info = pykotatool.config.getLDAPInfo()
151        try :
152            self.database = ldap.initialize(host) 
153            self.database.simple_bind_s(user, passwd)
154            self.basedn = dbname
155        except ldap.SERVER_DOWN :   
156            raise PyKotaStorageError, "LDAP backend for PyKota seems to be down !" # TODO : translate
157        except ldap.LDAPError :   
158            raise PyKotaStorageError, "Unable to connect to LDAP server %s as %s." % (host, user) # TODO : translate
159        else :   
160            self.closed = 0
161            self.tool.logdebug("Database opened (host=%s, dbname=%s, user=%s)" % (host, dbname, user))
162           
163    def close(self) :   
164        """Closes the database connection."""
165        if not self.closed :
166            del self.database
167            self.closed = 1
168            self.tool.logdebug("Database closed.")
169       
170    def genUUID(self) :   
171        """Generates an unique identifier.
172       
173           TODO : this one is not unique accross several print servers, but should be sufficient for testing.
174        """
175        return md5.md5("%s" % time.time()).hexdigest()
176       
177    def beginTransaction(self) :   
178        """Starts a transaction."""
179        self.tool.logdebug("Transaction begins... WARNING : No transactions in LDAP !")
180       
181    def commitTransaction(self) :   
182        """Commits a transaction."""
183        self.tool.logdebug("Transaction committed. WARNING : No transactions in LDAP !")
184       
185    def rollbackTransaction(self) :     
186        """Rollbacks a transaction."""
187        self.tool.logdebug("Transaction aborted. WARNING : No transaction in LDAP !")
188       
189    def doSearch(self, key, fields=None, base="", scope=ldap.SCOPE_SUBTREE) :
190        """Does an LDAP search query."""
191        try :
192            base = base or self.basedn
193            self.tool.logdebug("QUERY : Filter : %s, BaseDN : %s, Scope : %s, Attributes : %s" % (key, base, scope, fields))
194            result = self.database.search_s(base or self.basedn, scope, key, fields)
195        except ldap.LDAPError :   
196            raise PyKotaStorageError, _("Search for %s(%s) from %s(scope=%s) returned no answer.") % (key, fields, base, scope)
197        else :     
198            self.tool.logdebug("QUERY : Result : %s" % result)
199            return result
200           
201    def doAdd(self, dn, fields) :
202        """Adds an entry in the LDAP directory."""
203        try :
204            self.tool.logdebug("QUERY : ADD(%s, %s)" % (dn, str(fields)))
205            self.database.add_s(dn, modlist.addModlist(fields))
206        except ldap.LDAPError :
207            raise PyKotaStorageError, _("Problem adding LDAP entry (%s, %s)") % (dn, str(fields))
208        else :
209            return dn
210           
211    def doDelete(self, dn) :
212        """Deletes an entry from the LDAP directory."""
213        try :
214            self.tool.logdebug("QUERY : Delete(%s)" % dn)
215            self.database.delete_s(dn)
216        except ldap.LDAPError :
217            raise PyKotaStorageError, _("Problem deleting LDAP entry (%s)") % dn
218           
219    def doModify(self, dn, fields, ignoreold=1) :
220        """Modifies an entry in the LDAP directory."""
221        try :
222            oldentry = self.doSearch("objectClass=*", base=dn, scope=ldap.SCOPE_BASE)
223            self.tool.logdebug("QUERY : Modify(%s, %s ==> %s)" % (dn, oldentry[0][1], fields))
224            self.database.modify_s(dn, modlist.modifyModlist(oldentry[0][1], fields, ignore_oldexistent=ignoreold))
225        except ldap.LDAPError :
226            raise PyKotaStorageError, _("Problem modifying LDAP entry (%s, %s)") % (dn, fields)
227        else :
228            return dn
229           
230    def getUserFromBackend(self, username) :   
231        """Extracts user information given its name."""
232        user = StorageUser(self, username)
233        result = self.doSearch("(&(objectClass=pykotaAccount)(|(pykotaUserName=%s)(%s=%s)))" % (username, self.info["userrdn"], username), ["pykotaLimitBy", self.info["usermail"]], base=self.info["userbase"])
234        if result :
235            fields = result[0][1]
236            user.ident = result[0][0]
237            user.Email = fields.get(self.info["usermail"])
238            if user.Email is not None :
239                user.Email = user.Email[0]
240            user.LimitBy = fields.get("pykotaLimitBy")
241            if user.LimitBy is not None :
242                user.LimitBy = user.LimitBy[0]
243            result = self.doSearch("(&(objectClass=pykotaAccountBalance)(|(pykotaUserName=%s)(%s=%s)))" % (username, self.info["balancerdn"], username), ["pykotaBalance", "pykotaLifeTimePaid"], base=self.info["balancebase"])
244            if result :
245                fields = result[0][1]
246                user.idbalance = result[0][0]
247                user.AccountBalance = fields.get("pykotaBalance")
248                if user.AccountBalance is not None :
249                    if user.AccountBalance[0].upper() == "NONE" :
250                        user.AccountBalance = None
251                    else :   
252                        user.AccountBalance = float(user.AccountBalance[0])
253                user.AccountBalance = user.AccountBalance or 0.0       
254                user.LifeTimePaid = fields.get("pykotaLifeTimePaid")
255                if user.LifeTimePaid is not None :
256                    if user.LifeTimePaid[0].upper() == "NONE" :
257                        user.LifeTimePaid = None
258                    else :   
259                        user.LifeTimePaid = float(user.LifeTimePaid[0])
260                user.LifeTimePaid = user.LifeTimePaid or 0.0       
261            user.Exists = 1
262        return user
263       
264    def getGroupFromBackend(self, groupname) :   
265        """Extracts group information given its name."""
266        group = StorageGroup(self, groupname)
267        result = self.doSearch("(&(objectClass=pykotaGroup)(|(pykotaGroupName=%s)(%s=%s)))" % (groupname, self.info["grouprdn"], groupname), ["pykotaLimitBy"], base=self.info["groupbase"])
268        if result :
269            fields = result[0][1]
270            group.ident = result[0][0]
271            group.LimitBy = fields.get("pykotaLimitBy")
272            if group.LimitBy is not None :
273                group.LimitBy = group.LimitBy[0]
274            group.AccountBalance = 0.0
275            group.LifeTimePaid = 0.0
276            for member in self.getGroupMembers(group) :
277                if member.Exists :
278                    group.AccountBalance += member.AccountBalance
279                    group.LifeTimePaid += member.LifeTimePaid
280            group.Exists = 1
281        return group
282       
283    def getPrinterFromBackend(self, printername) :       
284        """Extracts printer information given its name."""
285        printer = StoragePrinter(self, printername)
286        result = self.doSearch("(&(objectClass=pykotaPrinter)(|(pykotaPrinterName=%s)(%s=%s)))" % (printername, self.info["printerrdn"], printername), ["pykotaPricePerPage", "pykotaPricePerJob"], base=self.info["printerbase"])
287        if result :
288            fields = result[0][1]
289            printer.ident = result[0][0]
290            printer.PricePerJob = float(fields.get("pykotaPricePerJob")[0] or 0.0)
291            printer.PricePerPage = float(fields.get("pykotaPricePerPage")[0] or 0.0)
292            printer.LastJob = self.getPrinterLastJob(printer)
293            printer.Exists = 1
294        return printer   
295       
296    def getUserPQuotaFromBackend(self, user, printer) :       
297        """Extracts a user print quota."""
298        userpquota = StorageUserPQuota(self, user, printer)
299        if user.Exists :
300            result = self.doSearch("(&(objectClass=pykotaUserPQuota)(pykotaUserName=%s)(pykotaPrinterName=%s))" % (user.Name, printer.Name), ["pykotaPageCounter", "pykotaLifePageCounter", "pykotaSoftLimit", "pykotaHardLimit", "pykotaDateLimit"], base=self.info["userquotabase"])
301            if result :
302                fields = result[0][1]
303                userpquota.ident = result[0][0]
304                userpquota.PageCounter = int(fields.get("pykotaPageCounter")[0] or 0)
305                userpquota.LifePageCounter = int(fields.get("pykotaLifePageCounter")[0] or 0)
306                userpquota.SoftLimit = fields.get("pykotaSoftLimit")
307                if userpquota.SoftLimit is not None :
308                    if userpquota.SoftLimit[0].upper() == "NONE" :
309                        userpquota.SoftLimit = None
310                    else :   
311                        userpquota.SoftLimit = int(userpquota.SoftLimit[0])
312                userpquota.HardLimit = fields.get("pykotaHardLimit")
313                if userpquota.HardLimit is not None :
314                    if userpquota.HardLimit[0].upper() == "NONE" :
315                        userpquota.HardLimit = None
316                    elif userpquota.HardLimit is not None :   
317                        userpquota.HardLimit = int(userpquota.HardLimit[0])
318                userpquota.DateLimit = fields.get("pykotaDateLimit")
319                if userpquota.DateLimit is not None :
320                    if userpquota.DateLimit[0].upper() == "NONE" : 
321                        userpquota.DateLimit = None
322                    else :   
323                        userpquota.DateLimit = userpquota.DateLimit[0]
324                userpquota.Exists = 1
325        return userpquota
326       
327    def getGroupPQuotaFromBackend(self, group, printer) :       
328        """Extracts a group print quota."""
329        grouppquota = StorageGroupPQuota(self, group, printer)
330        if group.Exists :
331            result = self.doSearch("(&(objectClass=pykotaGroupPQuota)(pykotaGroupName=%s)(pykotaPrinterName=%s))" % (group.Name, printer.Name), ["pykotaSoftLimit", "pykotaHardLimit", "pykotaDateLimit"], base=self.info["groupquotabase"])
332            if result :
333                fields = result[0][1]
334                grouppquota.ident = result[0][0]
335                grouppquota.SoftLimit = fields.get("pykotaSoftLimit")
336                if grouppquota.SoftLimit is not None :
337                    if grouppquota.SoftLimit[0].upper() == "NONE" :
338                        grouppquota.SoftLimit = None
339                    else :   
340                        grouppquota.SoftLimit = int(grouppquota.SoftLimit[0])
341                grouppquota.HardLimit = fields.get("pykotaHardLimit")
342                if grouppquota.HardLimit is not None :
343                    if grouppquota.HardLimit[0].upper() == "NONE" :
344                        grouppquota.HardLimit = None
345                    else :   
346                        grouppquota.HardLimit = int(grouppquota.HardLimit[0])
347                grouppquota.DateLimit = fields.get("pykotaDateLimit")
348                if grouppquota.DateLimit is not None :
349                    if grouppquota.DateLimit[0].upper() == "NONE" : 
350                        grouppquota.DateLimit = None
351                    else :   
352                        grouppquota.DateLimit = grouppquota.DateLimit[0]
353                grouppquota.PageCounter = 0
354                grouppquota.LifePageCounter = 0
355                usernamesfilter = "".join(["(pykotaUserName=%s)" % member.Name for member in self.getGroupMembers(group)])
356                result = self.doSearch("(&(objectClass=pykotaUserPQuota)(pykotaPrinterName=%s)(|%s))" % (printer.Name, usernamesfilter), ["pykotaPageCounter", "pykotaLifePageCounter"], base=self.info["userquotabase"])
357                if result :
358                    for userpquota in result :   
359                        grouppquota.PageCounter += int(userpquota[1].get("pykotaPageCounter")[0] or 0)
360                        grouppquota.LifePageCounter += int(userpquota[1].get("pykotaLifePageCounter")[0] or 0)
361                grouppquota.Exists = 1
362        return grouppquota
363       
364    def getPrinterLastJobFromBackend(self, printer) :       
365        """Extracts a printer's last job information."""
366        lastjob = StorageLastJob(self, printer)
367        result = self.doSearch("(&(objectClass=pykotaLastjob)(|(pykotaPrinterName=%s)(%s=%s)))" % (printer.Name, self.info["printerrdn"], printer.Name), ["pykotaLastJobIdent"], base=self.info["lastjobbase"])
368        if result :
369            lastjob.lastjobident = result[0][0]
370            lastjobident = result[0][1]["pykotaLastJobIdent"][0]
371            result = self.doSearch("objectClass=pykotaJob", ["pykotaUserName", "pykotaJobId", "pykotaPrinterPageCounter", "pykotaJobSize", "pykotaAction", "createTimestamp"], base="cn=%s,%s" % (lastjobident, self.info["jobbase"]), scope=ldap.SCOPE_BASE)
372            if result :
373                fields = result[0][1]
374                lastjob.ident = result[0][0]
375                lastjob.JobId = fields.get("pykotaJobId")[0]
376                lastjob.User = self.getUser(fields.get("pykotaUserName")[0])
377                lastjob.PrinterPageCounter = int(fields.get("pykotaPrinterPageCounter")[0] or 0)
378                lastjob.JobSize = int(fields.get("pykotaJobSize", [0])[0])
379                lastjob.JobAction = fields.get("pykotaAction")[0]
380                date = fields.get("createTimestamp")[0]
381                year = int(date[:4])
382                month = int(date[4:6])
383                day = int(date[6:8])
384                hour = int(date[8:10])
385                minute = int(date[10:12])
386                second = int(date[12:14])
387                lastjob.JobDate = "%04i-%02i-%02i %02i:%02i:%02i" % (year, month, day, hour, minute, second)
388                lastjob.Exists = 1
389        return lastjob
390       
391    def getGroupMembersFromBackend(self, group) :       
392        """Returns the group's members list."""
393        groupmembers = []
394        result = self.doSearch("(&(objectClass=pykotaGroup)(|(pykotaGroupName=%s)(%s=%s)))" % (group.Name, self.info["grouprdn"], group.Name), [self.info["groupmembers"]], base=self.info["groupbase"])
395        if result :
396            for username in result[0][1].get(self.info["groupmembers"], []) :
397                groupmembers.append(self.getUser(username))
398        return groupmembers       
399       
400    def getUserGroupsFromBackend(self, user) :       
401        """Returns the user's groups list."""
402        groups = []
403        result = self.doSearch("(&(objectClass=pykotaGroup)(%s=%s))" % (self.info["groupmembers"], user.Name), [self.info["grouprdn"]], base=self.info["groupbase"])
404        if result :
405            for (groupid, fields) in result :
406                groups.append(self.getGroup(fields.get(self.info["grouprdn"])[0]))
407        return groups       
408       
409    def getMatchingPrinters(self, printerpattern) :
410        """Returns the list of all printers for which name matches a certain pattern."""
411        printers = []
412        # see comment at the same place in pgstorage.py
413        result = self.doSearch("(&(objectClass=pykotaPrinter)(|%s))" % "".join(["(pykotaPrinterName=%s)" % pname for pname in printerpattern.split(",")]), ["pykotaPrinterName", "pykotaPricePerPage", "pykotaPricePerJob"], base=self.info["printerbase"])
414        if result :
415            for (printerid, fields) in result :
416                printername = fields["pykotaPrinterName"][0]
417                printer = StoragePrinter(self, printername)
418                printer.ident = printerid
419                printer.PricePerJob = float(fields.get("pykotaPricePerJob")[0] or 0.0)
420                printer.PricePerPage = float(fields.get("pykotaPricePerPage")[0] or 0.0)
421                printer.LastJob = self.getPrinterLastJob(printer)
422                printer.Exists = 1
423                printers.append(printer)
424                self.cacheEntry("PRINTERS", printer.Name, printer)
425        return printers       
426       
427    def getPrinterUsersAndQuotas(self, printer, names=["*"]) :       
428        """Returns the list of users who uses a given printer, along with their quotas."""
429        usersandquotas = []
430        result = self.doSearch("(&(objectClass=pykotaUserPQuota)(pykotaPrinterName=%s)(|%s))" % (printer.Name, "".join(["(pykotaUserName=%s)" % uname for uname in names])), ["pykotaUserName", "pykotaPageCounter", "pykotaLifePageCounter", "pykotaSoftLimit", "pykotaHardLimit", "pykotaDateLimit"], base=self.info["userquotabase"])
431        if result :
432            for (userquotaid, fields) in result :
433                user = self.getUser(fields.get("pykotaUserName")[0])
434                userpquota = StorageUserPQuota(self, user, printer)
435                userpquota.ident = userquotaid
436                userpquota.PageCounter = int(fields.get("pykotaPageCounter")[0] or 0)
437                userpquota.LifePageCounter = int(fields.get("pykotaLifePageCounter")[0] or 0)
438                userpquota.SoftLimit = fields.get("pykotaSoftLimit")
439                if userpquota.SoftLimit is not None :
440                    if userpquota.SoftLimit[0].upper() == "NONE" :
441                        userpquota.SoftLimit = None
442                    else :   
443                        userpquota.SoftLimit = int(userpquota.SoftLimit[0])
444                userpquota.HardLimit = fields.get("pykotaHardLimit")
445                if userpquota.HardLimit is not None :
446                    if userpquota.HardLimit[0].upper() == "NONE" :
447                        userpquota.HardLimit = None
448                    elif userpquota.HardLimit is not None :   
449                        userpquota.HardLimit = int(userpquota.HardLimit[0])
450                userpquota.DateLimit = fields.get("pykotaDateLimit")
451                if userpquota.DateLimit is not None :
452                    if userpquota.DateLimit[0].upper() == "NONE" : 
453                        userpquota.DateLimit = None
454                    else :   
455                        userpquota.DateLimit = userpquota.DateLimit[0]
456                userpquota.Exists = 1
457                usersandquotas.append((user, userpquota))
458                self.cacheEntry("USERPQUOTAS", "%s@%s" % (user.Name, printer.Name), userpquota)
459        usersandquotas.sort(lambda x, y : cmp(x[0].Name, y[0].Name))           
460        return usersandquotas
461               
462    def getPrinterGroupsAndQuotas(self, printer, names=["*"]) :       
463        """Returns the list of groups which uses a given printer, along with their quotas."""
464        groupsandquotas = []
465        result = self.doSearch("(&(objectClass=pykotaGroupPQuota)(pykotaPrinterName=%s)(|%s))" % (printer.Name, "".join(["(pykotaGroupName=%s)" % gname for gname in names])), ["pykotaGroupName"], base=self.info["groupquotabase"])
466        if result :
467            for (groupquotaid, fields) in result :
468                group = self.getGroup(fields.get("pykotaGroupName")[0])
469                grouppquota = self.getGroupPQuota(group, printer)
470                groupsandquotas.append((group, grouppquota))
471        groupsandquotas.sort(lambda x, y : cmp(x[0].Name, y[0].Name))           
472        return groupsandquotas
473       
474    def addPrinter(self, printername) :       
475        """Adds a printer to the quota storage, returns it."""
476        fields = { self.info["printerrdn"] : printername,
477                   "objectClass" : ["pykotaObject", "pykotaPrinter"],
478                   "cn" : printername,
479                   "pykotaPrinterName" : printername,
480                   "pykotaPricePerPage" : "0.0",
481                   "pykotaPricePerJob" : "0.0",
482                 } 
483        dn = "%s=%s,%s" % (self.info["printerrdn"], printername, self.info["printerbase"])
484        self.doAdd(dn, fields)
485        return self.getPrinter(printername)
486       
487    def addUser(self, user) :       
488        """Adds a user to the quota storage, returns it."""
489        newfields = {
490                       "pykotaUserName" : user.Name,
491                       "pykotaLimitBY" : (user.LimitBy or "quota"),
492                       "pykotaBalance" : str(user.AccountBalance or 0.0),
493                       "pykotaLifeTimePaid" : str(user.LifeTimePaid or 0.0),
494                    }   
495        mustadd = 1
496        if self.info["newuser"].lower() != 'below' :
497            result = self.doSearch("(&(objectClass=%s)(%s=%s))" % (self.info["newuser"], self.info["userrdn"], user.Name), None, base=self.info["userbase"])
498            if result :
499                (dn, fields) = result[0]
500                fields["objectClass"].extend(["pykotaAccount", "pykotaAccountBalance"])
501                fields.update(newfields)
502                self.doModify(dn, fields)
503                mustadd = 0
504               
505        if mustadd :
506            fields = { self.info["userrdn"] : user.Name,
507                       "objectClass" : ["pykotaObject", "pykotaAccount", "pykotaAccountBalance"],
508                       "cn" : user.Name,
509                     } 
510            fields.update(newfields)         
511            dn = "%s=%s,%s" % (self.info["userrdn"], user.Name, self.info["userbase"])
512            self.doAdd(dn, fields)
513        return self.getUser(user.Name)
514       
515    def addGroup(self, group) :       
516        """Adds a group to the quota storage, returns it."""
517        newfields = { 
518                      "pykotaGroupName" : group.Name,
519                      "pykotaLimitBY" : (group.LimitBy or "quota"),
520                    } 
521        mustadd = 1
522        if self.info["newgroup"].lower() != 'below' :
523            result = self.doSearch("(&(objectClass=%s)(%s=%s))" % (self.info["newgroup"], self.info["grouprdn"], group.Name), None, base=self.info["groupbase"])
524            if result :
525                (dn, fields) = result[0]
526                fields["objectClass"].extend(["pykotaGroup"])
527                fields.update(newfields)
528                self.doModify(dn, fields)
529                mustadd = 0
530               
531        if mustadd :
532            fields = { self.info["grouprdn"] : group.Name,
533                       "objectClass" : ["pykotaObject", "pykotaGroup"],
534                       "cn" : group.Name,
535                     } 
536            fields.update(newfields)         
537            dn = "%s=%s,%s" % (self.info["grouprdn"], group.Name, self.info["groupbase"])
538            self.doAdd(dn, fields)
539        return self.getGroup(group.Name)
540       
541    def addUserToGroup(self, user, group) :   
542        """Adds an user to a group."""
543        if user.Name not in [u.Name for u in self.getGroupMembers(group)] :
544            result = self.doSearch("objectClass=pykotaGroup", None, base=group.ident, scope=ldap.SCOPE_BASE)   
545            if result :
546                fields = result[0][1]
547                if not fields.has_key(self.info["groupmembers"]) :
548                    fields[self.info["groupmembers"]] = []
549                fields[self.info["groupmembers"]].append(user.Name)
550                self.doModify(group.ident, fields)
551                group.Members.append(user)
552               
553    def addUserPQuota(self, user, printer) :
554        """Initializes a user print quota on a printer."""
555        uuid = self.genUUID()
556        fields = { "cn" : uuid,
557                   "objectClass" : ["pykotaObject", "pykotaUserPQuota"],
558                   "pykotaUserName" : user.Name,
559                   "pykotaPrinterName" : printer.Name,
560                   "pykotaDateLimit" : "None",
561                   "pykotaPageCounter" : "0",
562                   "pykotaLifePageCounter" : "0",
563                 } 
564        dn = "cn=%s,%s" % (uuid, self.info["userquotabase"])
565        self.doAdd(dn, fields)
566        return self.getUserPQuota(user, printer)
567       
568    def addGroupPQuota(self, group, printer) :
569        """Initializes a group print quota on a printer."""
570        uuid = self.genUUID()
571        fields = { "cn" : uuid,
572                   "objectClass" : ["pykotaObject", "pykotaGroupPQuota"],
573                   "pykotaGroupName" : group.Name,
574                   "pykotaPrinterName" : printer.Name,
575                   "pykotaDateLimit" : "None",
576                 } 
577        dn = "cn=%s,%s" % (uuid, self.info["groupquotabase"])
578        self.doAdd(dn, fields)
579        return self.getGroupPQuota(group, printer)
580       
581    def writePrinterPrices(self, printer) :   
582        """Write the printer's prices back into the storage."""
583        fields = {
584                   "pykotaPricePerPage" : str(printer.PricePerPage),
585                   "pykotaPricePerJob" : str(printer.PricePerJob),
586                 }
587        self.doModify(printer.ident, fields)
588       
589    def writeUserLimitBy(self, user, limitby) :   
590        """Sets the user's limiting factor."""
591        fields = {
592                   "pykotaLimitBy" : limitby,
593                 }
594        self.doModify(user.ident, fields)         
595       
596    def writeGroupLimitBy(self, group, limitby) :   
597        """Sets the group's limiting factor."""
598        fields = {
599                   "pykotaLimitBy" : limitby,
600                 }
601        self.doModify(group.ident, fields)         
602       
603    def writeUserPQuotaDateLimit(self, userpquota, datelimit) :   
604        """Sets the date limit permanently for a user print quota."""
605        fields = {
606                   "pykotaDateLimit" : "%04i-%02i-%02i %02i:%02i:%02i" % (datelimit.year, datelimit.month, datelimit.day, datelimit.hour, datelimit.minute, datelimit.second),
607                 }
608        return self.doModify(userpquota.ident, fields)
609           
610    def writeGroupPQuotaDateLimit(self, grouppquota, datelimit) :   
611        """Sets the date limit permanently for a group print quota."""
612        fields = {
613                   "pykotaDateLimit" : "%04i-%02i-%02i %02i:%02i:%02i" % (datelimit.year, datelimit.month, datelimit.day, datelimit.hour, datelimit.minute, datelimit.second),
614                 }
615        return self.doModify(grouppquota.ident, fields)
616       
617    def writeUserPQuotaPagesCounters(self, userpquota, newpagecounter, newlifepagecounter) :   
618        """Sets the new page counters permanently for a user print quota."""
619        fields = {
620                   "pykotaPageCounter" : str(newpagecounter),
621                   "pykotaLifePageCounter" : str(newlifepagecounter),
622                 } 
623        return self.doModify(userpquota.ident, fields)         
624       
625    def writeUserAccountBalance(self, user, newbalance, newlifetimepaid=None) :   
626        """Sets the new account balance and eventually new lifetime paid."""
627        fields = {
628                   "pykotaBalance" : str(newbalance),
629                 }
630        if newlifetimepaid is not None :
631            fields.update({ "pykotaLifeTimePaid" : str(newlifetimepaid) })
632        return self.doModify(user.idbalance, fields)         
633           
634    def writeLastJobSize(self, lastjob, jobsize) :       
635        """Sets the last job's size permanently."""
636        fields = {
637                   "pykotaJobSize" : str(jobsize),
638                 }
639        self.doModify(lastjob.ident, fields)         
640       
641    def writeJobNew(self, printer, user, jobid, pagecounter, action, jobsize=None) :   
642        """Adds a job in a printer's history."""
643        uuid = self.genUUID()
644        fields = {
645                   "objectClass" : ["pykotaObject", "pykotaJob"],
646                   "cn" : uuid,
647                   "pykotaUserName" : user.Name,
648                   "pykotaPrinterName" : printer.Name,
649                   "pykotaJobId" : jobid,
650                   "pykotaPrinterPageCounter" : str(pagecounter),
651                   "pykotaAction" : action,
652                 }
653        if jobsize is not None :         
654            fields.update({ "pykotaJobSize" : str(jobsize) })
655        dn = "cn=%s,%s" % (uuid, self.info["jobbase"])
656        self.doAdd(dn, fields)
657        if printer.LastJob.Exists :
658            fields = {
659                       "pykotaLastJobIdent" : uuid,
660                     }
661            self.doModify(printer.LastJob.lastjobident, fields)         
662        else :   
663            lastjuuid = self.genUUID()
664            lastjdn = "cn=%s,%s" % (lastjuuid, self.info["lastjobbase"])
665            fields = {
666                       "objectClass" : ["pykotaObject", "pykotaLastJob"],
667                       "cn" : lastjuuid,
668                       "pykotaPrinterName" : printer.Name,
669                       "pykotaLastJobIdent" : uuid,
670                     } 
671            self.doAdd(lastjdn, fields)         
672           
673    def writeUserPQuotaLimits(self, userpquota, softlimit, hardlimit) :
674        """Sets soft and hard limits for a user quota."""
675        fields = { 
676                   "pykotaSoftLimit" : str(softlimit),
677                   "pykotaHardLimit" : str(hardlimit),
678                 }
679        self.doModify(userpquota.ident, fields)
680       
681    def writeGroupPQuotaLimits(self, grouppquota, softlimit, hardlimit) :
682        """Sets soft and hard limits for a group quota on a specific printer."""
683        fields = { 
684                   "pykotaSoftLimit" : str(softlimit),
685                   "pykotaHardLimit" : str(hardlimit),
686                 }
687        self.doModify(grouppquota.ident, fields)
688           
689    def deleteUser(self, user) :   
690        """Completely deletes an user from the Quota Storage."""
691        # TODO : What should we do if we delete the last person who used a given printer ?
692        # TODO : we can't reassign the last job to the previous one, because next user would be
693        # TODO : incorrectly charged (overcharged).
694        result = self.doSearch("(&(objectClass=pykotaLastJob)(pykotaUserName=%s))" % user.Name, base=self.info["lastjobbase"])
695        for (ident, fields) in result :
696            self.doDelete(ident)
697        result = self.doSearch("(&(objectClass=pykotaJob)(pykotaUserName=%s))" % user.Name, base=self.info["jobbase"])
698        for (ident, fields) in result :
699            self.doDelete(ident)
700        result = self.doSearch("(&(objectClass=pykotaUserPQuota)(pykotaUserName=%s))" % user.Name, ["pykotaUserName"], base=self.info["userquotabase"])
701        for (ident, fields) in result :
702            self.doDelete(ident)
703        result = self.doSearch("objectClass=pykotaAccount", None, base=user.ident, scope=ldap.SCOPE_BASE)   
704        if result :
705            fields = result[0][1]
706            for k in fields.keys() :
707                if k.startswith("pykota") :
708                    del fields[k]
709                elif k.lower() == "objectclass" :   
710                    todelete = []
711                    for i in range(len(fields[k])) :
712                        if fields[k][i].startswith("pykota") : 
713                            todelete.append(i)
714                    todelete.sort()       
715                    todelete.reverse()
716                    for i in todelete :
717                        del fields[k][i]
718            if fields.get("objectClass") or fields.get("objectclass") :
719                self.doModify(user.ident, fields, ignoreold=0)       
720            else :   
721                self.doDelete(user.ident)
722        result = self.doSearch("(&(objectClass=pykotaAccountBalance)(pykotaUserName=%s))" % user.Name, ["pykotaUserName"], base=self.info["balancebase"])
723        for (ident, fields) in result :
724            self.doDelete(ident)
725       
726    def deleteGroup(self, group) :   
727        """Completely deletes a group from the Quota Storage."""
728        result = self.doSearch("(&(objectClass=pykotaGroupPQuota)(pykotaGroupName=%s))" % group.Name, ["pykotaGroupName"], base=self.info["groupquotabase"])
729        for (ident, fields) in result :
730            self.doDelete(ident)
731        result = self.doSearch("objectClass=pykotaGroup", None, base=group.ident, scope=ldap.SCOPE_BASE)   
732        if result :
733            fields = result[0][1]
734            for k in fields.keys() :
735                if k.startswith("pykota") :
736                    del fields[k]
737                elif k.lower() == "objectclass" :   
738                    todelete = []
739                    for i in range(len(fields[k])) :
740                        if fields[k][i].startswith("pykota") : 
741                            todelete.append(i)
742                    todelete.sort()       
743                    todelete.reverse()
744                    for i in todelete :
745                        del fields[k][i]
746            if fields.get("objectClass") or fields.get("objectclass") :
747                self.doModify(group.ident, fields, ignoreold=0)       
748            else :   
749                self.doDelete(group.ident)
750           
Note: See TracBrowser for help on using the browser.