1 | """NDG User Roles interface to BADC Attribute Authority |
---|
2 | |
---|
3 | NERC Data Grid Project |
---|
4 | |
---|
5 | P J Kershaw 01/07/05 |
---|
6 | |
---|
7 | Copyright (C) 2005 CCLRC & NERC |
---|
8 | |
---|
9 | This software may be distributed under the terms of the Q Public License, |
---|
10 | version 1.0 or later. |
---|
11 | """ |
---|
12 | |
---|
13 | cvsID = '$Id$' |
---|
14 | |
---|
15 | # BADC Ingres database |
---|
16 | import ingresdbi |
---|
17 | |
---|
18 | # For parsing of properties file |
---|
19 | import cElementTree as ElementTree |
---|
20 | |
---|
21 | from AttAuthority import AAUserRoles, AAUserRolesError |
---|
22 | from X509 import X500DN |
---|
23 | |
---|
24 | |
---|
25 | |
---|
26 | class BADCUserRoles(AAUserRoles): |
---|
27 | """User Roles interface to BADC Attribute Authority""" |
---|
28 | |
---|
29 | def __init__(self, |
---|
30 | propertiesFilePath=None, |
---|
31 | CreateRoles=True, |
---|
32 | **connectKeys): |
---|
33 | |
---|
34 | """Connect to BADC User database |
---|
35 | |
---|
36 | Omit connectKeys or explicitly set CreateRoles to False if wish to |
---|
37 | avoid querying the database on object creation""" |
---|
38 | |
---|
39 | self.__con = None |
---|
40 | self.__rolesLUT = {} |
---|
41 | |
---|
42 | if propertiesFilePath is not None: |
---|
43 | connectKeys = self.readPropertiesFile(propertiesFilePath) |
---|
44 | |
---|
45 | # keywords for connection to database either from input arguments or |
---|
46 | # read from properties file |
---|
47 | if connectKeys.items() is not None: |
---|
48 | self.connect(**connectKeys) |
---|
49 | if CreateRoles: self.createRoles() |
---|
50 | |
---|
51 | |
---|
52 | def __del__(self): |
---|
53 | """Call close() - Commit any changes and close database connection""" |
---|
54 | try: |
---|
55 | self.close() |
---|
56 | except: |
---|
57 | pass |
---|
58 | |
---|
59 | |
---|
60 | def readPropertiesFile(self, propertiesFilePath): |
---|
61 | |
---|
62 | """Read the configuration properties for the database connection |
---|
63 | |
---|
64 | propertiesFilePath: file path to properties file |
---|
65 | """ |
---|
66 | |
---|
67 | if not isinstance(propertiesFilePath, basestring): |
---|
68 | raise AAUserRolesError("Input Properties file path " + \ |
---|
69 | "must be a valid string.") |
---|
70 | |
---|
71 | try: |
---|
72 | tree = ElementTree.parse(propertiesFilePath) |
---|
73 | |
---|
74 | except IOError, ioErr: |
---|
75 | raise AAUserRolesError(\ |
---|
76 | "Error parsing properties file \"%s\": %s" % \ |
---|
77 | (ioErr.filename, ioErr.strerror)) |
---|
78 | |
---|
79 | |
---|
80 | prop = tree.getroot() |
---|
81 | |
---|
82 | # Return properties as dictionary |
---|
83 | return dict([(elem.tag, elem.text) for elem in prop]) |
---|
84 | |
---|
85 | |
---|
86 | def connect(self, enableTrace=False, **ingresDBIConnectKeys): |
---|
87 | """Make a connection to the database""" |
---|
88 | |
---|
89 | if enableTrace is True: |
---|
90 | trace = (7, None) |
---|
91 | else: |
---|
92 | trace = (0, None) |
---|
93 | |
---|
94 | self.__con = ingresdbi.connect(**ingresDBIConnectKeys) |
---|
95 | |
---|
96 | |
---|
97 | def close(self): |
---|
98 | """Commit any changes and close database connection""" |
---|
99 | self.__con.commit() |
---|
100 | self.__con.close() |
---|
101 | |
---|
102 | |
---|
103 | def createRoles(self): |
---|
104 | """Query the BADC user database in order to create a list of user |
---|
105 | roles. |
---|
106 | |
---|
107 | This is called on object creation but can be called again to update |
---|
108 | an existing object following changes to the database content""" |
---|
109 | |
---|
110 | # Roles based on the institute type a user belongs to |
---|
111 | instSqlStmt = "select accountid, type from " + \ |
---|
112 | "tbusers, addresses, tbinstitutes " + \ |
---|
113 | "where tbusers.addresskey = addresses.addresskey and " +\ |
---|
114 | "addresses.institutekey = tbinstitutes.institutekey" |
---|
115 | |
---|
116 | cursor = self.__con.cursor() |
---|
117 | try: |
---|
118 | cursor.execute(instSqlStmt) |
---|
119 | instQueryRes = cursor.fetchall() |
---|
120 | |
---|
121 | except Exception, e: |
---|
122 | raise AAUserRolesError("Querying for institutes: %s" % str(e)) |
---|
123 | |
---|
124 | # Make a dictionary of username vs. roles list |
---|
125 | self.__rolesLUT = dict([(k, [v.lower()]) for k,v in instQueryRes]) |
---|
126 | |
---|
127 | |
---|
128 | # Get groups and NERC funded info to add as roles |
---|
129 | grpSqlStmt = "select accountid, nercfunded, grp from " + \ |
---|
130 | "tbusers, tbdatasetjoin, tbdatasets " + \ |
---|
131 | "where tbusers.userkey = tbdatasetjoin.userkey and " + \ |
---|
132 | "tbdatasetjoin.datasetid = tbdatasets.datasetid and " + \ |
---|
133 | "tbdatasetjoin.removed = 0" |
---|
134 | |
---|
135 | try: |
---|
136 | cursor.execute(grpSqlStmt) |
---|
137 | grpQueryRes = cursor.fetchall() |
---|
138 | |
---|
139 | except Exception, e: |
---|
140 | raise AAUserRolesError(\ |
---|
141 | "Querying for groups and NERC funded: %s" % str(e)) |
---|
142 | |
---|
143 | for i in grpQueryRes: |
---|
144 | # Check - some users may have been missed out in first query |
---|
145 | if not self.__rolesLUT.has_key(i[0]): |
---|
146 | self.__rolesLUT[i[0]] = [] |
---|
147 | |
---|
148 | # Add new groups as roles |
---|
149 | if i[2] and i[2] not in self.__rolesLUT[i[0]]: |
---|
150 | self.__rolesLUT[i[0]].append(i[2]) |
---|
151 | |
---|
152 | # Check for NERC funded |
---|
153 | if i[1] != 0 and 'nercFunded' not in self.__rolesLUT[i[0]]: |
---|
154 | self.__rolesLUT[i[0]].append('nercFunded') |
---|
155 | |
---|
156 | |
---|
157 | # Catch remaining users left out from join queries above |
---|
158 | cursor.execute("select accountid from tbusers") |
---|
159 | allQueryRes = cursor.fetchall() |
---|
160 | for i in allQueryRes: |
---|
161 | if i[0] and not self.__rolesLUT.has_key(i[0]): |
---|
162 | self.__rolesLUT[i[0]] = [] |
---|
163 | |
---|
164 | |
---|
165 | |
---|
166 | |
---|
167 | def usrIsRegistered(self, dn=None, x500DN=None): |
---|
168 | """Return boolean to indicate whether user with given DN is |
---|
169 | registered |
---|
170 | |
---|
171 | Interface method to AttAuthority - Overrides AAUserRoles base |
---|
172 | class""" |
---|
173 | |
---|
174 | # Parse username from DN string |
---|
175 | try: |
---|
176 | if dn: |
---|
177 | userName = X500DN(dn)['CN'] |
---|
178 | else: |
---|
179 | userName = x500DN['CN'] |
---|
180 | |
---|
181 | except Exception, e: |
---|
182 | raise AAUserRolesError("Parsing userName from DN %s: %s" % (dn,e)) |
---|
183 | |
---|
184 | |
---|
185 | sqlStmt = "select distinct accountid from tbusers " + \ |
---|
186 | "where accountid = '%s'" % userName |
---|
187 | |
---|
188 | try: |
---|
189 | self.__con.cursor().execute(sqlStmt) |
---|
190 | match = cursor.fetchall() |
---|
191 | |
---|
192 | except Exception, e: |
---|
193 | raise AAUserRolesError("Searching for user %s: %s" % (userName,e)) |
---|
194 | if len(match) == 1: |
---|
195 | return True |
---|
196 | else: |
---|
197 | return False |
---|
198 | |
---|
199 | |
---|
200 | def getRoles(self, dn=None, x500DN=None): |
---|
201 | """Return valid roles for the BADC database |
---|
202 | |
---|
203 | Interface method to AttAuthority - Overrides AAUserRoles base |
---|
204 | class |
---|
205 | |
---|
206 | dn: distinguished name as a string |
---|
207 | x500DN: distinguished name as an X500DN instance""" |
---|
208 | |
---|
209 | # Parse username from DN string |
---|
210 | try: |
---|
211 | if dn: |
---|
212 | userName = X500DN(dn)['CN'] |
---|
213 | else: |
---|
214 | userName = x500DN['CN'] |
---|
215 | |
---|
216 | except Exception, e: |
---|
217 | raise AAUserRolesError("Parsing userName from DN %s: %s" % (dn,e)) |
---|
218 | |
---|
219 | try: |
---|
220 | return self.__rolesLUT[userName] |
---|
221 | except: |
---|
222 | return [] |
---|
223 | |
---|
224 | |
---|
225 | def cursor(self): |
---|
226 | """Return a database cursor instance""" |
---|
227 | return self.__con.cursor() |
---|
228 | |
---|
229 | |
---|
230 | def printSurnames(self): |
---|
231 | """Test method - print all user surnames held in database""" |
---|
232 | |
---|
233 | c = self.__con.cursor() |
---|
234 | |
---|
235 | c.execute("select surname from tbusers order by surname") |
---|
236 | |
---|
237 | description = c.description |
---|
238 | pprint.pprint(description) |
---|
239 | |
---|
240 | rows = c.fetchall() |
---|
241 | |
---|
242 | for row in rows: |
---|
243 | count = 0 |
---|
244 | for column in row: |
---|
245 | print description[count][0] , ': ', column |
---|
246 | count += 1 |
---|
247 | |
---|
248 | print "-----------------------------" |
---|
249 | |
---|
250 | |
---|
251 | def badcUserRolesTest(**keys): |
---|
252 | |
---|
253 | userRoles = BADCUserRoles(**keys) |
---|
254 | userRoles.printSurnames() |
---|
255 | |
---|
256 | |
---|
257 | if __name__ == "__main__": |
---|
258 | badcUserRolesTest() |
---|