+ # step3
+ # unconditionnally retrieve my GID - use the general form
+ def my_gid_produce(self, output):
+ return self.gid_produce(output, self.hrn, "user")
+
+ # retrieve any credential (GetCredential) unconditionnal form
+ # we always use the GID as the SSL cert
+ def credential_produce(self, output, hrn, type):
+ self.assert_my_gid()
+ certificate_filename = self.my_gid_filename()
+ self.assert_private_key()
+ registry_proxy = SfaServerProxy(self.registry_url, self.private_key_filename(),
+ certificate_filename)
+ self.assert_my_credential()
+ my_credential_string = self.my_credential_string()
+ credential_string = registry_proxy.GetCredential(
+ my_credential_string, hrn, type)
+ self.plain_write(output, credential_string)
+ self.logger.debug(
+ "SfaClientBootstrap: Wrote result of GetCredential in {}".format(output))
+ return output
+
+ def slice_credential_produce(self, output, hrn):
+ return self.credential_produce(output, hrn, "slice")
+
+ def authority_credential_produce(self, output, hrn):
+ return self.credential_produce(output, hrn, "authority")
+
+ # retrieve any gid(Resolve) - unconditionnal form
+ # use my GID when available as the SSL cert, otherwise the self-signed
+ def gid_produce(self, output, hrn, type):
+ try:
+ self.assert_my_gid()
+ certificate_filename = self.my_gid_filename()
+ except:
+ self.assert_self_signed_cert()
+ certificate_filename = self.self_signed_cert_filename()
+
+ self.assert_private_key()
+ registry_proxy = SfaServerProxy(self.registry_url, self.private_key_filename(),
+ certificate_filename)
+ credential_string = self.plain_read(self.my_credential())
+ records = registry_proxy.Resolve(hrn, credential_string)
+ records = [record for record in records if record['type'] == type]
+ if not records:
+ raise RecordNotFound("hrn {} ({}) unknown to registry {}".format(
+ hrn, type, self.registry_url))
+ record = records[0]
+ self.plain_write(output, record['gid'])
+ self.logger.debug(
+ "SfaClientBootstrap: Wrote GID for {} ({}) in {}".format(hrn, type, output))
+ return output
+
+
+# http://trac.myslice.info/wiki/MySlice/Developer/SFALogin
+# produce a pkcs12 bundled certificate from GID and private key
+# xxx for now we put a hard-wired password that's just, well, 'password'
+# when leaving this empty on the mac, result can't seem to be loaded in
+# keychain..
+ def my_pkcs12_produce(self, filename):
+ password = input("Enter password for p12 certificate: ")
+ openssl_command = ['openssl', 'pkcs12', "-export"]
+ openssl_command += ["-password", "pass:{}".format(password)]
+ openssl_command += ["-inkey", self.private_key_filename()]
+ openssl_command += ["-in", self.my_gid_filename()]
+ openssl_command += ["-out", filename]
+ if subprocess.call(openssl_command) == 0:
+ print("Successfully created {}".format(filename))
+ else:
+ print("Failed to create {}".format(filename))
+
+ # Returns True if credential file is valid. Otherwise return false.
+ def validate_credential(self, filename):
+ valid = True
+ cred = Credential(filename=filename)
+ # check if credential is expires
+ if cred.get_expiration() < datetime.utcnow():
+ valid = False
+ return valid
+
+ # public interface
+
+ # return my_gid, run all missing steps in the bootstrap sequence
+ def bootstrap_my_gid(self):
+ self.self_signed_cert()
+ self.my_credential()
+ return self.my_gid()
+
+ # once we've bootstrapped we can use this object to issue any other SFA call
+ # always use my gid
+ def server_proxy(self, url):
+ self.assert_my_gid()
+ return SfaServerProxy(
+ url, self.private_key_filename(), self.my_gid_filename(),
+ verbose=self.verbose, timeout=self.timeout)
+
+ # now in some cases the self-signed is enough
+ def server_proxy_simple(self, url):
+ self.assert_self_signed_cert()
+ return SfaServerProxy(
+ url, self.private_key_filename(), self.self_signed_cert_filename(),
+ verbose=self.verbose, timeout=self.timeout)
+
+ # this method can optionnally be invoked to ensure proper
+ # installation of the private key that belongs to this user
+ # installs private_key in working dir with expected name -- preserve mode
+ # typically user_private_key would be ~/.ssh/id_rsa
+ # xxx should probably check the 2 files are identical
+ def init_private_key_if_missing(self, user_private_key):
+ private_key_filename = self.private_key_filename()
+ if not os.path.isfile(private_key_filename):
+ key = self.plain_read(user_private_key)
+ self.plain_write(private_key_filename, key)
+ os.chmod(private_key_filename, os.stat(user_private_key).st_mode)
+ self.logger.debug(
+ "SfaClientBootstrap: Copied private key from {} into {}"
+ .format(user_private_key, private_key_filename))
+
+ # private details
+ # stupid stuff
+ def fullpath(self, file):
+ return os.path.join(self.dir, file)
+
+ # the expected filenames for the various pieces
+ def private_key_filename(self):
+ return self.fullpath("{}.pkey".format(Xrn.unescape(self.hrn)))
+
+ def self_signed_cert_filename(self):
+ return self.fullpath("{}.sscert".format(self.hrn))
+
+ def my_credential_filename(self):
+ return self.credential_filename(self.hrn, "user")
+ # the tests use sfi -u <pi-user>; meaning that the slice credential filename
+ # needs to keep track of the user too
+
+ def credential_filename(self, hrn, type):
+ if type in ['user']:
+ basename = "{}.{}.cred".format(hrn, type)
+ else:
+ basename = "{}-{}.{}.cred".format(self.hrn, hrn, type)
+ return self.fullpath(basename)
+
+ def slice_credential_filename(self, hrn):
+ return self.credential_filename(hrn, 'slice')
+
+ def authority_credential_filename(self, hrn):
+ return self.credential_filename(hrn, 'authority')
+
+ def my_gid_filename(self):
+ return self.gid_filename(self.hrn, "user")
+
+ def gid_filename(self, hrn, type):
+ return self.fullpath("{}.{}.gid".format(hrn, type))
+
+ def my_pkcs12_filename(self):
+ return self.fullpath("{}.p12".format(self.hrn))
+
+# optimizing dependencies
+# originally we used classes GID or Credential or Certificate
+# like e.g.
+# return Credential(filename=self.my_credential()).save_to_string()
+# but in order to make it simpler to other implementations/languages..
+ def plain_read(self, filename):
+ with open(filename) as infile:
+ return infile.read()
+
+ def plain_write(self, filename, contents):
+ with open(filename, "w") as outfile:
+ outfile.write(contents)
+
+ def assert_filename(self, filename, kind):
+ if not os.path.isfile(filename):
+ raise IOError("Missing {} file {}".format(kind, filename))
+ return True
+
+ def assert_private_key(self):
+ return self.assert_filename(self.private_key_filename(), "private key")
+
+ def assert_self_signed_cert(self):
+ return self.assert_filename(self.self_signed_cert_filename(),
+ "self-signed certificate")
+
+ def assert_my_credential(self):
+ return self.assert_filename(self.my_credential_filename(),
+ "user's credential")
+
+ def assert_my_gid(self):
+ return self.assert_filename(self.my_gid_filename(), "user's GID")
+
+ # decorator to make up the other methods
+ def get_or_produce(filename_method, produce_method, validate_method=None):
+ # default validator returns true
+ def wrap(f):
+ def wrapped(self, *args, **kw):
+ filename = filename_method(self, *args, **kw)
+ if os.path.isfile(filename):
+ if not validate_method:
+ return filename
+ elif validate_method(self, filename):
+ return filename
+ else:
+ # remove invalid file
+ self.logger.warning(
+ "Removing {} - has expired".format(filename))
+ os.unlink(filename)
+ try:
+ produce_method(self, filename, *args, **kw)
+ return filename
+ except IOError:
+ raise
+ except:
+ error = sys.exc_info()[:2]
+ message = "Could not produce/retrieve {} ({} -- {})"\
+ .format(filename, error[0], error[1])
+ self.logger.log_exc(message)
+ raise Exception(message)
+ return wrapped
+ return wrap
+
+ @get_or_produce(self_signed_cert_filename, self_signed_cert_produce)
+ def self_signed_cert(self): pass
+
+ @get_or_produce(my_credential_filename, my_credential_produce, validate_credential)
+ def my_credential(self): pass
+
+ @get_or_produce(my_gid_filename, my_gid_produce)
+ def my_gid(self): pass
+
+ @get_or_produce(my_pkcs12_filename, my_pkcs12_produce)
+ def my_pkcs12(self): pass
+
+ @get_or_produce(credential_filename, credential_produce, validate_credential)
+ def credential(self, hrn, type): pass
+
+ @get_or_produce(slice_credential_filename, slice_credential_produce, validate_credential)
+ def slice_credential(self, hrn): pass
+
+ @get_or_produce(authority_credential_filename, authority_credential_produce, validate_credential)
+ def authority_credential(self, hrn): pass
+
+ @get_or_produce(gid_filename, gid_produce)
+ def gid(self, hrn, type): pass
+
+ # get the credentials as strings, for inserting as API arguments
+ def my_credential_string(self):
+ self.my_credential()
+ return self.plain_read(self.my_credential_filename())
+
+ def slice_credential_string(self, hrn):
+ self.slice_credential(hrn)
+ return self.plain_read(self.slice_credential_filename(hrn))
+
+ def authority_credential_string(self, hrn):
+ self.authority_credential(hrn)
+ return self.plain_read(self.authority_credential_filename(hrn))
+
+ # for consistency
+ def private_key(self):
+ self.assert_private_key()
+ return self.private_key_filename()
+
+ def delegate_credential_string(self, original_credential, to_hrn, to_type='authority'):
+ """
+ sign a delegation credential to someone else
+
+ original_credential : typically one's user- or slice- credential to be delegated to s/b else
+ to_hrn : the hrn of the person that will be allowed to do stuff on our behalf
+ to_type : goes with to_hrn, usually 'user' or 'authority'
+
+ returns a string with the delegated credential
+
+ this internally uses self.my_gid()
+ it also retrieves the gid for to_hrn/to_type
+ and uses Credential.delegate()"""
+
+ # the gid and hrn of the object we are delegating
+ if isinstance(original_credential, str):
+ original_credential = Credential(string=original_credential)
+ original_gid = original_credential.get_gid_object()
+ original_hrn = original_gid.get_hrn()
+
+ if not original_credential.get_privileges().get_all_delegate():
+ self.logger.error("delegate_credential_string: original credential {} does not have delegate bit set"
+ .format(original_hrn))
+ return
+
+ # the delegating user's gid
+ my_gid = self.my_gid()