source: TI12-security/trunk/python/ndg.security.common/ndg/security/common/credentialrepository/sqlobject.py @ 4568

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg.security.common/ndg/security/common/credentialrepository/sqlobject.py@4568
Revision 4568, 12.9 KB checked in by pjkersha, 11 years ago (diff)

Move sqlobject into credentialrepository sub-package

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