Changeset e83f2f2
- Timestamp:
- Dec 14, 2010 6:58:28 PM (14 years ago)
- Branches:
- axis_example, compt_changes, info-ops, master
- Children:
- c092b7f
- Parents:
- 2627eb3
- Files:
-
- 1 added
- 24 edited
Legend:
- Unmodified
- Added
- Removed
-
fedd/fedd_create.py
r2627eb3 re83f2f2 9 9 from string import join 10 10 11 from federation.proof import proof 11 12 from federation.fedid import fedid, generate_fedid 12 13 from federation.remote_service import service_caller 13 14 from federation.client_lib import client_opts, exit_with_fault, RPCException, \ 14 15 wrangle_standard_options, do_rpc, get_experiment_names, save_certfile,\ 15 get_abac_certs 16 get_abac_certs, log_authentication 16 17 from federation.util import abac_split_cert, abac_context_to_creds 17 18 from federation import topdl … … 247 248 caller=service_caller('New'), responseBody="NewResponseBody") 248 249 except RPCException, e: 249 exit_with_fault(e )250 exit_with_fault(e, 'New (create)', opts) 250 251 except RuntimeError, e: 251 252 sys.exit("Error processing RPC: %s" % e) … … 253 254 if opts.debug > 1: print >>sys.stderr, resp_dict 254 255 256 proof = proof.from_dict(resp_dict.get('proof', {})) 257 if proof and opts.auth_log: 258 log_authentication(opts.auth_log, 'New (create)', 'succeeded', proof) 255 259 # Save the experiment ID certificate if we need it 256 260 try: … … 303 307 serialize_only=opts.serialize_only, 304 308 tracefile=opts.tracefile, 305 caller=service_caller('Create' ), responseBody="CreateResponseBody")309 caller=service_caller('Create', max_retries=1), responseBody="CreateResponseBody") 306 310 except RPCException, e: 307 exit_with_fault(e )311 exit_with_fault(e, 'Create', opts) 308 312 except RuntimeError, e: 309 313 sys.exit("Error processing RPC: %s" % e) … … 318 322 if e_fedid: print "fedid: %s" % e_fedid 319 323 if st: print "status: %s" % st 320 324 proof = proof.from_dict(resp_dict.get('proof', {})) 325 if proof and opts.auth_log: 326 log_authentication(opts.auth_log, 'Create', 'succeeded', proof) -
fedd/fedd_ftopo.py
r2627eb3 re83f2f2 4 4 5 5 from federation import topdl 6 from federation.proof import proof 6 7 from federation.remote_service import service_caller 7 8 from federation.client_lib import client_opts, exit_with_fault, RPCException,\ 8 wrangle_standard_options, do_rpc, get_experiment_names, info_format 9 wrangle_standard_options, do_rpc, get_experiment_names, info_format, \ 10 log_authentication 9 11 10 12 class ftopo_opts(client_opts): … … 42 44 caller=service_caller('Info'), responseBody='InfoResponseBody') 43 45 except RPCException, e: 44 exit_with_fault(e )46 exit_with_fault(e, 'Ftopo', opts) 45 47 except RuntimeError, e: 46 48 sys.exit("Error processing RPC: %s" % e) … … 65 67 else: 66 68 sys.exit("Badly formatted response (no experiment descrption)!?") 69 proof = proof.from_dict(resp_dict.get('proof', {})) 70 if proof and opts.auth_log: 71 log_authentication(opts.auth_log, 'Ftopo', 'succeeded', proof) -
fedd/fedd_image.py
r2627eb3 re83f2f2 8 8 9 9 from federation import topdl 10 from federation.proof import proof 10 11 from federation.remote_service import service_caller 11 12 from federation.client_lib import client_opts, exit_with_fault, RPCException, \ 12 wrangle_standard_options, do_rpc, get_experiment_names, save_certfile 13 wrangle_standard_options, do_rpc, get_experiment_names, save_certfile,\ 14 log_authentication 13 15 14 16 … … 296 298 caller=service_caller('Info'), responseBody="InfoResponseBody") 297 299 300 proof = proof.from_dict(resp_dict.get('proof', {})) 301 if proof and opts.auth_log: 302 log_authentication(opts.auth_log, 'Image', 'succeeded', proof) 298 303 return extract_topo_from_message(resp_dict, opts.serialize_only) 299 304 … … 374 379 top = get_experiment_topo(opts, cert, url) 375 380 except RPCException, e: 376 exit_with_fault(e )381 exit_with_fault(e, 'image', opts) 377 382 except RuntimeError, e: 378 383 sys.exit("%s" % e) … … 386 391 except RPCException, e: 387 392 print >>sys.stderr, "Cannot extract a topology from %s" % opts.file 388 exit_with_fault(e )393 exit_with_fault(e, 'image', opts) 389 394 except RuntimeError, e: 390 395 sys.exit("Cannot extract a topology from %s: %s" % (opts.file, e)) -
fedd/fedd_info.py
r2627eb3 re83f2f2 3 3 import sys 4 4 5 from federation.proof import proof 5 6 from federation.remote_service import service_caller 6 7 from federation.client_lib import client_opts, exit_with_fault, RPCException,\ 7 wrangle_standard_options, do_rpc, get_experiment_names, info_format 8 wrangle_standard_options, do_rpc, get_experiment_names, info_format, \ 9 log_authentication 8 10 9 11 class exp_data_opts(client_opts): … … 43 45 caller=service_caller('Info'), responseBody='InfoResponseBody') 44 46 except RPCException, e: 45 exit_with_fault(e )47 exit_with_fault(e, 'Info', opts) 46 48 except RuntimeError, e: 47 49 sys.exit("Error processing RPC: %s" % e) … … 53 55 except RuntimeError, e: 54 56 print >>sys.stderr, "Warning: %s" % e 57 proof = proof.from_dict(resp_dict.get('proof', {})) 58 if proof and opts.auth_log: 59 log_authentication(opts.auth_log, 'Info', 'succeeded', proof) -
fedd/fedd_multiinfo.py
r2627eb3 re83f2f2 10 10 import time 11 11 12 from federation.proof import proof 12 13 from federation.remote_service import service_caller 13 14 from federation.client_lib import client_opts, exit_with_fault, RPCException,\ 14 wrangle_standard_options, do_rpc, info_format 15 wrangle_standard_options, do_rpc, info_format, log_authentication 15 16 16 17 class exp_data_opts(client_opts): … … 38 39 responseBody='MultiInfoResponseBody') 39 40 except RPCException, e: 40 exit_with_fault(e )41 exit_with_fault(e, 'MultiInfo', opts) 41 42 except RuntimeError, e: 42 43 sys.exit("Error processing RPC: %s" % e) … … 47 48 formatter(i, d) 48 49 print "---" 50 proof = proof.from_dict(resp_dict.get('proof', {})) 51 if proof and opts.auth_log: 52 log_authentication(opts.auth_log, 'MultiInfo', 'succeeded', proof) -
fedd/fedd_multistatus.py
r2627eb3 re83f2f2 3 3 import sys 4 4 5 from federation.proof import proof 5 6 from federation.remote_service import service_caller 6 7 from federation.client_lib import client_opts, exit_with_fault, RPCException, \ 7 wrangle_standard_options, do_rpc, get_experiment_names 8 wrangle_standard_options, do_rpc, get_experiment_names, \ 9 log_authentication 8 10 9 11 parser = client_opts() … … 22 24 responseBody='MultiInfoResponseBody') 23 25 except RPCException, e: 24 exit_with_fault(e )26 exit_with_fault(e, 'MultiInfo', opts) 25 27 except RuntimeError, e: 26 28 sys.exit("Error processing RPC: %s" % e) … … 33 35 print ":".join([ l or "" , "%s" % (f or "") , 34 36 exp.get('experimentStatus', "") ]) 37 proof = proof.from_dict(resp_dict.get('proof', {})) 38 if proof and opts.auth_log: 39 log_authentication(opts.auth_log, 'MultiInfo', 'succeeded', proof) -
fedd/fedd_new.py
r2627eb3 re83f2f2 2 2 3 3 import sys 4 from datetime import datetime 4 5 5 6 from federation.fedid import fedid, generate_fedid 6 7 from federation.remote_service import service_caller 8 from federation.proof import proof 7 9 from federation.client_lib import client_opts, exit_with_fault, RPCException, \ 8 10 wrangle_standard_options, do_rpc, get_experiment_names, \ 9 save_certfile, get_abac_certs 10 11 save_certfile, get_abac_certs, log_authentication 11 12 12 13 class new_opts(client_opts): … … 58 59 caller=service_caller("New"), responseBody='NewResponseBody') 59 60 except RPCException, e: 60 exit_with_fault(e )61 exit_with_fault(e, 'New', opts) 61 62 except RuntimeError, e: 62 63 sys.exit("Error processing RPC: %s" % e) … … 75 76 e_fedid, e_local = get_experiment_names(resp_dict.get('experimentID', None)) 76 77 st = resp_dict.get('experimentStatus', None) 78 proof = proof.from_dict(resp_dict.get('proof', {})) 77 79 78 80 if e_local: print "localname: %s" % e_local 79 81 if e_fedid: print "fedid: %s" % e_fedid 80 82 if st: print "status: %s" % st 83 if proof and opts.auth_log: 84 log_authentication(opts.auth_log, 'New', 'succeeded', proof) -
fedd/fedd_ns2topdl.py
r2627eb3 re83f2f2 4 4 5 5 from federation import topdl 6 from federation.proof import proof 6 7 from federation.remote_service import service_caller 7 8 from federation.client_lib import client_opts, exit_with_fault, RPCException, \ … … 45 46 responseBody="Ns2TopdlResponseBody") 46 47 except RPCException, e: 47 exit_with_fault(e )48 exit_with_fault(e, 'Ns2Topdl', opts) 48 49 except RuntimeError, e: 49 50 sys.exit("Error processing RPC: %s" % e) … … 69 70 else: 70 71 print topdl.topology_to_xml(top, top="experiment") 72 proof = proof.from_dict(resp_dict.get('proof', {})) 73 if proof and opts.auth_log: 74 log_authentication(opts.auth_log, 'New (create)', 'succeeded', proof) -
fedd/fedd_spewlog.py
r2627eb3 re83f2f2 4 4 import time 5 5 6 from federation.proof import proof 6 7 from federation.remote_service import service_caller 7 8 from federation.client_lib import client_opts, exit_with_fault, RPCException, \ … … 63 64 caller=service_caller('Info'), responseBody="InfoResponseBody") 64 65 except RPCException, e: 65 exit_with_fault(e )66 exit_with_fault(e, 'Info (spewlog)', opts) 66 67 except RuntimeError, e: 67 68 sys.exit("Error processing RPC: %s" % e) 69 70 proof = proof.from_dict(resp_dict.get('proof', {})) 71 if proof and opts.auth_log: 72 log_authentication(opts.auth_log, 'Info (spewlog)', 'succeeded', proof) 68 73 69 74 if not opts.serialize_only: -
fedd/fedd_terminate.py
r2627eb3 re83f2f2 4 4 5 5 from federation.fedid import fedid 6 from federation.proof import proof 6 7 from federation.remote_service import service_caller 7 8 from federation.client_lib import client_opts, exit_with_fault, RPCException, \ 8 wrangle_standard_options, do_rpc 9 wrangle_standard_options, do_rpc, log_authentication 9 10 10 11 class terminate_opts(client_opts): … … 66 67 responseBody='TerminateResponseBody') 67 68 except RPCException, e: 68 exit_with_fault(e )69 exit_with_fault(e, 'Terminate', opts) 69 70 except RuntimeError, e: 70 71 sys.exit("Error processing RPC: %s" % e) … … 78 79 out.close() 79 80 sys.exit("No log returned") 81 proof = proof.from_dict(resp_dict.get('proof', {})) 82 if proof and opts.auth_log: 83 log_authentication(opts.auth_log, 'Terminate', 'succeeded', proof) -
fedd/federation/access.py
r2627eb3 re83f2f2 301 301 # Check every attribute that we know how to map and take the first 302 302 # success. 303 fail_proofs = [ ] 303 304 for attr in check: 304 if self.auth.check_attribute(fid, attr.attr): 305 access_ok, proof = self.auth.check_attribute(fid, attr.attr, 306 with_proof=True) 307 if access_ok: 305 308 self.log.debug("Access succeeded for %s %s" % (attr.attr, fid)) 306 309 # XXX: needs to deal with dynamics 307 310 return copy.copy(attr.value), (False, False, False), \ 308 [ fid ] 311 [ fid ], proof 309 312 else: 313 fail_proofs.append(proof) 310 314 self.log.debug("Access failed for %s %s" % (attr.attr, fid)) 311 315 else: 312 raise service_error(service_error.access, "Access denied") 316 raise service_error(service_error.access, "Access denied", 317 proof=fail_proofs) 313 318 314 319 … … 448 453 return (exp, state) 449 454 450 def build_access_response(self, alloc_id, ap, services ):455 def build_access_response(self, alloc_id, ap, services, proof): 451 456 """ 452 457 Create the SOAP response. … … 461 466 msg = { 462 467 'allocID': alloc_id, 468 'proof': proof.to_dict(), 463 469 'fedAttr': [ 464 470 { 'attribute': 'domain', 'value': self.domain } , … … 789 795 # exception denying access that triggers a fault response back to the 790 796 # caller. 791 found, match, owners = self.lookup_access(req, fid)797 found, match, owners, proof = self.lookup_access(req, fid) 792 798 self.log.info( 793 799 "[RequestAccess] Access granted to %s with local creds %s" % \ … … 819 825 "Can't open %s/%s : %s" % (self.certdir, aid, e)) 820 826 self.log.debug('[RequestAccess] Returning allocation ID: %s' % allocID) 821 return { 'allocID': { 'fedid': allocID } }827 return { 'allocID': { 'fedid': allocID }, 'proof': proof.to_dict() } 822 828 823 829 def ReleaseAccess(self, req, fid): … … 849 855 self.log.debug("[ReleaseAccess] deallocation requested for %s", aid) 850 856 # Confirm access 851 if not self.auth.check_attribute(fid, auth_attr): 857 access_ok, proof = self.auth.check_attribute(fid, auth_attr, 858 with_proof=True) 859 if not access_ok: 852 860 self.log.debug("[ReleaseAccess] deallocation denied for %s", aid) 853 raise service_error(service_error.access, "Access Denied") 861 raise service_error(service_error.access, "Access Denied", 862 proof=proof) 854 863 855 864 # If there is an allocation in the state, delete it. Note the locking. … … 865 874 self.log.debug("[ReleaseAccess] Removing %s" % cf) 866 875 os.remove(cf) 867 return { 'allocID': req['allocID'] }876 return { 'allocID': req['allocID'], 'proof': proof.to_dict() } 868 877 else: 869 878 self.state_lock.release() -
fedd/federation/authorizer.py
r2627eb3 re83f2f2 1 1 #/usr/local/bin/python 2 2 3 from string import join4 3 from tempfile import mkstemp 5 4 from subprocess import call … … 12 11 from service_error import service_error 13 12 from util import abac_pem_type, abac_split_cert 13 from proof import proof 14 14 15 15 … … 116 116 if attrs: attrs.discard(attr) 117 117 118 def check_attribute(self, name, attr ):118 def check_attribute(self, name, attr, with_proof=False): 119 119 """ 120 120 Return True if name has attr (or if attr is global). Tuple names match … … 130 130 self.valid_name(name) 131 131 if attr in self.globals: 132 return True 132 if with_proof: return True, proof("me", name, attr) 133 else: return True 133 134 134 135 if isinstance(name, tuple): … … 137 138 if self.attrs.has_key(lookup): 138 139 if attr in self.attrs[lookup]: 139 return True 140 else: 141 return attr in self.attrs.get(self.auth_name(name), set()) 140 if with_proof: return True, proof("me", name, attr) 141 else: return True 142 # Drop through 143 if with_proof: return False, proof("me", name, attr) 144 else: return False 145 else: 146 if with_proof: 147 return attr in self.attrs.get(self.auth_name(name), set()), \ 148 proof("me", name, attr) 149 else: 150 return attr in self.attrs.get(self.auth_name(name), set()) 142 151 143 152 def set_global_attribute(self, attr): … … 375 384 376 385 377 def check_attribute(self, name, attr): 378 # XXX proof soon 386 def check_attribute(self, name, attr, with_proof=False): 379 387 if isinstance(name, tuple): 380 388 raise abac_authorizer.bad_name( … … 399 407 # Sigh. Unicode vs swig and swig seems to lose. Make sure 400 408 # everything we pass into ABAC is a str not a unicode. 401 rv, p roof= self.context.query(a, n)409 rv, p = self.context.query(a, n) 402 410 # XXX delete soon 403 if not rv and attr in self.globals: rv = True 404 self.lock.release() 405 406 return rv 411 if not rv and attr in self.globals: 412 rv = True 413 p = None 414 self.lock.release() 415 if with_proof: return rv, proof(self.fedid, name, a, p) 416 else: return rv 407 417 408 418 def set_global_attribute(self, attr): -
fedd/federation/client_lib.py
r2627eb3 re83f2f2 7 7 8 8 from string import join 9 from datetime import datetime 9 10 10 11 … … 31 32 callback=self.expand_file, 32 33 type="string", help="my certificate file") 34 self.add_option("--auth_log", action="callback", dest="auth_log", 35 callback=self.expand_file, default=None, 36 type="string", help="Log authentication decisions to this file") 33 37 self.add_option("--abac", action="callback", dest="abac_dir", 34 38 callback=self.expand_file, … … 53 57 const=sys.stderr, help="Print SOAP exchange to stderr") 54 58 55 def exit_with_fault(exc, out=sys.stderr): 59 def log_authentication(fn, action, outcome, proof): 60 f = open(fn, 'a') 61 print >>f, '%s %s at %s' % (action, outcome, datetime.now()) 62 if isinstance(proof, list): 63 for p in proof: 64 print >>f, p.to_xml() 65 else: 66 print >>f, proof.to_xml() 67 f.close() 68 69 70 def exit_with_fault(exc, action, opts, out=sys.stderr): 56 71 """ 57 72 Print an error message and exit. exc is the RPCException that caused the … … 72 87 code = -1 73 88 89 if exc.code == service_error.access and opts.auth_log: 90 try: 91 log_authentication(opts.auth_log, action, 'failed', exc.proof) 92 except EnvironmentError, e: 93 print >>sys.stderr, "Failed to log to %s: %s" % \ 94 (e.filename, e.strerror) 95 74 96 print>>out, codestr 75 97 print>>out, "Description: %s" % exc.desc … … 81 103 XMLPRC calls. 82 104 """ 83 def __init__(self, desc=None, code=None, errstr=None ):105 def __init__(self, desc=None, code=None, errstr=None, proof=None): 84 106 RuntimeError.__init__(self) 85 107 self.desc = desc … … 87 109 else: self.code = -1 88 110 self.errstr = errstr 111 self.proof = proof 89 112 90 113 class CertificateMismatchError(RuntimeError): pass … … 208 231 except service_error, e: 209 232 raise RPCException(desc=e.desc, code=e.code, 210 errstr=e.code_string() )233 errstr=e.code_string(), proof=e.proof) 211 234 elif transport == "xmlrpc": 212 235 if serialize_only: -
fedd/federation/deter_internal_access.py
r2627eb3 re83f2f2 215 215 aid = "%s" % auth_attr 216 216 attrs = req.get('fedAttr', []) 217 if not self.auth.check_attribute(fid, auth_attr): 217 access_ok, proof = self.auth.check_attribute(fid, auth_attr, 218 with_proof=True) 219 if not access_ok: 218 220 raise service_error(service_error.access, "Access denied") 219 221 else: … … 271 273 'allocID': req['allocID'], 272 274 'allocationLog': logv, 273 'segmentdescription': { 'topdldescription': rtopo.to_dict() } 275 'segmentdescription': { 'topdldescription': rtopo.to_dict() }, 276 'proof': proof.to_dict(), 274 277 } 275 278 retval = copy.deepcopy(self.state[aid]['started']) … … 289 292 290 293 self.log.debug("Terminate request for %s" %aid) 291 if not self.auth.check_attribute(fid, auth_attr): 294 access_ok, proof = self.auth.check_attribute(fid, auth_attr, 295 with_proof=True) 296 if not access_ok: 292 297 raise service_error(service_error.access, "Access denied") 293 298 … … 308 313 self.state[aid]['vlan'] = None 309 314 self.state_lock.release() 310 return { 'allocID': req['allocID'] }315 return { 'allocID': req['allocID'], 'proof': proof.to_dict() } -
fedd/federation/dragon_access.py
r2627eb3 re83f2f2 418 418 return (repo, alloc_log) 419 419 420 def finalize_experiment(self, topo, vlan_no, gri, aid, alloc_id ):420 def finalize_experiment(self, topo, vlan_no, gri, aid, alloc_id, proof): 421 421 """ 422 422 Place the relevant information in the global state block, and prepare … … 442 442 'topdldescription': rtopo.to_dict() 443 443 }, 444 'proof': proof.to_dict(), 444 445 } 445 446 retval = copy.deepcopy(self.state[aid]['started']) … … 462 463 aid = "%s" % auth_attr 463 464 attrs = req.get('fedAttr', []) 464 if not self.auth.check_attribute(fid, auth_attr): 465 access_ok, proof = self.auth.check_attribute(fid, auth_attr, 466 with_proof=True) 467 if not access_ok: 465 468 raise service_error(service_error.access, "Access denied") 466 469 else: … … 508 511 if gri: 509 512 return self.finalize_experiment(topo, vlan_no, gri, aid, 510 req['allocID'] )513 req['allocID'], proof) 511 514 elif err: 512 515 raise service_error(service_error.federant, … … 526 529 self.log.debug("Terminate request for %s" %aid) 527 530 attrs = req.get('fedAttr', []) 528 if not self.auth.check_attribute(fid, auth_attr): 531 access_ok, proof = self.auth.check_attribute(fid, auth_attr, 532 with_proof=True) 533 if not access_ok: 529 534 raise service_error(service_error.access, "Access denied") 530 535 … … 549 554 self.log.debug("Stop segment for GRI: %s" %gri) 550 555 self.stop_segment(user, gri) 551 return { 'allocID': req['allocID'] }556 return { 'allocID': req['allocID'], 'proof': proof.to_dict() } -
fedd/federation/emulab_access.py
r2627eb3 re83f2f2 24 24 from service_error import service_error 25 25 from remote_service import xmlrpc_handler, soap_handler, service_caller 26 from proof import proof as access_proof 26 27 27 28 import httplib … … 480 481 481 482 if self.auth_type == "legacy": 482 found, dyn, owners = self.legacy_lookup_access(req, fid) 483 found, dyn, owners= self.legacy_lookup_access(req, fid) 484 proof = access_proof("me", fid, "create") 483 485 elif self.auth_type == 'abac': 484 found, dyn, owners = self.lookup_access(req, fid, filter=pf)486 found, dyn, owners, proof = self.lookup_access(req, fid, filter=pf) 485 487 else: 486 488 raise service_error(service_error.internal, … … 523 525 "Can't open %s/%s : %s" % (self.certdir, aid, e)) 524 526 resp = self.build_access_response({ 'fedid': allocID } , 525 ap, services )527 ap, services, proof) 526 528 return resp 527 529 … … 570 572 self.log.debug("[access] deallocation requested for %s by %s" % \ 571 573 (aid, fid)) 572 if not self.auth.check_attribute(fid, auth_attr): 574 access_ok, proof = self.auth.check_attribute(fid, auth_attr, 575 with_proof=True) 576 if not access_ok: 573 577 self.log.debug("[access] deallocation denied for %s", aid) 574 578 raise service_error(service_error.access, "Access Denied") … … 624 628 self.log.debug("Removing %s" % cf) 625 629 os.remove(cf) 626 return { 'allocID': req['allocID'] }630 return { 'allocID': req['allocID'], 'proof': proof.to_dict() } 627 631 else: 628 632 self.state_lock.release() … … 997 1001 return (ename, proj, user, pubkey_base, secretkey_base, alloc_log) 998 1002 999 def finalize_experiment(self, starter, topo, aid, alloc_id ):1003 def finalize_experiment(self, starter, topo, aid, alloc_id, proof): 1000 1004 """ 1001 1005 Store key bits of experiment state in the global repository, including … … 1022 1026 'topdldescription': topo.clone().to_dict() 1023 1027 }, 1024 'embedding': embedding 1028 'embedding': embedding, 1029 'proof': proof.to_dict(), 1025 1030 } 1026 1031 retval = copy.copy(self.allocation[aid]['started']) … … 1046 1051 aid = "%s" % auth_attr 1047 1052 attrs = req.get('fedAttr', []) 1048 if not self.auth.check_attribute(fid, auth_attr): 1053 1054 access_ok, proof = self.auth.check_attribute(fid, auth_attr, 1055 with_proof=True) 1056 if not access_ok: 1049 1057 raise service_error(service_error.access, "Access denied") 1050 1058 else: … … 1112 1120 1113 1121 if rv: 1114 return self.finalize_experiment(starter, topo, aid, req['allocID']) 1122 return self.finalize_experiment(starter, topo, aid, req['allocID'], 1123 proof) 1115 1124 elif err: 1116 1125 raise service_error(service_error.federant, … … 1128 1137 aid = "%s" % auth_attr 1129 1138 attrs = req.get('fedAttr', []) 1130 if not self.auth.check_attribute(fid, auth_attr): 1139 1140 access_ok, proof = self.auth.check_attribute(fid, auth_attr, 1141 with_proof=True) 1142 if not access_ok: 1131 1143 raise service_error(service_error.access, "Access denied") 1132 1144 … … 1157 1169 debug=self.create_debug, boss=self.boss, cert=self.xmlrpc_cert) 1158 1170 stopper(self, user, proj, ename) 1159 return { 'allocID': req['allocID'] }1171 return { 'allocID': req['allocID'], 'proof': proof.to_dict() } -
fedd/federation/experiment_control.py
r2627eb3 re83f2f2 801 801 self.response = None 802 802 self.node = { } 803 self.proof = None 803 804 804 805 def make_map(self, resp): … … 850 851 self.log_collector.write(line) 851 852 self.make_map(r['StartSegmentResponseBody']) 853 if 'proof' in r: self.proof = r['proof'] 852 854 self.response = r 853 855 else: … … 1021 1023 # mapping. 1022 1024 embedding = [ ] 1025 proofs = { } 1023 1026 for s in starters: 1024 1027 for k, v in s.node.items(): … … 1028 1031 'testbed': s.testbed 1029 1032 }) 1033 if s.proof: 1034 proofs[s.testbed] = s.proof 1030 1035 log.info("[start_segment]: Experiment %s active" % eid) 1031 1036 … … 1044 1049 top.to_dict() 1045 1050 self.state[eid]['embedding'] = embedding 1051 # Append startup proofs 1052 for f in self.state[eid]['federant']: 1053 if 'name' in f and 'localname' in f['name']: 1054 if f['name']['localname'] in proofs: 1055 f['proof'].append(proofs[f['name']['localname']]) 1056 1046 1057 if self.state_filename: self.write_state() 1047 1058 self.state_lock.release() … … 1326 1337 raise service_error(service_error.protocol, 1327 1338 "Bad proxy response") 1339 if 'proof' not in r: 1340 raise service_error(service_error.protocol, 1341 "Bad access response (no access proof)") 1328 1342 1329 1343 tbparam[tb] = { 1330 1344 "allocID" : r['allocID'], 1331 1345 "uri": uri, 1346 "proof": [ r['proof'] ], 1332 1347 } 1333 1348 … … 1498 1513 self.auth.save() 1499 1514 1500 if not self.auth.check_attribute(fid, 'new'): 1501 raise service_error(service_error.access, "New access denied") 1515 access_ok, proof = self.auth.check_attribute(fid, 'new', 1516 with_proof=True) 1517 1518 if not access_ok: 1519 raise service_error(service_error.access, "New access denied", 1520 proof=[proof]) 1502 1521 1503 1522 try: … … 1543 1562 ], 1544 1563 'experimentStatus': 'empty', 1545 'experimentAccess': { 'X509' : expcert } 1564 'experimentAccess': { 'X509' : expcert }, 1565 'proof': proof.to_dict(), 1546 1566 } 1547 1567 … … 1814 1834 raise service_error(service_error.req, "No request?") 1815 1835 1816 self.check_experiment_access(fid, key)1836 proof = self.check_experiment_access(fid, key) 1817 1837 1818 1838 self.state_lock.acquire() 1819 1839 if self.state.has_key(key): 1820 1840 if self.state[key].has_key('vtopo'): 1821 rv = { 'experiment' : {keytype: key },\ 1822 'vtopo': self.state[key]['vtopo'],\ 1841 rv = { 'experiment' : {keytype: key }, 1842 'vtopo': self.state[key]['vtopo'], 1843 'proof': proof.to_dict(), 1823 1844 } 1824 1845 else: … … 1858 1879 raise service_error(service_error.req, "No request?") 1859 1880 1860 self.check_experiment_access(fid, key)1881 proof = self.check_experiment_access(fid, key) 1861 1882 1862 1883 self.state_lock.acquire() 1863 1884 if self.state.has_key(key): 1864 1885 if self.state[key].has_key('vis'): 1865 rv = { 'experiment' : {keytype: key },\ 1866 'vis': self.state[key]['vis'],\ 1886 rv = { 'experiment' : {keytype: key }, 1887 'vis': self.state[key]['vis'], 1888 'proof': proof.to_dict(), 1867 1889 } 1868 1890 else: … … 1885 1907 between when it was started and the beginning of resource allocation. 1886 1908 This is basically the information about each local allocation. This 1887 fills in the values of the placeholder allocation in the state. 1909 fills in the values of the placeholder allocation in the state. It 1910 also collects the access proofs and returns them as dicts for a 1911 response message. 1888 1912 """ 1889 1913 # save federant information … … 1893 1917 'allocID' : tbparams[k]['allocID'], 1894 1918 'uri': tbparams[k]['uri'], 1919 'proof': tbparams[k]['proof'], 1895 1920 } 1896 1921 … … 1903 1928 [ tbparams[tb]['federant'] for tb in tbparams.keys() \ 1904 1929 if tbparams[tb].has_key('federant') ] 1930 # Access proofs for the response message 1931 proofs = [copy.deepcopy(p) for k in tbparams.keys()\ 1932 for p in tbparams[k]['federant']['proof']] 1905 1933 if self.state_filename: 1906 1934 self.write_state() 1907 1935 self.state_lock.release() 1936 return proofs 1908 1937 1909 1938 def clear_placeholder(self, eid, expid, tmpdir): … … 1945 1974 1946 1975 # Make sure that the caller can talk to us 1947 self.check_experiment_access(fid, key)1976 proof = self.check_experiment_access(fid, key) 1948 1977 1949 1978 # Install the testbed map entries supplied with the request into a copy … … 2030 2059 vtopo = topdl.topology_to_vtopo(top) 2031 2060 vis = self.genviz(vtopo) 2032 self.save_federant_information(allocated, tbparams, eid, vtopo,2033 vis, top)2061 proofs = self.save_federant_information(allocated, tbparams, 2062 eid, vtopo, vis, top) 2034 2063 except service_error, e: 2035 2064 # If something goes wrong in the parse (usually an access error) … … 2042 2071 # Start the background swapper and return the starting state. From 2043 2072 # here on out, the state will stick around a while. 2044 2045 # XXX: I think this is redundant2046 # Let users touch the state2047 # self.auth.set_attribute(fid, expid)2048 # self.auth.set_attribute(expid, expid)2049 # Override fedids can manipulate state as well2050 # for o in self.overrides:2051 # self.auth.set_attribute(o, expid)2052 # self.auth.save()2053 2073 2054 2074 # Create a logger that logs to the experiment's state object as well as … … 2076 2096 ], 2077 2097 'experimentStatus': 'starting', 2098 'proof': [ proof.to_dict() ] + proofs, 2078 2099 } 2079 2100 … … 2121 2142 key = self.get_experiment_fedid(key) 2122 2143 2123 if self.auth.check_attribute(fid, key): 2124 return True 2144 access_ok, proof = self.auth.check_attribute(fid, key, with_proof=True) 2145 2146 if access_ok: 2147 return proof 2125 2148 else: 2126 raise service_error(service_error.access, "Access Denied") 2149 raise service_error(service_error.access, "Access Denied", 2150 proof) 2127 2151 2128 2152 … … 2133 2157 """ 2134 2158 self.log.info("Get handler %s %s" % (path, fid)) 2159 # XXX: log proofs? 2135 2160 if self.auth.check_attribute(fid, path): 2136 2161 return ("%s/%s" % (self.repodir, path), "application/binary") … … 2138 2163 return (None, None) 2139 2164 2140 def clean_info_response(self, rv ):2165 def clean_info_response(self, rv, proof): 2141 2166 """ 2142 2167 Remove the information in the experiment's state object that is not in … … 2162 2187 if f.has_key('allocID'): del f['allocID'] 2163 2188 if f.has_key('uri'): del f['uri'] 2189 rv['proof'] = proof.to_dict() 2164 2190 2165 2191 return rv … … 2188 2214 raise service_error(service_error.req, "No request?") 2189 2215 2190 self.check_experiment_access(fid, key)2216 proof = self.check_experiment_access(fid, key) 2191 2217 2192 2218 # The state may be massaged by the service function that called … … 2199 2225 2200 2226 if rv: 2201 return self.clean_info_response(rv )2227 return self.clean_info_response(rv, proof) 2202 2228 else: 2203 2229 raise service_error(service_error.req, "No such experiment") … … 2207 2233 Return all the stored info that this fedid can access 2208 2234 """ 2209 rv = { 'info': [ ] }2235 rv = { 'info': [ ], 'proof': [ ] } 2210 2236 2211 2237 self.state_lock.acquire() 2212 2238 for key in [ k for k in self.state.keys() if isinstance(k, fedid)]: 2213 2239 try: 2214 self.check_experiment_access(fid, key)2240 proof = self.check_experiment_access(fid, key) 2215 2241 except service_error, e: 2216 2242 if e.code == service_error.access: … … 2222 2248 if self.state.has_key(key): 2223 2249 e = copy.deepcopy(self.state[key]) 2224 e = self.clean_info_response(e )2250 e = self.clean_info_response(e, proof) 2225 2251 rv['info'].append(e) 2252 rv['proof'].append(proof.to_dict()) 2226 2253 self.state_lock.release() 2227 2254 return rv … … 2351 2378 if tmpdir: self.remove_dirs(tmpdir) 2352 2379 2353 2354 2380 def terminate_experiment(self, req, fid): 2355 2381 """ … … 2364 2390 2365 2391 key = self.get_experiment_key(req, 'experiment') 2366 self.check_experiment_access(fid, key)2392 proof = self.check_experiment_access(fid, key) 2367 2393 exp = req.get('experiment', False) 2368 2394 force = req.get('force', False) … … 2416 2442 if repo: 2417 2443 self.remove_dirs("%s/%s" % (self.repodir, repo)) 2418 2444 2419 2445 return { 2420 2446 'experiment': exp , 2421 2447 'deallocationLog': string.join(dealloc_list, ''), 2448 'proof': [proof.to_dict()], 2422 2449 } 2423 2450 else: … … 2438 2465 rv = { 'name': name } 2439 2466 2440 if name and self.auth.check_attribute(fid, name): 2467 if not name: 2468 raise service_error(service_error.req, "No name?") 2469 2470 access_ok, proof = self.auth.check_attribute(fid, name, with_proof=True) 2471 2472 if access_ok: 2441 2473 self.log.debug("[GetValue] asking for %s " % name) 2442 2474 try: … … 2448 2480 if v is not None: 2449 2481 rv['value'] = v 2482 rv['proof'] = proof.to_dict() 2450 2483 self.log.debug("[GetValue] got %s from %s" % (v, name)) 2451 2484 return rv 2452 2485 else: 2453 raise service_error(service_error.access, "Access Denied") 2486 raise service_error(service_error.access, "Access Denied", 2487 proof=proof) 2454 2488 2455 2489 … … 2466 2500 v = req.get('value', '') 2467 2501 2468 if name and self.auth.check_attribute(fid, name): 2502 if not name: 2503 raise service_error(service_error.req, "No name?") 2504 2505 access_ok, proof = self.auth.check_attribute(fid, name, with_proof=True) 2506 2507 if access_ok: 2469 2508 try: 2470 2509 self.synch_store.set_value(name, v) … … 2479 2518 raise service_error(service_error.federant, 2480 2519 "Synch key %s revoked" % name) 2481 return { 'name': name, 'value': v}2520 return { 'name': name, 'value': v, 'proof': proof.to_dict() } 2482 2521 else: 2483 raise service_error(service_error.access, "Access Denied") 2522 raise service_error(service_error.access, "Access Denied", 2523 proof=proof) -
fedd/federation/protogeni_access.py
r2627eb3 re83f2f2 215 215 return (None, None) 216 216 217 def build_access_response(self, alloc_id, services ):217 def build_access_response(self, alloc_id, services, proof): 218 218 """ 219 219 Create the SOAP response. … … 230 230 'fedAttr': [ 231 231 { 'attribute': 'domain', 'value': self.domain } , 232 ] 232 ], 233 'proof': proof.to_dict() 233 234 } 234 235 if self.dragon_endpoint: … … 262 263 263 264 # Request for this fedd 264 found, match, owners = self.lookup_access(req, fid)265 found, match, owners, proof = self.lookup_access(req, fid) 265 266 services, svc_state = self.export_services(req.get('service',[]), 266 267 None, None) … … 288 289 raise service_error(service_error.internal, 289 290 "Can't open %s/%s : %s" % (self.certdir, aid, e)) 290 return self.build_access_response({ 'fedid': allocID }, None )291 return self.build_access_response({ 'fedid': allocID }, None, proof) 291 292 292 293 … … 312 313 313 314 self.log.debug("[access] deallocation requested for %s", aid) 314 if not self.auth.check_attribute(fid, auth_attr): 315 access_ok , proof = self.auth.check_attribute(fid, auth_attr, 316 with_proof=True) 317 if not access_ok: 315 318 self.log.debug("[access] deallocation denied for %s", aid) 316 319 raise service_error(service_error.access, "Access Denied") … … 327 330 self.log.debug("Removing %s" % cf) 328 331 os.remove(cf) 329 return { 'allocID': req['allocID'] }332 return { 'allocID': req['allocID'], 'proof': proof.to_dict() } 330 333 else: 331 334 self.state_lock.release() … … 1251 1254 cpw, alloc_log) 1252 1255 1253 def finalize_experiment(self, topo, nodes, aid, alloc_id ):1256 def finalize_experiment(self, topo, nodes, aid, alloc_id, proof): 1254 1257 # Copy the assigned names into the return topology 1255 1258 rvtopo = topo.clone() … … 1273 1276 'topdldescription': rvtopo.to_dict() }, 1274 1277 'embedding': embedding, 1278 'proof': proof.to_dict(), 1275 1279 } 1276 1280 retval = copy.deepcopy(self.allocation[aid]['started']) … … 1295 1299 aid = "%s" % auth_attr 1296 1300 attrs = req.get('fedAttr', []) 1297 if not self.auth.check_attribute(fid, auth_attr): 1301 access_ok, proof = self.auth.check_attribute(fid, auth_attr, 1302 with_proof=True) 1303 if not access_ok: 1298 1304 raise service_error(service_error.access, "Access denied") 1299 1305 else: … … 1356 1362 1357 1363 if rv: 1358 return self.finalize_experiment(topo, nodes, aid, req['allocID']) 1364 return self.finalize_experiment(topo, nodes, aid, req['allocID'], 1365 proof) 1359 1366 elif err: 1360 1367 raise service_error(service_error.federant, … … 1392 1399 aid = "%s" % auth_attr 1393 1400 attrs = req.get('fedAttr', []) 1394 if not self.auth.check_attribute(fid, auth_attr): 1401 access_ok, proof = self.auth.check_attribute(fid, auth_attr, 1402 with_proof=True) 1403 if not access_ok: 1395 1404 raise service_error(service_error.access, "Access denied") 1396 1405 … … 1419 1428 self.stop_segment(segment_commands, user, staging, slice_cred, 1420 1429 slice_urn, cf, cpw) 1421 return { 'allocID': req['allocID'] }1430 return { 'allocID': req['allocID'], 'proof': proof.to_dict() } 1422 1431 1423 1432 def renew_segment(self, segment_commands, name, scred, slice_urn, interval, -
fedd/federation/remote_service.py
r2627eb3 re83f2f2 16 16 import httplib 17 17 18 from proof import proof 18 19 from service_error import service_error 19 20 from xmlrpclib import ServerProxy, dumps, loads, Fault, Error, Binary … … 66 67 # A map used to encapsulate fedids into xmlrpclib.Binary objects 67 68 encap_fedids = (('fedid', to_binary),) 69 70 # fields that are never unicoded, because they represent non strings. 71 do_not_unicode = set(['credential']) 68 72 69 73 @staticmethod … … 172 176 if isinstance(obj, dict): 173 177 for k in obj.keys(): 174 obj[k] = remote_service_base.make_unicode(obj[k]) 178 if k not in remote_service_base.do_not_unicode: 179 obj[k] = remote_service_base.make_unicode(obj[k]) 175 180 return obj 176 181 elif isinstance(obj, basestring) and not isinstance(obj, unicode): … … 513 518 'desc': e.fault.string or "Something Weird" } 514 519 if ee: 520 if 'proof' in ee: 521 pl = [ proof.from_dict(p) for p in ee['proof']] 522 else: 523 pl = None 515 524 raise service_error(ee.get('code', 'no code'), 516 ee.get('desc','no desc') )525 ee.get('desc','no desc'), proof=pl) 517 526 else: 518 527 raise service_error(service_error.internal, -
fedd/federation/server.py
r2627eb3 re83f2f2 136 136 self.send_fault(f) 137 137 resp = None 138 138 139 139 if resp != None: 140 140 sw = SoapWriter() … … 201 201 de._errstr=e.code_string() 202 202 de._desc=e.desc 203 for p in e.proof: 204 dp = ns0.proofType_Def(ns0.proofType_Def.schema, 205 "proof").pyclass() 206 dp._prover = p.prover 207 dp._principal = p.principal 208 dp._attribute = p.attribute 209 dp._credential = p.creds_to_certs() 210 if de._proof: de._proof.append(dp) 211 else: de._proof = [dp] 203 212 if e.is_server_error(): 204 213 raise Fault(Fault.Server, e.code_string(), detail=de) -
fedd/federation/service_error.py
r2627eb3 re83f2f2 28 28 federant, connect) 29 29 30 def __init__(self, code=None, desc=None, from_string=None ):30 def __init__(self, code=None, desc=None, from_string=None, proof=None): 31 31 self.code = code 32 32 self.desc = desc 33 self.proof = proof or [] 34 if not isinstance (self.proof, list): self.proof = [ proof ] 33 35 if code == None: 34 36 self.set_code_from_string(from_string) -
fedd/federation/skeleton_access.py
r2627eb3 re83f2f2 183 183 aid = "%s" % auth_attr 184 184 # Authorization check 185 if not self.auth.check_attribute(fid, auth_attr): 186 raise service_error(service_error.access, "Access denied") 185 access_ok, proof = self.auth.check_attribute(fid, auth_attr, 186 with_proof=True) 187 if not access_ok: 188 raise service_error(service_error.access, "Access denied", 189 proof=proof) 187 190 else: 188 191 # See if this is a replay of an earlier succeeded StartSegment - … … 242 245 'allocID': req['allocID'], 243 246 'allocationLog': "Allocatation complete", 244 'segmentdescription': { 'topdldescription': topo.to_dict() } 247 'segmentdescription': { 'topdldescription': topo.to_dict() }, 248 'proof': proof.to_dict(), 245 249 } 246 250 retval = copy.deepcopy(self.state[aid]['started']) … … 266 270 self.log.debug("Terminate request for %s" %aid) 267 271 # Check authorization 268 if not self.auth.check_attribute(fid, auth_attr): 269 raise service_error(service_error.access, "Access denied") 272 access_ok, proof = self.auth.check_attribute(fid, auth_attr, 273 with_proof=True) 274 if not access_ok: 275 raise service_error(service_error.access, "Access denied", 276 proof=proof) 270 277 271 278 # Authorized: remove the integer from the allocation. A more complex … … 281 288 self.state_lock.release() 282 289 283 return { 'allocID': req['allocID'] }290 return { 'allocID': req['allocID'], 'proof': proof.to_dict() } -
fedd/federation/thread_pool.py
r2627eb3 re83f2f2 4 4 import time 5 5 from threading import Lock, Thread, Condition 6 from service_error import service_error 6 7 7 8 class thread_pool: -
wsdl/fedd_types.xsd
r2627eb3 re83f2f2 131 131 132 132 <!-- end deprecated --> 133 134 <xsd:complexType name="proofType"> 135 <xsd:annotation> 136 <xsd:documentation> 137 A proof or partial proof of access rights 138 </xsd:documentation> 139 </xsd:annotation> 140 <xsd:sequence> 141 <xsd:element name="prover" type="xsd:string"/> 142 <xsd:element name="principal" type="xsd:string"/> 143 <xsd:element name="attribute" type="xsd:string"/> 144 <xsd:element name="credential" type="xsd:base64Binary" 145 maxOccurs="unbounded" minOccurs="0"/> 146 </xsd:sequence> 147 </xsd:complexType> 133 148 134 149 <xsd:simpleType name="statusType"> … … 478 493 <xsd:element name="experimentStatus" type="tns:statusType"/> 479 494 <xsd:element name="experimentAccess" type="tns:accessType"/> 495 <xsd:element name="proof" type="tns:proofType"/> 480 496 </xsd:sequence> 481 497 </xsd:complexType> … … 523 539 <xsd:element name="fedAttr" type="tns:fedAttrType" minOccurs="0" 524 540 maxOccurs="unbounded"/> 541 <xsd:element name="proof" type="tns:proofType"/> 525 542 </xsd:sequence> 526 543 </xsd:complexType> … … 546 563 <xsd:sequence> 547 564 <xsd:element name="allocID" type="tns:IDType"/> 565 <xsd:element name="proof" type="tns:proofType"/> 548 566 </xsd:sequence> 549 567 </xsd:complexType> … … 584 602 maxOccurs="unbounded"/> 585 603 <xsd:element name="experimentStatus" type="tns:statusType"/> 604 <xsd:element name="proof" type="tns:proofType" minOccurs="1" 605 maxOccurs="unbounded"/> 586 606 </xsd:sequence> 587 607 </xsd:complexType> … … 610 630 <xsd:element name="experiment" type="tns:IDType"/> 611 631 <xsd:element name="vtopo" type="tns:vtopoType"/> 632 <xsd:element name="proof" type="tns:proofType"/> 612 633 </xsd:sequence> 613 634 </xsd:complexType> … … 638 659 <xsd:element name="experiment" type="tns:IDType"/> 639 660 <xsd:element name="vis" type="tns:visType"/> 661 <xsd:element name="proof" type="tns:proofType"/> 640 662 </xsd:sequence> 641 663 </xsd:complexType> … … 666 688 <xsd:sequence> 667 689 <xsd:element name="name" type="tns:IDType" minOccurs="1" 690 maxOccurs="unbounded"/> 691 <xsd:element name="proof" type="tns:proofType" minOccurs="1" 668 692 maxOccurs="unbounded"/> 669 693 </xsd:sequence> … … 719 743 <xsd:element name="embedding" type="tns:embeddingMapType" minOccurs="0" 720 744 maxOccurs="unbounded"/> 745 <xsd:element name="proof" type="tns:proofType"/> 721 746 </xsd:sequence> 722 747 </xsd:complexType> … … 741 766 <xsd:sequence> 742 767 <xsd:element name="info" type="tns:infoResponseType" minOccurs="0" 768 maxOccurs="unbounded"/> 769 <xsd:element name="proof" type="tns:proofType" minOccurs="0" 743 770 maxOccurs="unbounded"/> 744 771 </xsd:sequence> … … 767 794 <xsd:element name="deallocationLog" type="xsd:string" minOccurs="0" 768 795 maxOccurs="1"/> 796 <xsd:element name="proof" type="tns:proofType" minOccurs="1" maxOccurs="unbounded"/> 769 797 </xsd:sequence> 770 798 </xsd:complexType> … … 805 833 <xsd:element name="fedAttr" type="tns:fedAttrType" minOccurs="0" 806 834 maxOccurs="unbounded"/> 835 <xsd:element name="proof" type="tns:proofType" /> 807 836 </xsd:sequence> 808 837 </xsd:complexType> … … 830 859 <xsd:element name="deallocationLog" type="xsd:string" minOccurs="0" 831 860 maxOccurs="1"/> 861 <xsd:element name="proof" type="tns:proofType" /> 832 862 </xsd:sequence> 833 863 </xsd:complexType> … … 854 884 <xsd:element name="experimentdescription" 855 885 type="tns:experimentDescriptionType"/> 886 <xsd:element name="proof" type="tns:proofType"/> 856 887 </xsd:sequence> 857 888 </xsd:complexType> … … 878 909 <xsd:element name="name" type="xsd:string"/> 879 910 <xsd:element name="value" type="xsd:string"/> 911 <xsd:element name="proof" type="tns:proofType"/> 880 912 </xsd:sequence> 881 913 </xsd:complexType> … … 902 934 <xsd:element name="name" type="xsd:string"/> 903 935 <xsd:element name="value" type="xsd:string" minOccurs="0" maxOccurs="1"/> 936 <xsd:element name="proof" type="tns:proofType"/> 904 937 </xsd:sequence> 905 938 </xsd:complexType> … … 939 972 </xsd:element> 940 973 <xsd:element name="desc" type="xsd:string"/> 974 <xsd:element name="proof" type="tns:proofType" 975 minOccurs="0" maxOccurs="unbounded"/> 941 976 </xsd:sequence> 942 977 </xsd:complexType>
Note: See TracChangeset
for help on using the changeset viewer.