root / pykota / trunk / bin / pkusers @ 2706

Revision 2706, 19.3 kB (checked in by jerome, 18 years ago)

pkusers now mostly works. Removing an user from a group with
LDAP is not yet done though. Also no test was done with
LDAP yet.
filldb now really creates users (it uses pkusers instead
of edpykota)

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1#! /usr/bin/env python
2# -*- coding: ISO-8859-15 -*-
3
4# PyKota Users Manager
5#
6# PyKota - Print Quotas for CUPS and LPRng
7#
8# (c) 2003, 2004, 2005, 2006 Jerome Alet <alet@librelogiciel.com>
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation; either version 2 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program; if not, write to the Free Software
21# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22#
23# $Id$
24#
25#
26
27import os
28import sys
29import pwd
30import grp
31
32from pykota.tool import PyKotaTool, PyKotaToolError, PyKotaCommandLineError, crashed, N_
33
34__doc__ = N_("""pkusers v%(__version__)s (c) %(__years__)s %(__author__)s
35
36An Users and Groups Manager for PyKota.
37
38command line usage :
39
40  pkusers [options] user1 user2 user3 ... userN
41 
42or : 
43
44  pkusers --groups [options] group1 group2 group3 ... groupN
45
46options :
47
48  -v | --version       Prints pkusers's version number then exits.
49  -h | --help          Prints this message then exits.
50 
51  -a | --add           Adds users if they don't exist on the database.
52                       If they exist, they are modified unless
53                       -s|--skipexisting is also used.
54                       
55  -d | --delete        Deletes users from the quota storage.
56 
57  -D | --description d Adds a textual description to users or groups.
58                       
59  -g | --groups        Edit users groups instead of users.
60                         
61  -o | --overcharge f  Sets the overcharging factor applied to the user
62                       when computing the cost of a print job. Positive or
63                       negative floating point values are allowed,
64                       this allows you to do some really creative
65                       things like giving money to an user whenever
66                       he prints. The number of pages in a print job
67                       is not modified by this coefficient, only the
68                       cost of the job for a particular user.
69                       Only users have such a coefficient.
70 
71  -i | --ingroups g1[,g2...]  Puts the users into each of the groups
72                              listed, separated by commas. The groups
73                              must already exist in the Quota Storage.
74                       
75  -l | --limitby l     Choose if the user/group is limited in printing                     
76                       by its account balance or by its page quota.
77                       The default value is 'quota'. Allowed values
78                       are 'quota' 'balance' 'noquota' 'noprint' 
79                       and 'nochange' :
80                       
81                         - quota : limit by number of pages per printer.
82                         - balance : limit by number of credits in account.
83                         - noquota : no limit, accounting still done.
84                         - nochange : no limit, accounting not done.
85                         - noprint : printing is denied.
86                       NB : nochange and noprint are not supported for groups.
87                       
88  -b | --balance b     Sets the user's account balance to b.                     
89                       Account balance may be increase or decreased
90                       if b is prefixed with + or -.
91                       WARNING : when decreasing account balance,
92                       the total paid so far by the user is decreased
93                       too.
94                       Groups don't have a real balance, but the
95                       sum of their users' account balance.
96                       
97  -C | --comment txt   Defines some informational text to be associated
98                       with a change to an user's account balance.
99                       Only meaningful if -b | --balance is also used.
100                       
101                       
102  -r | --remove        In combination with the --groups option above,                       
103                       remove users from the specified users groups.
104                       
105  -s | --skipexisting  In combination with the --add option above, tells
106                       pkusers to not modify existing users.
107                       
108  user1 through userN and group1 through groupN can use wildcards
109  if the --add option is not set.
110 
111examples :                             
112
113  $ pkusers --add john paul george ringo/ringo@example.com
114 
115  This will add users john, paul, george and ringo to the quota
116  database. User ringo's email address will also be set to
117  'ringo@example.com'
118 
119  $ pkusers --ingroups coders,it jerome
120 
121  User jerome is put into the groups "coders" and "it" which must
122  already exist in the quota database.
123           
124  $ pkusers --limitby balance jerome
125 
126  This will tell PyKota to limit jerome by his account's balance
127  when printing.
128 
129  $ pkusers --balance +10.0 --comment "He paid with his blood !" jerome
130 
131  This will increase jerome's account balance by 10.0 (in your
132  own currency). You can decrease the account balance with a
133  dash prefix, and set it to a fixed amount with no prefix.
134  A comment will be stored for this balance change.
135 
136  $ pkusers --delete jerome rachel
137 
138  This will completely delete jerome and rachel from the quota
139  database. All their quotas and jobs will be deleted too.
140 
141  $ pkusers --overcharge 2.5 poorstudent
142 
143  This will overcharge the poorstudent user by a factor of 2.5.
144 
145  $ pkusers --overcharge -1 jerome
146 
147  User jerome will actually earn money whenever he prints.
148 
149  $ pkusers --overcharge 0 boss
150 
151  User boss can print at will, it won't cost him anything because the
152  cost of each print job will be multiplied by zero before charging
153  his account.
154""")
155       
156class PKUsers(PyKotaTool) :       
157    """A class for a users and users groups manager."""
158    def main(self, names, options) :
159        """Manage users or groups."""
160        if not self.config.isAdmin :
161            raise PyKotaCommandLineError, "%s : %s" % (pwd.getpwuid(os.geteuid())[0], _("You're not allowed to use this command."))
162           
163        suffix = (options["groups"] and "Group") or "User"       
164       
165        if options["delete"] :   
166            self.display("%s...\n" % _("Deletion"))
167            todelete = getattr(self.storage, "getMatching%ss" % suffix)(",".join(names))
168            nbtotal = len(todelete)
169            for i in range(nbtotal) :
170                todelete[i].delete()
171                percent = 100.0 * float(i) / float(nbtotal)
172                self.display("\r%.02f%%" % percent)
173        else :
174            if options["add"] :   
175                self.display("%s...\n" % _("Creation"))
176                rejectunknown = self.config.getRejectUnknown()   
177                entries = []
178                nbtotal = len(names)
179                for i in range(nbtotal) :
180                    ename = names[i]
181                    email = ""
182                    if not options["groups"] :
183                        splitname = ename.split('/', 1)     # username/email
184                        if len(splitname) == 1 :
185                            splitname.append("")
186                        (ename, email) = splitname
187                        if email and (email.count('@') != 1) :
188                            self.printInfo(_("Invalid email address %s") % email)
189                            email = ""
190                    entry = getattr(self.storage, "get%s" % suffix)(ename)
191                    if entry.Exists :
192                        if options["skipexisting"] :
193                            self.printInfo(_("%s %s already exists, skipping.") % (suffix, entry.Name))
194                        else :   
195                            self.printInfo(_("%s %s already exists, will be modified.") % (suffix, entry.Name))
196                            entries.append(entry)
197                    else :
198                        if self.isValidName(entry.Name) :
199                            reject = 0
200                            if rejectunknown :
201                                if options["groups"] :
202                                    try :
203                                        grp.getgrnam(entry.Name)
204                                    except KeyError :   
205                                        self.printInfo(_("Unknown group %s") % entry.Name, "error")
206                                        reject = 1
207                                else :   
208                                    try :
209                                        pwd.getpwnam(entry.Name)
210                                    except KeyError :   
211                                        self.printInfo(_("Unknown user %s") % entry.Name, "error")
212                                        reject = 1
213                            if not reject :       
214                                if email :
215                                    entry.Email = email
216                                entry = getattr(self.storage, "add%s" % suffix)(entry)
217                                entries.append(entry)
218                        else :   
219                            if options["groups"] :
220                                self.printInfo(_("Invalid group name %s") % entry.Name)
221                            else :   
222                                self.printInfo(_("Invalid user name %s") % entry.Name)
223                               
224                    percent = 100.0 * float(i) / float(nbtotal)
225                    self.display("\r%.02f%%" % percent)
226                self.display("\r100.00%%\r        \r%s\n" % _("Done."))
227            else :       
228                if not names :
229                    names = ["*"]
230                entries = getattr(self.storage, "getMatching%ss" % suffix)(",".join(names))
231                if not entries :
232                    raise PyKotaCommandLineError, _("There's no %s matching %s") % (suffix.lower(), " ".join(names))
233                       
234            if options["list"] :
235                if suffix == "User" :
236                    maildomain = self.config.getMailDomain()
237                    smtpserver = self.config.getSMTPServer()
238                    for entry in entries :
239                        email = entry.Email
240                        if not email :
241                            if maildomain :     
242                                email = "%s@%s" % (entry.Name, maildomain)
243                            elif smtpserver :   
244                                email = "%s@%s" % (entry.Name, smtpserver)
245                            else :   
246                                email = "%s@%s" % (entry.Name, "localhost")
247                        msg = "%s - <%s>" % (entry.Name, email)
248                        if entry.Description :
249                            msg += " - %s" % entry.Description
250                        print msg   
251                        print "    %s" % (_("Limited by : %s") % entry.LimitBy)
252                        print "    %s" % (_("Account balance : %.2f") % (entry.AccountBalance or 0.0))
253                        print "    %s" % (_("Total paid so far : %.2f") % (entry.LifeTimePaid or 0.0))
254                        print "    %s" % (_("Overcharging factor : %.2f") % entry.OverCharge)
255                else :   
256                    for entry in entries :
257                        msg = "%s" % entry.Name
258                        if entry.Description :
259                            msg += " - %s" % entry.Description
260                        print msg   
261                        print "    %s" % (_("Limited by : %s") % entry.LimitBy)
262                        print "    %s" % (_("Group balance : %.2f") % (entry.AccountBalance or 0.0))
263                        print "    %s" % (_("Total paid so far : %.2f") % (entry.LifeTimePaid or 0.0))
264            else :
265                self.display("%s...\n" % _("Modification"))
266               
267                limitby = options["limitby"]
268                if limitby :
269                    limitby = limitby.strip().lower()
270                if limitby :
271                    if limitby not in ('quota', 'balance', 'noquota', \
272                                                'noprint', 'nochange') :
273                        raise PyKotaCommandLineError, _("Invalid limitby value %s") % options["limitby"]
274                    if (limitby in ('nochange', 'noprint')) and options["groups"] :   
275                        raise PyKotaCommandLineError, _("Invalid limitby value %s") % options["limitby"]
276                   
277                overcharge = options["overcharge"]
278                if overcharge :
279                    try :
280                        overcharge = float(overcharge.strip())
281                    except (ValueError, AttributeError) :   
282                        raise PyKotaCommandLineError, _("Invalid overcharge value %s") % options["overcharge"]
283                       
284                balance = options["balance"]
285                if balance :
286                    balance = balance.strip()
287                    try :
288                        balancevalue = float(balance)
289                    except ValueError :   
290                        raise PyKotaCommandLineError, _("Invalid balance value %s") % options["balance"]
291                   
292                if options["ingroups"] :
293                    usersgroups = self.storage.getMatchingGroups(options["ingroups"])
294                    if not usersgroups :
295                        raise PyKotaCommandLineError, _("There's no users group matching %s") % " ".join(options["ingroups"].split(','))
296                else :         
297                    usersgroups = []
298                       
299                description = options["description"]
300                if description :
301                    description = options["description"].strip()
302                   
303                comment = options["comment"]
304                if comment :
305                    comment = options["comment"].strip()
306                   
307                nbtotal = len(entries)
308                for i in range(nbtotal) :       
309                    entry = entries[i]
310                    if description is not None : # NB : "" is allowed !
311                        entry.setDescription(description)
312                    if limitby :   
313                        entry.setLimitBy(limitby)
314                       
315                    if suffix == "User" :   
316                        if overcharge is not None : # NB : 0 is allowed !     
317                            entry.setOverChargeFactor(overcharge)
318                           
319                    entry.save()   
320                   
321                    if suffix == "User" :
322                        if balance :
323                            if balance.startswith("+") or balance.startswith("-") :
324                                newbalance = float(entry.AccountBalance or 0.0) + balancevalue
325                                newlifetimepaid = float(entry.LifeTimePaid or 0.0) + balancevalue
326                                entry.setAccountBalance(newbalance, newlifetimepaid, comment)
327                            else :
328                                diff = balancevalue - float(entry.AccountBalance or 0.0)
329                                newlifetimepaid = float(entry.LifeTimePaid or 0.0) + diff
330                                entry.setAccountBalance(balancevalue, newlifetimepaid, comment)
331                                       
332                        for ugroup in usersgroups :
333                            if options["remove"] :
334                                ugroup.delUserFromGroup(entry)
335                            else :
336                                ugroup.addUserToGroup(entry)
337                               
338                    percent = 100.0 * float(i) / float(nbtotal)
339                    self.display("\r%.02f%%" % percent)
340                               
341        if not options["list"] :               
342            self.display("\r100.00%%\r        \r%s\n" % _("Done."))
343                     
344if __name__ == "__main__" : 
345    retcode = 0
346    try :
347        defaults = { \
348                     "comment" : "", \
349                   }
350        short_options = "hvaD:dgl:rso:i:b:C:L"
351        long_options = ["help", "version", "add", "description=", \
352                        "delete", "groups", "list", "remove", \
353                        "skipexisting", "overcharge=", \
354                        "ingroups=", "limitby=", "balance=", "comment=", \
355                       ]
356                       
357       
358        # Initializes the command line tool
359        manager = PKUsers(doc=__doc__)
360        manager.deferredInit()
361       
362        # parse and checks the command line
363        (options, args) = manager.parseCommandline(sys.argv[1:], short_options, long_options)
364       
365        # sets long options
366        options["help"] = options["h"] or options["help"]
367        options["version"] = options["v"] or options["version"]
368        options["add"] = options["a"] or options["add"]
369        options["description"] = options["D"] or options["description"]
370        options["delete"] = options["d"] or options["delete"] 
371        options["groups"] = options["g"] or options["groups"]
372        options["list"] = options["L"] or options["list"]
373        options["remove"] = options["r"] or options["remove"]
374        options["skipexisting"] = options["s"] or options["skipexisting"]
375        options["limitby"] = options["l"] or options["limitby"]
376        options["balance"] = options["b"] or options["balance"] 
377        options["ingroups"] = options["i"] or options["ingroups"]
378        options["overcharge"] = options["o"] or options["overcharge"]
379        options["comment"] = options["C"] or options["comment"] or defaults["comment"]
380       
381        if options["help"] :
382            manager.display_usage_and_quit()
383        elif options["version"] :
384            manager.display_version_and_quit()
385        elif (options["delete"] and (options["add"] or options["remove"] or options["description"])) \
386           or (options["skipexisting"] and not options["add"]) \
387           or (options["list"] and (options["add"] or options["delete"] or options["remove"] or options["description"])) \
388           or (options["groups"] and (options["balance"] or options["ingroups"] or options["overcharge"])) :
389            raise PyKotaCommandLineError, _("incompatible options, see help.")
390        elif options["remove"] and not options["ingroups"] :   
391            raise PyKotaCommandLineError, _("You have to pass user groups names on the command line")
392        elif (not args) and options["add"] :
393            raise PyKotaCommandLineError, _("You have to pass user or group names on the command line")
394        else :
395            retcode = manager.main(args, options)
396    except KeyboardInterrupt :       
397        sys.stderr.write("\nInterrupted with Ctrl+C !\n")
398        retcode = -3
399    except PyKotaCommandLineError, msg :   
400        sys.stderr.write("%s : %s\n" % (sys.argv[0], msg))
401        retcode = -2
402    except SystemExit :       
403        pass
404    except :
405        try :
406            manager.crashed("pkusers failed")
407        except :   
408            crashed("pkusers failed")
409        retcode = -1
410
411    try :
412        manager.storage.close()
413    except (TypeError, NameError, AttributeError) :   
414        pass
415       
416    sys.exit(retcode)   
Note: See TracBrowser for help on using the browser.