[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