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

Revision 1027, 19.3 kB (checked in by jalet, 21 years ago)

Two big bugs fixed, time to release something ;-)

  • 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.6  2003/06/13 19:07:57  jalet
24# Two big bugs fixed, time to release something ;-)
25#
26# Revision 1.5  2003/06/10 16:37:54  jalet
27# Deletion of the second user which is not needed anymore.
28# Added a debug configuration field in /etc/pykota.conf
29# All queries can now be sent to the logger in debug mode, this will
30# greatly help improve performance when time for this will come.
31#
32# Revision 1.4  2003/06/10 10:45:32  jalet
33# Not implemented methods now raise an exception when called.
34#
35# Revision 1.3  2003/06/06 20:49:15  jalet
36# Very latest schema. UNTESTED.
37#
38# Revision 1.2  2003/06/06 14:21:08  jalet
39# New LDAP schema.
40# Small bug fixes.
41#
42# Revision 1.1  2003/06/05 11:19:13  jalet
43# More good work on LDAP storage.
44#
45#
46#
47
48#
49# My IANA assigned number, for
50# "Conseil Internet & Logiciels Libres, J�me Alet"
51# is 16868. Use this as a base to create the LDAP schema.
52#
53
54import fnmatch
55
56from pykota.storage import PyKotaStorageError
57
58try :
59    import ldap
60except ImportError :   
61    import sys
62    # TODO : to translate or not to translate ?
63    raise PyKotaStorageError, "This python version (%s) doesn't seem to have the python-ldap module installed correctly." % sys.version.split()[0]
64   
65class Storage :
66    def __init__(self, pykotatool, host, dbname, user, passwd) :
67        """Opens the LDAP connection."""
68        # raise PyKotaStorageError, "Sorry, the LDAP backend for PyKota is not yet implemented !"
69        self.tool = pykotatool
70        self.debug = pykotatool.config.getDebug()
71        self.closed = 1
72        try :
73            self.database = ldap.initialize(host) 
74            self.database.simple_bind_s(user, passwd)
75            self.basedn = dbname
76        except ldap.SERVER_DOWN :   
77            raise PyKotaStorageError, "LDAP backend for PyKota seems to be down !" # TODO : translate
78        else :   
79            self.closed = 0
80            if self.debug :
81                self.tool.logger.log_message("Database opened (host=%s, dbname=%s, user=%s)" % (host, dbname, user), "debug")
82           
83    def __del__(self) :       
84        """Closes the database connection."""
85        if not self.closed :
86            del self.database
87            self.closed = 1
88            if self.debug :
89                self.tool.logger.log_message("Database closed.", "debug")
90       
91    def doSearch(self, key, fields, base="", scope=ldap.SCOPE_SUBTREE) :
92        """Does an LDAP search query."""
93        try :
94            # prepends something more restrictive at the beginning of the base dn
95            if self.debug :
96                self.tool.logger.log_message("QUERY : BaseDN : %s, Scope : %s, Filter : %s, Attributes : %s" % ((base or self.basedn), scope, key, fields), "debug")
97            result = self.database.search_s(base or self.basedn, scope, key, fields)
98        except ldap.NO_SUCH_OBJECT :   
99            return
100        else :     
101            return result
102       
103    def getMatchingPrinters(self, printerpattern) :
104        """Returns the list of all printers as tuples (id, name) for printer names which match a certain pattern."""
105        result = self.doSearch("objectClass=pykotaPrinter", ["pykotaPrinterName"])
106        if result :
107            return [(printerid, printer["pykotaPrinterName"][0]) for (printerid, printer) in result if fnmatch.fnmatchcase(printer["pykotaPrinterName"][0], printerpattern)]
108           
109    def getPrinterId(self, printername) :       
110        """Returns a printerid given a printername."""
111        result = self.doSearch("(&(objectClass=pykotaPrinter)(|(pykotaPrinterName=%s)(cn=%s)))" % (printername, printername), ["pykotaPrinterName"])
112        if result :
113            return result[0][0]
114           
115    def getPrinterPrices(self, printerid) :       
116        """Returns a printer prices per page and per job given a printerid."""
117        result = self.doSearch("(|(pykotaPrinterName=*)(cn=*))", ["pykotaPricePerPage", "pykotaPricePerJob"], base=printerid, scope=ldap.SCOPE_BASE)
118        if result :
119            return (float(result[0][1]["pykotaPricePerPage"][0]), float(result[0][1]["pykotaPricePerJob"][0]))
120           
121    def setPrinterPrices(self, printerid, perpage, perjob) :
122        """Sets prices per job and per page for a given printer."""
123        raise PyKotaStorageError, "Not implemented !"
124   
125    def getUserId(self, username) :
126        """Returns a userid given a username."""
127        result = self.doSearch("(&(objectClass=pykotaAccount)(|(pykotaUserName=%s)(uid=%s)))" % (username, username), ["uid"])
128        if result :
129            return result[0][0]
130           
131    def getGroupId(self, groupname) :
132        """Returns a groupid given a grupname."""
133        result = self.doSearch("(&(objectClass=pykotaGroup)(|(pykotaGroupName=%s)(cn=%s)))" % (groupname, groupname), ["cn"])
134        if result is not None :
135            (groupid, dummy) = result[0]
136            return groupid
137           
138    def getJobHistoryId(self, jobid, userid, printerid) :       
139        """Returns the history line's id given a (jobid, userid, printerid).
140       
141           TODO : delete because shouldn't be needed by the LDAP backend
142        """
143        raise PyKotaStorageError, "Not implemented !"
144           
145    def getPrinterUsers(self, printerid) :       
146        """Returns the list of userids and usernames which uses a given printer."""
147        # first get the printer's name from the id
148        result = self.doSearch("objectClass=pykotaPrinter", ["pykotaPrinterName", "cn"], base=printerid, scope=ldap.SCOPE_BASE)
149        if result :
150            fields = result[0][1]
151            printername = (fields.get("pykotaPrinterName") or fields.get("cn"))[0]
152            result = self.doSearch("(&(objectClass=pykotaUserPQuota)(pykotaPrinterName=%s))" % printername, ["pykotaUserName"]) 
153            if result :
154                return [(pquotauserid, fields["pykotaUserName"][0]) for (pquotauserid, fields) in result]
155       
156    def getPrinterGroups(self, printerid) :       
157        """Returns the list of groups which uses a given printer."""
158        # first get the printer's name from the id
159        result = self.doSearch("objectClass=pykotaPrinter", ["pykotaPrinterName", "cn"], base=printerid, scope=ldap.SCOPE_BASE)
160        if result :
161            fields = result[0][1]
162            printername = (fields.get("pykotaPrinterName") or fields.get("cn"))[0]
163            result = self.doSearch("(&(objectClass=pykotaGroupPQuota)(pykotaPrinterName=%s))" % printername, ["pykotaGroupName"]) 
164            if result :
165                return [(pquotagroupid, fields["pykotaGroupName"][0]) for (pquotagroupid, fields) in result]
166       
167    def getGroupMembersNames(self, groupname) :       
168        """Returns the list of user's names which are member of this group."""
169        result = self.doSearch("(&(objectClass=pykotaGroup)(|(pykotaGroupName=%s)(cn=%s)))" % (groupname, groupname), ["memberUid", "uniqueMember", "member"])
170        if result :
171            fields = result[0][1]
172            return fields.get("memberUid") or fields.get("uniqueMember") or fields.get("member")
173       
174    def getUserGroupsNames(self, userid) :       
175        """Returns the list of groups' names the user is a member of."""
176        raise PyKotaStorageError, "Not implemented !"
177       
178    def addPrinter(self, printername) :       
179        """Adds a printer to the quota storage, returns its id."""
180        raise PyKotaStorageError, "Not implemented !"
181       
182    def addUser(self, username) :       
183        """Adds a user to the quota storage, returns its id."""
184        raise PyKotaStorageError, "Not implemented !"
185       
186    def addGroup(self, groupname) :       
187        """Adds a group to the quota storage, returns its id."""
188        raise PyKotaStorageError, "Not implemented !"
189       
190    def addUserPQuota(self, username, printerid) :
191        """Initializes a user print quota on a printer, adds the user to the quota storage if needed."""
192        raise PyKotaStorageError, "Not implemented !"
193       
194    def addGroupPQuota(self, groupname, printerid) :
195        """Initializes a group print quota on a printer, adds the group to the quota storage if needed."""
196        raise PyKotaStorageError, "Not implemented !"
197       
198    def increaseUserBalance(self, userid, amount) :   
199        """Increases (or decreases) an user's account balance by a given amount."""
200        raise PyKotaStorageError, "Not implemented !"
201       
202    def getUserBalance(self, userquotaid) :   
203        """Returns the current account balance for a given user quota identifier."""
204        # first get the user's name from the user quota id
205        result = self.doSearch("objectClass=pykotaUserPQuota", ["pykotaUserName"], base=userquotaid, scope=ldap.SCOPE_BASE)
206        if result :
207            username = result[0][1]["pykotaUserName"][0]
208            result = self.doSearch("(&(objectClass=pykotaAccountBalance)(|(pykotaUserName=%s)(uid=%s)))" % (username, username), ["pykotaBalance", "pykotaLifeTimePaid"])
209            if result :
210                fields = result[0][1]
211                return (float(fields["pykotaBalance"][0]), float(fields["pykotaLifeTimePaid"][0]))
212       
213    def getUserBalanceFromUserId(self, userid) :   
214        """Returns the current account balance for a given user id."""
215        # first get the user's name from the user id
216        result = self.doSearch("objectClass=pykotaAccount", ["pykotaUserName", "uid"], base=userid, scope=ldap.SCOPE_BASE)
217        if result :
218            fields = result[0][1]
219            username = (fields.get("pykotaUserName") or fields.get("uid"))[0]
220            result = self.doSearch("(&(objectClass=pykotaAccountBalance)(|(pykotaUserName=%s)(uid=%s)))" % (username, username), ["pykotaBalance", "pykotaLifeTimePaid"])
221            if result :
222                fields = result[0][1]
223                return (float(fields["pykotaBalance"][0]), float(fields["pykotaLifeTimePaid"][0]))
224       
225    def getGroupBalance(self, groupquotaid) :   
226        """Returns the current account balance for a given group, as the sum of each of its users' account balance."""
227        # first get the group's name from the group quota id
228        result = self.doSearch("objectClass=pykotaGroupPQuota", ["pykotaGroupName"], base=groupquotaid, scope=ldap.SCOPE_BASE)
229        if result :
230            groupname = result[0][1]["pykotaGroupName"][0]
231            members = self.getGroupMembersNames(groupname)
232            balance = lifetimepaid = 0.0
233            for member in members :
234                userid = self.getUserId(member)
235                if userid :
236                    userbal = self.getUserBalanceFromUserId(userid)
237                    if userbal :
238                        (bal, paid) = userbal
239                        balance += bal
240                        lifetimepaid += paid
241            return (balance, lifetimepaid)           
242       
243    def getUserLimitBy(self, userid) :   
244        """Returns the way in which user printing is limited."""
245        result = self.doSearch("objectClass=pykotaAccount", ["pykotaLimitBy"], base=userid, scope=ldap.SCOPE_BASE)
246        if result :
247            return result[0][1]["pykotaLimitBy"][0]
248       
249    def getGroupLimitBy(self, groupquotaid) :   
250        """Returns the way in which group printing is limited."""
251        # first get the group's name from the group quota id
252        result = self.doSearch("objectClass=pykotaGroupPQuota", ["pykotaGroupName"], base=groupquotaid, scope=ldap.SCOPE_BASE)
253        if result :
254            groupname = result[0][1]["pykotaGroupName"][0]
255            result = self.doSearch("(&(objectClass=pykotaGroup)(pykotaGroupName=%s))" % groupname, ["pykotaLimitBy"])
256            if result :
257                return result[0][1]["pykotaLimitBy"][0]
258       
259    def setUserBalance(self, userid, balance) :   
260        """Sets the account balance for a given user to a fixed value."""
261        raise PyKotaStorageError, "Not implemented !"
262       
263    def limitUserBy(self, userid, limitby) :   
264        """Limits a given user based either on print quota or on account balance."""
265        raise PyKotaStorageError, "Not implemented !"
266       
267    def limitGroupBy(self, groupid, limitby) :   
268        """Limits a given group based either on print quota or on sum of its users' account balances."""
269        raise PyKotaStorageError, "Not implemented !"
270       
271    def setUserPQuota(self, userid, printerid, softlimit, hardlimit) :
272        """Sets soft and hard limits for a user quota on a specific printer given (userid, printerid)."""
273        raise PyKotaStorageError, "Not implemented !"
274       
275    def setGroupPQuota(self, groupid, printerid, softlimit, hardlimit) :
276        """Sets soft and hard limits for a group quota on a specific printer given (groupid, printerid)."""
277        raise PyKotaStorageError, "Not implemented !"
278       
279    def resetUserPQuota(self, userid, printerid) :   
280        """Resets the page counter to zero for a user on a printer. Life time page counter is kept unchanged."""
281        raise PyKotaStorageError, "Not implemented !"
282       
283    def resetGroupPQuota(self, groupid, printerid) :   
284        """Resets the page counter to zero for a group on a printer. Life time page counter is kept unchanged."""
285        raise PyKotaStorageError, "Not implemented !"
286       
287    def updateUserPQuota(self, userid, printerid, pagecount) :
288        """Updates the used user Quota information given (userid, printerid) and a job size in pages."""
289        raise PyKotaStorageError, "Not implemented !"
290       
291    def getUserPQuota(self, userquotaid, printerid) :
292        """Returns the Print Quota information for a given (userquotaid, printerid)."""
293        # first get the user's name from the id
294        result = self.doSearch("objectClass=pykotaUserPQuota", ["pykotaUserName", "pykotaPageCounter", "pykotaLifePageCounter", "pykotaSoftLimit", "pykotaHardLimit", "pykotaDateLimit"], base=userquotaid, scope=ldap.SCOPE_BASE)
295        if result :
296            fields = result[0][1]
297            datelimit = fields["pykotaDateLimit"][0].strip()
298            if (not datelimit) or (datelimit.upper() == "NONE") : 
299                datelimit = None
300            return { "lifepagecounter" : int(fields["pykotaLifePageCounter"][0]), 
301                     "pagecounter" : int(fields["pykotaPageCounter"][0]),
302                     "softlimit" : int(fields["pykotaSoftLimit"][0]),
303                     "hardlimit" : int(fields["pykotaHardLimit"][0]),
304                     "datelimit" : datelimit
305                   }
306       
307    def getGroupPQuota(self, grouppquotaid, printerid) :
308        """Returns the Print Quota information for a given (grouppquotaid, printerid)."""
309        result = self.doSearch("objectClass=pykotaGroupPQuota", ["pykotaGroupName", "pykotaSoftLimit", "pykotaHardLimit", "pykotaDateLimit"], base=grouppquotaid, scope=ldap.SCOPE_BASE)
310        if result :
311            fields = result[0][1]
312            groupname = fields["pykotaGroupName"][0]
313            datelimit = fields["pykotaDateLimit"][0].strip()
314            if (not datelimit) or (datelimit.upper() == "NONE") : 
315                datelimit = None
316            quota = {
317                      "softlimit" : int(fields["pykotaSoftLimit"][0]),
318                      "hardlimit" : int(fields["pykotaHardLimit"][0]),
319                      "datelimit" : datelimit
320                    }
321            members = self.getGroupMembersNames(groupname)
322            pagecounter = lifepagecounter = 0
323            printerusers = self.getPrinterUsers(printerid)
324            if printerusers :
325                for (userid, username) in printerusers :
326                    if username in members :
327                        userpquota = self.getUserPQuota(userid, printerid)
328                        if userpquota :
329                            pagecounter += userpquota["pagecounter"]
330                            lifepagecounter += userpquota["lifepagecounter"]
331            quota.update({"pagecounter": pagecounter, "lifepagecounter": lifepagecounter})               
332            return quota
333       
334    def setUserDateLimit(self, userid, printerid, datelimit) :
335        """Sets the limit date for a soft limit to become an hard one given (userid, printerid)."""
336        raise PyKotaStorageError, "Not implemented !"
337       
338    def setGroupDateLimit(self, groupid, printerid, datelimit) :
339        """Sets the limit date for a soft limit to become an hard one given (groupid, printerid)."""
340        raise PyKotaStorageError, "Not implemented !"
341       
342    def addJobToHistory(self, jobid, userid, printerid, pagecounter, action) :
343        """Adds a job to the history: (jobid, userid, printerid, last page counter taken from requester)."""
344        raise PyKotaStorageError, "Not implemented !"
345   
346    def updateJobSizeInHistory(self, historyid, jobsize) :
347        """Updates a job size in the history given the history line's id."""
348        raise PyKotaStorageError, "Not implemented !"
349   
350    def getPrinterPageCounter(self, printerid) :
351        """Returns the last page counter value for a printer given its id, also returns last username, last jobid and history line id."""
352        result = self.doSearch("objectClass=pykotaPrinter", ["pykotaPrinterName", "cn"], base=printerid, scope=ldap.SCOPE_BASE)
353        if result :
354            fields = result[0][1]
355            printername = (fields.get("pykotaPrinterName") or fields.get("cn"))[0]
356            result = self.doSearch("(&(objectClass=pykotaLastjob)(|(pykotaPrinterName=%s)(cn=%s)))" % (printername, printername), ["pykotaLastJobIdent"])
357            if result :
358                lastjobident = result[0][1]["pykotaLastJobIdent"][0]
359                result = self.doSearch("(&(objectClass=pykotaJob)(cn=%s))" % lastjobident, ["pykotaUserName", "pykotaPrinterName", "pykotaJobId", "pykotaPrinterPageCounter", "pykotaJobSize", "pykotaAction", "createTimestamp"])
360                if result :
361                    pass # TODO
362       
363    def addUserToGroup(self, userid, groupid) :   
364        """Adds an user to a group."""
365        raise PyKotaStorageError, "Not implemented !"
366       
367    def deleteUser(self, userid) :   
368        """Completely deletes an user from the Quota Storage."""
369        raise PyKotaStorageError, "Not implemented !"
370       
371    def deleteGroup(self, groupid) :   
372        """Completely deletes an user from the Quota Storage."""
373        raise PyKotaStorageError, "Not implemented !"
374       
375    def computePrinterJobPrice(self, printerid, jobsize) :   
376        """Returns the price for a job on a given printer."""
377        # TODO : create a base class with things like this
378        prices = self.getPrinterPrices(printerid)
379        if prices is None :
380            perpage = perjob = 0.0
381        else :   
382            (perpage, perjob) = prices
383        return perjob + (perpage * jobsize)
Note: See TracBrowser for help on using the browser.