[Zope3-dev] Mail delivery failed: returning message to sender
Mail Delivery System
Mailer-Daemon at python.org
Tue Feb 17 09:03:28 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 1At5nx-0007Nm-Lb; Tue, 17 Feb 2004 09:02:29 -0500
From: zope3-dev at zope.org (srichter)
Reply-To: zope3-dev at zope.org
To: ;
Subject: [NewPrincipalSourcePlugins] first draft (generated from LaTeX file)
Message-ID: <20040217090229EST 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/NewPrincipalSourcePlugins/subscribeform>
List-Unsubscribe: <http://dev.zope.org/Wikis/DevSite/Projects/ComponentArchitecture/NewPrincipalSourcePlugins/subscribeform>
List-Archive: <http://dev.zope.org/Wikis/DevSite/Projects/ComponentArchitecture/NewPrincipalSourcePlugins>
List-Help: <http://dev.zope.org/Wikis/DevSite/Projects/ComponentArchitecture>
Date: Tue, 17 Feb 2004 09:02:29 -0500
X-Spam-Status: OK (default 0.000)
++added:
??changed: - IsWorkInProgress - mainly an outline - - Author IsDraft
Authors
??changed: - Difficulty - - SprinterLevel - - Skills - - - Basic
understanding of the Zope 3 Component Architecture - - - Understand
the purpose and differences between permissions, roles and -
principals. - - - Basic knowledge about the Authentication Service.
Optional. 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:
- 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 therefore critically necessary to provide
- facilities to connect to these external authentication sources.
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.
??changed: - register it with the Authentication Service. register it
with the Authentication Service .
??changed:
- - while one can become very fancy in implementing a feature-rich principal
- source implementation, we are concentrating here on the most simple
- case. The exercises point out many of the improvements that can be done
- later.
-
- - It will be our goal to create a file-based principal source, so that it
- could read a /etc/passwd-like file (it will not be able to read passwd
- files, since we do not know whether everyone has the crypt module
- installed on her/his machine)
-
- - therefore, component should be able to read and parse files of the
- format (users are seperated by a newline character)::
-
- login:password:title:other_stuff
-
- - An 'IPrincipalSource' component promises to implement three simple
- methods:
-
- o getPrincipal(id) - This method gets a particular principal by its id,
-[388 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)
[226 more lines...]
??changed: - - Exercise 2: The current implementation requires the
passwords to be - plain text, which is of course a huge security risk.
Implement a version - of the plugin that can handle plain text,
crypt-based and SHA - passwords. Implement a setting in the user
interface that let's the user - select one of the encryption types. -
- - Exercise 3: Implement a version of the plugin that provides a
write - interface, i.e. you should be able to add, edit and delete -
Exercise 2: The current implementation requires the passwords to be
plain text, which is of course a huge security risk. Implement a
version of the plugin that can handle plain text, crypt-based and SHA
passwords. Implement a setting in the user interface that let's the
user select one of the encryption types.
- Exercise 3: Implement a version of the plugin that provides a
write interface, i.e. you should be able to add, edit and delete
??changed: - - Exercise 4: It is very limiting to require a special
file format for - the principal data. It would be useful to develop a
method that allows - the user to specify the file format. Implement
this feature and provide - an intuitive user interface for this
feature. (This is a tough one; feel - free to make some assumptions to
solve the problem.) - - - Exercise 5: Reading in the user data for
every single authentication - call is quiet expensive, so it would be
helpful to implement some - caching features. This can be done in two
ways: (1) Use the caching - framework to implement a cached version of
the source or (2) save the - list of principals in a volatile
attribute (i.e. _v_principals) and - check for every call whether the
file had been modified since the last - time it was read. - Exercise
4: It is very limiting to require a special file format for the
principal data. It would be useful to develop a method that allows the
user to specify the file format. Implement this feature and provide an
intuitive user interface for this feature. (This is a tough one; feel
free to make some assumptions to solve the problem.)
- Exercise 5: Reading in the user data for every single
authentication call is quiet expensive, so it would be helpful to
implement some caching features. This can be done in two ways: (1) Use
the caching framework to implement a cached version of the source or
(2) save the list of principals in a volatile attribute (i.e. '_'
) and check for every call whether the file had been modified since the
last time it was read.
--
forwarded from http://dev.zope.org/Wikis/DevSite/Projects/ComponentArchitecture/NewPrincipalSourcePlugins
More information about the Zope3-dev
mailing list