1. Create the database tables.
Create
the following tables in the database (this is the postgres SQL to
create the tables). I choose the normalize the database and use unique
ids to link the tables together, you could do without the ids and just
use the username and groupname as keys if you like:
CREATE SEQUENCE "user_id_seq"; CREATE SEQUENCE "group_id_seq"; CREATE TABLE "users" ( "user_id" int4 NOT NULL, "username" text, "password" text); CREATE TABLE "groups" ( "group_id" int4, "groupname" text); CREATE TABLE "user_group_map" ( "user_id" int4, "group_id" int4); CREATE UNIQUE INDEX "users_pkey" on "users" using btree ( "user_id" "int4_ops" );
2. Patch Zope to allow the crypt module to be called.
If you want to encrypt your passwords you have two options:
- a. if you don't mind changing the source code you can patch the Zope sources to add 'crpyt' to the list of modules that Zope allows to be called from DTML.
- b. if you don't want to change the source code you can add an External Method to perform the encryption.
I
only deal with the first method, because it is the one I chose. You can
skip this step and return to it after you have everything running
without encryption.
Find
the file [ZOPEHOME]/lib/python/DocumentTemplate/DT_Util.py, you will
need to have access to the filesystem on which Zope is installed to be
able to do this.
Take a copy of the file encase you mess things up.
Find the part of the file that looks like this:
d['string']=string d['math']=math d['whrandom']=whrandom try: import random d['random']=random except: pass
Insert the follow code fragment (is purpose is self explanatory):
try: import crypt d['crypt']=crypt except: pass
The code should now look like this:
d['string']=string d['math']=math d['whrandom']=whrandom try: import crypt d['crypt']=crypt except: pass try: import random d['random']=random except: pass
You must now restart Zope so that the changes to the source will take effect.
3. Create an instance of GUF.
The
ground work is now in place and you can start the work inside Zope.
First task is to set up GUF as it comes 'out-of-the-box'. You should
read the instructions on the README tab of the product.
It
is best to play with acl_users folders in a Zope folder that does not
contain anything important until you get it working. It is possible to
lock yourself out of a folder if you get things wrong.
Create a new folder to testing things out call it 'test_guf' and select both 'Create public interface' and 'Create user folder'.
Delete the 'acl_users' folder in 'test_guf'.
Create a GenericUserFolder called 'acl_users' in 'test_guf'.
Set the permissions as directed in GUF readme
Now ensure that you can log in as jorge:secret
4. Create a database connection.
Insert a 'ZPyGreSQLDA' object in the acl_users folder and give it the connection string for the database created in step 1.
5. Create the SQL methods to access the database.
Now we must create the SQLMethods used to access the database. We create the following methods:
SQL_add_use | Adds a new entry to the usertable and sets a default role. |
SQL_change_password | Changes the password of an existing user |
SQL_get_password | return the password for a given user |
SQL_get_roles | return the roles set for a given user |
SQL_get_user_list | return a list of all the users |
This
is not a complete list of all of the functions that you may need to
perform, for instance you would probably want to delete users, change
roles, add roles etc. but it gives a reasonable idea from which you can
role your own.
The contents of these methods follows:
SQL_add_user | |
Arguments | username password |
insert into users values (nextval('user_id_seq'), <dtml-sqlvar username type=string>, <dtml-sqlvar password type=string>) <dtml-var sql_delimiter> insert into user_group_map (user_id, group_id) select currval('user_id_seq'), group_id from groups where groupname = 'Subscriber' |
SQL_change_password | |
Arguments | username password |
update users set password = <dtml-sqlvar password type=string> where username = <dtml-sqlvar username type=string> |
SQL_get_password | |
Arguments | username |
select password as real_password from users where users.username = <dtml-sqlvar username type=string> |
SQL_get_roles | |
Arguments | username |
select groupname from groups, users, user_group_map where users.username = <dtml-sqlvar username type=string> and users.user_id = user_group_map.user_id and groups.group_id = user_group_map.group_id |
SQL_get_user_list | |
Arguments | None |
select username from users |
6. Customize the GUF instance to call the SQL methods.
Now we must change the DTML in the GUF default methods so that they call our new SQL methods:
If you are going to use encrypted passwords change userAuthenticate to:
<dtml-in "SQL_get_password(username=username)"> <dtml-if "_.crypt.crypt(password,'ab')==_['sequence-item'].real_password"> <dtml-return "_.int('1')"> </dtml-if> </dtml-in> <dtml-return "_.int('0')">
If you are not using encrypted passwords change userAuthenticate to:
<dtml-in "SQL_get_password(username=username)"> <dtml-if "password==_['sequence-item'].real_password"> <dtml-return "_.int('1')"> </dtml-if> </dtml-in> <dtml-return "_.int('0')">
change userList to:
<dtml-return "SQL_get_user_list()">
change userRoles to:
<dtml-call "REQUEST.set('ret','')"> <dtml-in "SQL_get_roles(username=username)"> <dtml-call "REQUEST.set('ret', ret + ' ' + _['sequence-item'].groupname)"> </dtml-in> <dtml-return ret>
7. Create user forms to manage passwords.
To demonstrate how to create forms that can be used to administer you users try to following for and response method:
create a DTMLMethod called change_password_form containing:
<HTML> <HEAD> <TITLE>Change Password</TITLE> </HEAD> <BODY BGCOLOR="#FFFFFF" LINK="#000099" VLINK="#555555"> <h1>Change Password</h1> <FORM ACTION="change_password_report" METHOD="POST"> <TABLE> <TR> <TD ALIGN="LEFT" VALIGN="TOP"> <STRONG>Username</STRONG> </TD> <TD ALIGN="LEFT" VALIGN="TOP"> <INPUT TYPE="text" NAME="username" SIZE="20"> </TD> </TR> <TR> <TD ALIGN="LEFT" VALIGN="TOP"> <STRONG>Password</STRONG> </TD> <TD ALIGN="LEFT" VALIGN="TOP"> <INPUT TYPE="PASSWORD" NAME="password" SIZE="20"> </TD> </TR> <TR> <TD ALIGN="LEFT" VALIGN="TOP"> <STRONG>New Password</STRONG> </TD> <TD ALIGN="LEFT" VALIGN="TOP"> <INPUT TYPE="PASSWORD" NAME="first_password" SIZE="20"> </TD> </TR> <TR> <TD ALIGN="LEFT" VALIGN="TOP"> <STRONG>Confirm</STRONG> </TD> <TD ALIGN="LEFT" VALIGN="TOP"> <INPUT TYPE="PASSWORD" NAME="second_password" SIZE="20"> </TD> </TR>
create a DTMLDocument called change_password_report called:
<!--#var standard_html_header--> <dtml-call "REQUEST.set('val','0')"> <dtml-in "SQL_get_password(username=username)"> <dtml-if "_.crypt.crypt(password,'ab')==_['sequence-item'].real_password"> <dtml-call "REQUEST.set('val','1')"> </dtml-if> </dtml-in> <dtml-if "val == '1'"> <dtml-if "first_password==second_password"> <dtml-call "REQUEST.set('password',_.crypt.crypt(first_password,'ab'))"> <dtml-call SQL_change_password> <h2>Your Password has been changes</h2> <p>Thank you.</p> <dtml-else> <h2>Failed</h2> <p>Passwords don't match</p> <p>First: <dtml-var first_password></p> <p>Second: <dtml-var second_password></p> </dtml-if> <dtml-else> <h2>Failed</h2> <p>Old Password does not match.</p> </dtml-if> <!--#var standard_html_footer-->
8. Set permissions to protect the use database.
It
is now time to setup the permissions on the GUF objects to ensure that
the SQL methods can only be called the code that we have created.
Create a user defined role called Authoriser in the acl_users folder do not give it any permissions.
set the 'Use Database Methods' permission for 'Authoriser' on all the SQLMethods created above.
set the Proxy role "Authoriser" on:
- change_password_report
- userAuthenticate
- userList
- userRoles
9. Force GUF to see the changes
All
is now ready. Because of some oddities in the permissions stuff in Zope
we must flush the GUF cache before any of out changes will come in to
affect.
From the 'cache' tab of the acl_users folder click the flush button to have all the changes take effect.
No comments:
Post a Comment