6-1-Security.stx
Zope 2.2 Product Developer's Guide - Chapter VI - Zope Security
Introduction
Zope's primary goal is to present a foundation for creating customized web applications. A typical web application needs to be securely managed. Different types of users need different kinds of access to the components that make up an application. To this end, Zope includes a comprehensive --and often confusing-- set of security features. This chapter's goal is to shed a little light on Zope security in the context of Product development.
We're going to take a bottom-up view of Zope security in this chapter. We won't actually get to implementing security in a Product until near its end. The first ten sections of this chapter will give us some context by exposing us to the Zope security model in abstract, though we'll throw in a little bit of coding along the way just for fun and demonstration purposes. The remaining sections will concentrate on how to implement Product security in the context of sample applications for ZClasses and Python-based products.
NOTE: Much of the introductory material covered in this document is also covered in the O'Reilly Zope Book.
Declarative and Programmatic Security
Security within Zope is defined by users, user folders, authentication credentials, roles, permissions, and ownership. These are the facilities that allow you to secure the object instances which are created from your Product. In the case you're building a Product using ZClasses, these facilities protect your Product definitions themselves. All of these security features can be used both declaratively and programatically* during Product development.
Zope declarative security is accomplished through the manipulation
of permissions and roles. All Zope objects may be protected
from unauthorized access "ahead of time" by providing permission
settings to objects and by assigning roles to Zope users. For
example, I might change security settings of Zope objects related
to a Product I've developed in order to assert that "only users
with the Manager
role can view a particular folder or its
subobjects." Declarative security in Zope is roughly analogous to
assigning files on a UNIX filesystem permission bit settings and
user group memberships. Declarative security allows me to secure
Zope object independently of my application code.
Programmatic security in Zope means that I can perform security
assertions in my running code. For example, I might make a
decision in DTML code to show users with the Manager
role a
certain bit of HTML that I don't show to users with the Anonymous
role. Programmatic security can be difficult to maintain, and
should be used sparingly when you're developing under Zope. A good
rule of thumb is to use declarative security wherever possible,
only resorting to programmatic security when absolutely necessary.
You won't see the terms "declarative" and "programmatic" inside the Zope management interface to describe security. For example, you won't see a tab that says "Declarative Security" on a Zope management screen, and you won't see a DTML Method with the words "[insert programmatic security code here]". The terms are purely conceptual. They are useful to describe what we're going to try to do a little later in this chapter, however.
The Superuser
Users play a big part in Zope security. But before we can talk
about Zope users in general, we need to understand a few things
about the Zope "superuser". When Zope is first installed, you may
access the Zope management interface using an externally-defined
superuser
account. The the Zope superuser
user is defined in
the $INSTANCE_HOME/access file (FYI: $INSTANCE_HOME always refers
to the "main" Zope directory), externally to any other Zope
authentication data repository. It cannot be managed like other
Zope users. Instead, you need to directly edit the access
file
or use the zpasswd.py
utility included with Zope to change the
superuser password and username.
Curiously, one of the first things a Zope installer needs to do is to define additional management accounts with which he or she can actually add content, because the superuser does not have the power to define content on the site. The additional management accounts created during this "bootstrapping" process effectively become more powerful than the superuser itself.
You say, "More powerful than the superuser? How can a normal user
be more powerful than the all-powerful superuser? That's contrary
to all that is good and holy." Well, personally I can't say that I
disagree with you. Nonetheless, in Zope speak, the superuser
user is special, and may only be used to "bootstrap" the system,
manage other users, and fix improperly-set permissions on objects
within the site. It may not be used to create, edit, copy or move
objects, and thus is useless for development or content management
purposes.
This possibly unfamiliar definition of "superuser" is necessitated
by the fact that all web-defined objects in Zope which are
"executable" (DTML Methods, External Methods, Python Methods, etc.)
are executed with the effective permissions defined by the
intersection of the privileges of the user who "owns" the object
(generally the content creator) and the privileges of the person
"executing" the object (the site visitor). The superuser, despite
being crippled from a content management perspective, is actually
very powerful. It has the ability to traverse the site without
restriction or to manage authentication and permission settings.
Therefore, the creation of content by the superuser is restricted.
This ensures that you don't accidentally "shoot yourself in the
foot" by using the superuser account to browse a Zope site which
contains malicious scripts contributed by persons with lesser
privilege. We'll get to the concepts of execution and ownership a
little further in this chapter, but just trust me for now, and know
that the superuser is purposely crippled as far as content
management activity goes. In future versions of Zope, the
superuser
account may be renamed to something more intuitive,
like bootstrapuser
or somesuch, but for now you'll need to deal
with the unfamiliarity as best you can.
Thus, all Zope installations must contain at least one defacto
"management" account which is added at installation time and which
has effectively more privilege from a content management
perspective than the superuser itself. This is an enforcement of
the "don't do anything as root
or Administrator
that you don't
absolutely need to" precept honored by most competent UNIX and
Windows NT system administrators. Though you probably won't be
administering all (or possibly any of) the sites in which your
Product is installed, it's helpful to understand the role of the
Zope superuser
when your customers are experiencing problems
attempting to install your Product as the superuser. They can't.
Tell them to define a management user, and then walk them through
the process of installing your product as that management user.
Here's what they might see if they try to perform content
management or object creation duties (such as adding or changing
instances of your Product) as the superuser:
Figure 6-0 - Superuser cannot own
User Folders
"User folders" are authentication data repositories which contain Zope user data. Defining user folders is not a step that a Product developer is required to take during development. Generally, user folder definitions will be handled by the content manager, a webmaster or another delegate whom installs your Product, and he or she will be responsible for the ongoing task of maintaining users. However, it's crucial for developers to understand the role of user folders in the Zope security machinery, how user folders relate to roles, and how multiple user folders within the same site relate to each other. As a Product developer, you may even be required to code your own user folder implementation (for example, to match an existing cookie-based authentication scheme), so it's wise to have at least a passing understanding of what is going on "under the hood".
When Zope is installed for the first time, an object named
acl_users
is automatically created at the root of your Zope
"instance space." (NOTE: When I say "instance space" I broadly
refer to all parts of Zope outside the Control Panel viewable from
within the management interface. This distinction will become
important later, when we talk about "class space" in relation to
ZClasses.) If you've poked around inside the Zope management
interface a little bit you may have discovered that you can add and
remove user definitions using the management facility that the
acl_users
object exposes. If not, go look inside it now. Here's
what mine looks like:
Figure 6-1 - My acl_users
management interface
If I click on the chrism
user, it allows me to edit the user definition:
Figure 6-2 - The chrism
user being edited
This acl_users
object is an instance of a "user folder". A user
folder, in Zope terms, is a "special" Zope object which you can use
to define and manage the definitions of your users: their names,
their respective roles, and various other attributes such as
domain restrictions. Users are defined in Zope by their presence
in a user folder within the Zope instance space. User folder
instances always have a Zope id
of acl_users
, a naming
convention that is followed by all user folders. User folders are
not renamable, and cannot be cut and pasted between sections of a
site, although they can be imported and exported.
NOTE: It's beneficial to know for purposes of development and research that the terms "user folder", "acl_users", "user database" (and, rarely, the more obscure "__allow_groups__") are more or less equivalent in Zope terminology. In this discussion, I even make the conscious mistake of giving them a new name, "authentication data repositories," for purposes of descriptive accuracy. But just know that user folders are an integral part of Zope security, whatever they may be called, and that while within the Zope web management interface, if you run across a user folder, it will always be named "acl_users". It's also useful to know that no matter what kind of user folder you're working with (there are several different kinds, such as a GenericUserFolder, smbUserFolder, etcUserFolder, LoginManager, UserDb, and a multitude of others serving niche needs), each exposes more or less the same user folder API, which makes them more or less interchangeable as long as you heed the interface. (Note: Currently there is no consensus on what should make up a User Folder API. This is slightly embarassing, but it's true. The Interfaces Wiki on Zope.org has more information on what a User Folder API should look like.)
When I first installed Zope and logged on as the superuser
, I
defined a (handsome and charming) chrism
user within this
particular user folder, and thus he shows up in Fig 6-1 and can be
edited ala Fig 6-2. Additionally, as the superuser
, I granted
chrism
the Manager
role within this user folder, and therefore
chrism
has a great deal of power over the system at a very high
level. This level of power is typical of that possessed by a "site
manager". Because as superuser
, I granted chrism
the Manager
role in the "root" user folder, chrism
is effectively the most
powerful (and handsome) user on this particular Zope system,
arguably possessing even more content management power than the
superuser
user itself, as explained in the preceding section.
Though multiple user folders may exist in a single Zope site, only a single user folder may exist within any given Zope Folder. If you try to add an additional user folder to a Folder which already contains a user folder, you'll see something like this:
Figure 6-3 - Cannot add more than one user folder to a single Folder
Despite this limitation, more than one user folder can exist in a
Zope site. Because my chrism
user has the privilege to do so,
I'll go create a Marketing
folder, and I'll create another
acl_users
user folder object inside of it:
Figure 6-4 - Adding a user folder to a different Folder
There are good reasons why I'd want to create a Marketing
Folder
object. Maybe I want to put some marketing content in there to
partition my site. But why would I want to create another user
folder inside the Marketing
Folder? The answer is delegation.
Zope has been engineered with the goal of providing services to
"customers who have customers," such as newspaper companies who
manage section editors whom, in turn, manage columnists. Instead
of providing a single monolithic authentication repository, Zope
allows content managers to delegate the power to create and manage
additional authentication repositories to other users. These
additional authentication repositories (user folders) can be
managed by the site manager, the authentication repository creator,
or by an arbitrary user to whom the site manager or repository
creator chooses to delegate this power. Users created within this
authentication repository have privileges which only extend to the
Folder in which the repository was created and "below".
In my case, I've created a Marketing
Folder which contains a user
folder. I may now define a user within the Marketing
Folder's
user folder and grant that user the ability to manage additional
users only within the Marketing Folder's user folder. I'll do so
by creating a user, jed
, and I'll give him the power manage users
within the Marketing
user folder by assigning him the Manager
role, as well as the Marketing
role, which is a new role which I
created in the Security
tab of the root folder.
Figure 6-5 - jed
added to the Marketing
user folder
Figure 6-6 - roles provided to jed
in the Marketing
user folder
Via the Zope management interface, arbitrary users with appropriate permission can define additional Folders. Within these, they may define additional user folders, and thus may define users and dole out roles to the users they create as they see fit. The users which are defined in that round of delegation may then themselves choose to define Folders which contain user folders, in which they can delegate roles to other people, and so on, and so on, ad infinitum. Roles are ways of granting power to users on a granular level. They will be discussed in a later section.
In our example, Jed is free to define a new Folder within the
Marketing Folder which itself contains a user folder, and he may
define and grant access to users within this Folder just as I did
as chrism
when I created the Marketing Folder and defined jed
in the Marketing user folder.
Jed can make full use of the Zope management interface to delegate
power to his co-workers as he sees fit without needing to consult
chrism
the (good-looking, devilish) site manager. Jed's
co-workers, if he has defined their users in the Marketing
Folder
and granted those users the appropriate roles, may also create
additional Folders within the "Marketing" Folder, and may then
create user folders within them, extending the delegation.
This lack of decentralization tends to gets a little mind-boggling,
but the goal is to give "more power to more people," a central Zope
tenet. It's important to understand that control flows "down" from
the Zope root folder. Although Jed has Manager
privileges in the
Marketing
folder and in folders "below" the Marketing
folder,
he does not have Manager
privileges "above" within the "root"
folder (indeed he has no privileges at all), because chrism
didn't extend to him those privileges. This means that Jed can
control and delegate the control of objects created within the
Marketing
folder and its subfolders, but he cannot change or
delegate control of objects created within the "root" folder. He
can't even use the management interface in the "root" folder.
Only the chrism
and superuser
users have the power to do this
in this particular case.
As an example, let's try to understand what happens when Jed tries
to access the management interface of the "root" folder by visiting
http://localhost:8080/manage
. While logged in as Jed, I'll try
it:
Figure 6-7 - Jed trying to hack his way in to the root mgmt interface
Jed was denied access to the management interface of the root
folder. But why is this? It's because there is no user defined as
jed
in the root user folder.
While attempting to authenticate and authorize users, the Zope
security machinery checks the "closest" user folder first, working
its way "upwards" through the Zope user folder definitions via
acquisition if it cannot find a user folder in the same Folder as
the object being accessed. In this case, the "closest" user folder
is the root user folder, because we accessed the "root" Folder via
the http://localhost:8080/manage
URL (remember that the "/" of
your site is almost always the "root" Folder, and that manage
is
a method defined on it). Since the jed
user is not defined in
the root user folder, the security machinery raises an
"Unauthorized" message and denies access.
If there had been a jed
user in the root user folder, and if
that user had been granted the Manager
role, and if the
password defined for that user matched the authentication
credentials sent by the browser (left over from Jed's login to the
Marketing
Folder), Jed would have been granted access to manage
objects in the root folder. Likewise, if Jed knew the chrism
or
superuser
username and password, he could enter it at a following
browser authentication prompt to get access.
Look at the next figure, in which the chrism
user seems to have
no difficulty at all in accessing the Marketing
user folder:
Figure 6-8 - rugged chrism
user visits the Marketing
User
Folder
Because the chrism
user has the Manager
role within the "root"
user folder. he can manage objects in the root folder and in all
folders below. He can also add users to the Marketing
user
folder, if he so desires.
The magic that causes this to happen is acquisition and
delegation. Within the code shared between the Zope publishing
machinery, the actual user object which represents a Zope user, and
the user folder in the Marketing
folder that is visited when a
web request comes in to http://localhost:8080/Marketing/manage, the
set of decisions made by Zope are:
- Since the
manage
method of theMarketing
folder is protected by a permission which does not allow anonymous access, obtain user credentials from the request to ensure the user is authorized to access the management screen. - Since the
Marketing
folder contains a user folder, use it to obtain the visitor's credentials from the request. - If the
Marketing
user folder has a user definition with the username provided by the request, check the password against the one it knows for this username. If it doesn't match, raise an error indicating the user is unauthorized. If it does match, let the user in and give him the roles that are defined for him within theMarketing
user folder. - If the
Marketing
user folder doesn't have a user definition for the username provided by the request, delegate the authentication request to the "closest" user folder via acquisition. In this case, the "closest" user folder is the "root" user folder.
This means that as the chrism
user, when visiting the Marketing
folder, credentials are first checked against the user
definitions in the Marketing
user folder, and when it decides
that chrism
user doesn't exist in itself, it delegates the
authentication procedure to the "root" user folder which finally
authenticates chrism
and provides chrism
with the Manager
role. This allows me to manage objects both in the Marketing
Folder and the "root" Folder when I log in as chrism
. This
"chaining" of authentication checks happens every time you access a
Zope object via a web (or FTP) request.
The user folder created automagically during Zope installation is known as the "root user folder", and it's a good idea not to mess with it too much other than to define top-level users. Though you can replace it with a different kind of user folder, the procedure to do so is arcane and difficult. Instead of attempting to replace it, it's probably a good idea to define additional user folders that live in different Folders "below" the root.
Note, however, that the definition of many user folders within a site is almost always a bad idea if you don't really need them because authentication can become quite complicated and may require advanced understanding of how acquisition works and intimate knowledge of the implementation of various user folder instances. It's best to stick to the KISS principle ("Keep It Simple, Stupid"). If you can get by with a single user folder, do so in your production environment. But while developing your Products, keep in mind that the folks using your Product may require multiple or arbitrary user folder instances, and may expect your Product to work with all and any of them.
Let's see some code that operates on a User Folder. It's a silly
example, but bear with me. Let's create a DTML method with an id
of user_name
and a body of:
<dtml-with acl_users> <dtml-var expr="getUser('chrism').getUserName()"> </dtml-with>
If the user which executes this code has the appropriate set of
roles (generally "Manager-level" roles), he or she will see that
the chrism
user has the username chrism
when this DTML method
is viewed (I told you it was a stupid example). Let's try it in
Python for the heck of it (you can use an External Method or a
Python Method for this):
def get_roles(self): """ returns 'chrism' """ return self.acl_users.getUser('chrism').getUserName():
This is not particularly interesting or useful, but hopefully you get a flavor of how the user folder object is employed. For other interesting ways to use user folders, see the user folder API documentation.
We've covered a lot of ground on user folders. We've found out that user folders are authentication data repositories, and that they may be referred to using a number of different terms. We've found that user folders are web-manageable, and allow site managers to specify user names, roles, and domain specifications. We've found that there are many different kinds of user folders, and that multiple user folders can exist within a single Zope installation. We've also found that user folders participate in the Zope acquisition machinery and delegate authentication duties to each other through this machinery.
Authentication Credentials
Consider the typical web request. Joe Bloggs hits your Zope-powered website from Internet Explorer on his new Pentium XI which he just purchased in a crack-and-dent sale from Best Buy. It is probably safe to assume that you don't want Joe to be visiting the Zope management interface, futzing with your instance data, overwriting rows in your database tables, or making modifications to your Product code. However, you, as a busy software professional or content manager may need to use a computer at an Internet cafe or you may even want to use Joe's new $1800.00 Packard Bell to do just those sorts of things. Therein lies a paradox.
One of the strange and wonderful --and potentially dangerous--
things about Zope is that all users are treated more or less like
"first class citizens". Possessing the proper credentials, a user
coming in from just about anywhere (minus domain restrictions, of
course) can manage the objects that make up your Zope-powered site
using the Zope web management interface. If you'd like to manage
Zope from an Internet cafe, it's really no problem. You need not
ask permission to install special software on the cafe machine.
Instead, just append /manage
to the URL from which you want to
start the management inteface in within a browser and log in as a
privileged user. But it's in your best interest to understand how
Zope comes to possess the credentials of a requester, so Joe Bloggs
doesn't end up hosing your carefully crafted site when he appends
/manage
to the same URL.
Zope may obtain the requester's credentials as data in an HTTP header or as part of a cookie sent by a browser. Which one of these methods does your Zope use? Probably HTTP basic authentication unless you've replaced the stock user folder object in the root of your Zope installation with another or you've extended Zope by installing and instantiating a different user folder Product which employs a different credentials-gathering scheme. The user folder that is installed automatically by Zope in the root folder is an instance of a Standard user folder, which is an object that uses the ZODB to store username, password, and domain data and which uses the "HTTP basic authentication" scheme (see RFC 2068) to obtain user credentials.
Authentication credentials are exposed to Zope as a result of a visitor attempting to access a "protected" area of your site. When the visitor hits a URL within your Zope object space for the first time, if the method of the object that the URL represents is inaccessible by the "anonymous user", the Zope publishing machinery asks the "closest" user folder --the user folder in the same container as the accessed object or via acquisition-- to authenticate the request. User folders in general, and the Standard user folder in particular, examine information passed in from the publishing machinery to determine the username and password which the visitor presented to Zope. If this username and password can be validated by the user folder, and if the roles assigned to the user permit they type of access required by the request, Zope permits access and carries out the transaction requested.
It's important to know that full authorization checks do not
happen if the method represented by the URL is accessible by an
"anonymous user". Instead, Zope short-circuits the authorization
procedure and just carries out the transaction represented by the
request, populating the AUTHENTICATED_USER attribute of the
REQUEST object with Anonymous User
. This can bite you sometimes,
especially when performing programmatic security checks. If your
method does not require authentication or authorization, your
user will be considered the Anonymous User
even if he or she had
previously logged in to the site as a result of his or her
interaction with a method that required authentication and
authorization.
Though there are other methods of obtaining user credentials, such as cookie-based authentication schemes, the Standard user folder implementation can only obtain user credentials using the HTTP basic authentication scheme. Other user folder implementations (such as etcUserFolder) support different methods of obtaining user authentication credentials. They will not be discussed here, and we will concentrate on how Zope can be made to authenticate users against a Standard user folder using HTTP basic authentication.
HTTP basic authentication is a very basic authentication scheme
formalized in 1996 by the W3. HTTP basic
authentication makes it possible for a web server to authenticate
and authorize users who employ a user agent (a browser) which
complies with the basic authentication specifications. All of the
commercially popular browsers support HTTP basic authentication.
To describe its operation, and to give you some understanding of
what's going on "under the hood" of the browser and within Zope,
I'll give you a low-level blow-by-blow description of what happens
when our fictitious user "Jed" opens a new browser window and
visits the /Marketing/manage
URL:
- Jed turns on his computer and opens Netscape Communicator.
- Jed types in to his browser address bar
http://www.azopeserver.com:8080/Marketing/manage
- Jed's browser connects with the Zope server's TCP/IP port 8080,
and issues the following request to Zope:
GET /Marketing/manage HTTP/1.1
This request indicates that Jed's browser wants to render the
document which resides at /Marketing/manage
, and that it wants
to use the HTTP 1.1 protocol (see RFC 2068).
- Since Zope has no idea at this point who the requester is, *and
because the
/manage
method of the root Folder is protected by permissions and roles and is not accessible by an anonymous user*, it responds with this bit of text:HTTP/1.1 401 Unauthorized Server: Zope/(unreleased version) ZServer/1.1b1 Date: Mon, 24 Jul 2000 01:55:51 GMT Www-Authenticate: basic realm="Zope" Content-Type: text/html Content-Length: 1404 [.. a bunch of error stuff in HTML ..]
Note the line Www-Authenticate: basic realm="Zope"
. This is
the important bit.
- Jed's browser, instead of rendering the HTML that I've shortened
to
[.. a bunch of error stuff in HTML ..]
for purposes of brevity, notices theWww-Authenticate
line andpops up
an authentication dialog box. - Jed types
jed
as his username, enters his (correct) password, and presses Enter. - Jed's browser sends another
GET
request for the/Marketing/manage
page, this time slightly modified:GET /Marketing/manage HTTP/1.1 Authorization: Basic amVkOmplZA== Note new line 'Authorization: Basic' followed by the string 'amVkOmplZA=='. Jed's browser is telling us that it wants to authorize against the '/Marketing/manage' URL. The strange line noise in there is the base64 encoded string "jed:jed", which represents Jed's username ('jed') and password ('jed') separated by a colon. The username and password are not encrypted in any way, and despite being base64 encoded can essentially be thought of as being passed "in the clear." - The 'Marketing' Folder's management screen is displayed in Jed's browser. After this first bit of back-and-forth, Jed's browser "remembers" the '/Marketing/manage' URL and *sends his username and password with every subsequent request to that URL and sub-URLs (such as '/Marketing/manage/foo') related to it when requested via the Www-Authenticate header provided by the server.* The user folder at '/Marketing/manage/acl_users' recognizes Jed by his credentials, which are passed in the authentication header of the HTTP request. This is how the browser-server combination preserves the illusion of "statefulness" between browser requests. But this highlights some peculiarities of using HTTP basic authentication. First, there is no good way to "log out." Additionally, *two* transactions between the server and the agent are necessary for each request (the server presenting the 'Www-Authenticate' challenge and the client responding with the 'Authorize:' response). Each time a "protected" resource is encountered, the agent must re-authenticate itself with its cached credentials. This is a good place to interject a FAQ and FAQ answer. Many beginning users and developers get confused by the apparent inability to "log out" of Zope once they've authenticated to a Zope server via HTTP basic authentication. It's unfortunately a built-in limitation of HTTP basic authentication and cannot easily be fixed without using a different authentication scheme through a different user folder implementation. To 'log out' of Zope, creating and viewing this DTML method *and pressing "OK"* in the pop-up authentication dialog box during its view works well:: <dtml-raise Unauthorized> Unauthorized. </dtml-raise> I generally make a method like this in the root of my Zope installations and call it 'logout'. Viewing this DTML method also provides developers with the opportunity to log in as a different user when the pop-up box is displayed. Note that if the "cancel" button is pressed on the ensuing authentication dialog, the authenticated user will *not* be logged out. As an additional method of "logging out" developers and users may also quit their browsers and start them again, which clears the browser of authentication information. On the browser's subsequent restart, it will not pass any authentication information in the header until another authentication process has been completed. Zope supports HTTP basic authentication because it is possibly the most widely used form of web-based authentication on the Internet, despite its limitations. If basic authentication doesn't fulfill your needs, you may want to try one of the other user folder implementations available for Zope that use different credentials-gathering schemes such as cookies. Hopefully you've now got an understanding of how Zope obtains user credentials via HTTP basic authentication. The next few sections will deal with how Zope uses those credentials to enforce the security policy of a site. D. Users We've been throwing the word "users" around without a definition for some time now. Let's try to nail the details. Conceptually, in Zope, users represent people. In a perfect world, there would be a single Zope user for each person that wanted to use a single Zope system. But in reality, it's also possible that a single person might wish to be represented by two Zope users (for instance, in the case that a sysadmin has a "management" account and a "normal" account). It's also possible that a Zope user may represent more than one person (for example, in the case that a group of people shares a Zope account). However a user is employed, it is represented in Zope by a User object. This User object *knows* things (e.g. its domain spec, the correct username and password for the user it represents). The User object also *does* things (e.g. it provides its username to a caller, returns the *roles* granted to the user it represents). The things it "knows" are its attributes, and the things it "does" are its methods. If we can grab hold of the User object that represents a person or group within Zope, we can do some mildly interesting things with it. The transient 'REQUEST' object contains a reference to the currently logged-in User object, which is constructed during the process of gathering and validating user credentials. By coding DTML or Python within Zope, we can find out more about the currently logged in user by making use of the methods of the User object via the Authenticated User API (see the Zope Help System API Documentation section accessible via the Zope management interface Help button). I'm going to construct a DTML method within Zope that shows me information about its viewer. I'll give it an 'id' of 'user_roles', and give it a body like so:: <dtml-with REQUEST> <dtml-in expr="AUTHENTICATED_USER.getRoles()"> <dtml-var sequence-item><br> </dtml-in> </dtml-with> Here's what it looks like on my system: <img src="FIG-6-9.gif"> Figure 6-9 - The 'user_roles' DTML method While I'm logged in as 'chrism', I'll view it. <img src="FIG-6-10.gif"> Figure 6-10 - Viewing the 'user_roles' DTML method as 'chrism' This little DTML script iterates through the roles of the currently logged in user ('chrism'), and reports back each of the roles (which are just strings) separated by an HTML break tag. Since the 'chrism' user in this case only has one *role* ('Manager'), it is the only one reported. The method that does the dirty work here is 'getRoles()' of the 'AUTHENTICATED_USER' object, which is itself contained within the REQUEST object. To get a better understanding of what is going on here, let's rewrite the DTML script another way:: <dtml-in expr="REQUEST['AUTHENTICATED_USER'].getRoles()"> <dtml-var sequence-item><br> </dtml-in> Here's one more way:: <dtml-in expr="REQUEST.AUTHENTICATED_USER.getRoles()"> <dtml-var sequence-item><br> </dtml-in> Let's rewrite it as an External Method in Python:: def user_roles(REQUEST): """ return HTML representing user's roles """ user = REQUEST['AUTHENTICATED_USER'] roles = user.getRoles() html = "" for role in roles: html = html + "%s%s" % (role, "<br>") return html As we can see, the 'REQUEST' object contains a reference to the User object ('AUTHENTICATED_USER'). The User object has a method named 'getRoles()' which provides us with a sequence of roles--which are strings--that have been granted to the currently logged-in user. What happens when I log out of Zope as (unbearably charming) 'chrism' and try to run this method? What will be returned? <img src="FIG-6-11.gif"> Figure 6-11 - Visiting the 'user_roles' method without any credentials (e.g. from a freshly started browser) After logging out as 'chrism' and revisiting the 'user_roles' method as an unauthenticated user, you can see that Zope allows me to view the document (because it's not restricted from anonymous view by permission settings), and it considers me to possess the 'Anonymous' role. So if I have the 'Anonymous' *role* as an unauthenticated user, what *user* does it think I am? I'll construct another DTML method that will tell us the answer to this burning question named 'user_id':: <dtml-with "REQUEST['AUTHENTICATED_USER']"> <dtml-var getUserName> </dtml-with> <img src="FIG-6-12.gif"> Figure 6-12 - Viewing the 'user_id' method as an anonymous user. When Zope has no idea who you are, as far as it's concerned, you're the 'Anonymous User' (*no one* expects the Anonymous User!). The 'Anonymous User' account is a catch-all account that has a low privilege level on the system. It is not defined in any user folder, it is "built in" to Zope. You'll see it sometimes referred to as the "nobody" user as well, by convention. As a side-effect of construcing this method, we've also learned that the getUserName method of the User object returns the name of the currently logged-in user as a string. We've gone through an example of a way to programatically interrogate the User object which represents the currently logged-in user. We've also seen the way Zope treats unauthenticated users. Key points are that the User object is available via the transient 'REQUEST' object, that User objects have methods, that users without credentials possess only the 'Anonymous' role, and the username of unauthenticated users is 'Anonymous User'. Consult the Authenticated User API documentation (see the Zope Help System API Documentation section) for the full story on the other methods which exist on a User object.
Roles and Permissions
We've found out all about users, user folders, and authentication credentials. Now we'll explore roles and permissions. These two concepts are possibly the most important parts to Zope security.
Probably the easiest way to think about the concept of roles in
Zope is to compare them to the concept of groups in operating
system products. For example, in UNIX, users belong to groups by
virtue of their assignment to a primary group in the /etc/passwd
file or to their assignment to a number of groups via NIS netgroups
or a similar mechanism. In Windows NT, you can use the User
Manager utility to assign users to local groups and domain groups.
In Zope, roles relate almost directly to these concepts. Roles,
in general, serve the purpose of providing a facility to construct
groups of users.
If you're more familiar with software design engineering than operating system security, it may be helpful also to know that in Zope, roles can also be effectively used as embodiments of "actors". At Digital Creations, we construct applications for our customers using a subset of the "Rational Unified Process" (a foo-foo term for a somewhat common-sense software design and implementation process championed by the company named Rational Software). In the RUP, part of the design process is to determine the "actors" which are to make use of a proposed application. For any multiuser application, different people will need to use the application differently. For example, in an application constructed for data entry, there might be a "Data Entry Employee" actor, and a "Data Entry Systems Manager" actor, each of which would need to use the application in ways that the other might not. When constructing multiuser applications, at Digital Creations, we try to use roles to represent and classify actors within the application. Hopefully you get the idea. I'm not going to get in to the RUP further, as it's not really germane to the discussion, the topic was only introduced as a way for people familiar with software design engineering to understand Zope roles better.
In the section on User Folders, we saw how we could provide Zope users with roles via the web management interface. Let's take another look at that interface.
Figure 6-13 - The user folder management interface, viewing the
chrism
user.
We can see from Fig 6-13 that the chrism
user has the Manager
role within this user folder. What does this mean? Well, the
answer is "not much" without exposing the availablity of another
set of attributes that we haven't seen much, called permissions.
A role is a name that ties users to permissions. All Zope object instances may be protected in some fashion or another by permissions. Properly logging in as a user represents authentication of your identity (your user credentials) to Zope, which means Zope knows "who you are." The combination of roles and permissions represent a way to tie user authorization to the performance of actions, which means Zope can determine "what you can do" using what it knows about your roles in the context of the permissions defined on the object you're trying to access. While your user may be authenticated when logged in to Zope, your user may not be authorized to perform a certain action due to the permission settings of the object you're trying to access combined with your user's roles in the context of that object.
A permission is the smallest unit of access to an object, roughly
equivalent to the atomic permissions on files seen in Windows NT or
UNIX: R (Read), W(Write), X(Execute), etc. However, unlike these
types of mnemonic permissions shared by all sorts of different file
types in an operating system product, in Zope, a permission usually
describes a fine-grained logical operation which takes place upon
an object, such as View Management Screens
or Add Properties
.
It may additionally be helpful for software developers experienced
with Smalltalk (or other derived OO languages) to think of Zope
permissions as "method categories", where a permission represents
access granted to a user to execute a group of methods, as well as
a generally convenient way to classify methods. We'll get into how
methods relate to permissions a little later on in this section,
it's just a tip for now.
It's important to understand that different types of objects define different types of permissions as appropriate for the purpose of the object. Permissions may be shared among objects or they may only be meaningful for one type of object. For example, many object instances may expose the "View" permission, but it's pretty likely that only an object instance that's a part of the ZHaircut product would expose the permission "Update ZHaircut." It's vitally important, as a Product developer, to understand that you must define or reuse roles and permissions for your Product's objects as a part of Product development if you wish your Product's objects to participate in the Zope security machinery.
Because permissions and roles are just names of actions represented
by strings, they are easy to share among Products. You should
define new permissions and roles instead of reusing system-defined
ones only when you need to. For example, it would be considered
bad form to create a permission named View MyFoo
if you could
have just used the system-defined View
permission to protect the
same action with the same results. However, you should define
permissions and roles for actions particular to your Product which
you wish to keep independent of other Products. For example, you
will probably want to define an Add MyFoo
permission vs. reusing
an existing permission AddSomebodyElsesFoo
. And you might want
to reuse the Manager
role instead of creating a new
MyFooProductManager
role in your Product if you don't need to
keep the responsibilities of managing your product's independent
from those of the general Manager
role.
Let's take a look at a real-world list of roles and permissions through the Zope management interface:
Figure 6-14 - Roles and permissions in the Zope management interface.
The management screen in Fig 6-14 was accessed from the Security
tab of the root object in my local Zope installation. The column
headers at the top (Anonymous
, Manager
, Marketing
, Owner
)
represent the roles defined on my system within the context of this
object. The row headers along the left-hand side represent
permissions. The checkboxes at the intersections of the columns
and rows represent the assignment of a permission to a role in the
context of (in this case) the Zope root folder. The act of
assigning a permission to a role in the context of an object
determines which roles (groups) can perform the action
represented by the permission.
In Fig 6-14, the role Anonymous
has the permission Access
Contents Information
, and not much else. The role Marketing
has
no permissions. The role Manager
has all the permissions we
can see in this screenshot (there are more that extend below those
shown).
One of the results of this configuration is that users with the
Manager
role has the permission Add Documents, Images and
Files
. Let's concentrate on this for a moment. Permission names,
by their nature, should describe an atomic action or a set of
atomic actions. Our example seems to mean that a user possessing
the Manager
role can add documents, images, and files to the Zope
on my hard disk. Pretty easy. Except... what does that mean,
exactly? What kind of document? A DTML document? A PDF document?
What kind of file? What does Add Documents, Images, and Files
really mean? To be quite honest, I'm not really sure. The
opaqueness of permission names is one of the problems with the
current permissions implementation in Zope, and it may hopefully
soon be remedied by the ability to attach comments to permissions,
or maybe be augmented with some sort of introspection capability.
But for now, it serves as a convenient jumping off point to
describe a single permission in more detail, in which we
(literally) will find out what Add Documents, Images, and Files
really means to Zope.
Here's where it starts to get hard. Go grab a drink while I grep my hard drive.
I'm back. Having a role which allows you to Add Documents,
Images, and Files
means this: you may add Zope DTML Methods
and
DTML Documents
, Zope Image
objects, and Zope File
objects to
the container on which this intersection of permission/role
information is defined. More explicitly, it means that you can
utilize the following methods defined within the Zope core:
- You may execute the method named
addForm
defined In theOFS
package'sDTMLMethod
module - You may execute the method named
addForm
defined in theOFS
package'sDTMLDocument
module - You may execute the method named
manage_addImageForm
defined in theOFS
package'sImage
module - You may execute the method named
manage_addFileForm
defined in theOFS
package'sFile
module
Let's not sweat the gory details of what this means in terms of
Python right now or how I found the meaning of this permission name
(package
and module
are Python-specific terms which I've
neglected thus far to define, and I don't want to go into a
description of the Zope source tree right now). Instead, let's
whine! The permission name lied to us. It told us that we were
able to create documents, files, and images with its possession.
It didn't mention anything about DTML Methods, even though it
controls their creation as well. It was also vague, not telling us
what types of "documents" it allowed us to add. As it turns out,
it refers only to a DTML Document, but it would be almost
impossible for us to infer that from its name in a site with many
installed Products which define some sort of "document" object.
This is all pretty sad, but besides letting us know we need to
provide a facility for describing permissions within Zope, it gives
us an opportunity to demonstrate the importance of naming
permissions explicitly and descriptively. It also gives us the
necessary excuse to describe the logic that underlies the "pretty
name" that permissions let us show to our application users.
As shown in the Add Documents, Images, and Files
explanation, a
fact becomes evident: permissions are method groupings. When I
state that "permissions are method groupings", what I mean is that
a permission definition is made up of one or more methods that are
defined on an object which you create during Product development.
If a permission is associated with one of the methods of an object
you create, it means that a user which possesses a role which has
been granted that permission may execute that method. It also
means that a user who does not possess a role which has this
permission may not execute this method. In our example, four
methods are represented by the Add Documents, Images, and Files
permission: a constructor method for DTML Documents, a constructor
method for DTML Methods, a constructor method for Images, and a
constructor method for files. In our example, if I possess a role
that has been assigned the "Add Documents, Images, and Files' in
the context of the root folder, I may add DTML Methods, DTML
Documents, Images, and Files to the Zope root folder. Since my
user chrism
has the Manager
role in the context of the Zope
root folder, I can do all these things. However, if the (strong,
manly) chrism
user did not possess the Manager
role, I would
not be able to add these things to the Zope root folder, because I
could not make use of the methods which perform their creation, and
it does not seem that any other roles possess the necessary
permission which would allow me to do so.
Method protection and grouping is the underlying definition of a
Zope permission, and it also clarifies a statement I made earlier
having to do with a concept that might be familiar to Smalltalk
programmers: you may think of permissions as "method categories" or
"method groupings" which desribe a particular intent or set of
intentions on the part of the user. For example, if in your
Product, you define a permission named "Delete Underwear", you can
then group the methods which make up the actions associated with
deleting underwear into this permission. For example, if you've
defined a DTML method or Python Method or base class method that
actually performs the deletion of underwear named
deleteUnderwear
, you would probably want to assign the "Delete
Underwear" permission to it within your Product's declarative
security settings. Likewise, if you had another public method
which was similar, such as deleteUnderwearConditionally
(for
whatever reason), you would probably also want to associate it with
the "Delete Underwear" permission instead of making a new
permission named "Delete Underwear Conditionally". Thus, one
permission can be used to group and protect a set of methods.
User Defined Roles
Arbitrary roles may be added by Zope site managers in any object
which exposes the Security
tab in the Zope management interface
by using the User Defined Roles
form within the resulting
management screen. Let's take a look:
Figure 6-15 - User-defined role add form.
What you're seeing in Fig 6-15 is the "bottom" of the management screen shown when you visit the "Security" tab in a "folderish" object like a Folder or a ZCatalog. You'll note that the "User defined roles" section has a textbox for adding a rolename and an "Add Role" button. Why is this useful? It provides site managers a way to create "special" roles easily without creating Products. If a site manager chooses to programatically or declaratively protect objects using roles beyond the definitions that are provided by Zope or by your Product, she may do so by creating a user defined role, associating permissions with the role on particular object instances, and associating users with the role she creates. She might do this in order to provide special site security that you did not anticipate when you developed your Product. User defined roles extend a site manager's ability to granularly secure object instances.
Adding a "user defined role" to a Folder makes this role available for use within objects defined in the the Folder the role was defined in and within all objects in subfolders of the Folder the role was defined in. Due to this, we can say that user defined roles "flow down" the folder hierarchy, however they never "flow up" the folder hierarchy. If you define a role in a subfolder, its parent folder will know nothing about it. However, its child folders will. The child folder will also know about all the roles defined in all of its parent folders. This is actually true of all product-defined roles, user-defined roles, and "local roles" (which we haven't yet discussed), but it's particularly recognizable when dealing with user defined roles.
It's wise not to get too carried away with user defined roles.
Let's consider assigning user defined roles to users that have a
"home" user folder that is "above" the folder in which the user
defined role was created. Well... you can't. At least the role
cannot be assigned the role via the Zope management interface. For
example, if I create the user defined role "Gub" in the Marketing
folder of my Zope site, I will not be able to assign the "Gub" role
to the user chrism
, because his "home" user folder is the user
folder in the root of my Zope installation, which is "above" the
folder in which the "Gub" role was defined. However, the jed
user is perfectly capable of being assigned the "Gub" role, because
his "home" user folder is inside the Marketing folder, where I
defined the role.
To be honest, I don't really want to think about this too much. I'm a firm believer in making things as simple as possible. Here's the bottom line: though Zope makes almost everything possible, using Zope features expressly for the sake of their existence is just not worth it most of the time. To keep things simple, if you're not doing complicated delegation, and only if you need them, user defined roles are best created at the root folder of your Zope installation, where they're sure to "flow down" to all defined user folders. However, for complicated security setups with lots of delegation, the ability to create user defined roles in subfolders can be quite successfully combined with local roles, which we'll explore in the next section.
Local Roles
A previously defined role may be added to a single user's list of
roles explictly in the context of a single object instance and its
children using a "local role," also accessible through the
Security
management view. Local roles can be added to any object
which exposes the Security
tab in the Zope management interface.
This is something new. We've figured out that roles are a way of
grouping users. Local roles let you shortcut the limitations of
declarative assignment of roles to users in the user's user folder.
Using local roles, you can provide roles in addition to those
granted to specific users on a per-user basis only in the context
of the object on which they're defined as well as all of its
subobjects. Let's take a look at a local roles assignment screen:
Figure 6-16 - Local roles management form in the Marketing folder.
I accessed this management screen by clicking on the "local roles"
link contained within the explanation wording in the Security
tab
management interface screen of the Marketing folder. In the
resulting screen shown in Fig 6-16, we can see a brief but accurate
description of local roles in the form of explanatory text at the
very top of the page, the concepts behind which I think we've
already covered. We also see two forms.
The first form displays the local roles already granted to users in
the context of the Marketing folder. We can see from this that the
(stunning) chrism
user has already been granted the Owner
local
role for this object. We'll talk a little bit later about what the
Owner
role represents, but for now it should suffice to say that
this local role was granted to the chrism
user on the Marketing
folder as a result of the chrism
user being its creator. If I
wanted to, I could revoke this local role by selecting the checkbox
next to chrism
and clicking "Remove". I don't, so I won't.
The second form shows us a list of users and a list of roles. The
idea here is that you may select a single user to which you can
grant any or all of the roles shown in the "Roles" box. It's
interesting to note here that the list of users is a composite of
the users defined in the root user folder and the users defined in
the Marketing user folder (ie. chrism
is defined in the root user
folder, and jed
is defined in the Marketing user folder). It's
also interesting to note that the roles presented for selection are
a composite of several system roles (Manager, Owner) and several
user defined roles (Marketing
, clambake
, and gub
).
Furthermore, it's interesting to note that the user defined roles
that show up in the role box are a composite of user defined roles
which were defined on the root Zope folder (Marketing
) and two
others which were defined on the Marketing
folder (clambake
and
gub
). You didn't see me add the clambake
user defined role
because you were busy, but I added it on the root folder a while
ago.
The selection of a user in the "User" box combined with the
selection of roles in the "Roles" box and the submission of the
form will assign a set of local roles to the user within the
Marketing folder on which it is defined. For instance, I will
choose to provide the jed
user the clambake
and gub
local
roles.
Figure 6-17 - Assiging big Jed the clambake
and gub
local
roles.
Now Jed will have the clambake
and gub
roles within the
Marketing folder and within any folderish objects which get defined
beneath the Marketing folder. What does this buy us? To be
honest, I haven't really been paying attention, but let's find out.
We'll find out what roles Jed has been given in his user
definition.
Figure 6-18 - Figuring out what roles Jed has in his user definition.
Well, it seems that Jed has been granted the Manager
and
Marketing
roles within the acl_user folder his user is defined in
(which is in the Marketing folder). With the addition of these
local roles, Jed will have these effective roles when he visits
anything in the Marketing
folder and below: Manager
,
Marketing
, clambake
, gub
. Jed's 'acl_user'-defined roles
combine with the local roles assigned within the Marketing folder
object when he visits stuff inside there. We've not given Jed the
clambake
and gub
roles everywhere, just within the Marketing
folder. If we had given Jed these local roles inside the context
of a different object --for example, inside a DTML Method-- he
would only obtain the clambake
and gub
roles (plus the ones
that have already been granted to him in his user definition) when
he visited that object, and since a DTML method has no children
objects, he would have those roles in no other context unless we
explicitly assigned them to him.
Remember a while back when I said that user-defined roles created
on subfolders were a candidate for combination with local roles?
Though I'm not going to demonstrate it, it's a fact that I can't
assign the chrism
user the gub
role within his user definition
inside the user folder in which he is defined. It just won't show
up in that view, because 'chrism''s user folder knows nothing about
it. But I can provide him with the gub
role via the local
roles facility on the Marketing
folder if I so chose. It would
be kind of silly, because he's already got the Manager
role at
the root, so the local roles would not be very helpful (the gub
role wouldn't provide him any additonal access), but nonetheless, I
could do it if I wanted to. I don't, of course. This trick is
more useful if you've got otherwise unprivileged users defined at a
level "above" where you've defined a user defined role, such as in
the case that you're doing a lot of delegation mixed in with
complicated security policies. You can use local roles to get
around not being able to grant users roles within their user folder
definition by adding contextual roles to their user-folder defined
ones. Using this feature requires too much thought for my taste,
but it's there if you need it.
Local roles should be used sparingly. If all of this explanation of local roles is gibberish to you, well, don't worry about it. There will come a time during Product development that you'll run across a hard security problem that can be solved via their use. Read this section again at that time. Until then, forget everything I just said.
Proxy Roles
An "executable" object can make use of a "proxy role" definition,
which replaces the roles of the executing user when run. This
feature may be used to expand or limit access to resources when an
object is executed. Proxy roles are different animals than local
roles. Local roles add roles to a specific user in the context
of an object, while proxy roles replace roles of the executing
user in the context of an object. They are defined by visiting the
Proxy
tab in the management interface of any executable object.
Executable objects include DTML Methods, DTML Documents, SQL
Methods, External Methods, Python Methods, and Perl Methods, so the
management interface of these kinds of objects is where you'll see
the Proxy
tab. Let's take a look at the Proxy
tab of an
existing DTML Method:
Figure 6-19 - Proxy roles dialog of the user_list
DTML Method.
In Fig 6-19, we have the option of defining proxy roles which grant
any level of access between "make no change" (select none of the
roles in the proxy roles box and click "Change") and "all roles"
(select Anonymous
, Manager
, Marketing
, Owner
, and
clambake
and click "Change"). Or anything in between (for
example, just Owner
).
When we select a single role or a set of roles from the
dialog and click "Change," we're defining the effective roles of
any executing user at execution time as those roles which we choose
in the dialog. This can be used to extend access (for example,
choosing a Manager
proxy role would allow this DTML method to do
manager-level things even if an executing user did not have the
Manager
role assigned to him via a local role or user
folder-defined role. This can also be used to limit access. For
example, choosing the Anonymous
proxy role would limit this DTML
method to executing with the Anonymous
role even if the executing
user has high privilege. This behavior is useful because it allows
us to change the privilege level of an executing user in the
context of a single object's execution. I might want to set up a
proxy role on a DTML method which creates an object which, under
normal circumstances, the executing user would not be permitted to
create.
On a per-object basis, proxy roles can only be assigned to
potential executors out of the pool of effective roles possessed by
the object's owner. What this means is that the selectable proxy
roles shown in the Proxy
screen will always be limited to the
effective roles possessed by the owner of the object. We haven't
talked about ownership much yet, but it's coming up soon. Suffice
to say that this feature allows safe delegation of the ability to
create proxy roles in an environment with complicated security.
If you don't know why you would need proxy roles, chances are you don't. Don't make life hard on yourself, and keep it simple by not trying to use them until you run across a situation where you definitely think you might need them.
Roles Defined by Products
Roles may additionally be defined by Product authors as a part of their Product definition code. Regardless of whether a role is created manually by a Zope site manager or automatically by a Product author as a result of a site manager installing the Product which creates an additional role or set of roles, the resulting roles are treated identically by Zope.
Permissions Defined by Products
Permissions may only be defined by Products and Zope core code. Certain incantations within your Zope code add Product-specific permission definitions to the Zope instance in which your Product is installed.
Ownership
Because Zope is completely web-manageable, and because it has a sometimes mind-boggling assortment of security features, it allows you to do some astoundingly cool things quite safely. For instance, you can let arbitrary user run DTML, Python, or Perl code on your Zope-powered webserver with a fair level of assurance that those users will not be able to gain access to protected areas of your site or otherwise deface it. I personally really get a kick out of this. An example of this feature in action is the Zope.org site, which allows anyone to sign up and script DTML (and possibly by the time you read this, Python and Perl) in their own member space to their heart's content.
We call this ability "safe scripting." Because DTML, Python, and Perl code created by anonymous or pseudo-anonymous Web users in Zope is "safety checked" before it executes, providing this level of access this isn't complete Internet security suicide, as it would be if you for instance gave random visitors the ability to construct and execute scripts on your server via a web interface in raw Perl via CGI. Safe scripting, in simple terms, assures that people can't do bad things on your website while scripting, while still extending them the power to do useful things via presentation and scripting languages such as DTML, Python, and Perl. It's also a piece of the puzzle that lets you delegate content management power on a Zope-powered website. You need not do anything special to "turn this feature on", it's just... there. If users have the appropriate permissions, they can add executables to your Zope site.
As I've stated a number of times in this guide, using Zope features solely because they exist is not a smart idea. If you don't need to extend users the ability to enter script code into your website.. don't do it. But in case that it's appealing to be able to allow untrusted or semitrusted folks to contribute script content to your website, by all means, feel free to give users access to do so, because they really can't do too much damage. The concept in Zope of ownership is one facility that makes this a reality.
When a user creates content in Zope, she automatically becomes the
Owner
of that content. For example, if I create a DTML Method
with an id
of create_management_user
in the root folder of my
local Zope installation like so:
<dtml-with acl_users> <dtml-call expr="REQUEST.set('name', 'fudgeguy')"> <dtml-call expr="REQUEST.set('password', 'fudge')"> <dtml-call expr="REQUEST.set('confirm', 'fudge')"> <dtml-call expr="REQUEST.set('roles', ['Manager'])"> <dtml-call expr="REQUEST.set('domains', '')"> <dtml-call expr="manage_users('Add', REQUEST)"> </dtml-with> Success!
Can you guess what it does? When a trusted user runs it, it adds a
fudgeguy
user and in doing so, provides him with the Manager
role within the top-level user folder. Holy hat! That's a pretty
powerful little DTML method. But if just any old user could
construct this method and run it successfully we'd be in trouble,
for obvious reasons. If Joe Bloggs could make this DTML method
work successfully, he could then log in as this user and would at
this point probably have complete control of your Zope site. Not
good.
Don't worry about it, because it just can't happen unless you
really mess up by giving Joe the Manager users
permission. If we
added joe
to our root acl_users folder, and we gave the
clambake
method all permissions except Manage users
, and we
subsequently granted the joe
user the clambake
role, Joe will
be able to add DTML into our site, he'll be able to view the
management screens, he'll be able to poke around in everybody's
stuff, he'll be able to create database connections, and so on.
He'll be able to just about anything. But when he tried to run the
DTML that composes the create_management_user
method, he'd see
this:
Figure 6-20 - joe
trying to view the create_management_user
DTML method
Denied! In Fig 6-20 we see the power of roles and permission in
action. A user can only access methods contained in permission
assigned to a role which he has been granted. Since Joe has the
clambake
role in the context of the root folder, and the
clambake
role hasn't been granted the Manage users
permission,
Joe cannot access the manage_users
method of the acl_users
folder. This is old hat. We've been over this before.
Things however get a little trickier when we add the concept of ownership into the mix.
Since Joe has the capability of adding DTML to my site, he could
easily construct a DTML method with the body of what I put in the
create_management_user
method. Though he could not successfully
execute the code himself, he could get tricky. If he lured the
unsuspecting (but still devilishly handsome) chrism
management
user into executing a method he created that has this body, without
ownership playing a part it would be possible for Joe to trick
chrism
into executing the method by just visiting its URL. Maybe
he would post a news item on my site that said "Cool Stuff At This
Link, Click Here!!" at which point I would be absolutely compelled
to click a URL which led to the create_management_user
method
while I was logged in as the chrism
management user. Without the
concept of ownership, the DTML method would happily execute, and
the fudgeguy
user would be added to the root user folder. Joe
could then could log in as the fudgeguy
user and have free reign
of the site by virtue of fudgeguy
possessing the Manager
role.
Well, luckily, this can't happen either, thanks to ownership.
"Executable" objects (DTML Methods, DTML Documents, SQL Methods,
Python Methods, Perl Methods, and External Methods) execute with
effective permissions equal to the intersection of the permissions
of the object's owner and the executing user. When Joe creates
his version of the create_management_user
DTML method (he's
cleverly named it get_me_some_manager_access
), Joe becomes that
method's owner. Whenever the get_me_some_manager_access
method
is executed, it will execute with the effective intersection of
permissions between Joe's user (as the owner) and the executing
user. This means in our specific example that access will be
granted to the manage_users
method of the acl_users
object
during the execution of the method only if both the executing user
and Joe have the appropriate permission to be able to execute it.
In reality, this means that the method can never be successfully
executed by anyone, due to Joe's lack of the Manage users
permission. If chrism
tries to execute Joe's trojan
get_me_some_manager_access
method, the system will reject the
request because the intersection of permissions between chrism
and joe
does not allow access to the manage_users
method. The
rejection of the request will cause an "Unauthorized" exception to
be raised by Zope, and the transaction which encapsulates the
request will not be completed, thus no harm is done.
I know this is compilcated. Don't let all this scare you, it's complicated, but it's mostly complicated only because it's highly dynamic. You also needn't worry much about it unless you intend to allow untrusted or semitrusted users to add executable Zope content to your site. It's convenient to think of executables on a Zope site as equivalent to executables in UNIX, they're just much easier to run. In UNIX, you need to type "rm -rf " or something similar as a privileged user to do real damage, or at least you need to type in the name of a shell script which contains an equivalent command. Executing programs in Zope is much easier. You run executables by viewing them, which usually consists of single-clicking on a link. Because they're so easy to run, and because web browsers allow redirection without the consent of the operator in which HTML can direct your browser to a URL without your knowledge, the restrictions imposed on executable content via ownership in Zope are a highly Good Thing. If these restrictions didn't exist, it would be possible for someone to construct a page internal to your site which did bad stuff* when you clicked on them or were redirected to them.
*NOTE: The reason we know all about this problem is because Zope version prior to version 2.2 do not have the concept of ownership. All the bad things that ownership protects you against can easily happen to you under Zope versions prior to 2.2. It's highly recommended that if you're running a version of Zope prior to 2.2 that you upgrade.*
It's also beneficial to note here that your browser can make you vulnerable to security breaches within Zope whether you're running Zope 2.2 or an earlier version. Whatever version of Zope you're using, these are good rules to follow (with or without the ownership concept):
- Do not view untrusted content while logged on with privileges that could be abused and always end your session with those privileges as soon as your work is done.
- Create temporary users with "just enough privileges" to do a particular task and perfom as much work as possible with these restricted privilege users.
- Turn off JavaScript in your browser while administering the site! People can do tricky things with JavaScript.
- Keep an eye out for suspicious activity - looking at the "undo" tab on the top-level folder of your site is a good way to get an idea of who has been doing what.
- Keep good backups and if possible avoid packing your site - that will give you the most options for undoing site changes if you need to.
Other important things about ownership:
- The superuser cannot own anything. This is what makes it crippled. This is on purpose.
- An executable does not store direct a reference to its owner. Rather, a tuple consisting of the physical path to an owner's user folder and the owner id will be used to look up the owner.
- Ownership is mostly independent of the "Owner" local role. The
"Owner" local role concept predates Zope 2.2, and for backwards
compatibility purposes, its been necessary to keep it, despite
its redundance in the face of real ownership. Steps have been
taken to make this a little more palpable, however. Whenever an
owner is changed, the new owner will get the owner local role on
the object. The one exception to this rule is that the
Anonymous User
user never gets the owner role. - Only one user can be the owner of an object, while many users can have the owner local role on an object. Owner local roles can be changed without changing the owner.
- If the owner of an object is deleted, the object becomes owned by
the special
nobody
user. - Owners can be changed. Users possessing the "Take ownership" permission will be able to take ownership of objects via the object's "Ownership" tab of the Zope management interface. Note that ownership may not be given away, but only taken explicitly.
- Ownership is not effected by editing an object. An object's ownership does not change as a result of editing it or changing it.
- Ownership of an object is assigned to the copying or importing
user when an object is copied or imported.
- Objects created by Zope itself (such as objects created during
Product initialization) are created without an owner. Objects in
the Control Panel are never owned. There is a special Zope user
named
system
that performs any operations done during startup. Thesystem
user is allowed to create objects and the objects created bysystem
are either unowned or acquire their ownership from the place they are added. - If an unowned executable is called directly or indirectly from another executable, then the executable can access resources as long as the person running the executable can.
- It is not possible to contrive to make an object unowned outside
of the Control Panel without having
Manager
privileges.
- Objects created by Zope itself (such as objects created during
Product initialization) are created without an owner. Objects in
the Control Panel are never owned. There is a special Zope user
named
Warts and Gotchas
What now comprises Zope has been around now in various forms since about 1995. It's now the year 2000. The period in between then and now is equivalent to six hundred million Internet years (according to my estimations). Over that period of time, Zope has collected its fair share of warts that are difficult to remove for backward-compatibility reasons. You'll undoubtedly be bitten by some of these, so this is an important section. In later versions of Zope, it's likely that they will be phased out and ultimately removed.
manage_*
Methods
Python Product methods whose names begin with the string "manage_" ("manage-under" methods) are only accessible to users who possess the "Manager" role, no matter what permissions you set on them. I dont know the history behind this, so I can't comment on it further. This does not hold true for methods of ZClass-based Products.
Python Base Class Methods Without A Docstring
Methods in Python base classes or within Python-based Product classes may have "docstrings". This is a feature of the Python language. An example:
def mymethod(self): """ This is a docstring """ pass
This method (named mymethod) has a docstring with the content "This is a docstring." Zope treats Python base class and Python Product methods with docstrings "specially." If a Python base class or Product method contains a docstring, it indicates to the Zope publishing machinery that this method is "web publishable", which means it may be called directly from the web. It means that someone can type the method's URL into a browser and be presented with the results of the method. If it does not have a docstring, however, it may not be accessed directly through the web. Methods without docstrings when called from a URL will cause the publishing machinery to return a "Not Found" error. These methods, however, may be called via DTML or other scripting facilities on the same Zope site. This is a "hack" that will most likely be replaced by a permissions-based restriction in future Zope versions.
Methods And Attributes Which Begin With An Underscore Character
Methods and attributes with a leading underscore such as "_myattribute" or "_mymethod()" defined in Python Products may not be accessed via DTML or other through-the-web scripting facilities.
Python Product Security ("Disk-Based")
Finally, we've reached a point where we can start applying all the security knowledge we've accumulated in the sections above. Zope allows you to create a Product in two ways: via a "Python Product", which is a collection of Python modules and DTML files on your OS' filesystem, and via a "Through The Web Product", which is a collection of ZClasses. This first section deals with the security of "disk-based" Python Products.
A Simple Python Security Demonstration Product (PythonForum)
A simple ("disk-based") Python Product named PythonForum
has been
developed with the intention of showing the Zope security machinery
"in action". It's a small Zope Product written entirely in Python
that shows delegation, role manipulation, programmatic security,
and other security features. It is available on the Zope.org site
a few clicks away from the URL http://www.zope.org/Members/mcdonc.
Some of the upcoming sections will refer to this Python Product as
a reference point. It's probably a good idea to install it, or at
to at least unpack it and examine the source code.
Download and install the PythonForum Product by expanding it into your "top-level" Zope folder (if you're on Windows, use WinZip to expand it). A directory named "PythonForum" should show up in your lib/python/Products directory. Then restart your Zope, and instantiate a PythonForum as a user with "Manager" privileges, and play around with it a little bit. While you're playing around with the product itself, browse the source code. There are lots of comments in the source code that try to explain what is going on at each step. PLEASE NOTE THAT THIS PRODUCT WORKS ONLY WITH ZOPE 2.2.1 and GREATER! If you have an earlier version of Zope, either upgrade it or install the PythonForum product on a independent copy of Zope 2.2.1 or greater.
For your convenience, colorized Python source code files (and non-colorized DTML source files) from the PythonForum Product are available online. They are referenced lightly in the succeeding sections.
- Forum.py
- __init__.py
- constructForumForm.dtml
- constructCommentForm.dtml
- viewForum.dtml
- viewComment.dtml
The Zope Python Product Initialization Process
Zope permits access to resources deterministically. When you code your Product in Python, certain declarative assertions made by you within your Product code tip Zope's security machinery off to what your intentions are for allowing or denying object access. These declarative security assertions are gathered for the most part during Product initialization. Production initialization happens in two places:
- when your Product's modules are imported.
- when your Product's
__init__.py
module'sinitialize()
function is called.
The initialization process serves the purpose of processing the declarative security assertions made within a Product's code.
Python Product Declarative Security Assertions
Declarative security assertions are part of class and attribute definitions made by your Product. It's important to know that in the below descriptions, the word "attribute" can mean either a Python method (as defined by a "def" statement) or a Python attribute (as defined via assignment). There are four major assertions to be made as part of Zope class and attribute definition:
__roles__
- a class attribute or an attribute of an attribute which explicitly defines the roles required to access the protected item when an instance of the class or an attribute is called by through-the-web code. Explicitly setting roles for instances and attributes is not frequently necessary.__class_init__
- a method of Zope classes called to initialize security settings for Zope when the class is imported and processed by the initialization machinery. This almost always delegates to the functionGlobals.__default_class_init__
. This method must be defined on all classes that do not inherit from the ZopePersistent
class for the class' security declarations to be processed.__ac_permissions__
- a class attribute which is a tuple representing the permissions necessary to access attributes of the class when an instance of the class is called by through-the-web code. This is the major workhorse in declarative security.__allow_access_to_unprotected_subobjects__
- a class attribute which, when true, allows subobjects of an instance that are not protected by a definition in the instance's__ac_permissions__
to be accessed by through-the-web code. When this attribute is false, class and instance attributes of the class which are not listed in__ac_permissions__
cannot be accessed by through-the-web code. When this attribute does not exist, the default is to not allow access to unprotected subobjects (e.g. the same as__allow_access_to_unprotected_subobjects__=0
). Setting this attribute to "true" reduces the potential "secureness" of your Product. It's wise to set this attribute to false and declare all the attributes you want to be accessible within__ac_permissions__
.
The Cumulative Nature of Python Product Security Declarations
Security declarations are generally made as a part of a class definition within Zope. Because Python classes support multiple inheritance, it's possible that a class may inherit from other classes which themselves declare security assertions. It's important to know that you only need to protect attributes and methods of your class with declarative security assertions. You need not bother trying to protect methods and attributes inherited by your class via its base classes unless you wish to explicitly redefine their existing security assertions. These class' security assertions are processed independently from your derived class during class security initialization.
__roles__
MechanismPython, the language in which Zope is written, has no built-in security features. Thus, as Zope evolved, the security features necessary for web collaboration were implemented on top of Python, but at a very low level. Zope more or less extends Python by implementing libraries and initialization routines to meet the security requirements of a collaborative application server.
The "lowest" level of this security is implemented by Zope's parsing of Python Product classes for "role" declarations. When a Python product is initialized, if the proper directives are provided within the code of the classes defined, Zope will protect attributes and methods defined within the classes.
Let's take a look at part of the Comment class of the PythonForum
Product. Particularly, let's look at the explicit __roles__
declarations made within the class, paired with the attributes they
protect:
mypublicattribute = "public" mypublicattribute__roles__ = None myprivateattribute = "private" myprivateattribute__roles__ = () mysemiprivateattribute = "semiprivate" mysemiprivateattribute__roles__ = ('Manager',) The Importance of '__class_init__' To The Class Initialization Process The "flag" that triggers Zope's security-declaration-initialization process is a class attribute named '__class_init__'. Classes which inherit from the Zope 'Persistent' class have this attribute predefined for them. *Classes which do not inherit from Persistent need to declare it explicitly*. Declaring this attribute is almost always the same for any class that does not inherit from the Persistent class. Here's the common idiom, by example:: import Globals class Foo: """ The foo class """ # initialize the security declarations of the class __class_init__ = Globals.__default_class_init__ Don't think too hard about what this means. Just know that if you have a class which does not inherit from Persistent that has methods or attributes that need to be accessed from DTML or other through-the-web code, you must define a '__class_init__' for it if its security declarations are important to you. You may also see this common idiom in Zope code at the module level:: Globals.__default_class_init__(MyClass) This performs exactly the same operation as defining a '__class_init__ = Globals.__default_class_init__' class attribute in your Zope class, it's just a different way of "spelling" it. This the way the PythonForum Product initializes security on its classes (see Forum.py). When in doubt, and you're not sure whether your Zope Product class needs a '__class_init__', go ahead and define it as '__class_init__ = Globals.__default_class_init__' (make sure to 'import Globals' first). Adding this "incantation" to a class that doesn't need it won't hurt anything, and it may save you some debugging time later.
The Python Product Security Workhorse (__ac_permissions
)
__ac_permissions__ = (
(View
,
[getBody
,getTitle
,getUserName
,getCreationDate
,view
,
'index_html'],
[Forum Participant
,'Manager']),
)
A ZClass Product
No ZClass product has yet been developed, I'm afraid. Hopefully, this will be "soon to come" (once I have a few days).
The Zope Security Policy
The Zope Security Policy is the primary Zope security policy implementation, although it may (infrequently) be replaced by user-defined policies. This policy is used when validating access to objects from DTML and other through-the-web code (such as PythonMethods and PerlMethods). It is not honored when disk-based Python code is the caller. Here is a summary of the policies implemented by the Zope standard security policy on through-the-web code:
- When performing a "named" access, if the name requested starts
with
aq_
, then the name must beaq_parent
oraq_explicit
. - If the value being retrieved doesn't have roles:
- If the value was acquired, then access is denied.
- If the value was not acquired:
The container is checked for a security assertion that defines how access should be granted to unprotected subobjects. The container can have a security assertion in two ways:
- The container can have an attribute
__allow_access_to_unprotected_subobjects__
, which has the assertion as a value. - An item can be added to the dictionary at
AccessControl.SimpleObjectPolicies.ContainerAssertions
with the container type as a key and the assertion as a value. This mechanism is used to make assertions for Python extension types like standard Python types, such as lists and dictionaries.
An assertion can take 4 forms:
- It can be None, in which case, access is denied to all unprotected subobjects.
- It can be a Python integer, in which case, access is denied to all unprotected subobjects if the value is 0 and may be allowed otherwise.
- It can be a Python dictionary, in which case access may be allowed if the dictionary has a true value for the name used, if the access was a named access, or for None, if the access was not a named access. If the dictionary doesn't have a value or has a false value, then access is denied.
- It can be a callable object, in which case, it will be called with the named used for access or None, if the access was not a named access, and the value accessed. Access may be allowed if the value returned from calling the assertion is true and denied otherwise.
If the container doesn't have a
__roles__
attribute, directly or through acquisition, then access is allowed, assuming that there was an appropriate unprotected sub-object assertion.If the container has a
__roles__
attribute, directly or through acquisition, then the roles value is used as if it was found in the accessed value. Assuming that there was an appropriate unprotected sub-object assertion, then the roles will be checked as described below. - The container can have an attribute
- If the value's roles (including roles gotten via the value's
container) is None or contains
Anonymous
, then access is allowed. - If the authenticated user has any of the value's roles (including roles gotten via the value's container), then access is allowed.
- If an Executable Object is being run and the Executable Object
has a
proxy_roles
attribute, then access is provided if the proxy roles overlap the accessed value's roles (including roles gotten via the value's container). - If none of the above three conditions apply, then access is denied.
Programmatic Security
Zope provides a set of related APIs for programatically manipulating and checking security during code runtime. These APIs exist:
- Security Policy
- Security Management
- Security Manager
- DTML Security API
- User Object API
Together, they form a convenient mechanism for validating user object access while you're coding in DTML or Python.
Security Policies
Security Policies are generally used to determine whether access should be granted to an object. The Security Policy used within Zope is almost always the default Zope Security Policy (discussed in the previous section). Security Managers (yet to be discussed) delegate access checking to Security Policies. Security Policies are objects which implement the following methods:
- validate(accessed, container, name, value, context)
- Validate access .
Arguments:
- accessed
- the object that was being accessed
- container
- the object the value was found in
- name
- The name used to access the value
- value
- The value returned by the access
- context
- A Security Context, which provides access to information such as the context stack and AUTHENTICATED_USER.
Example:
None. You generally need not call this unless you're coding for the Zope core.
- checkPermission(permission, object, context)
- Check whether the
security context allows the given permission on the given object.
Arguments:
- permission
- A permission name
- object
- The object being accessed according to the permission
- context
- A SecurityContext, which provides access to information shuch as the context stack and AUTHENTICATED_USER.
Example:
None. You generally need not call this unless you're coding for the Zope core.
Security Management
The security management interface exists to provide access to Security Managers and to provide a way to change the system Security Policy. The interface is implemented by the AccessControl package (in lib/python/AccessControl).
- getSecurityManager()
- Get a SecurityManager object.
Example:
import AccessControl security = AccessControl.getSecurityManager()
- setSecurityPolicy(a SecurityPolicy)
- Set the system default
security policy. This method should only be caused by system
startup code. It should never, for example, be called during a
web request.
Example:
None. You probably never want to replace the default security policy.
Security Manager
A security manager provides methods for checking access and
managing executable context and policies. It is used in the Zope
core extensively. Most Product authors will use the
checkPermissions
and getUser
methods it defines, but will
probably not be all that interested in the other methods it
defines, as they are generally handled "magically" by the Zope
Security Policy. A Security Manager object has the following
methods:
- validate(accessed, container, name, value)
- Validate access.
Arguments:
- accessed
- the acquisition-wrapped object in which the value object was accessed.
- container
- the actual container object in which the value object was found.
- name
- The attribute name used to access the value object.
- value
- The value object retrieved though the access (the object itself).
The arguments may be provided as keyword arguments. Some of these arguments may be ommitted, however, the policy may reject access in some cases when arguments are omitted. It is best to provide all the values possible.
A boolean value is returned indicating whether the value is accessible. An Unauthorized exception may be raised in some cases.
This method is useful to check security assertions within the context of other objects. For example, an object may be accessible when accessed in the context of a different object, but may not be accessible when accessed directly.
Example:
import AccessControl security = AccessControl.getSecurityManager() security.validate(Portal.Works,Portal, 'Foo', Foo) This example provides the case of an object accessed from DTML named "foo". In a DTML method that is called via /Portal/Works/foo, the "foo" object is *accessed* via acquisition (the first argument). But it actually "lives" in the "Portal" *container* (the second argument). Its *name* is the string 'Foo' (the third argument). Its has the *value* of the Foo object (the fourth argument). The call to geSecurityManager.validate() performed on it would return a boolean value indicating whether this object can be accessed in the context of its container and acquisition path, and as determined by the effective Security Policy and aggregated permissions associated with the object. You probably never need to call this during Product coding, but it's available.
- validateValue(value)
- Validate access. This is a shortcut for
the common case of validating a value without providing access
information.
A boolean value is returned indicating whether the value is accessible. An Unauthorized exception may be raised in some cases.
Example:
import AccessControl AccessControl.getSecurityManager().validateValue(Foo) This example provides the case of an object value validated outside the context of any container or acquisition context. You probably won't need to call this during Product coding.
- checkPermission(permission, object)
- Check whether the security
context allows the given permission on the given object. Return a
boolean value.
Arguments:
- permission
- A permission name
- object
- The object being accessed according to the permission
Example:
import AccessControl AccessControl.getSecurityManager().checkPermission('Add Folders', Foo) Checks whether the user executing the method has the 'Add Folders' permission in the context of the 'Foo' object. This is useful during Product coding.
- addContext(anExecutableObject)
- Add an ExecutableObject to the
current security context.
There is no return.
Example:
None. You probably never need to call this during Product coding.
- removeContext(anExecutableObject)
- Remove an ExecutableObject.
There is no return.
- getUser()
- Return the authenticated user.
Example:
import AccessControl AccessControl.getSecurityManager().getUser() This is equivalent to something like REQUEST['AUTHENTICATED_USER'] but is a bit cleaner, especially if 'REQUEST' isn't handy.
- calledByExecutable()
- Determine if called through an ExecutableObject.
A true value is returned if any ExecutableObjects have (directly or indirectly) called the current method and a true value otherwise.
This can be used to determine if an object was called (more or less) directly from a URL, or if it was called by through-the-web provided code.
Example:
None. You probably never need to call this during Product coding.
DTML Security API
The DTML Security API an interface implemented by the DTML
namespace (aka _
) that provides tools for checking access. It
provides the following methods:
- SecurityValidate(accessed, container, name, value)
- Validate access.
Arguments:
- accessed
- the object that was being accessed
- container
- the object the value was found in
- name
- The name used to access the value
- value
- The value retrieved though the access.
The arguments may be provided as keyword arguments. Some of these arguments may be ommitted, however, the policy may reject access in some cases when arguments are ommitted. It is best to provide all the values possible.
Example:
None. You probably won't need to call this during DTML coding.
- SecurityValidateValue(value)
- Convenience for common case of
simple value validation.
Example:
None. You probably won't need to call this during DTML coding.
- SecurityCheckPermission(permission, object)
- Check whether the
security context allows the given permission on the given object.
Arguments:
- permission
- A permission name
- object
- The object being accessed according to the permission
Example:
To check whether it's possible to add a folder to folder ACME: <dtml-if "_.SecurityCheckPermission('Add Folders', ACME)">
- SecurityGetUser()
- Get the current authenticated user
Example:
To print the current username: <dtml-var "_.SecurityGetUser().getUserName()"> This is equivalent to something like REQUEST['AUTHENTICATED_USER'] but is a bit cleaner, especially if 'REQUEST' isn't handy.
- SecurityCalledByExecutable()
- Return a boolean value indicating
if this context was called by an executable
Example:
None. You probably won't need to call this during DTML coding.
The end. Sorry for the abruptness of this ending. I had hoped to wrap things up a little more cleanly, and to give a blow-by-blow narrative of the PythonForum product and a ZClass product, but in the interest of time, I've decided to release this document "as is". Please send comments to [email protected].
This document is part of a continuing effort to devise a "Zope Developer's Guide". If you're interested in contributing, or if you have comments, please drop me a line.
- General
- The Superuser
- User Folders
- Authentication Credentials
- Users
- Roles and Permissions
- Roles as "actors"
- Permissions as "method categories"
- User-defined Roles
- Local Roles
- Proxy Roles
- Roles Defined By Products
- Permissions Defined By Products
- Ownership (jim's server-side trojan docs)
- Warts and Gotchas
- manage_* methods
- methods that start with an underscore
- Python base class methods without a docstring
- Programmatic Security (interfaces wiki SecurityPolicies)
- SecurityManagement
- SecurityPolicy
- SecurityManager
- DTMLSecurityAPI
- In ZClass-based products
- Permission objects
- Permission mappings
- In Python-based products
- Securing your product methods
- __ac_permissions__
- __class_init__
- NO __ac_roles__
- __roles__
- NO __call__.__roles__ (BaseRequest.py)
- NO __allow_groups__
- Globals.default_class_init__(ob) and the role of the Persistent class
- __allow_unprotected_access_to_subobjects__
- the role of the "RoleManager" class
- how ExtensionClass behavior facilitates security (extension classes can have attributes on methods, which define security)
- Securing your product methods