comparison doc/design.txt @ 910:299f4890427d

documentation reorg post-new-security
author Richard Jones <richard@users.sourceforge.net>
date Mon, 29 Jul 2002 23:30:14 +0000
parents 38a74d1351c5
children 42924a2fcacf
comparison
equal deleted inserted replaced
909:ef9c759c243e 910:299f4890427d
248 operation. 248 operation.
249 249
250 Hyperdb Interface Specification 250 Hyperdb Interface Specification
251 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 251 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
252 252
253 TODO: replace the Interface Specifications with links to the pydoc
254
253 The hyperdb module provides property objects to designate 255 The hyperdb module provides property objects to designate
254 the different kinds of properties. These objects are used when 256 the different kinds of properties. These objects are used when
255 specifying what properties belong in classes:: 257 specifying what properties belong in classes::
256 258
257 class String: 259 class String:
292 Here is the interface provided by the hyperdatabase:: 294 Here is the interface provided by the hyperdatabase::
293 295
294 class Database: 296 class Database:
295 """A database for storing records containing flexible data types.""" 297 """A database for storing records containing flexible data types."""
296 298
297 def __init__(self, storagelocator, journaltag): 299 def __init__(self, config, journaltag=None):
298 """Open a hyperdatabase given a specifier to some storage. 300 """Open a hyperdatabase given a specifier to some storage.
299 301
302 The 'storagelocator' is obtained from config.DATABASE.
300 The meaning of 'storagelocator' depends on the particular 303 The meaning of 'storagelocator' depends on the particular
301 implementation of the hyperdatabase. It could be a file name, 304 implementation of the hyperdatabase. It could be a file name,
302 a directory path, a socket descriptor for a connection to a 305 a directory path, a socket descriptor for a connection to a
303 database over the network, etc. 306 database over the network, etc.
304 307
416 the nodes in this class, the matching node's id is returned; 419 the nodes in this class, the matching node's id is returned;
417 otherwise a KeyError is raised. 420 otherwise a KeyError is raised.
418 """ 421 """
419 422
420 def find(self, propname, nodeid): 423 def find(self, propname, nodeid):
421 """Get the ids of nodes in this class which link to a given node. 424 """Get the ids of nodes in this class which link to the given nodes.
422 425
426 'propspec' consists of keyword args propname={nodeid:1,}
423 'propname' must be the name of a property in this class, or a 427 'propname' must be the name of a property in this class, or a
424 KeyError is raised. That property must be a Link or Multilink 428 KeyError is raised. That property must be a Link or Multilink
425 property, or a TypeError is raised. 'nodeid' must be the id of 429 property, or a TypeError is raised.
426 an existing node in the class linked to by the given property, 430
427 or an IndexError is raised. 431 Any node in this class whose 'propname' property links to any of the
428 """ 432 nodeids will be returned. Used by the full text indexing, which
433 knows that "foo" occurs in msg1, msg3 and file7, so we have hits
434 on these issues:
435
436 db.issue.find(messages={'1':1,'3':1}, files={'7':1})
437 """
438
439 def filter(self, search_matches, filterspec, sort, group):
440 ''' Return a list of the ids of the active nodes in this class that
441 match the 'filter' spec, sorted by the group spec and then the
442 sort spec.
443 '''
429 444
430 def list(self): 445 def list(self):
431 """Return a list of the ids of the active nodes in this class.""" 446 """Return a list of the ids of the active nodes in this class."""
432 447
433 def count(self): 448 def count(self):
450 objects, or a TypeError is raised. None of the keys in 'properties' 465 objects, or a TypeError is raised. None of the keys in 'properties'
451 may collide with the names of existing properties, or a ValueError 466 may collide with the names of existing properties, or a ValueError
452 is raised before any properties have been added. 467 is raised before any properties have been added.
453 """ 468 """
454 469
455 TODO: additional methods 470 def getnode(self, nodeid, cache=1):
471 ''' Return a Node convenience wrapper for the node.
472
473 'nodeid' must be the id of an existing node of this class or an
474 IndexError is raised.
475
476 'cache' indicates whether the transaction cache should be queried
477 for the node. If the node has been modified and you need to
478 determine what its values prior to modification are, you need to
479 set cache=0.
480 '''
481
482 class Node:
483 ''' A convenience wrapper for the given node. It provides a mapping
484 interface to a single node's properties
485 '''
456 486
457 Hyperdatabase Implementations 487 Hyperdatabase Implementations
458 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 488 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
459 489
460 Hyperdatabase implementations exist to create the interface described in the 490 Hyperdatabase implementations exist to create the interface described in the
461 `hyperdb interface specification`_ 491 `hyperdb interface specification`_
462 over an existing storage mechanism. Examples are relational databases, 492 over an existing storage mechanism. Examples are relational databases,
463 \*dbm key-value databases, and so on. 493 \*dbm key-value databases, and so on.
464 494
465 TODO: finish 495 Several implementations are provided - they belong in the roundup.backends
496 package.
466 497
467 498
468 Application Example 499 Application Example
469 ~~~~~~~~~~~~~~~~~~~ 500 ~~~~~~~~~~~~~~~~~~~
470 501
548 Some of the classes in the Roundup database are considered 579 Some of the classes in the Roundup database are considered
549 issue classes. 580 issue classes.
550 The Roundup database layer adds detectors and user nodes, 581 The Roundup database layer adds detectors and user nodes,
551 and on issues it provides mail spools, nosy lists, and superseders. 582 and on issues it provides mail spools, nosy lists, and superseders.
552 583
553 TODO: where functionality is implemented.
554
555 Reserved Classes 584 Reserved Classes
556 ~~~~~~~~~~~~~~~~ 585 ~~~~~~~~~~~~~~~~
557 586
558 Internal to this layer we reserve three special classes 587 Internal to this layer we reserve three special classes
559 of nodes that are not issues. 588 of nodes that are not issues.
560 589
561 Users 590 Users
562 """""""""""" 591 """""
563 592
564 Users are stored in the hyperdatabase as nodes of 593 Users are stored in the hyperdatabase as nodes of
565 class "user". The "user" class has the definition:: 594 class "user". The "user" class has the definition::
566 595
567 hyperdb.Class(db, "user", username=hyperdb.String(), 596 hyperdb.Class(db, "user", username=hyperdb.String(),
568 password=hyperdb.String(), 597 password=hyperdb.String(),
569 address=hyperdb.String()) 598 address=hyperdb.String())
570 db.user.setkey("username") 599 db.user.setkey("username")
571 600
572 Messages 601 Messages
573 """"""""""""""" 602 """"""""
574 603
575 E-mail messages are represented by hyperdatabase nodes of class "msg". 604 E-mail messages are represented by hyperdatabase nodes of class "msg".
576 The actual text content of the messages is stored in separate files. 605 The actual text content of the messages is stored in separate files.
577 (There's no advantage to be gained by stuffing them into the 606 (There's no advantage to be gained by stuffing them into the
578 hyperdatabase, and if messages are stored in ordinary text files, 607 hyperdatabase, and if messages are stored in ordinary text files,
593 that are stored in the system). 622 that are stored in the system).
594 The "summary" property contains a summary of the message for display 623 The "summary" property contains a summary of the message for display
595 in a message index. 624 in a message index.
596 625
597 Files 626 Files
598 """""""""""" 627 """""
599 628
600 Submitted files are represented by hyperdatabase 629 Submitted files are represented by hyperdatabase
601 nodes of class "file". Like e-mail messages, the file content 630 nodes of class "file". Like e-mail messages, the file content
602 is stored in files outside the database, 631 is stored in files outside the database,
603 named after the file node designator (e.g. "file17"). 632 named after the file node designator (e.g. "file17").
729 758
730 Class(db, "issue", fixer=hyperdb.Multilink("user"), 759 Class(db, "issue", fixer=hyperdb.Multilink("user"),
731 topic=hyperdb.Multilink("keyword"), 760 topic=hyperdb.Multilink("keyword"),
732 priority=hyperdb.Link("priority"), 761 priority=hyperdb.Link("priority"),
733 status=hyperdb.Link("status")) 762 status=hyperdb.Link("status"))
734
735 763
736 (The "order" property hasn't been explained yet. It 764 (The "order" property hasn't been explained yet. It
737 gets used by the Web user interface for sorting.) 765 gets used by the Web user interface for sorting.)
738 766
739 The above isn't as pretty-looking as the schema specification 767 The above isn't as pretty-looking as the schema specification
913 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 941 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
914 942
915 A single command, roundup, provides basic access to 943 A single command, roundup, provides basic access to
916 the hyperdatabase from the command line:: 944 the hyperdatabase from the command line::
917 945
918 roundup get [-list] designator[, designator,...] propname 946 roundup-admin help
919 roundup set designator[, designator,...] propname=value ... 947 roundup-admin get [-list] designator[, designator,...] propname
920 roundup find [-list] classname propname=value ... 948 roundup-admin set designator[, designator,...] propname=value ...
921 949 roundup-admin find [-list] classname propname=value ...
922 TODO: more stuff here 950
951 See ``roundup-admin help commands`` for a complete list of commands.
923 952
924 Property values are represented as strings in command arguments 953 Property values are represented as strings in command arguments
925 and in the printed results: 954 and in the printed results:
926 955
927 - Strings are, well, strings. 956 - Strings are, well, strings.
1161 property using links that allow you to download files 1190 property using links that allow you to download files
1162 checklist for a Link or Multilink property, 1191 checklist for a Link or Multilink property,
1163 display checkboxes for the available choices to permit filtering 1192 display checkboxes for the available choices to permit filtering
1164 ========= ==================================================================== 1193 ========= ====================================================================
1165 1194
1195 TODO: See the htmltemplate pydoc for a complete list of the functions
1196
1166 1197
1167 Index Views 1198 Index Views
1168 ~~~~~~~~~~~ 1199 ~~~~~~~~~~~
1169 1200
1170 An index view contains two sections: a filter section 1201 An index view contains two sections: a filter section
1179 An index view specifier looks like this (whitespace 1210 An index view specifier looks like this (whitespace
1180 has been added for clarity):: 1211 has been added for clarity)::
1181 1212
1182 /issue?status=unread,in-progress,resolved&amp; 1213 /issue?status=unread,in-progress,resolved&amp;
1183 topic=security,ui&amp; 1214 topic=security,ui&amp;
1184 :group=+priority&amp; 1215 :group=priority&amp;
1185 :sort=-activity&amp; 1216 :sort=-activity&amp;
1186 :filters=status,topic&amp; 1217 :filters=status,topic&amp;
1187 :columns=title,status,fixer 1218 :columns=title,status,fixer
1188 1219
1189 1220
1272 <td><display call="plain('fixer')"></td> 1303 <td><display call="plain('fixer')"></td>
1273 </property> 1304 </property>
1274 </tr> 1305 </tr>
1275 1306
1276 Sorting 1307 Sorting
1277 """""""""""""" 1308 """""""
1278 1309
1279 String and Date values are sorted in the natural way. 1310 String and Date values are sorted in the natural way.
1280 Link properties are sorted according to the value of the 1311 Link properties are sorted according to the value of the
1281 "order" property on the linked nodes if it is present; or 1312 "order" property on the linked nodes if it is present; or
1282 otherwise on the key string of the linked nodes; or 1313 otherwise on the key string of the linked nodes; or
1363 The spool section lists messages in the issue's "messages" 1394 The spool section lists messages in the issue's "messages"
1364 property. The index of messages displays the "date", "author", 1395 property. The index of messages displays the "date", "author",
1365 and "summary" properties on the message nodes, and selecting a 1396 and "summary" properties on the message nodes, and selecting a
1366 message takes you to its content. 1397 message takes you to its content.
1367 1398
1399 Access Control
1400 --------------
1401
1402 At each point that requires an action to be performed, the security mechanisms
1403 are asked if the current user has permission. This permission is defined as a
1404 Permission.
1405
1406 Individual assignment of Permission to user is unwieldy. The concept of a
1407 Role, which encompasses several Permissions and may be assigned to many Users,
1408 is quite well developed in many projects. Roundup will take this path, and
1409 allow the multiple assignment of Roles to Users, and multiple Permissions to
1410 Roles. These definitions are not persistent - they're defined when the
1411 application initialises.
1412
1413 There will be two levels of Permission. The Class level permissions define
1414 logical permissions associated with all nodes of a particular class (or all
1415 classes). The Node level permissions define logical permissions associated
1416 with specific nodes by way of their user-linked properties.
1417
1418
1419 Access Control Interface Specification
1420 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1421
1422 The security module defines::
1423
1424 class Permission:
1425 ''' Defines a Permission with the attributes
1426 - name
1427 - description
1428 - klass (optional)
1429
1430 The klass may be unset, indicating that this permission is not
1431 locked to a particular hyperdb class. There may be multiple
1432 Permissions for the same name for different classes.
1433 '''
1434
1435 class Role:
1436 ''' Defines a Role with the attributes
1437 - name
1438 - description
1439 - permissions
1440 '''
1441
1442 class Security:
1443 def __init__(self, db):
1444 ''' Initialise the permission and role stores, and add in the
1445 base roles (for admin user).
1446 '''
1447
1448 def getPermission(self, permission, classname=None):
1449 ''' Find the Permission matching the name and for the class, if the
1450 classname is specified.
1451
1452 Raise ValueError if there is no exact match.
1453 '''
1454
1455 def hasPermission(self, permission, userid, classname=None):
1456 ''' Look through all the Roles, and hence Permissions, and see if
1457 "permission" is there for the specified classname.
1458 '''
1459
1460 def hasNodePermission(self, classname, nodeid, **propspec):
1461 ''' Check the named properties of the given node to see if the
1462 userid appears in them. If it does, then the user is granted
1463 this permission check.
1464
1465 'propspec' consists of a set of properties and values that
1466 must be present on the given node for access to be granted.
1467
1468 If a property is a Link, the value must match the property
1469 value. If a property is a Multilink, the value must appear
1470 in the Multilink list.
1471 '''
1472
1473 def addPermission(self, **propspec):
1474 ''' Create a new Permission with the properties defined in
1475 'propspec'
1476 '''
1477
1478 def addRole(self, **propspec):
1479 ''' Create a new Role with the properties defined in 'propspec'
1480 '''
1481
1482 def addPermissionToRole(self, rolename, permission):
1483 ''' Add the permission to the role's permission list.
1484
1485 'rolename' is the name of the role to add permission to.
1486 '''
1487
1488 Modules such as ``cgi_client.py`` and ``mailgw.py`` define their own
1489 permissions like so (this example is ``cgi_client.py``)::
1490
1491 def initialiseSecurity(security):
1492 ''' Create some Permissions and Roles on the security object
1493
1494 This function is directly invoked by security.Security.__init__()
1495 as a part of the Security object instantiation.
1496 '''
1497 p = security.addPermission(name="Web Registration",
1498 description="Anonymous users may register through the web")
1499 security.addToRole('Anonymous', p)
1500
1501 Detectors may also define roles in their init() function::
1502
1503 def init(db):
1504 # register an auditor that checks that a user has the "May Resolve"
1505 # Permission before allowing them to set an issue status to "resolved"
1506 db.issue.audit('set', checkresolvedok)
1507 p = db.security.addPermission(name="May Resolve", klass="issue")
1508 security.addToRole('Manager', p)
1509
1510 The instance dbinit module then has in ``open()``::
1511
1512 # open the database - it must be modified to init the Security class
1513 # from security.py as db.security
1514 db = Database(instance_config, name)
1515
1516 # add some extra permissions and associate them with roles
1517 ei = db.security.addPermission(name="Edit", klass="issue",
1518 description="User is allowed to edit issues")
1519 db.security.addPermissionToRole('User', ei)
1520 ai = db.security.addPermission(name="View", klass="issue",
1521 description="User is allowed to access issues")
1522 db.security.addPermissionToRole('User', ai)
1523
1524 In the dbinit ``init()``::
1525
1526 # create the two default users
1527 user.create(username="admin", password=Password(adminpw),
1528 address=instance_config.ADMIN_EMAIL, roles='Admin')
1529 user.create(username="anonymous", roles='Anonymous')
1530
1531 Then in the code that matters, calls to ``hasPermission`` and
1532 ``hasNodePermission`` are made to determine if the user has permission
1533 to perform some action::
1534
1535 if db.security.hasPermission('issue', 'Edit', userid):
1536 # all ok
1537
1538 if db.security.hasNodePermission('issue', nodeid, assignedto=userid):
1539 # all ok
1540
1541 Code in the core will make use of these methods, as should code in auditors in
1542 custom templates. The htmltemplate will implement a new tag, ``<require>``
1543 which has the form::
1544
1545 <require permission="name,name,name" assignedto="$userid" status="open">
1546 HTML to display if the user has the permission.
1547 <else>
1548 HTML to display if the user does not have the permission.
1549 </require>
1550
1551 where:
1552
1553 - the permission attribute gives a comma-separated list of permission names.
1554 These are checked in turn using ``hasPermission`` and requires one to
1555 be OK.
1556 - the other attributes are lookups on the node using ``hasNodePermission``. If
1557 the attribute value is "$userid" then the current user's userid is tested.
1558
1559 Any of these tests must pass or the ``<require>`` check will fail. The section
1560 of html within the side of the ``<else>`` that fails is remove from processing.
1561
1562 Authentication of Users
1563 ~~~~~~~~~~~~~~~~~~~~~~~
1564
1565 Users must be authenticated correctly for the above controls to work. This is
1566 not done in the current mail gateway at all. Use of digital signing of
1567 messages could alleviate this problem.
1568
1569 The exact mechanism of registering the digital signature should be flexible,
1570 with perhaps a level of trust. Users who supply their signature through their
1571 first message into the tracker should be at a lower level of trust to those
1572 who supply their signature to an admin for submission to their user details.
1573
1574
1575 Anonymous Users
1576 ~~~~~~~~~~~~~~~
1577
1578 The "anonymous" user must always exist, and defines the access permissions for
1579 anonymous users. Unknown users accessing Roundup through the web or email
1580 interfaces will be logged in as the "anonymous" user.
1581
1582
1583 Use Cases
1584 ~~~~~~~~~
1585
1586 public - end users can submit bugs, request new features, request support
1587 The Users would be given the default "User" Role which gives "View" and
1588 "Edit" Permission to the "issue" class.
1589 developer - developers can fix bugs, implement new features, provide support
1590 A new Role "Developer" is created with the Permission "Fixer" which is
1591 checked for in custom auditors that see whether the issue is being
1592 resolved with a particular resolution ("fixed", "implemented",
1593 "supported") and allows that resolution only if the permission is
1594 available.
1595 manager - approvers/managers can approve new features and signoff bug fixes
1596 A new Role "Manager" is created with the Permission "Signoff" which is
1597 checked for in custom auditors that see whether the issue status is being
1598 changed similar to the developer example.
1599 admin - administrators can add users and set user's roles
1600 The existing Role "Admin" has the Permissions "Edit" for all classes
1601 (including "user") and "Web Roles" which allow the desired actions.
1602 system - automated request handlers running various report/escalation scripts
1603 A combination of existing and new Roles, Permissions and auditors could
1604 be used here.
1605 privacy - issues that are only visible to some users
1606 A new property is added to the issue which marks the user or group of
1607 users who are allowed to view and edit the issue. An auditor will check
1608 for edit access, and the htmltemplate <require> tag can check for view
1609 access.
1610
1368 1611
1369 Deployment Scenarios 1612 Deployment Scenarios
1370 -------------------- 1613 --------------------
1371 1614
1372 The design described above should be general enough 1615 The design described above should be general enough

Roundup Issue Tracker: http://roundup-tracker.org/