[Zope3-dev] Mail delivery failed: returning message to sender

Mail Delivery System Mailer-Daemon at python.org
Tue Feb 17 09:04:25 EST 2004


This message was created automatically by mail delivery software.

A message that you sent could not be delivered to one or more of its
recipients. This is a permanent error. The following address(es) failed:

  corey at streamreel.net
    SMTP error from remote mailer after RCPT TO:<corey at streamreel.net>:
    host iris2.directnic.com [204.251.10.82]: 550 5.7.1 No such recipient

------ This is a copy of the message, including all the headers. ------

Return-path: <zope3-dev at zope.org>
Received: from cache1.zope.org ([12.155.117.38])
	by mail.python.org with esmtp (Exim 4.22)
	id 1At5p7-0007eh-Mv; Tue, 17 Feb 2004 09:03:41 -0500
From: zope3-dev at zope.org (srichter)
Reply-To: zope3-dev at zope.org
To: ;
Subject: [NewResource] first draft (generated from LaTeX file)
Message-ID: <20040217090340EST at dev.zope.org>
X-BeenThere: zope3-dev at zope.org
X-Zwiki-Version: 0.21.1
Precedence: bulk
List-Id:  <zope3-dev at zope.org>
List-Post: <mailto:zope3-dev at zope.org>
List-Subscribe: <http://dev.zope.org/Wikis/DevSite/Projects/ComponentArchitecture/NewResource/subscribeform>
List-Unsubscribe: <http://dev.zope.org/Wikis/DevSite/Projects/ComponentArchitecture/NewResource/subscribeform>
List-Archive: <http://dev.zope.org/Wikis/DevSite/Projects/ComponentArchitecture/NewResource>
List-Help: <http://dev.zope.org/Wikis/DevSite/Projects/ComponentArchitecture>
Date: Tue, 17 Feb 2004 09:03:41 -0500
X-Spam-Status: OK (default 0.000)

??changed:
-Creating a new Browser Resource 
New Principal-Source Plugins
  

??changed: - IsWorkInProgress - outline only; - - Author IsDraft

  Authors

??changed: - Difficulty - - NewComerLevel - - Skills - - - Basic ZCML
knowledge Difficulty

    Sprinter Level  

  Skills

    - You should have a basic understanding of the Zope 3 component
      architecture.

    - It is necessary to understand the purpose and differences
      between permissions, roles and principals.

    - Basic knowledge about the Authentication Service. Optional.  


??changed:
-    Certain output, like images and style sheets are not associated with any
-    other component, so that using a View does not work. For this reason
-    Resources have been thought of, which will be introduced in this
-    mini-recipe. 
        Many systems provide their own mechanisms for authentication.
    Examples include '/etc/passwd' , NIS, Radius and relational
    databases. For a generic platform like Zope it is critically necessary to
    provide facilities to connect to these external authentication sources.

    Zope 3 provides an advanced Authentication Service that provides an
    interface to integrate any external authentication source by simply
    developing a simple plugin. In this recipe we create such a plugin and
    register it with the Authentication Service  .  


??changed:
-    - Goal: Register a file called resource.txt as a Browser Resource.
-
-    - Create a file 'resource.txt' containing:
-    
-      Hello, I am a Zope 3 Resource Component!
-
-    - Now simply register the resource using:
-
-      01 <browser:resource 
-      02     name="resource.txt" file="resource.txt" layer="default" />
-
-    - You should be able to access the resource now via a Browser using
-      "http://localhost:8080/@@/resource.txt"
-
-    - the '@@' in the URL tells the traversal mechanism that the following
-      object is a resource.
-
-    - If you have an image resource, you might want to use different
-      configuration
-[10 more lines...]
    While one can become very fancy in implementing a feature-rich
    principal source implementation, we are concentrating on the most simple
    case here. The exercises point out many of the improvements that can be
    done during later development.

    The goal of this recipe is to create a file-based principal source,
    so that it could read a '/etc/passwd' -like file (it will not
    actually be able to read passwd files, since we do not know whether
    everyone has the 'crypt' module installed on her/his machine). The
    format of the file that we want to be able to read and parse is (users
    are seperated by a newline character)::

      login:password:title:other_stuff
        Let's now turn to the principal source. A component implementing
    'IPrincipalSource' promises to provide three simple methods:  

    -'getPrincipal(id)' - This method gets a particular principal by
      its id, which is unique for this particular source. If no principal
      with the supplied id is found, a 'NotFoundError' is raised.

    -'getPrincipals(name)' - This method returns a list of principals
      whose login somehow contain the substring specified in 'name' .
      If 'name' is an empty string, all principals of this source are
      returned.

    -'authenticate(login, password)' - This method is required by the
      'ILoginPasswordPrincipalSource' interface and provides
      authentication for the provided principal. There are other ways to
      implement authentication for these principals, but they add unnecessary
      complexity. 'None' is returned, if no match is made.  


    The next step is to provide an implementation of 'IPrincipalSource' 
    for password files. Create a new sub-package called 'passwdauth' in
    the 'zope.products.demo' package. Now the first step is to define
    the interfaces, as usual.

    
  Defining the interface


    "What interface do we need?", you might wonder. In order for a
    file-based principle source plugin to provide principals, we need to know
    the file that contains the data and knowing about this file is certainly
    part of the API for this plugin. So we want to create a specific
    interface that contains a filename. If we make this attribute a schema
    field, we can even use the interface/schema to create autogenerated add
    and edit forms.

    In the 'passwdauth' directory add a 'interfaces.py' file and add the
    following contents::

      01 from zope.schema import TextLine
      02 from zope.app.i18n import ZopeMessageIDFactory as _
      04 from zope.app.interfaces.services.pluggableauth import IPrincipalSource
      06 class IFileBasedPrincipalSource(IPrincipalSource):
      07     """Describes file-based principal sources."""
      09     filename = TextLine(
      10         title = _(u'File Name'),
      11         description=_(u'File name of the data file.'),
      12         default = u'/etc/passwd')

    o Line 1: Usual imports of the TextLine field for the filename
      property.

    o Line 2: This is the typical I18n boilerplate (not much though);
      all text strings wrapped by the underscore function will be
      internationalized, or in other terms localizable.

    o Line 4-5: Our file-based principal source is still of type 'IPrincipalSource' 
      , so let's make it the base interface.

    o Line 10-14: Typical internationalized text line field
      declaration, making '/etc/passwd' the default value (even though
      the product will not work with this file due to the crypt issue). You
      might want to add a different default, also based on the operating
      system you are on.  


    
  Writing the tests


    The next step is to write some unit tests that assure that the file
    parser does its job right. But first we need to develop a small data file
    with which we can test the plugin with. Create a file called 'passwd.sample' 
    and add the following two principal entries::

      01 foo1:bar1:Foo Bar 1
      02 foo2:bar2:Foo Bar 2

    Now we have a user with login 'foo1' and one known as 'foo2' ,
    having 'bar1' and 'bar2' as passwords, respectively.

    In the following test code We will only test the aforementioned
    three methods of the principal source. The file reading code is not
    seperately checked, sinde it will be well tested through the other tests.

    Create a 'tests.py' file and add the code below.::

      01 import os
      02 from zope.products.demo import passwdauth
      03 from zope.exceptions import NotFoundError
      04 from unittest import TestCase, TestSuite, main, makeSuite
      06 class PasswdPrincipalSourceTest(TestCase):
      08     def setUp(self):
      09         dir = os.path.split(passwdauth.__file__)[0]
      10         self.source = passwdauth.PasswdPrincipalSource(
      11             os.path.join(dir, 'passwd.sample'))
      13     def test_getPrincipal(self):
      14         self.assertEqual(self.source.getPrincipal('\t\tfoo1').password, 'bar1')
      15         self.assertEqual(self.source.getPrincipal('\t\tfoo2').password, 'bar2')
      16         self.assertRaises(NotFoundError, self.source.getPrincipal, '\t\tfoo')
      18     def test_getPrincipals(self):
      19         self.assertEqual(len(self.source.getPrincipals('foo')), 2)
      20         self.assertEqual(len(self.source.getPrincipals('')), 2)
      21         self.assertEqual(len(self.source.getPrincipals('2')), 1)
      23     def test_authenticate(self):
      24         self.assertEqual(self.source.authenticate('foo1', 'bar1').id, 'foo1')
      25         self.assertEqual(self.source.authenticate('foo1', 'bar'), None)
      26         self.assertEqual(self.source.authenticate('foo', 'bar'), None)
      28 def test_suite():
      29     return TestSuite((
      30         makeSuite(PasswdPrincipalSourceTest),
      31         ))
      33 if __name__=='__main__':
      34     main(defaultTest='test_suite')

    o Line 1, 2, 9-11: The reason we imported 'os' and the 'tests' 
      package itself, was to be able to get to the directory of the code as
      seen in line 9. Once we have the directory it is easy to build up the
      data file path and initialize the principal source (line 10-11).

    o Line 13-16: Test the 'getPrincipal(id)' method. The last test
      checks that the correct error is thrown in case of a failure.

    o Line 18-21: The test for 'getPrincipals(name)' mainly tests that
      the resulting user list is correctly filtered based on the 'name' 
      parameter value.

    o Line 23-26: The authentication test concentrates on checking
      that really only a valid login name and password pair receives a
      positive authentication by returning the principal object.

    o Line 28-34: This is the usual test boiler plate.  


    You can now run the tests either using Zope's 'test.py' test runner
    or by executing the script directly; the latter method requires the
    Python path to be set correctly to 'ZOPE3/src' .

    
  Implementing the plugin


    The implementation of the plugin should be straight forward and bear
    no surprises. The tests already express all the necessary semantics. We
    only have not discussed the data structure of the principal itself yet.
    For it we can reuse the 'SimplePrincipal' , which is a basic 'IUser' 
    implementation that contains all the data fields 'IUserSchemafied' )
    relevant to a principal: id, login (username), password, title and
    description.

    Note that in Zope 3 the principal knows absolutely nothing about its
    roles, permissions or anything else about security. This information is
    handled by other components of the system and is subject to policy
    settings. Now we are ready to realize the principal source. In the '_' 
    file of the 'passwdauth' package we add the following
    implementation::

      01 import os
      02 from persistence import Persistent
      03 from zope.app.container.contained import Contained
      04 from zope.app.interfaces.services.pluggableauth import \
      05      ILoginPasswordPrincipalSource
      06 from zope.app.location import locate
      07 from zope.app.services.pluggableauth import SimplePrincipal
      08 from zope.exceptions import NotFoundError
      09 from zope.interface import implements
      10 from interfaces import IFileBasedPrincipalSource
      12 class PasswdPrincipalSource(Contained, Persistent):
      13     """A Principal Source for /etc/passwd-like files."""
      15     implements(ILoginPasswordPrincipalSource, 
      16                IFileBasedPrincipalSource)
      18     def __init__(self, filename=''):
      19         self.filename = filename
      21     def readPrincipals(self):
      22         if not os.path.exists(self.filename):
      23             return []
      24         file = open(self.filename, 'r')
      25         principals = []
      26         for line in file.readlines():
      27             if line.strip() != '':
      28                 user_info = line.strip().split(':', 3)
      29                 p = SimplePrincipal(*user_info)
[255 more lines...]

--
forwarded from http://dev.zope.org/Wikis/DevSite/Projects/ComponentArchitecture/NewResource



More information about the Zope3-dev mailing list