1
Title: Writing a Custom Login/Authentication Handler
9
By default, GNUe clients look at a database connection, determine what
10
fields it needs in order to login (e.g., username and password), and then
11
asks its platform/interface dependent login handler to prompt the user for
12
this information. Once returned, the client connects to the database using
15
If needed, you can intercept a client's normal login handler to add your
21
Sometimes it is not enough to prompt for the "database" login. Perhaps
22
you want finer control over logins, or simply need to authenticate against
23
something besides the database.
25
You might want to authenticate against an NIS source, LDAP, or some custom
26
source. Maybe you have many users and want to authenticate against rows in
27
a database table and have the actual database login name/password be a
28
common one that no one knows; i.e., while users Jason and James might log
29
in using "jason" and "james", they might both connect to the database as
30
"commonuser". Since you may not trust James, you only want him to know
31
that he is logging in as "james" and never know that he is being connected
39
First, a little explanation of how logins work:
41
1. A client needs to initialize a connection to a database, so it passes
42
GNUe-Common a description of the database and asks for it to connect.
44
2. GNUe-Common looks at the database description, determines what values
45
are needed to connect (usually, username and password).
47
3. GNUe-Common creates an instance of (or uses an existing instance of)
48
gnue.common.GLoginHander.LoginHandler, or if the client provides a
49
more advanced login handler (e.g., GNUe Form's graphical handler), an
52
4. GNUe-Common calls LoginHandler.getLogin(), passing it basic information
53
about the needed connection and a list of values that login handler
54
should provide (i.e., '_username' & '_password').
56
5. GNUe-Common uses the results of the call to LoginHandler.getLogin() to
57
create a connection to the database.
60
Now, if needed, you can create an Authenticator class that basically
61
intervenes in between steps #3-4 above and provide your own functionality.
63
If all goes well, your login handler will be used the next time you log in.
67
1) Almost the only restriction placed on getLogin's functionality is
68
that it must return a hash containing at least the values requested
69
when getLogin was called, using the value id's supplied. Your code,
70
in theory, can do whatever it needs in order to return these values.
72
HOWEVER: Even though it is possible, it is NOT recommended that you
73
try to write your code to prompt for the values. It is HIGHLY
74
recommended that you return values in the getLoginFields method so
75
LoginHandler.getLogin can prompt for input. The LoginHandler will
76
know how to handle the current user's environment (i.e., should it
77
display a GTK login box, generate HTML for a login box, not display
78
a box at all, but prompt using good ol' fashioned text prompts?)
79
It is quite extensible. If you need to prompt for more fields than
80
simply Username and Password, then simply add a definition to your
81
requiredFields tuple and let the LoginHandler's getLogin prompt for
85
There are three steps to adding a custom authenticator:
87
1. Create (or copy) a Python file that implements an Authenticator.
88
An Authenticator contains a class called Authenticator, which has
89
two methods, getRequiredFields() and login(). See the example below
90
for details on what's expected from these two methods.
92
2. Place this file in either your Python search path, or in a
93
path specified by ImportPath (in the [common] section of gnue.conf)
95
3. In your connections.conf file, add a custom_auth parameter that
96
is the name of the file (without the path or .py extension):
102
custom_auth = MyPostgresAuthenticator
105
Creating a custom authenticator
106
===============================
107
A slightly more complicated example. You have a table in one of your databases
108
called "users", that has a user and password field.
110
Your users will connect to the database using the username and password:
111
dbUser and dbPassword.
113
You, however, want them to use their own username and password to be
114
authenticated, but after getting authenticated, the database only cares
115
about the real "dbUser" and "dbPassword".
117
File: MyPostgresAuthenticator.py
118
===============================================================================
120
from gnue.common.GConnections import LoginError
124
# getLoginFields is passed an list consisting of:
126
# Attribute Name, Label/Description, Is Password?
128
# This list is a list of the values the dbdriver
129
# expects to get back in order to login.
131
# It should return a similarly formatted list that
132
# tells the LoginHandler what values need to be
133
# prompted for. (Typically, _username and _password)
134
# If nothing should be prompted (e.g., you have a
135
# certificate) then return () (an empty list).
137
def getLoginFields(self, dbRequiredFields):
138
return dbRequiredFields
141
# Authenticate the user givem the values prompted for.
142
# If the information is incorrect, then raise
143
# gnue.common.GConnections.LoginError
145
# It should return a dictionary of {Attribute Name: Value}
147
def login(self, loginData):
148
conn = psycopg.connect(user="theValidator",
152
cursor = conn.cursor()
153
results = cursor.execute (
154
'select 1 from users where username=%s and password=%s',
155
(loginData['_username'],
156
loginData['_password']) )
157
if not cursor.fetchone():
160
loginData['_username'] = 'dbLogin'
161
loginData['_password'] = 'dbPassword'
164
===============================================================================
166
Note: there are no hard and fast rules about what can go into the
167
connections.conf file. If your authenticator needs more information
168
stored in connections.conf, it is fine to do so.
170
However, to avoid namespace collisions, you should probably prefix
171
any custom connection.conf entries with common prefix. For example,
172
if you are writing an NIS adapter, you should prefix entries in
173
connections.conf like:
175
auth_nis_domain = MYDOMAIN
177
instead of something like:
190
If you have written a customized login handler using backends not currently
191
in our samples file (i.e., against NIS, Kerberos, LDAP, etc) and would like
192
to donate your example for others to learn from, please email it to: