From 63b23f25ef3af8bab0eef62a44378b042b123eed Mon Sep 17 00:00:00 2001 From: richard Date: Tue, 9 Oct 2001 07:25:59 +0000 Subject: [PATCH] Added the Password property type. See "pydoc roundup.password" for implementation details. Have updated some of the documentation too. git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@278 57a73879-2fb5-44c3-a270-3262357dd7e2 --- CHANGES.txt | 9 ++- doc/announcement.txt | 10 ++- doc/index.html | 40 +++++++--- roundup-admin | 47 +++++++----- roundup/backends/back_anydbm.py | 14 +++- roundup/backends/back_bsddb.py | 13 +++- roundup/backends/back_bsddb3.py | 13 +++- roundup/cgi_client.py | 12 ++- roundup/htmltemplate.py | 14 +++- roundup/hyperdb.py | 42 +++++++++- roundup/init.py | 9 ++- roundup/mailgw.py | 26 ++++++- roundup/password.py | 111 +++++++++++++++++++++++++++ roundup/templates/classic/dbinit.py | 11 ++- roundup/templates/extended/dbinit.py | 11 ++- test/test_db.py | 12 ++- test/test_schema.py | 10 ++- 17 files changed, 337 insertions(+), 67 deletions(-) create mode 100644 roundup/password.py diff --git a/CHANGES.txt b/CHANGES.txt index 1add3f6..b20e4ae 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -18,6 +18,13 @@ Changed: . The schemas have had their page headings modified to cope with the new login handling. Existing installations should copy the interfaces.py file from the roundup lib directory to their instance home. + . Passwords are now encoded by default (except exising databases which + will only be encoded when the passwords are changed). The scheme used + at the moment is SHA - but the code is flexible enough to take any + number of encoding systems. + . The roundup-admin tool always operates as the "admin" user now. Database + protection should be achieved using file system protections (see the + documentation for details.) Fixed: . Incorrectly had a Bizar Software copyright on the cgitb.py module from @@ -30,8 +37,8 @@ Fixed: . Fixed a deviation from the spec: trying to modify the 'id' property of an item now throws an exception. . The plain() template function now html-escapes the content. + . Change message was stuffing up for multilinks with no key property. --------------------- 2001-08-30 - 0.2.8 Fixed: diff --git a/doc/announcement.txt b/doc/announcement.txt index 4e04ced..cabdc4c 100644 --- a/doc/announcement.txt +++ b/doc/announcement.txt @@ -1,10 +1,12 @@ Roundup 0.3.0 - an issue tracking system -** note for existing users of extended schema +** existing users _must_ read the MIGRATION.txt that accompanies the +source. -This release includes several bug fixes and usability improvements. It -also switches the CGI interface authentication over from HTTP Basic to cookie -based. For a more detailed in the CHANGES file accompanying the source. +This release includes several bug fixes and usability improvements. It +switches the CGI interface authentication over from HTTP Basic to cookie +based. It introduces encoded password storage. For a more detailed in +the CHANGES file accompanying the source. Roundup is a simple-to-use and -install issue-tracking system with command-line, web and e-mail interfaces. It is based on the winning design diff --git a/doc/index.html b/doc/index.html index ea67985..bbf54e2 100644 --- a/doc/index.html +++ b/doc/index.html @@ -21,7 +21,7 @@
  • Command Line Tool
  • E-Mail Interface
  • Web Interface -
  • Users (Users and permissions, Adding users) +
  • Users and Access Control (Users and permissions, Adding users)
  • Issues
  • User Guide @@ -126,6 +126,14 @@ asked a series of questions:
  • Administration user "admin" password. +You should also think about whether there is going to be controlled access +to the instance on the machine the instance is running on. That is, who can +actually make changes to the database using the roundup-admin tool. See +the section on Users and Access Control for +information on how to secure your instance from the start. + +

    + Roundup is configurable using an instance_config.py file in the instance home. It should be edited before roundup is used, and may have the following variable declarations: @@ -227,9 +235,25 @@ roundup doesn't know who they are, they can't change anything. This has the following repurcussions:

    Command-line interface -
    The data modification commands (create, init, retire, set) are not -available without a login, and if one is not supplied on the command line -(-u user:pass) then it will be prompted for. +
    The data modification commands (create, init, retire, set) are +performed as the "admin" user. It is therefore important that the database +be protected by the filesystem if protection is required. On a Unix system, +the easiest and most flexible method of doing so is: +
      +
    1. Add a new user and group to your system (e.g. "issue_tracker") +
    2. When creating a new instance home, use the following commands +(substituting instance_home for the directory you want to use):
      +
      +mkdir instance_home
      +chown issue_tracker:issue_tracker instance_home
      +chmod g+rwxs instance_home
      +chmod o-rwx instance_home
      +roundup-admin -i instance_home init
      +
      +
    3. Now, edit the /etc/group line for issue_tracker so it includes the unix +logins of all the users who are going to administer your roundup instance. +
    +
    E-Mail interface
    Users are identified by e-mail address - a new user entry will be created for any e-mail address that is not recognised, so users are @@ -240,12 +264,6 @@ entry with the username "anonymous", then unidentified users are automatically logged in as that user. This gives them write access.

    -There has been only a half-hearted attempt to restrict certain activities -to the "admin" user. For example, the "extended" schema web interface enables -some fnuctionality for the "admin" user. On the fil-side, it is possible to -obtain the admin user's password using the read-only access on the command -line (it would also be possible to access the database files directly to -obtain this information).

    Adding users

    To add users, use one of the following interfaces: @@ -1085,7 +1103,7 @@ system on their time.

     


    -$Id: index.html,v 1.10 2001-10-08 21:49:30 richard Exp $ +$Id: index.html,v 1.11 2001-10-09 07:25:59 richard Exp $

     

    diff --git a/roundup-admin b/roundup-admin index 86bc4ae..85eee36 100755 --- a/roundup-admin +++ b/roundup-admin @@ -16,7 +16,7 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -# $Id: roundup-admin,v 1.21 2001-10-05 02:23:24 richard Exp $ +# $Id: roundup-admin,v 1.22 2001-10-09 07:25:59 richard Exp $ import sys if int(sys.version[0]) < 2: @@ -24,7 +24,7 @@ if int(sys.version[0]) < 2: sys.exit(1) import string, os, getpass, getopt, re -from roundup import date, roundupdb, init +from roundup import date, roundupdb, init, password import roundup.instance def usage(message=''): @@ -180,6 +180,8 @@ def do_set(db, args): type = properties[key] if isinstance(type, hyperdb.String): continue + elif isinstance(type, hyperdb.Password): + props[key] = password.Password(value) elif isinstance(type, hyperdb.Date): props[key] = date.Date(value) elif isinstance(type, hyperdb.Interval): @@ -379,11 +381,6 @@ def main(): return 0 if opt == '-i': instance_home = arg - if opt == '-u': - l = arg.split(':') - name = l[0] - if len(l) > 1: - password = l[1] if opt == '-c': comma_sep = 1 @@ -418,16 +415,6 @@ def main(): if command == 'init': return do_init(instance_home, args) - # open the database - if command in ('create', 'set', 'retire', 'freshen'): - while not name: - name = raw_input('Login name: ') - while not password: - password = getpass.getpass(' password: ') - - # get the instance - instance = roundup.instance.open(instance_home) - function = figureCommands().get(command, None) # not a valid command @@ -435,7 +422,11 @@ def main(): usage('Unknown command "%s"'%command) return 1 - db = instance.open(name or 'admin') + # get the instance + instance = roundup.instance.open(instance_home) + db = instance.open('admin') + + # do the command try: return function(db, args[1:]) finally: @@ -449,6 +440,26 @@ if __name__ == '__main__': # # $Log: not supported by cvs2svn $ +# Revision 1.21 2001/10/05 02:23:24 richard +# . roundup-admin create now prompts for property info if none is supplied +# on the command-line. +# . hyperdb Class getprops() method may now return only the mutable +# properties. +# . Login now uses cookies, which makes it a whole lot more flexible. We can +# now support anonymous user access (read-only, unless there's an +# "anonymous" user, in which case write access is permitted). Login +# handling has been moved into cgi_client.Client.main() +# . The "extended" schema is now the default in roundup init. +# . The schemas have had their page headings modified to cope with the new +# login handling. Existing installations should copy the interfaces.py +# file from the roundup lib directory to their instance home. +# . Incorrectly had a Bizar Software copyright on the cgitb.py module from +# Ping - has been removed. +# . Fixed a whole bunch of places in the CGI interface where we should have +# been returning Not Found instead of throwing an exception. +# . Fixed a deviation from the spec: trying to modify the 'id' property of +# an item now throws an exception. +# # Revision 1.20 2001/10/04 02:12:42 richard # Added nicer command-line item adding: passing no arguments will enter an # interactive more which asks for each property in turn. While I was at it, I diff --git a/roundup/backends/back_anydbm.py b/roundup/backends/back_anydbm.py index 9f31240..a7345cd 100644 --- a/roundup/backends/back_anydbm.py +++ b/roundup/backends/back_anydbm.py @@ -15,10 +15,10 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -#$Id: back_anydbm.py,v 1.8 2001-09-29 13:27:00 richard Exp $ +#$Id: back_anydbm.py,v 1.9 2001-10-09 07:25:59 richard Exp $ import anydbm, os, marshal -from roundup import hyperdb, date +from roundup import hyperdb, date, password # # Now the database @@ -104,6 +104,8 @@ class Database(hyperdb.Database): node[key] = node[key].get_tuple() elif isinstance(properties[key], hyperdb.Interval): node[key] = node[key].get_tuple() + elif isinstance(properties[key], hyperdb.Password): + node[key] = str(node[key]) # now save the marshalled data db[nodeid] = marshal.dumps(node) @@ -126,6 +128,10 @@ class Database(hyperdb.Database): res[key] = date.Date(res[key]) elif isinstance(properties[key], hyperdb.Interval): res[key] = date.Interval(res[key]) + elif isinstance(properties[key], hyperdb.Password): + p = password.Password() + p.unpack(res[key]) + res[key] = p if not cldb: db.close() return res @@ -220,6 +226,10 @@ class Database(hyperdb.Database): # #$Log: not supported by cvs2svn $ +#Revision 1.8 2001/09/29 13:27:00 richard +#CGI interfaces now spit up a top-level index of all the instances they can +#serve. +# #Revision 1.7 2001/08/12 06:32:36 richard #using isinstance(blah, Foo) now instead of isFooType # diff --git a/roundup/backends/back_bsddb.py b/roundup/backends/back_bsddb.py index 4562d29..3923fbd 100644 --- a/roundup/backends/back_bsddb.py +++ b/roundup/backends/back_bsddb.py @@ -15,10 +15,10 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -#$Id: back_bsddb.py,v 1.9 2001-08-12 06:32:36 richard Exp $ +#$Id: back_bsddb.py,v 1.10 2001-10-09 07:25:59 richard Exp $ import bsddb, os, marshal -from roundup import hyperdb, date +from roundup import hyperdb, date, password # # Now the database @@ -103,6 +103,8 @@ class Database(hyperdb.Database): node[key] = node[key].get_tuple() elif isinstance(properties[key], hyperdb.Interval): node[key] = node[key].get_tuple() + elif isinstance(properties[key], hyperdb.Password): + node[key] = str(node[key]) # now save the marshalled data db[nodeid] = marshal.dumps(node) @@ -124,6 +126,10 @@ class Database(hyperdb.Database): res[key] = date.Date(res[key]) elif isinstance(properties[key], hyperdb.Interval): res[key] = date.Interval(res[key]) + elif isinstance(properties[key], hyperdb.Password): + p = password.Password() + p.unpack(res[key]) + res[key] = p if not cldb: db.close() return res @@ -219,6 +225,9 @@ class Database(hyperdb.Database): # #$Log: not supported by cvs2svn $ +#Revision 1.9 2001/08/12 06:32:36 richard +#using isinstance(blah, Foo) now instead of isFooType +# #Revision 1.8 2001/08/07 00:24:42 richard #stupid typo # diff --git a/roundup/backends/back_bsddb3.py b/roundup/backends/back_bsddb3.py index 8c402e5..1fd0eaa 100644 --- a/roundup/backends/back_bsddb3.py +++ b/roundup/backends/back_bsddb3.py @@ -15,10 +15,10 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -#$Id: back_bsddb3.py,v 1.7 2001-08-12 06:32:36 richard Exp $ +#$Id: back_bsddb3.py,v 1.8 2001-10-09 07:25:59 richard Exp $ import bsddb3, os, marshal -from roundup import hyperdb, date +from roundup import hyperdb, date, password # # Now the database @@ -103,6 +103,8 @@ class Database(hyperdb.Database): node[key] = node[key].get_tuple() elif isinstance(properties[key], hyperdb.Interval): node[key] = node[key].get_tuple() + elif isinstance(properties[key], hyperdb.Password): + node[key] = str(node[key]) # now save the marshalled data db[nodeid] = marshal.dumps(node) @@ -124,6 +126,10 @@ class Database(hyperdb.Database): res[key] = date.Date(res[key]) elif isinstance(properties[key], hyperdb.Interval): res[key] = date.Interval(res[key]) + elif isinstance(properties[key], hyperdb.Password): + p = password.Password() + p.unpack(res[key]) + res[key] = p if not cldb: db.close() return res @@ -219,6 +225,9 @@ class Database(hyperdb.Database): # #$Log: not supported by cvs2svn $ +#Revision 1.7 2001/08/12 06:32:36 richard +#using isinstance(blah, Foo) now instead of isFooType +# #Revision 1.6 2001/08/07 00:24:42 richard #stupid typo # diff --git a/roundup/cgi_client.py b/roundup/cgi_client.py index a437a42..e8e6c0b 100644 --- a/roundup/cgi_client.py +++ b/roundup/cgi_client.py @@ -15,12 +15,12 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -# $Id: cgi_client.py,v 1.28 2001-10-08 00:34:31 richard Exp $ +# $Id: cgi_client.py,v 1.29 2001-10-09 07:25:59 richard Exp $ import os, cgi, pprint, StringIO, urlparse, re, traceback, mimetypes import base64, Cookie, time -import roundupdb, htmltemplate, date, hyperdb +import roundupdb, htmltemplate, date, hyperdb, password class Unauthorised(ValueError): pass @@ -503,7 +503,10 @@ class Client: return self.login(message='No such user "%s"'%name) # and that the password is correct + pw = self.db.user.get(uid, 'password') + print password, pw, `pw` if password != self.db.user.get(uid, 'password'): + self.make_user_anonymous() return self.login(message='Incorrect password') # construct the cookie @@ -659,6 +662,8 @@ def parsePropsFromForm(cl, form, nodeid=0): proptype = cl.properties[key] if isinstance(proptype, hyperdb.String): value = form[key].value.strip() + elif isinstance(proptype, hyperdb.Password): + value = password.Password(form[key].value.strip()) elif isinstance(proptype, hyperdb.Date): value = date.Date(form[key].value.strip()) elif isinstance(proptype, hyperdb.Interval): @@ -701,6 +706,9 @@ def parsePropsFromForm(cl, form, nodeid=0): # # $Log: not supported by cvs2svn $ +# Revision 1.28 2001/10/08 00:34:31 richard +# Change message was stuffing up for multilinks with no key property. +# # Revision 1.27 2001/10/05 02:23:24 richard # . roundup-admin create now prompts for property info if none is supplied # on the command-line. diff --git a/roundup/htmltemplate.py b/roundup/htmltemplate.py index c073a75..2bfa2b5 100644 --- a/roundup/htmltemplate.py +++ b/roundup/htmltemplate.py @@ -15,11 +15,11 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -# $Id: htmltemplate.py,v 1.24 2001-09-27 06:45:58 richard Exp $ +# $Id: htmltemplate.py,v 1.25 2001-10-09 07:25:59 richard Exp $ import os, re, StringIO, urllib, cgi, errno -import hyperdb, date +import hyperdb, date, password class Base: def __init__(self, db, templates, classname, nodeid=None, form=None, @@ -53,6 +53,9 @@ class Plain(Base): if isinstance(propclass, hyperdb.String): if value is None: value = '' else: value = str(value) + elif isinstance(propclass, hyperdb.Password): + if value is None: value = '' + else: value = '*encrypted*' elif isinstance(propclass, hyperdb.Date): value = str(value) elif isinstance(propclass, hyperdb.Interval): @@ -106,6 +109,9 @@ class Field(Base): value = cgi.escape(value) value = '"'.join(value.split('"')) s = ''%(property, value, size) + elif isinstance(propclass, hyperdb.Password): + size = size or 30 + s = ''%(property, size) elif isinstance(propclass, hyperdb.Link): linkcl = self.db.classes[propclass.classname] l = ['