Code

audit some user properties for valid values (roles, address) (sf bugs 742968 and...
authorrichard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2>
Tue, 24 Jun 2003 04:16:35 +0000 (04:16 +0000)
committerrichard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2>
Tue, 24 Jun 2003 04:16:35 +0000 (04:16 +0000)
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@1759 57a73879-2fb5-44c3-a270-3262357dd7e2

CHANGES.txt
roundup/cgi/client.py
templates/classic/detectors/userauditor.py [new file with mode: 0644]
templates/classic/html/issue.search.html
templates/classic/html/page.html
templates/minimal/detectors/userauditor.py [new file with mode: 0644]

index 4f501b1e66539cb7096c6527b40fb6a021c4f829..4a2fa65b6baff0e99de862e95caf9d91676671fd 100644 (file)
@@ -8,6 +8,8 @@ are given with the most recent entry first.
 - handle New User creation (sf bug 754510)
 - fix hackish message escaping (sf bug 757128)
 - fix :required ordering problem (sf bug 740214)
+- audit some user properties for valid values (roles, address) (sf bugs
+  742968 and 739653)
 
 
 2003-06-10 0.6.0b3
index 09f866d51286f0994370b07bb7b1caaa8cc957fb..3c7e9f09aa69b6d5f7b0498b678675cbe3ed1cd8 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: client.py,v 1.122 2003-06-24 03:58:57 richard Exp $
+# $Id: client.py,v 1.123 2003-06-24 04:16:35 richard Exp $
 
 __doc__ = """
 WWW request handler (also used in the stand-alone server).
@@ -1598,7 +1598,8 @@ You should then receive another email with the new password.
 
         # we'll store info about the individual class/item edit in these
         all_required = {}       # required props per class/item
-        all_props = {}          # props present per class/item
+        all_props = {}          # props to set per class/item
+        got_props = {}          # props received per class/item
         all_propdef = {}        # note - only one entry per class
         all_links = []          # as many as are required
 
@@ -1662,6 +1663,8 @@ You should then receive another email with the new password.
             if not all_props.has_key(this):
                 all_props[this] = {}
             props = all_props[this]
+            if not got_props.has_key(this):
+                got_props[this] = {}
 
             # is this a link command?
             if d['link']:
@@ -1866,6 +1869,10 @@ You should then receive another email with the new password.
                     raise ValueError, _('Error with %s property: %s')%(
                         propname, msg)
 
+            # register that we got this property
+            if value:
+                got_props[this][propname] = 1
+
             # get the old value
             if nodeid and not nodeid.startswith('-'):
                 try:
@@ -1914,9 +1921,9 @@ You should then receive another email with the new password.
         s = []
         for thing, required in all_required.items():
             # register the values we got
-            got = all_props.get(thing, {})
+            got = got_props.get(thing, {})
             for entry in required[:]:
-                if got.get(entry, ''):
+                if got.has_key(entry):
                     required.remove(entry)
 
             # any required values not present?
diff --git a/templates/classic/detectors/userauditor.py b/templates/classic/detectors/userauditor.py
new file mode 100644 (file)
index 0000000..6d40d45
--- /dev/null
@@ -0,0 +1,45 @@
+# Copyright (c) 2003 Richard Jones (richard@mechanicalcat.net)
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+#   The above copyright notice and this permission notice shall be included in
+#   all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+#$Id: userauditor.py,v 1.1 2003-06-24 04:16:35 richard Exp $
+
+def audit_user_fields(db, cl, nodeid, newvalues):
+    ''' Make sure user properties are valid.
+
+        - email address has no spaces in it
+        - roles specified exist
+    '''
+    if newvalues.has_key('address') and ' ' in newvalues['address']:
+        raise ValueError, 'Email address must not contain spaces'
+
+    if newvalues.has_key('roles'):
+        roles = [x.lower().strip() for x in newvalues['roles'].split(',')]
+        for rolename in roles:
+            if not db.security.role.has_key(rolename):
+                raise ValueError, 'Role "%s" does not exist'%rolename
+
+
+def init(db):
+    # fire before changes are made
+    db.user.audit('set', audit_user_fields)
+    db.user.audit('create', audit_user_fields)
+
+# vim: set filetype=python ts=4 sw=4 et si
+#SHA: d4aea7465d4b7ca78de71ed9e73e09ce29b1b111
index 4f9112d9bbc1911dff74d05398e58af60f997be6..24563be1a825e330648f9ce2d645a849aada3514 100644 (file)
@@ -9,10 +9,15 @@
 <input type="hidden" name=":action" value="search">
 
 <table class="form" tal:define="
-   cols python:'id activity priority title status assignedto'.split();
-   defsort python:['activity'];
-   defgroup python:['priority'];
-   defdisp python:'id activity title status assignedto'.split()">
+   cols python:request.columns or 'id activity title status assignedto'.split();
+   sort_on python:request.sort[1] or 'activity';
+   group_on python:request.group[1] or 'priority';
+
+   search_input templates/page/macros/search_input;
+   column_input templates/page/macros/column_input;
+   sort_input templates/page/macros/sort_input;
+   group_input templates/page/macros/group_input;
+   search_select templates/page/macros/search_select;">
 
 <tr>
  <th class="header">&nbsp;</th>
  <th class="header">Group on</th>
 </tr>
 
-<tr>
- <th>All text*:</th>
- <td><input name=":search_text"
-            tal:attributes="value request/form/:search_text/value | nothing">
- </td>
- <td>&nbsp;</td>
- <td>&nbsp;</td>
- <td>&nbsp;</td>
-</tr>
-
-<tr>
- <th>Title:</th>
- <td><input name="title"></td>
- <td><input type="checkbox" name=":columns" value="title" checked></td>
- <td><input type="radio" name=":sort" value="title"></td>
- <td>&nbsp;</td>
-</tr>
-
-<tr>
- <th>Topic:</th>
- <td>
-  <select name="topic">
-   <option value="">don't care</option>
-   <option value="">------------</option>
-   <option tal:repeat="s db/keyword/list" tal:attributes="value s/name"
-           tal:content="s/name">topic to filter on</option>
-  </select>
- </td>
- <td><input type="checkbox" name=":columns" value="topic" checked></td>
- <td><input type="radio" name=":sort" value="topic"></td>
- <td><input type="radio" name=":group" value="topic"></td>
-</tr>
-
-<tr>
- <th>ID:</th>
- <td><input name="id"></td>
- <td><input type="checkbox" name=":columns" value="id" checked></td>
- <td><input type="radio" name=":sort" value="id"></td>
- <td>&nbsp;</td>
-</tr>
-
-<tr>
- <th>Creation date:</th>
- <td><input name="creation"></td>
- <td><input type="checkbox" name=":columns" value="creation"></td>
- <td><input type="radio" name=":sort" value="creation"></td>
- <td><input type="radio" name=":group" value="creation"></td>
-</tr>
-
-<tr>
- <th>Creator:</th>
- <td>
-  <select name="creator">
-   <option value="">don't care</option>
-   <option tal:attributes="value request/user/id">created by me</option>
-   <option value="-1">------------</option>
-   <option tal:repeat="s db/user/list" tal:attributes="value s/id"
-     tal:content="s/username">user to filter on</option>
-  </select>
- </td>
- <td><input type="checkbox" name=":columns" value="creator" checked></td>
- <td><input type="radio" name=":sort" value="creator"></td>
- <td><input type="radio" name=":group" value="creator"></td>
-</tr>
-
-<tr>
- <th>Activity:</th>
- <td><input name="activity"></td>
- <td><input type="checkbox" name=":columns" value="activity" checked></td>
- <td><input type="radio" name=":sort" value="activity"></td>
- <td>&nbsp;</td>
-</tr>
-
-<tr>
- <th>Priority:</th>
- <td>
-  <select name="priority">
-   <option value="">don't care</option>
-   <option value="-1">not selected</option>
-   <option value="">------------</option>
-   <option tal:repeat="s db/priority/list" tal:attributes="value s/id"
-     tal:content="s/name">priority to filter on</option>
-  </select>
- </td>
- <td><input type="checkbox" name=":columns" value="priority"></td>
- <td><input type="radio" name=":sort" value="priority"></td>
- <td><input type="radio" name=":group" value="priority"></td>
-</tr>
-
-<tr>
- <th>Status:</th>
- <td>
-  <select name="status">
-   <option value="">don't care</option>
-   <option value="-1,1,2,3,4,5,6,7">not resolved</option>
-   <option value="-1">not selected</option>
-   <option value="">------------</option>
-   <option tal:repeat="s db/status/list" tal:attributes="value s/id"
-           tal:content="s/name">status to filter on</option>
-  </select>
- </td>
- <td><input type="checkbox" name=":columns" value="status" checked></td>
- <td><input type="radio" name=":sort" value="status"></td>
- <td><input type="radio" name=":group" value="status"></td>
-</tr>
-
-<tr>
- <th>Assigned To:</th>
- <td>
-  <select name="assignedto">
-   <option value="">don't care</option>
-   <option tal:attributes="value request/user/id">assigned to me</option>
-   <option value="-1">unassigned</option>
-   <option value="">------------</option>
-   <option tal:repeat="s db/user/list" tal:attributes="value s/id"
-     tal:content="s/username">user to filter on</option>
-  </select>
- </td>
- <td><input type="checkbox" name=":columns" value="assignedto" checked></td>
- <td><input type="radio" name=":sort" value="assignedto"></td>
- <td><input type="radio" name=":group" value="assignedto"></td>
+<tr tal:define="name string::search_text">
+  <th>All text*:</th>
+  <td metal:use-macro="search_input"></td>
+  <td>&nbsp;</td>
+  <td>&nbsp;</td>
+  <td>&nbsp;</td>
+</tr>
+
+<tr tal:define="name string:title">
+  <th>Title:</th>
+  <td metal:use-macro="search_input"></td>
+  <td metal:use-macro="column_input"></td>
+  <td metal:use-macro="sort_input"></td>
+  <td>&nbsp;</td>
+</tr>
+
+<tr tal:define="name string:topic;
+                db_klass string:keyword;
+                db_content string:name;">
+  <th>Topic:</th>
+  <td metal:use-macro="search_select"></td>
+  <td metal:use-macro="column_input"></td>
+  <td metal:use-macro="sort_input"></td>
+  <td metal:use-macro="group_input"></td>
+</tr>
+
+<tr tal:define="name string:id">
+  <th>ID:</th>
+  <td metal:use-macro="search_input"></td>
+  <td metal:use-macro="column_input"></td>
+  <td metal:use-macro="sort_input"></td>
+  <td>&nbsp;</td>
+</tr>
+
+<tr tal:define="name string:creation">
+  <th>Creation Date:</th>
+  <td metal:use-macro="search_input"></td>
+  <td metal:use-macro="column_input"></td>
+  <td metal:use-macro="sort_input"></td>
+  <td metal:use-macro="group_input"></td>
+</tr>
+
+<tr tal:define="name string:creator;
+                db_klass string:user;
+                db_content string:username;">
+  <th>Creator:</th>
+  <td metal:use-macro="search_select">
+    <option metal:fill-slot="extra_options"
+            tal:attributes="value request/user/id">created by me</option>
+  </td>
+  <td metal:use-macro="column_input"></td>
+  <td metal:use-macro="sort_input"></td>
+  <td metal:use-macro="group_input"></td>
+</tr>
+
+<tr tal:define="name string:activity">
+  <th>Activity:</th>
+  <td metal:use-macro="search_input"></td>
+  <td metal:use-macro="column_input"></td>
+  <td metal:use-macro="sort_input"></td>
+  <td>&nbsp;</td>
+</tr>
+
+<tr tal:define="name string:priority;
+                db_klass string:priority;
+                db_content string:name;">
+  <th>Priority:</th>
+  <td metal:use-macro="search_select">
+    <option metal:fill-slot="extra_options" value="-1"
+            tal:attributes="selected python:value == '-1'">not selected</option>
+  </td>
+  <td metal:use-macro="column_input"></td>
+  <td metal:use-macro="sort_input"></td>
+  <td metal:use-macro="group_input"></td>
+</tr>
+
+<tr tal:define="name string:status;
+                db_klass string:status;
+                db_content string:name;">
+  <th>Status:</th>
+  <td metal:use-macro="search_select">
+    <tal:block metal:fill-slot="extra_options">
+      <option value="-1,1,2,3,4,5,6,7"
+              tal:attributes="selected python:value == '-1,1,2,3,4,5,6,7'">not resolved</option>
+      <option value="-1"
+              tal:attributes="selected python:value == '-1'">not selected</option>
+    </tal:block>    
+  </td>
+  <td metal:use-macro="column_input"></td>
+  <td metal:use-macro="sort_input"></td>
+  <td metal:use-macro="group_input"></td>
+</tr>
+
+<tr tal:define="name string:assignedto;
+                db_klass string:user;
+                db_content string:username;">
+  <th>Assigned to:</th>
+  <td metal:use-macro="search_select">
+    <tal:block metal:fill-slot="extra_options">
+      <option tal:attributes="value request/user/id">assigned to me</option>
+      <option value="-1" tal:attributes="selected python:value == '-1'">unassigned</option>
+    </tal:block>
+  </td>
+  <td metal:use-macro="column_input"></td>
+  <td metal:use-macro="sort_input"></td>
+  <td metal:use-macro="group_input"></td>
 </tr>
 
 <tr>
 <th>Pagesize:</th>
-<td><input type="text" name=":pagesize" size="3" value="50"></td>
+<td><input name=":pagesize" size="3" value="50"
+           tal:attributes="value request/form/:pagesize/value | default"></td>
 </tr>
 
 <tr>
 <th>Start With:</th>
-<td><input type="text" name=":startwith" size="3" value="0"></td>
+<td><input name=":startwith" size="3" value="0"
+           tal:attributes="value request/form/:startwith/value | default"></td>
 </tr>
 
 <tr>
 <th>Sort Descending:</th>
-<td><input type="checkbox" name=":sortdir" checked>
+<td><input type="checkbox" name=":sortdir"
+           tal:attributes="checked python:request.sort[0] == '-' or request.sort[0] is None">
 </td>
+</tr>
 
 <tr>
 <th>Group Descending:</th>
-<td><input type="checkbox" name=":groupdir">
+<td><input type="checkbox" name=":groupdir"
+           tal:attributes="checked python:request.group[0] == '-'">
 </td>
 </tr>
 
 <tr>
 <th>Query name**:</th>
 <td><input name=":queryname"
-            tal:attributes="value request/form/:queryname/value | nothing">
-</td>
-</tr>
-
-<tr><td>&nbsp;</td>
-<td><input type="submit" value="Search"></td>
+           tal:attributes="value request/form/:queryname/value | default"></td>
 </tr>
 
 <tr><td>&nbsp;</td>
index 64b0116fa18d9e36f90679e6b4339ae332954a48..ee9a5038cae789bb07ddddbcad382d3e4f8cae78 100644 (file)
 
 </body>
 </html>
+
+<td metal:define-macro="search_input">
+  <input tal:attributes="value python:request.form.getvalue(name) or nothing;
+                         name name">
+</td>
+
+<td metal:define-macro="search_select">
+  <select tal:attributes="name name"
+          tal:define="value python:request.form.getvalue(name)">
+    <option value="">don't care</option>
+    <tal:block metal:define-slot="extra_options"></tal:block>
+    <option value="">------------</option>      
+    <option tal:repeat="s python:db[db_klass].list()"
+            tal:attributes="value s/id; selected python:value == s.id"
+            tal:content="python:s[db_content]"></option>
+  </select>
+</td>
+
+<td metal:define-macro="column_input">
+  <input type="checkbox" name=":columns"
+         tal:attributes="value name;
+                         checked python:name in cols">
+</td>
+
+<td metal:define-macro="sort_input">
+  <input type="radio" name=":sort"
+         tal:attributes="value name;
+                         checked python:name == sort_on">
+</td>
+
+<td metal:define-macro="group_input">
+  <input type="radio" name=":group"
+         tal:attributes="value name;
+                         checked python:name == group_on">
diff --git a/templates/minimal/detectors/userauditor.py b/templates/minimal/detectors/userauditor.py
new file mode 100644 (file)
index 0000000..6d40d45
--- /dev/null
@@ -0,0 +1,45 @@
+# Copyright (c) 2003 Richard Jones (richard@mechanicalcat.net)
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+#   The above copyright notice and this permission notice shall be included in
+#   all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+#$Id: userauditor.py,v 1.1 2003-06-24 04:16:35 richard Exp $
+
+def audit_user_fields(db, cl, nodeid, newvalues):
+    ''' Make sure user properties are valid.
+
+        - email address has no spaces in it
+        - roles specified exist
+    '''
+    if newvalues.has_key('address') and ' ' in newvalues['address']:
+        raise ValueError, 'Email address must not contain spaces'
+
+    if newvalues.has_key('roles'):
+        roles = [x.lower().strip() for x in newvalues['roles'].split(',')]
+        for rolename in roles:
+            if not db.security.role.has_key(rolename):
+                raise ValueError, 'Role "%s" does not exist'%rolename
+
+
+def init(db):
+    # fire before changes are made
+    db.user.audit('set', audit_user_fields)
+    db.user.audit('create', audit_user_fields)
+
+# vim: set filetype=python ts=4 sw=4 et si
+#SHA: d4aea7465d4b7ca78de71ed9e73e09ce29b1b111