/[papo]/gnue/common/doc/technotes/00005.txt
ViewVC logotype

Diff of /gnue/common/doc/technotes/00005.txt

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.2 by charlie, Tue Aug 27 18:15:51 2002 UTC revision 1.3 by styxman, Fri Nov 15 15:32:54 2002 UTC
# Line 1  Line 1 
1  Title: Writing a Custom Login/Authentication Handler  Title: Writing a Custom Login/Authentication Handler
2  Status: Obsolete  Status: Current
3    Revised: 18-SEP-2002
4    
5    
6  Introduction  Introduction
7  ------------  ------------
8    
9  By default, GNUe clients look at a database connection, determine what  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  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  asks its platform/interface dependent login handler to prompt the user for
12  this information. Once returned, the client connects to the database using  this information. Once returned, the client connects to the database using
13  this information.  this information.
14    
15  If needed, you can intercept a client's normal login handler to add your  If needed, you can intercept a client's normal login handler to add your
16  own behavior.  own behavior.
17    
18    
19  Why?  Why?
20  ----  ----
21  Sometimes it is not enough to prompt for the "database" login.  Perhaps  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  you want finer control over logins, or simply need to authenticate against
23  something besides the database.  something besides the database.
24    
25  You might want to authenticate against an NIS source, LDAP, or some custom  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  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  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  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  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  "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  that he is logging in as "james" and never know that he is being connected
32  as "commonuser".  as "commonuser".
33    
34    
# Line 35  as "commonuser". Line 36  as "commonuser".
36  What do I do?  What do I do?
37  -------------  -------------
38    
39  First, a little explanation of how logins work:  First, a little explanation of how logins work:
40      
41  1. A client needs to initialize a connection to a database, so it passes  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.     GNUe-Common a description of the database and asks for it to connect.
43    
44  2. GNUe-Common looks at the database description, determines what values  2. GNUe-Common looks at the database description, determines what values
45     are needed to connect (usually, username and password).     are needed to connect (usually, username and password).
46    
47  3. GNUe-Common creates an instance of (or uses an existing instance of)  3. GNUe-Common creates an instance of (or uses an existing instance of)
48     gnue.common.GLoginHander.LoginHandler, or if the client provides a     gnue.common.GLoginHander.LoginHandler, or if the client provides a
49     more advanced login handler (e.g., GNUe Form's graphical handler), an     more advanced login handler (e.g., GNUe Form's graphical handler), an
50     instance of this.     instance of this.
51    
52  4. GNUe-Common calls LoginHandler.getLogin(), passing it basic information  4. GNUe-Common calls LoginHandler.getLogin(), passing it basic information
53     about the needed connection and a list of values that login handler     about the needed connection and a list of values that login handler
54     should provide (i.e., '_username' & '_password').     should provide (i.e., '_username' & '_password').
55    
56  5. GNUe-Common uses the results of the call to LoginHandler.getLogin() to  5. GNUe-Common uses the results of the call to LoginHandler.getLogin() to
57     create a connection to the database.     create a connection to the database.
58    
59    
60  Now, if needed, you can subclass LoginHandler (basically intercepting  Now, if needed, you can create an Authenticator class that basically
61  steps #3-4 above) and provide your own functionality.  All that is needed  intervenes in between steps #3-4 above and provide your own functionality.
 is for you to provide class with a method called getLogin that takes as  
 input a tuple of:  
   
     (database id, description, (requiredValues) )  
   
 and return a hash containing a dictionary/hash:  
       
     { 'field': 'value' }.  
   
 RequiredValues is a tuple of tuples containing:  
   
     (value id, value description, isPassword)  
   
 Example of a getLogin call as used by GNUe Common  
   
     loginHandler.getLogin ('gnue',                  # Connection ID  
                            'GNUe Test Database',    # Connection Name  
                            ( ('_username','User Name', 0),    # Required  
                              ('_password','Password', 1) ) )  # Fields  
   
 You would want to write your own handler as such:  
   
   
     #####################################################  
     #  
     # Template for a custom login handler  
     #  
       
     # Import the base login handler;  
     # Note: it is important that this line not change.  
     from gnue.common.GLoginHander import LoginHandler  
       
     # Do not rename this class.  
     # GNUe clients expect to see a MyLoginHander class  
     class MyLoginHandler(LoginHandler):  
       
       #  
       # getLogin is passed an list consisting of:  
       #   Connection Name  
       #   Connection Comments/Description  
       #   List of Fields to Input:  
       #      Attribute Name, Label/Description, Is Password?  
       #  
       # It should return a dictionary of {Attribute Name: Inputted Value}  
       #  
       def getLogin(self, loginData):  
       
         connName = loginData[0]  
         connDescription = loginData[1]  
         requiredFields = loginData[2]  
       
         # Perform any tasks prior to prompting for username/password.  
         # At this point, you might want to add your needed fields to  
         # requiredFields or set default values.  Note: to set default  
         # values, set  'self._defaults['fieldname'] = 'default'  
         # (e.g., 'self._defaults['_username'] = os.env['LOGNAME'] )  
       
         loginFields = LoginHandler.getLogin (self, (connName,  
                                                     connDescription,  
                                                     requiredFields) )  
       
         # Perform any tasks prior to returning username/password  
         # The entered username/password is stored in loginFields['_username']  
         # and loginFields['_password']. At this point  
         
         return loginFields  
       
     #  
     #####################################################  
   
       
 Save this file as a .py file and put it's location in your gnue.conf (change  
 "loginHandler =" to "loginHandler = /path/to/myhandler.py" )  
62    
63  If all goes well, your login handler will be used the next time you log in.  If all goes well, your login handler will be used the next time you log in.
64    
65  Some notes:  Some notes:
66    
67    1) GNUe-Common will expect to see to two objects in your handler's    1) GNUe-Common will expect to see to two objects in your handler's
68       namespace:   LoginHandler   (from the import statement) and       namespace:   LoginHandler   (from the import statement) and
69                    MyLoginHandler (your customized login class)                    MyLoginHandler (your customized login class)
70    
71       For this reason, do NOT change the import statement to read:       For this reason, do NOT change the import statement to read:
72         from gnue.common import GLoginHandler         from gnue.common import GLoginHandler
73       and then change the class' parent to be GLoginHandler.LoginHandler.       and then change the class' parent to be GLoginHandler.LoginHandler.
74       If you do, your customized handler will probably not work the way       If you do, your customized handler will probably not work the way
75       you expect.       you expect.
76    
77    2) Almost the only restriction placed on getLogin's functionality is    2) Almost the only restriction placed on getLogin's functionality is
78       that it must return a hash containing at least the values requested       that it must return a hash containing at least the values requested
79       when getLogin was called, using the value id's supplied. Your code,       when getLogin was called, using the value id's supplied. Your code,
80       in theory, can do whatever it needs in order to return these values.       in theory, can do whatever it needs in order to return these values.
81    
82       HOWEVER: Even though it is possible, it is NOT recommended that you       HOWEVER: Even though it is possible, it is NOT recommended that you
83       try to write your code to prompt for the values.  It is HIGHLY       try to write your code to prompt for the values.  It is HIGHLY
84       recommended that you call LoginHandler.getLogin (your superclass's       recommended that you return values in the getLoginFields method so
85       getLogin) to prompt for input.  The superclass' LoginHandler will         LoginHandler.getLogin can prompt for input.  The LoginHandler will
86       know how to handle the current user's environment (i.e., should it       know how to handle the current user's environment (i.e., should it
87       display a GTK login box, generate HTML for a login box, not display       display a GTK login box, generate HTML for a login box, not display
88       a box at all, but prompt using good ol' fashioned text prompts?)       a box at all, but prompt using good ol' fashioned text prompts?)
89       It is quite extensible.  If you need to prompt for more fields than       It is quite extensible.  If you need to prompt for more fields than
90       simply Username and Password, then simply add a definition to your       simply Username and Password, then simply add a definition to your
91       requiredFields tuple and let the superclass' getLogin prompt for you.       requiredFields tuple and let the LoginHandler's getLogin prompt for
92         you.
93    
94    
95    There are three steps to adding a custom authenticator:
96    
97    1. Create (or copy) a Python file that implements an Authenticator.
98       An Authenticator contains a class called Authenticator, which has
99       two methods, getRequiredFields() and login().  See the example below
100       for details on what's expected from these two methods.
101    
102    2. Place this file in either your Python search path, or in a
103       path specified by ImportPath (in the [common] section of gnue.conf)
104    
105    3. In your connections.conf file, add a custom_auth parameter that
106       is the name of the file (without the path or .py extension):
107    
108         [myconn]
109         adapter = psycopg
110         host = localhost
111         dbname = mydb
112         custom_auth = MyPostgresAuthenticator
113    
114    
115    Creating a custom authenticator
116    ===============================
117    A slightly more complicated example.  You have a table in one of your databases
118    called "users", that has a user and password field.
119    
120    Your users will connect to the database using the username and password:
121    dbUser  and dbPassword.
122    
123    You, however, want them to use their own username and password to be
124    authenticated, but after getting authenticated, the database only cares
125    about the real "dbUser" and "dbPassword".
126    
127    File: MyPostgresAuthenticator.py
128    ===============================================================================
129    import psycopg
130    from gnue.common.GConnections import LoginError
131    
132    class Authenticator:
133      #
134      # getLoginFields is passed an list consisting of:
135      #   Fields to Input:
136      #      Attribute Name, Label/Description, Is Password?
137      #
138      #   This list is a list of the values the dbdriver
139      #   expects to get back in order to login.
140      #
141      #   It should return a similarly formatted list that
142      #   tells the LoginHandler what values need to be
143      #   prompted for. (Typically, _username and _password)
144      #   If nothing should be prompted (e.g., you have a
145      #   certificate) then return () (an empty list).
146      #
147      def getLoginFields(self, dbRequiredFields):
148        return dbRequiredFields
149    
150      #
151      # Authenticate the user givem the values prompted for.
152      # If the information is incorrect, then raise
153      # gnue.common.GConnections.LoginError
154      #
155      # It should return a dictionary of {Attribute Name: Value}
156      #
157      def login(self, loginData):
158        conn = psycopg.connect(user="theValidator",
159                     password="hotmomma",
160                     dbname="logins",
161                     host="localhost"')
162        cursor = conn.cursor()
163        results = cursor.execute (
164            'select 1 from users where username=%s and password=%s',
165            (loginData['_username'],
166             loginData['_password']) )
167        if not results.fetchone():
168          raise LoginError
169        else:
170          loginData['_username'] = 'dbLogin'
171          loginData['_password'] = 'dbPassword'
172    ===============================================================================
173    
174    Note: there are no hard and fast rules about what can go into the
175    connections.conf file.  If your authenticator needs more information
176    stored in connections.conf, it is fine to do so.
177    
178    However, to avoid namespace collisions, you should probably prefix
179    any custom connection.conf entries with common prefix. For example,
180    if you are writing an NIS adapter, you should prefix entries in
181    connections.conf like:
182    
183      auth_nis_domain = MYDOMAIN
184    
185    instead of something like:
186    
187      domain = MYDOMAIN
188    
189    More examples:
190    
191      auth_ldap_*
192      auth_pg_*
193      auth_mysql_*
194      auth_kerberos_*
195      auth_custom_*
196    
197    
198  If you have written a customized login handler using backends not currently  If you have written a customized login handler using backends not currently
199  in our samples file (i.e., against NIS, Kerberos, LDAP, etc) and would like  in our samples file (i.e., against NIS, Kerberos, LDAP, etc) and would like
200  to donate your example for others to learn from, please email it to:  to donate your example for others to learn from, please email it to:
201    
202         info@gnue.org         info@gnue.org
       
203    

Legend:
Removed from v.1.2  
changed lines
  Added in v.1.3

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26