Changeset ee950c2 for fedd/federation
- Timestamp:
- Jan 10, 2012 5:28:15 PM (13 years ago)
- Branches:
- compt_changes, info-ops, master
- Children:
- f77a256
- Parents:
- d2e86f6
- Location:
- fedd/federation
- Files:
-
- 2 deleted
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
fedd/federation/access.py
rd2e86f6 ree950c2 15 15 16 16 from util import * 17 from allocate_project import allocate_project_local, allocate_project_remote18 17 from fedid import fedid, generate_fedid 19 18 from authorizer import authorizer … … 305 304 if access_ok: 306 305 self.log.debug("Access succeeded for %s %s" % (attr.attr, fid)) 307 # XXX: needs to deal with dynamics 308 return copy.copy(attr.value), (False, False, False), \ 309 [ fid ], proof 306 return copy.copy(attr.value), [ fid ], proof 310 307 else: 311 308 fail_proofs.append(proof) … … 451 448 return (exp, state) 452 449 453 def build_access_response(self, alloc_id, ap, services, proof):450 def build_access_response(self, alloc_id, pname, services, proof): 454 451 """ 455 452 Create the SOAP response. … … 467 464 'fedAttr': [ 468 465 { 'attribute': 'domain', 'value': self.domain } , 469 { 'attribute': 'project', 'value':470 ap['project'].get('name', {}).get('localname', "???") },471 466 ] 472 467 } 473 468 469 if pname: 470 msg['fedAttr'].append({ 'attribute': 'project', 'value': pname }) 474 471 if self.dragon_endpoint: 475 472 msg['fedAttr'].append({'attribute': 'dragon', -
fedd/federation/deter_internal_access.py
rd2e86f6 ree950c2 13 13 14 14 from util import * 15 from allocate_project import allocate_project_local, allocate_project_remote16 15 from fedid import fedid, generate_fedid 17 16 from authorizer import authorizer, abac_authorizer … … 27 26 28 27 from access import access_base 29 from legacy_access import legacy_access30 28 31 29 # Make log messages disappear if noone configures a fedd logger … … 36 34 fl.addHandler(nullHandler()) 37 35 38 class access(access_base , legacy_access):36 class access(access_base): 39 37 @staticmethod 40 38 def parse_vlans(v, log=None): … … 83 81 # authorization information 84 82 self.auth_type = config.get('access', 'auth_type') \ 85 or ' legacy'83 or 'abac' 86 84 self.auth_dir = config.get('access', 'auth_dir') 87 85 accessdb = config.get("access", "accessdb") 88 86 # initialize the authorization system 89 if self.auth_type == 'legacy': 90 self.access = { } 91 if accessdb: 92 self.legacy_read_access(accessdb) 93 elif self.auth_type == 'abac': 87 if self.auth_type == 'abac': 94 88 self.auth = abac_authorizer(load=self.auth_dir) 95 89 self.access = [ ] … … 99 93 raise service_error(service_error.internal, 100 94 "Unknown auth_type: %s" % self.auth_type) 101 102 if self.auth_type == 'legacy':103 # Add the ownership attributes to the authorizer. Note that the104 # indices of the allocation dict are strings, but the attributes are105 # fedids, so there is a conversion.106 self.state_lock.acquire()107 for k in self.state.keys():108 for o in self.state[k].get('owners', []):109 self.auth.set_attribute(o, fedid(hexstr=k))110 self.auth.set_attribute(fedid(hexstr=k),fedid(hexstr=k))111 # If the allocation has a vlan assigned, remove it from the112 # available vlans113 v = self.state[k].get('vlan', None)114 if v:115 self.vlans.discard(v)116 self.state_lock.release()117 118 self.lookup_access = self.legacy_lookup_access_base119 # under ABAC we use access.lookup_access120 121 95 122 96 self.call_GetValue= service_caller('GetValue') -
fedd/federation/dragon_access.py
rd2e86f6 ree950c2 12 12 from subprocess import Popen, call, PIPE, STDOUT 13 13 from access import access_base 14 from legacy_access import legacy_access15 14 16 15 from util import * 17 from allocate_project import allocate_project_local, allocate_project_remote18 16 from fedid import fedid, generate_fedid 19 17 from authorizer import authorizer, abac_authorizer … … 36 34 fl.addHandler(nullHandler()) 37 35 38 class access(access_base , legacy_access):36 class access(access_base): 39 37 """ 40 38 The implementation of access control based on mapping users to projects. … … 63 61 # authorization information 64 62 self.auth_type = config.get('access', 'auth_type') \ 65 or ' legacy'63 or 'abac' 66 64 self.auth_dir = config.get('access', 'auth_dir') 67 65 accessdb = config.get("access", "accessdb") 68 66 # initialize the authorization system 69 if self.auth_type == 'legacy': 70 self.access = { } 71 if accessdb: 72 self.legacy_read_access(accessdb, self.make_repo) 73 # Add the ownership attributes to the authorizer. Note that the 74 # indices of the allocation dict are strings, but the attributes are 75 # fedids, so there is a conversion. 76 self.state_lock.acquire() 77 for k in self.state.keys(): 78 for o in self.state[k].get('owners', []): 79 self.auth.set_attribute(o, fedid(hexstr=k)) 80 self.auth.set_attribute(fedid(hexstr=k),fedid(hexstr=k)) 81 self.state_lock.release() 82 self.lookup_access = self.legacy_lookup_access_base 83 elif self.auth_type == 'abac': 67 if self.auth_type == 'abac': 84 68 self.auth = abac_authorizer(load=self.auth_dir) 85 69 self.access = [ ] -
fedd/federation/emulab_access.py
rd2e86f6 ree950c2 16 16 17 17 from access import access_base 18 from legacy_access import legacy_access19 18 20 19 from util import * 21 from allocate_project import allocate_project_local, allocate_project_remote22 20 from fedid import fedid, generate_fedid 23 21 from authorizer import authorizer, abac_authorizer … … 42 40 fl.addHandler(nullHandler()) 43 41 44 class access(access_base , legacy_access):42 class access(access_base): 45 43 """ 46 44 The implementation of access control based on mapping users to projects. … … 109 107 # authorization information 110 108 self.auth_type = config.get('access', 'auth_type') \ 111 or ' legacy'109 or 'abac' 112 110 self.auth_dir = config.get('access', 'auth_dir') 113 111 accessdb = config.get("access", "accessdb") 114 112 # initialize the authorization system 115 if self.auth_type == 'legacy': 116 self.access = { } 117 if accessdb: 118 self.legacy_read_access(accessdb, self.legacy_access_tuple) 119 elif self.auth_type == 'abac': 113 if self.auth_type == 'abac': 120 114 self.auth = abac_authorizer(load=self.auth_dir) 121 115 self.access = [ ] … … 128 122 # read_state in the base_class 129 123 self.state_lock.acquire() 130 for a in ('allocation', 'projects', 'keys', 'types'): 131 if a not in self.state: 132 self.state[a] = { } 124 if 'allocation' not in self.state: self.state['allocation']= { } 133 125 self.allocation = self.state['allocation'] 134 self.projects = self.state['projects']135 self.keys = self.state['keys']136 self.types = self.state['types']137 if self.auth_type == "legacy":138 # Add the ownership attributes to the authorizer. Note that the139 # indices of the allocation dict are strings, but the attributes are140 # fedids, so there is a conversion.141 for k in self.allocation.keys():142 for o in self.allocation[k].get('owners', []):143 self.auth.set_attribute(o, fedid(hexstr=k))144 if self.allocation[k].has_key('userconfig'):145 sfid = self.allocation[k]['userconfig']146 fid = fedid(hexstr=sfid)147 self.auth.set_attribute(fid, "/%s" % sfid)148 126 self.state_lock.release() 149 127 self.exports = { … … 195 173 self.call_GetValue = service_caller('GetValue', log=self.log) 196 174 197 if not config.has_option("allocate", "uri"):198 self.allocate_project = \199 allocate_project_local(config, auth)200 else:201 self.allocate_project = \202 allocate_project_remote(config, auth)203 204 205 # If the project allocator exports services, put them in this object's206 # maps so that classes that instantiate this can call the services.207 self.soap_services.update(self.allocate_project.soap_services)208 self.xmlrpc_services.update(self.allocate_project.xmlrpc_services)209 210 @staticmethod211 def legacy_access_tuple(str):212 """213 Convert a string of the form (id[:resources:resouces], id, id) into a214 tuple of the form (project, user, user) where users may be names or215 fedids. The resources strings are obsolete and ignored.216 """217 def parse_name(n):218 if n.startswith('fedid:'): return fedid(hexstr=n[len('fedid:'):])219 else: return n220 221 str = str.strip()222 if str.startswith('(') and str.endswith(')'):223 str = str[1:-1]224 names = [ s.strip() for s in str.split(",")]225 if len(names) > 3:226 raise self.parse_error("More than three fields in name")227 first = names[0].split(":")228 if first == 'fedid:':229 del first[0]230 first[0] = fedid(hexstr=first[0])231 names[0] = first[0]232 233 for i in range(1,2):234 names[i] = parse_name(names[i])235 236 return tuple(names)237 else:238 raise self.parse_error('Bad mapping (unbalanced parens)')239 240 175 @staticmethod 241 176 def access_tuple(str): … … 243 178 Convert a string of the form (id, id) into an access_project. This is 244 179 called by read_access to convert to local attributes. It returns 245 a tuple of the form (project, user, user) where the two users are 246 always the same. 180 a tuple of the form (project, user). 247 181 """ 248 182 … … 251 185 # The slice takes the parens off the string. 252 186 proj, user = str[1:-1].split(',') 253 return (proj.strip(), user.strip() , user.strip())187 return (proj.strip(), user.strip()) 254 188 else: 255 189 raise self.parse_error( … … 258 192 # RequestAccess support routines 259 193 260 def legacy_lookup_access(self, req, fid): 261 """ 262 Look up the local access control information mapped to this fedid and 263 credentials. In this case it is a (project, create_user, access_user) 264 triple, and a triple of three booleans indicating which, if any will 265 need to be dynamically created. Finally a list of owners for that 266 allocation is returned. 267 268 lookup_access_base pulls the first triple out, and it is parsed by this 269 routine into the boolean map. Owners is always the controlling fedid. 270 """ 271 # Return values 272 rp = None 273 ru = None 274 # This maps a valid user to the Emulab projects and users to use 275 found, match = self.legacy_lookup_access_base(req, fid) 276 tb, project, user = match 277 278 if found == None: 279 raise service_error(service_error.access, 280 "Access denied - cannot map access") 281 282 # resolve <dynamic> and <same> in found 283 dyn_proj = False 284 dyn_create_user = False 285 dyn_service_user = False 286 287 if found[0] == "<same>": 288 if project != None: 289 rp = project 290 else : 291 raise service_error(\ 292 service_error.server_config, 293 "Project matched <same> when no project given") 294 elif found[0] == "<dynamic>": 295 rp = None 296 dyn_proj = True 297 else: 298 rp = found[0] 299 300 if found[1] == "<same>": 301 if user_match == "<any>": 302 if user != None: rcu = user[0] 303 else: raise service_error(\ 304 service_error.server_config, 305 "Matched <same> on anonymous request") 306 else: 307 rcu = user_match 308 elif found[1] == "<dynamic>": 309 rcu = None 310 dyn_create_user = True 311 else: 312 rcu = found[1] 313 314 if found[2] == "<same>": 315 if user_match == "<any>": 316 if user != None: rsu = user[0] 317 else: raise service_error(\ 318 service_error.server_config, 319 "Matched <same> on anonymous request") 320 else: 321 rsu = user_match 322 elif found[2] == "<dynamic>": 323 rsu = None 324 dyn_service_user = True 325 else: 326 rsu = found[2] 327 328 return (rp, rcu, rsu), (dyn_create_user, dyn_service_user, dyn_proj),\ 329 [ fid ] 330 331 def do_project_allocation(self, dyn, project, user): 332 """ 333 Call the project allocation routines and return the info. 334 """ 335 if dyn: 336 # Compose the dynamic project request 337 # (only dynamic, dynamic currently allowed) 338 preq = { 'AllocateProjectRequestBody': \ 339 { 'project' : {\ 340 'user': [ \ 341 { \ 342 'access': [ { 343 'sshPubkey': self.ssh_pubkey_file } ], 344 'role': "serviceAccess",\ 345 }, \ 346 { \ 347 'access': [ { 348 'sshPubkey': self.ssh_pubkey_file } ], 349 'role': "experimentCreation",\ 350 }, \ 351 ], \ 352 }\ 353 }\ 354 } 355 return self.allocate_project.dynamic_project(preq) 356 else: 357 preq = {'StaticProjectRequestBody' : \ 358 { 'project': \ 359 { 'name' : { 'localname' : project },\ 360 'user' : [ \ 361 {\ 362 'userID': { 'localname' : user }, \ 363 'access': [ { 364 'sshPubkey': self.ssh_pubkey_file } ], 365 'role': 'experimentCreation'\ 366 },\ 367 {\ 368 'userID': { 'localname' : user}, \ 369 'access': [ { 370 'sshPubkey': self.ssh_pubkey_file } ], 371 'role': 'serviceAccess'\ 372 },\ 373 ]}\ 374 }\ 375 } 376 return self.allocate_project.static_project(preq) 377 378 def save_project_state(self, aid, ap, dyn, owners): 379 """ 380 Parse out and save the information relevant to the project created for 381 this experiment. That info is largely in ap and owners. dyn indicates 382 that the project was created dynamically. Return the user and project 383 names. 194 def save_project_state(self, aid, pname, uname, owners): 195 """ 196 Save the project, user, and owners associated with this allocation. 197 This creates the allocation entry. 384 198 """ 385 199 self.state_lock.acquire() 386 200 self.allocation[aid] = { } 387 self.allocation[aid]['auth'] = set() 388 try: 389 pname = ap['project']['name']['localname'] 390 except KeyError: 391 pname = None 392 393 if dyn: 394 if not pname: 395 self.state_lock.release() 396 raise service_error(service_error.internal, 397 "Misformed allocation response?") 398 if pname in self.projects: self.projects[pname] += 1 399 else: self.projects[pname] = 1 400 self.allocation[aid]['project'] = pname 401 else: 402 # sproject is a static project associated with this allocation. 403 self.allocation[aid]['sproject'] = pname 404 405 self.allocation[aid]['keys'] = [ ] 406 407 try: 408 for u in ap['project']['user']: 409 uname = u['userID']['localname'] 410 if u['role'] == 'experimentCreation': 411 self.allocation[aid]['user'] = uname 412 for k in [ k['sshPubkey'] for k in u['access'] \ 413 if k.has_key('sshPubkey') ]: 414 kv = "%s:%s" % (uname, k) 415 if self.keys.has_key(kv): self.keys[kv] += 1 416 else: self.keys[kv] = 1 417 self.allocation[aid]['keys'].append((uname, k)) 418 except KeyError: 419 self.state_lock.release() 420 raise service_error(service_error.internal, 421 "Misformed allocation response?") 422 201 self.allocation[aid]['project'] = pname 202 self.allocation[aid]['user'] = uname 423 203 self.allocation[aid]['owners'] = owners 424 204 self.write_state() … … 481 261 self.auth.save() 482 262 483 if self.auth_type == "legacy": 484 found, dyn, owners= self.legacy_lookup_access(req, fid) 485 proof = access_proof("me", fid, "create") 486 elif self.auth_type == 'abac': 487 found, dyn, owners, proof = self.lookup_access(req, fid, filter=pf) 263 if self.auth_type == 'abac': 264 found, owners, proof = self.lookup_access(req, fid, filter=pf) 488 265 else: 489 266 raise service_error(service_error.internal, … … 491 268 ap = None 492 269 493 # This only happens in legacy lookups, but if this user has access to494 # the testbed but not the project to be exported, raise the error.495 if ep and ep != found[0]:496 raise service_error(service_error.access,497 "Cannot export %s" % ep)498 499 if self.ssh_pubkey_file:500 ap = self.do_project_allocation(dyn[1], found[0], found[1])501 else:502 raise service_error(service_error.internal,503 "SSH access parameters required")504 270 # keep track of what's been added 505 271 allocID, alloc_cert = generate_fedid(subj="alloc", log=self.log) 506 272 aid = unicode(allocID) 507 273 508 pname, uname = self.save_project_state(aid, ap, dyn[1], owners)274 pname, uname = self.save_project_state(aid, found[0], found[1], owners) 509 275 510 276 services, svc_state = self.export_services(req.get('service',[]), … … 526 292 "Can't open %s/%s : %s" % (self.certdir, aid, e)) 527 293 resp = self.build_access_response({ 'fedid': allocID } , 528 ap, services, proof)294 pname, services, proof) 529 295 return resp 530 531 def do_release_project(self, del_project, del_users, del_types):532 """533 If a project and users has to be deleted, make the call.534 """535 msg = { 'project': { }}536 if del_project:537 msg['project']['name']= {'localname': del_project}538 users = [ ]539 for u in del_users.keys():540 users.append({ 'userID': { 'localname': u },\541 'access' : \542 [ {'sshPubkey' : s } for s in del_users[u]]\543 })544 if users:545 msg['project']['user'] = users546 if len(del_types) > 0:547 msg['resources'] = { 'node': \548 [ {'hardware': [ h ] } for h in del_types ]\549 }550 if self.allocate_project.release_project:551 msg = { 'ReleaseProjectRequestBody' : msg}552 self.allocate_project.release_project(msg)553 296 554 297 def ReleaseAccess(self, req, fid): … … 579 322 raise service_error(service_error.access, "Access Denied") 580 323 581 # If we know this allocation, reduce the reference counts and582 # remove the local allocations. Otherwise report an error. If583 # there is an allocation to delete, del_users will be a dictonary584 # of sets where the key is the user that owns the keys in the set.585 # We use a set to avoid duplicates. del_project is just the name586 # of any dynamic project to delete. We're somewhat lazy about587 # deleting authorization attributes. Having access to something588 # that doesn't exist isn't harmful.589 del_users = { }590 del_project = None591 del_types = set()592 593 324 self.state_lock.acquire() 594 325 if aid in self.allocation: 595 326 self.log.debug("Found allocation for %s" %aid) 596 327 self.clear_allocation_authorization(aid, state_attr='allocation') 597 for k in self.allocation[aid]['keys']:598 kk = "%s:%s" % k599 self.keys[kk] -= 1600 if self.keys[kk] == 0:601 if not del_users.has_key(k[0]):602 del_users[k[0]] = set()603 del_users[k[0]].add(k[1])604 del self.keys[kk]605 606 if 'project' in self.allocation[aid]:607 pname = self.allocation[aid]['project']608 self.projects[pname] -= 1609 if self.projects[pname] == 0:610 del_project = pname611 del self.projects[pname]612 613 if 'types' in self.allocation[aid]:614 for t in self.allocation[aid]['types']:615 self.types[t] -= 1616 if self.types[t] == 0:617 if not del_project: del_project = t[0]618 del_types.add(t[1])619 del self.types[t]620 621 328 del self.allocation[aid] 622 329 self.write_state() 623 330 self.state_lock.release() 624 # If we actually have resources to deallocate, prepare the call. 625 if del_project or del_users: 626 self.do_release_project(del_project, del_users, del_types) 627 # And remove the access cert 331 # Remove the access cert 628 332 cf = "%s/%s.pem" % (self.certdir, aid) 629 333 self.log.debug("Removing %s" % cf) … … 939 643 if aid in self.allocation: 940 644 proj = self.allocation[aid].get('project', None) 941 if not proj:942 proj = self.allocation[aid].get('sproject', None)943 645 self.state_lock.release() 944 646 … … 1215 917 if aid in self.allocation: 1216 918 proj = self.allocation[aid].get('project', None) 1217 if not proj:1218 proj = self.allocation[aid].get('sproject', None)1219 919 user = self.allocation[aid].get('user', None) 1220 920 ename = self.allocation[aid].get('experiment', None) … … 1265 965 if topo: topo = topo.clone() 1266 966 proj = self.allocation[aid].get('project', None) 1267 if not proj:1268 proj = self.allocation[aid].get('sproject', None)1269 967 user = self.allocation[aid].get('user', None) 1270 968 ename = self.allocation[aid].get('experiment', None) -
fedd/federation/protogeni_access.py
rd2e86f6 ree950c2 27 27 28 28 from access import access_base 29 from legacy_access import legacy_access30 29 from protogeni_proxy import protogeni_proxy 31 30 from geniapi_proxy import geniapi_proxy … … 42 41 fl.addHandler(nullHandler()) 43 42 44 class access(access_base , legacy_access):43 class access(access_base): 45 44 """ 46 45 The implementation of access control based on mapping users to projects. … … 115 114 # authorization information 116 115 self.auth_type = config.get('access', 'auth_type') \ 117 or ' legacy'116 or 'abac' 118 117 self.auth_dir = config.get('access', 'auth_dir') 119 118 accessdb = config.get("access", "accessdb") 120 119 # initialize the authorization system 121 if self.auth_type == 'legacy': 122 self.access = { } 123 if accessdb: 124 self.legacy_read_access(accessdb, self.make_access_info) 125 # Add the ownership attributes to the authorizer. Note that the 126 # indices of the allocation dict are strings, but the attributes are 127 # fedids, so there is a conversion. 128 self.state_lock.acquire() 129 for k in self.state.get('allocation', {}).keys(): 130 for o in self.state['allocation'][k].get('owners', []): 131 self.auth.set_attribute(o, fedid(hexstr=k)) 132 self.auth.set_attribute(fedid(hexstr=k),fedid(hexstr=k)) 133 134 self.state_lock.release() 135 self.lookup_access = self.legacy_lookup_access_base 136 elif self.auth_type == 'abac': 120 if self.auth_type == 'abac': 137 121 self.auth = abac_authorizer(load=self.auth_dir) 138 122 self.access = [ ] -
fedd/federation/skeleton_access.py
rd2e86f6 ree950c2 18 18 19 19 from access import access_base 20 from legacy_access import legacy_access21 20 22 21 # Make log messages disappear if noone configures a fedd logger. This is … … 32 31 33 32 # The plug-in itself. 34 class access(access_base , legacy_access):33 class access(access_base): 35 34 """ 36 35 This is a demonstration plug-in for fedd. It responds to all the … … 78 77 # authorization information 79 78 self.auth_type = config.get('access', 'auth_type') \ 80 or ' legacy'79 or 'abac' 81 80 self.auth_dir = config.get('access', 'auth_dir') 82 81 accessdb = config.get("access", "accessdb") 83 # initialize the authorization system. In each case we make a call to82 # initialize the authorization system. We make a call to 84 83 # read the access database that maps from authorization information 85 84 # into local information. The local information is parsed by the 86 85 # translator above. 87 if self.auth_type == 'legacy': 88 self.access = { } 89 if accessdb: 90 try: 91 self.legacy_read_access(accessdb, self.parse_access_string) 92 except EnvironmentError, e: 93 self.log.error("Cannot read %s: %s" % \ 94 (config.get("access", "accessdb"), e)) 95 raise e 96 # The base class initializer has read the state dictionary from the 97 # state file, if there is one. The state variable includes 98 # information about each active allocation, keyed by the allocation 99 # identifier. This loop extracts the owners stored with each 100 # allocation and associates an access attribute with them. Each 101 # owner is allowed to access each thing they own. This is a 102 # specialization of the state handling. ABAC records this 103 # information explicitly so this loop only executes for legacy 104 # code. 105 self.state_lock.acquire() 106 for k in self.state.keys(): 107 # Add the owners 108 for o in self.state[k].get('owners', []): 109 self.auth.set_attribute(o, fedid(hexstr=k)) 110 # The principal represented by the allocation itself is also 111 # allowed to make accesses. 112 self.auth.set_attribute(fedid(hexstr=k),fedid(hexstr=k)) 113 self.state_lock.release() 114 # This access controller does not specialize the process of looking 115 # up local information. This aliases the lookup_access method to 116 # be easier to read. 117 self.lookup_access = self.legacy_lookup_access_base 118 elif self.auth_type == 'abac': 86 if self.auth_type == 'abac': 119 87 # Load the current authorization state 120 88 self.auth = abac_authorizer(load=self.auth_dir)
Note: See TracChangeset
for help on using the changeset viewer.