Code

Added the Password property type. See "pydoc roundup.password" for
authorrichard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2>
Tue, 9 Oct 2001 07:25:59 +0000 (07:25 +0000)
committerrichard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2>
Tue, 9 Oct 2001 07:25:59 +0000 (07:25 +0000)
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

17 files changed:
CHANGES.txt
doc/announcement.txt
doc/index.html
roundup-admin
roundup/backends/back_anydbm.py
roundup/backends/back_bsddb.py
roundup/backends/back_bsddb3.py
roundup/cgi_client.py
roundup/htmltemplate.py
roundup/hyperdb.py
roundup/init.py
roundup/mailgw.py
roundup/password.py [new file with mode: 0644]
roundup/templates/classic/dbinit.py
roundup/templates/extended/dbinit.py
test/test_db.py
test/test_schema.py

index 1add3f6d9909a755d2206c0e9d1f032068eff124..b20e4ae0ef926cb75f922f4bdc6cd634640befa6 100644 (file)
@@ -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:
index 4e04ced8438eee97ee9315509781b58a63816c8f..cabdc4cb11110da52135f20566fbf17fa3052cd0 100644 (file)
@@ -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 
index ea67985af30de19e6a9ec2b7126408ba17e215f3..bbf54e2cc18e96433965de8e1fc66ac0a6635ca3 100644 (file)
@@ -21,7 +21,7 @@
         <li><a href="#startcmd">Command Line Tool</a>
         <li><a href="#startweb">E-Mail Interface</a>
         <li><a href="#startweb">Web Interface</a>
-        <li><a href="#users">Users</a> (Users and permissions, Adding users)
+        <li><a href="#users">Users and Access Control</a> (Users and permissions, Adding users)
         <li><a href="#issues">Issues</a>
     </ul>
 <li><a href="#guide">User Guide</a>
@@ -126,6 +126,14 @@ asked a series of questions:
     <li>Administration user "admin" password.
 </ol>
 
+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 <a href="#users">Users and Access Control</a> for
+information on how to secure your instance from the start.
+
+<p>
+
 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:
 <dl>
 <dt><strong>Command-line interface</strong>
-<dd>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.
+<dd>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:
+<ol>
+<li>Add a new user and group to your system (e.g. "issue_tracker")
+<li>When creating a new instance home, use the following commands
+(substituting instance_home for the directory you want to use):<br>
+<pre>
+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
+</pre>
+<li>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.
+</ol>
+
 <dt><strong>E-Mail interface</strong>
 <dd>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.
 </dl>
 <p>
-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).
 
 <h3>Adding users</h3>
 To add users, use one of the following interfaces:
@@ -1085,7 +1103,7 @@ system on their time.
 
 <p>&nbsp;</p>
 <hr>
-$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 $
 <p>&nbsp;</p>
 
 </body></html>
index 86bc4ae7441976703c121d78f222104a6edf2e93..85eee36243e2607bbeb6bd644c025b5d231506b0 100755 (executable)
@@ -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
index 9f3124081171957316d70e5a67caf0f5064bac45..a7345cd681aadf6ac0e882d65d83d8e8d0731171 100644 (file)
 # 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
 #
index 4562d29900f113a21bd5b03dd7f3dab511da570d..3923fbd66279368154718def6c94ce00a3c375bc 100644 (file)
 # 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
 #
index 8c402e5421041330389eb68e501759f61c45a356..1fd0eaae92af56a9d6081cab43208dca1986fb64 100644 (file)
 # 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
 #
index a437a42b6567f6557cff2c1a141fa4aa7b7f6a84..e8e6c0ba3f230ce3b32a09764acec9cb9011683e 100644 (file)
 # 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.
index c073a75e9ea357d6fe95227a98a775cc47dc9dcc..2bfa2b508ba72cabc3897658cda66f7d6d57ba83 100644 (file)
 # 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 = '&quot;'.join(value.split('"'))
             s = '<input name="%s" value="%s" size="%s">'%(property, value, size)
+        elif isinstance(propclass, hyperdb.Password):
+            size = size or 30
+            s = '<input type="password" name="%s" size="%s">'%(property, size)
         elif isinstance(propclass, hyperdb.Link):
             linkcl = self.db.classes[propclass.classname]
             l = ['<select name="%s">'%property]
@@ -747,6 +753,10 @@ def newitem(client, templates, db, classname, form, replace=re.compile(
 
 #
 # $Log: not supported by cvs2svn $
+# Revision 1.24  2001/09/27 06:45:58  richard
+# *gak* ... xmp is Old Skool apparently. Am using pre again by have the option
+# on the plain() template function to escape the text for HTML.
+#
 # Revision 1.23  2001/09/10 09:47:18  richard
 # Fixed bug in the generation of links to Link/Multilink in indexes.
 #   (thanks Hubert Hoegl)
index 32729f19d2e9d91fc662c7f6bd3ffddbcc2a7176..4a96b7bf0c8f2df998798cf419987c7a57065f3f 100644 (file)
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: hyperdb.py,v 1.21 2001-10-05 02:23:24 richard Exp $
+# $Id: hyperdb.py,v 1.22 2001-10-09 07:25:59 richard Exp $
 
 # standard python modules
 import cPickle, re, string
 
 # roundup modules
-import date
+import date, password
 
 
 #
@@ -32,6 +32,11 @@ class String:
     def __repr__(self):
         return '<%s>'%self.__class__
 
+class Password:
+    """An object designating a Password property."""
+    def __repr__(self):
+        return '<%s>'%self.__class__
+
 class Date:
     """An object designating a Date property."""
     def __repr__(self):
@@ -189,13 +194,17 @@ class Class:
                 if type(value) != type(''):
                     raise TypeError, 'new property "%s" not a string'%key
 
+            elif isinstance(prop, Password):
+                if not isinstance(value, password.Password):
+                    raise TypeError, 'new property "%s" not a Password'%key
+
             elif isinstance(prop, Date):
                 if not isinstance(value, date.Date):
-                    raise TypeError, 'new property "%s" not a Date'% key
+                    raise TypeError, 'new property "%s" not a Date'%key
 
             elif isinstance(prop, Interval):
                 if not isinstance(value, date.Interval):
-                    raise TypeError, 'new property "%s" not an Interval'% key
+                    raise TypeError, 'new property "%s" not an Interval'%key
 
         for key, prop in self.properties.items():
             if propvalues.has_key(key):
@@ -347,6 +356,10 @@ class Class:
                 if value is not None and type(value) != type(''):
                     raise TypeError, 'new property "%s" not a string'%key
 
+            elif isinstance(prop, Password):
+                if not isinstance(value, password.Password):
+                    raise TypeError, 'new property "%s" not a Password'% key
+
             elif isinstance(prop, Date):
                 if not isinstance(value, date.Date):
                     raise TypeError, 'new property "%s" not a Date'% key
@@ -400,6 +413,7 @@ class Class:
         None, or a TypeError is raised.  The values of the key property on
         all existing nodes must be unique or a ValueError is raised.
         """
+        # TODO: validate that the property is a String!
         self.key = propname
 
     def getkey(self):
@@ -800,6 +814,26 @@ def Choice(name, *options):
 
 #
 # $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
index b41561c4d1e4dd53e022d8e951230ea896751a29..f7de005fd8cbd3f714c408fe29839b26a274ed51 100644 (file)
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: init.py,v 1.15 2001-08-07 00:24:42 richard Exp $
+# $Id: init.py,v 1.16 2001-10-09 07:25:59 richard Exp $
 
 import os, shutil, sys, errno
 
-import roundup.instance
+import roundup.instance, password
 
 def copytree(src, dst, symlinks=0):
     """Recursively copy a directory tree using copy2().
@@ -99,10 +99,13 @@ from roundup.backends.back_%s import Database'''%backend
 
     # now import the instance and call its init
     instance = roundup.instance.open(instance_home)
-    instance.init(adminpw)
+    instance.init(password.Password(adminpw))
 
 #
 # $Log: not supported by cvs2svn $
+# Revision 1.15  2001/08/07 00:24:42  richard
+# stupid typo
+#
 # Revision 1.14  2001/08/07 00:15:51  richard
 # Added the copyright/license notice to (nearly) all files at request of
 # Bizar Software.
index 9ce05937f54bc4c881116f90f5650421715247de..fd66143d850ac125d34e7b684357b9bfd020987e 100644 (file)
@@ -72,13 +72,13 @@ are calling the create() method to create a new node). If an auditor raises
 an exception, the original message is bounced back to the sender with the
 explanatory message given in the exception. 
 
-$Id: mailgw.py,v 1.16 2001-10-05 02:23:24 richard Exp $
+$Id: mailgw.py,v 1.17 2001-10-09 07:25:59 richard Exp $
 '''
 
 
 import string, re, os, mimetools, cStringIO, smtplib, socket, binascii, quopri
 import traceback
-import hyperdb, date
+import hyperdb, date, password
 
 class MailUsageError(ValueError):
     pass
@@ -221,6 +221,8 @@ Subject was: "%s"
 '''%(key, subject)
                 if isinstance(type, hyperdb.String):
                     props[key] = value 
+                if isinstance(type, hyperdb.Password):
+                    props[key] = password.Password(value)
                 elif isinstance(type, hyperdb.Date):
                     props[key] = date.Date(value)
                 elif isinstance(type, hyperdb.Interval):
@@ -400,6 +402,26 @@ def parseContent(content, blank_line=re.compile(r'[\r\n]+\s*[\r\n]+'),
 
 #
 # $Log: not supported by cvs2svn $
+# Revision 1.16  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.15  2001/08/30 06:01:17  richard
 # Fixed missing import in mailgw :(
 #
diff --git a/roundup/password.py b/roundup/password.py
new file mode 100644 (file)
index 0000000..cc76c31
--- /dev/null
@@ -0,0 +1,111 @@
+#
+# Copyright (c) 2001 Bizar Software Pty Ltd (http://www.bizarsoftware.com.au/)
+# This module is free software, and you may redistribute it and/or modify
+# under the same terms as Python, so long as this copyright message and
+# disclaimer are retained in their original form.
+#
+# IN NO EVENT SHALL BIZAR SOFTWARE PTY LTD BE LIABLE TO ANY PARTY FOR
+# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING
+# OUT OF THE USE OF THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# BIZAR SOFTWARE PTY LTD SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE.  THE CODE PROVIDED HEREUNDER IS ON AN "AS IS"
+# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
+# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+# 
+# $Id: password.py,v 1.1 2001-10-09 07:25:59 richard Exp $
+
+import sha, re
+
+def encodePassword(plaintext, scheme):
+    '''Encrypt the plaintext password.
+    '''
+    if scheme == 'SHA':
+        s = sha.sha(plaintext).hexdigest()
+    elif scheme == 'plaintext':
+        pass
+    else:
+        raise ValueError, 'Unknown encryption scheme "%s"'%scheme
+    return s
+
+class Password:
+    '''The class encapsulates a Password property type value in the database. 
+
+    The encoding of the password is one if None, 'SHA' or 'plaintext'. The
+    encodePassword function is used to actually encode the password from
+    plaintext. The None encoding is used in legacy databases where no
+    encoding scheme is identified.
+
+    The scheme is stored with the encoded data in the database:
+        {scheme}data
+
+    Example usage:
+    >>> p = Password('sekrit')
+    >>> p == 'sekrit'
+    1
+    >>> p != 'not sekrit'
+    1
+    >>> 'sekrit' == p
+    1
+    >>> 'not sekrit' != p
+    1
+    '''
+
+    default_scheme = 'SHA'        # new encryptions use this scheme
+    pwre = re.compile(r'{(\w+)}(.+)')
+
+    def __init__(self, plaintext=None):
+        '''Call setPassword if plaintext is not None.'''
+        if plaintext is not None:
+            self.password = encodePassword(plaintext, self.default_scheme)
+            self.scheme = self.default_scheme
+        else:
+            self.password = None
+            self.scheme = self.default_scheme
+
+    def unpack(self, encrypted):
+        '''Set the password info from the scheme:<encryted info> string
+           (the inverse of __str__)
+        '''
+        m = self.pwre.match(encrypted)
+        if m:
+            self.scheme = m.group(1)
+            self.password = m.group(2)
+        else:
+            # currently plaintext - encrypt
+            self.password = encodePassword(plaintext, self.default_scheme)
+            self.scheme = self.default_scheme
+
+    def setPassword(self, plaintext):
+        '''Sets encrypts plaintext.'''
+        self.password = encodePassword(plaintext, self.scheme)
+
+    def __cmp__(self, plaintext):
+        '''Compare this password against the plaintext.'''
+        if self.password is None:
+            raise ValueError, 'Password not set'
+        return cmp(self.password, encodePassword(plaintext, self.scheme))
+
+    def __str__(self):
+        '''Stringify the encrypted password for database storage.'''
+        if self.password is None:
+            raise ValueError, 'Password not set'
+        return '{%s}%s'%(self.scheme, self.password)
+
+def test():
+    p = Password('sekrit')
+    assert p == 'sekrit'
+    assert p != 'not sekrit'
+    assert 'sekrit' == p
+    assert 'not sekrit' != p
+
+if __name__ == '__main__':
+    test()
+
+#
+# $Log: not supported by cvs2svn $
+#
+#
+# vim: set filetype=python ts=4 sw=4 et si
index b29fb40176c4353c121e3645a6d3abd2ca3538c5..45ee343bf99ef4c32921eabd0b82b08df9a25371 100644 (file)
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: dbinit.py,v 1.7 2001-08-07 00:24:43 richard Exp $
+# $Id: dbinit.py,v 1.8 2001-10-09 07:25:59 richard Exp $
 
 import os
 
 import instance_config
-from roundup import roundupdb, cgi_client, mailgw 
+from roundup import roundupdb
 import select_db
 
 from roundup.roundupdb import Class, FileClass
@@ -45,7 +45,7 @@ def open(name=None):
     ''' as from the roundupdb method openDB 
  
     ''' 
-    from roundup.hyperdb import String, Date, Link, Multilink
+    from roundup.hyperdb import String, Password, Date, Link, Multilink
 
     # open the database
     db = Database(instance_config.DATABASE, name)
@@ -64,7 +64,7 @@ def open(name=None):
     keyword.setkey("name")
 
     user = Class(db, "user", 
-                    username=String(),   password=String(),
+                    username=String(),   password=Password(),
                     address=String(),    realname=String(), 
                     phone=String(),      organisation=String())
     user.setkey("username")
@@ -125,6 +125,9 @@ def init(adminpw):
 
 #
 # $Log: not supported by cvs2svn $
+# Revision 1.7  2001/08/07 00:24:43  richard
+# stupid typo
+#
 # Revision 1.6  2001/08/07 00:15:51  richard
 # Added the copyright/license notice to (nearly) all files at request of
 # Bizar Software.
index 93389261b80d377ecc0013ae6caee6ad39eec661..8dcbb3e6407ecce0b93d928966552ff5c568b3ba 100644 (file)
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: dbinit.py,v 1.11 2001-08-07 00:24:43 richard Exp $
+# $Id: dbinit.py,v 1.12 2001-10-09 07:25:59 richard Exp $
 
 import os
 
 import instance_config
 from roundup import roundupdb
 import select_db
+
 from roundup.roundupdb import Class, FileClass
 
 class Database(roundupdb.Database, select_db.Database):
@@ -44,7 +45,7 @@ def open(name=None):
     ''' as from the roundupdb method openDB 
  
     ''' 
-    from roundup.hyperdb import String, Date, Link, Multilink
+    from roundup.hyperdb import String, Password, Date, Link, Multilink
 
     # open the database
     db = Database(instance_config.DATABASE, name)
@@ -60,9 +61,10 @@ def open(name=None):
 
     keywords = Class(db, "keyword", 
                     name=String())
+    keywords.setkey("name")
 
     user = Class(db, "user", 
-                    username=String(),   password=String(),
+                    username=String(),   password=Password(),
                     address=String(),    realname=String(), 
                     phone=String(),      organisation=String())
     user.setkey("username")
@@ -173,6 +175,9 @@ def init(adminpw):
 
 #
 # $Log: not supported by cvs2svn $
+# Revision 1.11  2001/08/07 00:24:43  richard
+# stupid typo
+#
 # Revision 1.10  2001/08/07 00:15:51  richard
 # Added the copyright/license notice to (nearly) all files at request of
 # Bizar Software.
index d5318b65500c600299c603b2a3f224e13a78cbf2..83da8f1e3acf6dae7db9fa074cee20dce6c7e735 100644 (file)
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: test_db.py,v 1.7 2001-08-29 06:23:59 richard Exp $ 
+# $Id: test_db.py,v 1.8 2001-10-09 07:25:59 richard Exp $ 
 
 import unittest, os, shutil
 
-from roundup.hyperdb import String, Link, Multilink, Date, Interval, Class, \
-    DatabaseError
+from roundup.hyperdb import String, Password, Link, Multilink, Date, \
+    Interval, Class, DatabaseError
 
 def setupSchema(db, create):
     status = Class(db, "status", name=String())
@@ -30,7 +30,7 @@ def setupSchema(db, create):
         status.create(name="in-progress")
         status.create(name="testing")
         status.create(name="resolved")
-    Class(db, "user", username=String(), password=String())
+    Class(db, "user", username=String(), password=Password())
     Class(db, "issue", title=String(), status=Link("status"),
         nosy=Multilink("user"))
 
@@ -242,6 +242,10 @@ def suite():
 
 #
 # $Log: not supported by cvs2svn $
+# Revision 1.7  2001/08/29 06:23:59  richard
+# Disabled the bsddb3 module entirely in the unit testing. See CHANGES for
+# details.
+#
 # Revision 1.6  2001/08/07 00:24:43  richard
 # stupid typo
 #
index ce670e51b175d49ad55ef310f66dd406f1017dd3..4367e1e0096d40381b98e67fdcae2671dd48f7a0 100644 (file)
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: test_schema.py,v 1.4 2001-08-07 00:24:43 richard Exp $ 
+# $Id: test_schema.py,v 1.5 2001-10-09 07:25:59 richard Exp $ 
 
 import unittest, os, shutil
 
 from roundup.backends import anydbm
-from roundup.hyperdb import String, Link, Multilink, Date, Interval, Class
+from roundup.hyperdb import String, Password, Link, Multilink, Date, \
+    Interval, Class
 
 class SchemaTestCase(unittest.TestCase):
     def setUp(self):
@@ -64,7 +65,7 @@ class SchemaTestCase(unittest.TestCase):
         self.assert_(issue, 'no class object returned')
 
     def testC_User(self):
-        user = Class(self.db, "user", username=String(), password=String())
+        user = Class(self.db, "user", username=String(), password=Password())
         self.assert_(user, 'no class object returned')
         user.setkey("username")
 
@@ -75,6 +76,9 @@ def suite():
 
 #
 # $Log: not supported by cvs2svn $
+# Revision 1.4  2001/08/07 00:24:43  richard
+# stupid typo
+#
 # Revision 1.3  2001/08/07 00:15:51  richard
 # Added the copyright/license notice to (nearly) all files at request of
 # Bizar Software.