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

Revision 1105, 35.5 kB (checked in by jalet, 21 years ago)

PyKota now tries to add its attributes intelligently in existing LDAP
directories.

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