source: TI12-security/trunk/python/ndg.security.common/ndg/security/common/SQLObject.py @ 4381

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg.security.common/ndg/security/common/SQLObject.py@4381
Revision 4381, 13.2 KB checked in by pjkersha, 12 years ago (diff)

Renamed CredWallet? -> CredentialWallet?

  • Property svn:keywords set to Id
Line 
1"""SQLObject Object Relational Mapper database interface for NDG Security
2CredentialRepository
3
4NERC Data Grid Project
5"""
6__author__ = "P J Kershaw"
7__date__ = "03/10/06"
8__copyright__ = "(C) 2007 STFC & NERC"
9__license__ = \
10"""This software may be distributed under the terms of the Q Public
11License, version 1.0 or later."""
12__contact__ = "P.J.Kershaw@rl.ac.uk"
13__revision__ = '$Id$'
14
15# SQLObject Database interface
16from sqlobject import *
17
18# For parsing of properties files
19
20# For parsing of properties file
21try: # python 2.5
22    from xml.etree import cElementTree as ElementTree
23except ImportError:
24    # if you've installed it yourself it comes this way
25    import cElementTree as ElementTree
26
27from CredentialWallet import CredentialRepository as CredentialRepositoryBase
28from CredentialWallet import CredentialRepositoryError
29
30
31#_____________________________________________________________________________
32class CredentialRepository(CredentialRepositoryBase):
33    """Interface to Credential Repository Database
34   
35    Nb. inherits from CredentialWallet.CredentialRepository to ensure correct interface
36    to the wallet"""
37
38    # valid configuration property keywords
39    __validKeys = ['dbURI']
40   
41   
42    #_________________________________________________________________________   
43    def __init__(self, propFilePath=None, dbPPhrase=None, **prop):
44        """Initialise Credentials Repository Database object.
45
46        If the connection string or properties file is set a connection
47        will be made
48
49        dbURI:              <db type>://<username>:<passwd>@<hostname>/dbname
50        propFilePath: file path to properties file
51
52        Nb. propFilePath setting overrides input dbURI
53        """
54           
55        self.__con = None
56        self.__prop = {}
57       
58        if propFilePath is not None:
59           
60            # Read database URI set in file
61            self.readProperties(propFilePath, dbPPhrase=dbPPhrase)
62           
63        elif prop != {}:
64           
65            # Database URI may have been set as an input keyword argument
66            self.setProperties(dbPPhrase=dbPPhrase, **prop)
67
68
69    #_________________________________________________________________________   
70    def __setConnection(self,
71                        dbType=None,
72                        dbUserName=None,
73                        dbPPhrase=None,
74                        dbHostname=None,
75                        dbName=None,
76                        dbURI=None,
77                        chkConnection=True):
78        """Establish a database connection from a database URI
79
80        pass a URI OR the parameters to construct the URI
81           
82        dbURI: "<db type>://<username>:<passwd>:<hostname>/dbname"
83
84        or
85
86        dbURI: "<db type>://<username>:%PPHRASE%:<hostname>/dbname"
87        + passPhrase
88
89        - %PPHRASE% is substituted with the input passPhrase keyword
90       
91        or
92       
93        dbType:         database type e.g. 'mysql'
94        dbUserName:     username
95        dbPPhrase:      pass-phrase
96        dbHostname:     name of host where database resides
97        dbName:         name of the database
98
99
100        chkConnection:  check that the URI is able to connect to the
101        """
102
103        try:
104            if dbURI:
105                # Check for pass-phrase variable set in URI '%PPHRASE%'
106                dbURIspl = dbURI.split('%')
107                if len(dbURIspl) == 3:
108                   
109                    if dbPPhrase is None:
110                        raise CredentialRepositoryError, "No database pass-phrase set"
111                   
112                    dbURI = dbURIspl[0] + dbPPhrase + dbURIspl[2]
113               
114            else:
115                # Construct URI from individual inputs
116                dbURI = dbType + '://' + dbUserName + ':' + dbPPhrase + \
117                        ':' + dbHostname + '/' + dbName
118        except Exception, e:
119            # Checking form missing keywords
120            raise CredentialRepositoryError, "Error creating database URI: %s" % e
121
122        try:
123            self.__con = connectionForURI(dbURI)
124        except Exception, e:
125            raise CredentialRepositoryError, "Error creating database connection: %s" % e
126
127        if chkConnection:
128            try:
129                self.__con.makeConnection()
130               
131            except Exception, e:
132                raise CredentialRepositoryError, \
133                        "Error connecting to Credential Repository: %s" % e
134
135           
136        # Copy the connection object into the table classes
137        CredentialRepository.UserID._connection = self.__con
138        CredentialRepository.UserCredential._connection = self.__con
139         
140
141    #_________________________________________________________________________   
142    def setProperties(self, dbPPhrase=None, **prop):
143        """Update existing properties from an input dictionary
144        Check input keys are valid names"""
145       
146        for key in prop.keys():
147            if key not in self.__validKeys:
148                raise CredentialRepositoryError, "Property name \"%s\" is invalid" % key
149               
150        self.__prop.update(prop)
151
152
153        # Update connection setting
154        if 'dbURI' in prop:
155            self.__setConnection(dbURI=prop['dbURI'], dbPPhrase=dbPPhrase)
156
157
158    #_________________________________________________________________________   
159    def readProperties(self,
160                       propFilePath=None,
161                       propElem=None,
162                       dbPPhrase=None):
163        """Read the configuration properties for the CredentialRepository
164
165        propFilePath|propElem
166
167        propFilePath: set to read from the specified file
168        propElem:     set to read beginning from a cElementTree node"""
169
170        if propFilePath is not None:
171
172            try:
173                tree = ElementTree.parse(propFilePath)
174                propElem = tree.getroot()
175               
176            except IOError, e:
177                raise CredentialRepositoryError, \
178                                "Error parsing properties file \"%s\": %s" % \
179                                (e.filename, e.strerror)
180
181            except Exception, e:
182                raise CredentialRepositoryError, \
183                                "Error parsing properties file \"%s\": %s" % \
184                                (propFilePath, str(e))
185
186        if propElem is None:
187            raise CredentialRepositoryError, \
188    "Error parsing properties file \"%s\": root element is not defined" % \
189                                propFilePath
190
191
192        # Read properties into a dictionary
193        prop = {}
194        for elem in propElem:
195                   
196            # Check for environment variables in file paths
197            tagCaps = elem.tag.upper()
198            if 'FILE' in tagCaps or 'PATH' in tagCaps or 'DIR' in tagCaps:
199                elem.text = os.path.expandvars(elem.text)
200
201            prop[elem.tag] = elem.text
202           
203        self.setProperties(dbPPhrase=dbPPhrase, **prop)
204
205           
206    #_________________________________________________________________________   
207    def addUser(self, userName, dn):
208        """A new user to Credentials Repository"""
209        try:
210            self.UserID(userName=userName, dn=dn)
211
212        except Exception, e:
213            raise CredentialRepositoryError, "Error adding new user '%s': %s" % \
214                                                                (userName, e)
215
216
217    #_________________________________________________________________________   
218    def auditCredentials(self, dn=None, **attCertValidKeys):
219        """Check the attribute certificates held in the repository and delete
220        any that have expired
221
222        dn:                Only audit for the given user distinguished Name.
223                           if not set, all records are audited
224        attCertValidKeys:  keywords which set how to check the Attribute
225                           Certificate e.g. check validity time, XML
226                           signature, version etc.  Default is check
227                           validity time only"""
228
229        if attCertValidKeys == {}:
230            # Default to check only the validity time
231            attCertValidKeys = {    'chkTime':          True,
232                                    'chkVersion':       False,
233                                    'chkProvenance':    False,
234                                    'chkSig':           False }
235           
236        try:
237            if dn:
238                # Only audit for the given user distinguished Name
239                credList = self.UserCredential.selectBy(dn=dn)
240            else:
241                # Audit all credentials
242                credList = self.UserCredential.select()
243           
244        except Exception, e:
245            raise CredentialRepositoryError,"Selecting credentials from repository: " + \
246                                 str(e)
247
248        # Iterate through list of credentials deleting records where the
249        # certificate is invalid
250        try:
251            for cred in credList:
252                attCert = AttCertParse(cred.attCert)
253               
254                if not attCert.isValid(**attCertValidKeys):
255                    self.UserCredential.delete(cred.id)
256                   
257        except Exception, e:
258            try:
259                raise CredentialRepositoryError, "Deleting credentials for '%s': %s" % \
260                                                       (cred.dn, e)
261            except:
262                raise CredentialRepositoryError, "Deleting credentials: %s" % e
263
264
265    #_________________________________________________________________________   
266    def getCredentials(self, dn):
267        """Get the list of credentials for a given user's DN"""
268
269        try:
270            return self.UserCredential.selectBy(dn=dn)
271           
272        except Exception, e:
273            raise CredentialRepositoryError, "Selecting credentials for %s: %s" % (dn, e)
274
275
276    #_________________________________________________________________________   
277    def addCredentials(self, dn, attCertList):
278        """Add new attribute certificates for a user.  The user must have
279        been previously registered in the repository
280
281        dn:             users Distinguished name
282        attCertList:   list of attribute certificates"""
283       
284        try:
285            userCred = self.UserID.selectBy(dn=dn)
286           
287            if userCred.count() == 0:
288                # Add a new user record HERE instead of at user registration
289                # time.  This decouples CredentialRepository from MyProxy and
290                # user registration process. Previously, a user not recognised
291                # exception would have been raised here.  'userName' field
292                # of UserID table is now perhaps superfluous.
293                #
294                # P J Kershaw 26/04/06
295                self.addUser(X500DN(dn)['CN'], dn)
296
297        except Exception, e:
298            raise CredentialRepositoryError, "Checking for user \"%s\": %s" % (dn, e)
299
300       
301        # Carry out check? - filter out certs in db where a new cert
302        # supercedes it - i.e. expires later and has the same roles
303        # assigned - May be too complicated to implement
304        #uniqAttCertList = [attCert for attCert in attCertList \
305        #    if min([attCert == cred.attCert for cred in userCred])]
306       
307               
308        # Update database with new entries
309        try:
310            for attCert in attCertList:
311                self.UserCredential(dn=dn, attCert=str(attCert))
312
313        except Exception, e:
314            raise CredentialRepositoryError, "Adding new user credentials for " + \
315                                  "user %s: %s" % (dn, str(e))
316
317
318    #_________________________________________________________________________   
319    def _initTables(self, prompt=True):
320        """Use with EXTREME caution - this method will initialise the database
321        tables removing any previous records entered"""
322 
323        if prompt:
324            resp = raw_input(\
325        "Are you sure you want to initialise the database tables? (yes/no) ")
326   
327            if resp.upper() != "YES":
328                print "Tables unchanged"
329                return
330       
331        self.UserID.createTable()
332        self.UserCredential.createTable()
333        print "Tables created"
334
335           
336    #_________________________________________________________________________
337    # Database tables defined using SQLObject derived classes
338    # Nb. These are class variables of the CredentialRepository class
339    class UserID(SQLObject):
340        """SQLObject derived class to define Credentials Repository db table
341        to store user information"""
342
343        # to be assigned to connectionForURI(<db URI>)
344        _connection = None
345
346        # Force table name
347        _table = "UserID"
348
349        userName = StringCol(dbName='userName', length=30)
350        dn = StringCol(dbName='dn', length=128)
351
352
353    class UserCredential(SQLObject):
354        """SQLObject derived class to define Credentials Repository db table
355        to store user credentials information"""
356
357        # to be assigned to connectionForURI(<db URI>)
358        _connection = None
359
360        # Force table name
361        _table = "UserCredential"
362
363       
364        # User name field binds with UserCredential table
365        dn = StringCol(dbName='dn', length=128)
366
367        # Store complete attribute certificate text
368        attCert = StringCol(dbName='attCert')
Note: See TracBrowser for help on using the repository browser.