def subdomain_record_to_profile(my_rec): owner_addr = my_rec.address assert isinstance(my_rec.zonefile_str, (str, unicode)) parsed_zf = bs_zonefile.decode_name_zonefile(my_rec.subdomain_name, my_rec.zonefile_str) urls = user_db.user_zonefile_urls(parsed_zf) # try to get pubkey from zonefile, or default to ``owner`` pubkey user_data_pubkey = None try: user_data_pubkey = user_db.user_zonefile_data_pubkey(parsed_zf) if user_data_pubkey is not None: user_data_pubkey = str(user_data_pubkey) except ValueError: pass # no pubkey defined in zonefile try: user_profile = storage.get_mutable_data( None, user_data_pubkey, blockchain_id=None, data_address=owner_addr, owner_address=None, urls=urls, drivers=None, decode=True, ) except: user_profile = None if user_profile is None: user_profile = {'error' : 'Error fetching the data for subdomain {}'.format(my_rec.get_fqn())} data = { 'profile' : user_profile, 'zonefile' : parsed_zf } return data
def load_name_profile(name, user_zonefile, data_address, owner_address, use_zonefile_urls=True, storage_drivers=None, decode=True): """ Fetch and load a user profile, given the user zonefile. Try to verify using the public key in the zonefile (if one is present), and fall back to the user-address if need be (it should be the hash of the profile JWT's public key). Return the user profile on success (either as a dict, or as a string if decode=False) Return None on error """ # get user's data public key try: user_data_pubkey = user_db.user_zonefile_data_pubkey( user_zonefile ) except ValueError, v: # user decided to put multiple keys under the same name into the zonefile. # so don't use them. log.exception(v) user_data_pubkey = None
def get_profile(name, zonefile_storage_drivers=None, profile_storage_drivers=None, proxy=None, user_zonefile=None, name_record=None, include_name_record=False, include_raw_zonefile=False, use_zonefile_urls=True, use_legacy=False, use_legacy_zonefile=True, decode_profile=True): """ Given a name, look up an associated profile. Do so by first looking up the zonefile the name points to, and then loading the profile from that zonefile's public key. Notes on backwards compatibility (activated if use_legacy=True and use_legacy_zonefile=True): * (use_legacy=True) If the user's zonefile is really a legacy profile from Onename, then the profile returned will be the converted legacy profile. The returned zonefile will still be a legacy profile, however. The caller can check this and perform the conversion automatically. * (use_legacy_zonefile=True) If the name points to a current zonefile that does not have a data public key, then the owner address of the name will be used to verify the profile's authenticity. Returns {'status': True, 'profile': profile, 'zonefile': zonefile, 'public_key': ...} on success. * If include_name_record is True, then include 'name_record': name_record with the user's blockchain information * If include_raw_zonefile is True, then include 'raw_zonefile': raw_zonefile with unparsed zone file Returns {'error': ...} on error """ proxy = get_default_proxy() if proxy is None else proxy user_profile_pubkey = None res = subdomains.is_address_subdomain(str(name)) if res: subdomain, domain = res[1] try: return subdomains.resolve_subdomain(subdomain, domain) except subdomains.SubdomainNotFound as e: log.exception(e) return { 'error': "Failed to find name {}.{}".format(subdomain, domain) } raw_zonefile = None if user_zonefile is None: user_zonefile = get_name_zonefile( name, proxy=proxy, name_record=name_record, include_name_record=True, storage_drivers=zonefile_storage_drivers, include_raw_zonefile=include_raw_zonefile, allow_legacy=True) if 'error' in user_zonefile: return user_zonefile raw_zonefile = None if include_raw_zonefile: raw_zonefile = user_zonefile.pop('raw_zonefile') user_zonefile = user_zonefile['zonefile'] # is this really a legacy profile? if blockstack_profiles.is_profile_in_legacy_format(user_zonefile): if not use_legacy: return {'error': 'Profile is in legacy format'} # convert it log.debug('Converting legacy profile to modern profile') user_profile = blockstack_profiles.get_person_from_legacy_format( user_zonefile) elif not user_db.is_user_zonefile(user_zonefile): if not use_legacy: return {'error': 'Name zonefile is non-standard'} # not a legacy profile, but a custom profile log.debug('Using custom legacy profile') user_profile = copy.deepcopy(user_zonefile) else: # get user's data public key data_address, owner_address = None, None try: user_data_pubkey = user_db.user_zonefile_data_pubkey(user_zonefile) if user_data_pubkey is not None: user_data_pubkey = str(user_data_pubkey) data_address = keylib.ECPublicKey(user_data_pubkey).address() except ValueError: # multiple keys defined; we don't know which one to use user_data_pubkey = None if not use_legacy_zonefile and user_data_pubkey is None: # legacy zonefile without a data public key return {'error': 'Name zonefile is missing a public key'} # find owner address if name_record is None: name_record = get_name_blockchain_record(name, proxy=proxy) if name_record is None or 'error' in name_record: log.error( 'Failed to look up name record for "{}"'.format(name)) return {'error': 'Failed to look up name record'} assert 'address' in name_record.keys(), json.dumps(name_record, indent=4, sort_keys=True) owner_address = name_record['address'] # get user's data public key from the zonefile urls = None if use_zonefile_urls and user_zonefile is not None: urls = user_db.user_zonefile_urls(user_zonefile) user_profile = None user_profile_pubkey = None try: user_profile_res = storage.get_mutable_data( name, user_data_pubkey, blockchain_id=name, data_address=data_address, owner_address=owner_address, urls=urls, drivers=profile_storage_drivers, decode=decode_profile, return_public_key=True) user_profile = user_profile_res['data'] user_profile_pubkey = user_profile_res['public_key'] except Exception as e: log.exception(e) return {'error': 'Failure in parsing and fetching profile'} if user_profile is None or json_is_error(user_profile): if user_profile is None: log.error('no user profile for {}'.format(name)) else: log.error('failed to load profile for {}: {}'.format( name, user_profile['error'])) return {'error': 'Failed to load user profile'} # finally, if the caller asked for the name record, and we didn't get a chance to look it up, # then go get it. ret = { 'status': True, 'profile': user_profile, 'zonefile': user_zonefile, 'public_key': user_profile_pubkey } if include_name_record: if name_record is None: name_record = get_name_blockchain_record(name, proxy=proxy) if name_record is None or 'error' in name_record: log.error('Failed to look up name record for "{}"'.format(name)) return {'error': 'Failed to look up name record'} ret['name_record'] = name_record if include_raw_zonefile: if raw_zonefile is not None: ret['raw_zonefile'] = raw_zonefile return ret
def get_name_profile(name, zonefile_storage_drivers=None, profile_storage_drivers=None, create_if_absent=False, proxy=None, user_zonefile=None, name_record=None, include_name_record=False, include_raw_zonefile=False, use_zonefile_urls=True, decode_profile=True): """ Given the name of the user, look up the user's record hash, and then get the record itself from storage. If the user's zonefile is really a legacy profile, then the profile will be the converted legacy profile. The returned zonefile will still be a legacy profile, however. The caller can check this and perform the conversion automatically. Returns (profile, zonefile) on success. If include_name_record is True, then zonefile['name_record'] will be defined and will contain the user's blockchain information Returns (None, {'error': ...}) on failure """ if proxy is None: proxy = get_default_proxy() raw_zonefile = None if user_zonefile is None: user_zonefile = get_name_zonefile( name, create_if_absent=create_if_absent, proxy=proxy, name_record=name_record, include_name_record=True, storage_drivers=zonefile_storage_drivers, include_raw_zonefile=include_raw_zonefile) if user_zonefile is None: return (None, {'error': 'No user zonefile'}) if 'error' in user_zonefile: return (None, user_zonefile) name_record = user_zonefile['name_record'] del user_zonefile['name_record'] raw_zonefile = None if include_raw_zonefile: raw_zonefile = user_zonefile['raw_zonefile'] del user_zonefile['raw_zonefile'] user_zonefile = user_zonefile['zonefile'] # is this really a legacy profile? if blockstack_profiles.is_profile_in_legacy_format(user_zonefile): # convert it log.debug("Converting legacy profile to modern profile") user_profile = blockstack_profiles.get_person_from_legacy_format( user_zonefile) elif not user_db.is_user_zonefile(user_zonefile): # not a legacy profile, but a custom profile log.debug("Using custom legacy profile") user_profile = copy.deepcopy(user_zonefile) else: # get user's data public key user_address = None old_address = None try: user_data_pubkey = user_db.user_zonefile_data_pubkey(user_zonefile) if user_data_pubkey is not None: user_data_pubkey = str(user_data_pubkey) user_address = virtualchain.BitcoinPublicKey( user_data_pubkey).address() except ValueError: # user decided to put multiple keys under the same name into the zonefile. # so don't use them. user_data_pubkey = None # convert to address if name_record is None: name_record = proxy.get_name_blockchain_record(name) if name_record is None or 'error' in name_record: log.error("Failed to look up name record for '%s'" % name) return (None, {'error': 'Failed to look up name record'}) old_address = name_record['address'] if user_address is None: # cut to the chase user_address = old_address user_profile = load_name_profile( name, user_zonefile, user_address, old_address, use_zonefile_urls=use_zonefile_urls, storage_drivers=profile_storage_drivers, decode=decode_profile) if user_profile is not None: if decode_profile: assert isinstance(user_profile, dict) else: assert type(user_profile) in [str, unicode] else: log.debug("WARN: no user profile for %s" % name) if create_if_absent: user_profile = user_db.make_empty_user_profile() else: return (None, {'error': 'Failed to load user profile'}) # finally, if the caller asked for the name record, and we didn't get a chance to look it up, # then go get it. if include_name_record: if name_record is None: name_record = proxy.get_name_blockchain_record(name) if name_record is None or 'error' in name_record: log.error("Failed to look up name record for '%s'" % name) return (None, {'error': 'Failed to look up name record'}) user_zonefile['name_record'] = name_record if include_raw_zonefile: if raw_zonefile is not None: user_zonefile['raw_zonefile'] = raw_zonefile return (user_profile, user_zonefile)
def get_profile(name, zonefile_storage_drivers=None, profile_storage_drivers=None, proxy=None, user_zonefile=None, name_record=None, include_name_record=False, include_raw_zonefile=False, use_zonefile_urls=True, use_legacy=False, use_legacy_zonefile=True, decode_profile=True): """ Given a name, look up an associated profile. Do so by first looking up the zonefile the name points to, and then loading the profile from that zonefile's public key. Notes on backwards compatibility (activated if use_legacy=True and use_legacy_zonefile=True): * (use_legacy=True) If the user's zonefile is really a legacy profile from Onename, then the profile returned will be the converted legacy profile. The returned zonefile will still be a legacy profile, however. The caller can check this and perform the conversion automatically. * (use_legacy_zonefile=True) If the name points to a current zonefile that does not have a data public key, then the owner address of the name will be used to verify the profile's authenticity. Returns {'status': True, 'profile': profile, 'zonefile': zonefile} on success. * If include_name_record is True, then include 'name_record': name_record with the user's blockchain information * If include_raw_zonefile is True, then include 'raw_zonefile': raw_zonefile with unparsed zone file Returns {'error': ...} on error """ proxy = get_default_proxy() if proxy is None else proxy raw_zonefile = None if user_zonefile is None: user_zonefile = get_name_zonefile( name, proxy=proxy, name_record=name_record, include_name_record=True, storage_drivers=zonefile_storage_drivers, include_raw_zonefile=include_raw_zonefile, allow_legacy=True ) if 'error' in user_zonefile: return user_zonefile raw_zonefile = None if include_raw_zonefile: raw_zonefile = user_zonefile.pop('raw_zonefile') user_zonefile = user_zonefile['zonefile'] # is this really a legacy profile? if blockstack_profiles.is_profile_in_legacy_format(user_zonefile): if not use_legacy: return {'error': 'Profile is in legacy format'} # convert it log.debug('Converting legacy profile to modern profile') user_profile = blockstack_profiles.get_person_from_legacy_format(user_zonefile) elif not user_db.is_user_zonefile(user_zonefile): if not use_legacy: return {'error': 'Name zonefile is non-standard'} # not a legacy profile, but a custom profile log.debug('Using custom legacy profile') user_profile = copy.deepcopy(user_zonefile) else: # get user's data public key data_address, owner_address = None, None try: user_data_pubkey = user_db.user_zonefile_data_pubkey(user_zonefile) if user_data_pubkey is not None: user_data_pubkey = str(user_data_pubkey) data_address = keylib.ECPublicKey(user_data_pubkey).address() except ValueError: # multiple keys defined; we don't know which one to use user_data_pubkey = None if not use_legacy_zonefile and user_data_pubkey is None: # legacy zonefile without a data public key return {'error': 'Name zonefile is missing a public key'} # find owner address if name_record is None: name_record = get_name_blockchain_record(name, proxy=proxy) if name_record is None or 'error' in name_record: log.error('Failed to look up name record for "{}"'.format(name)) return {'error': 'Failed to look up name record'} assert 'address' in name_record.keys(), json.dumps(name_record, indent=4, sort_keys=True) owner_address = name_record['address'] # get user's data public key from the zonefile urls = None if use_zonefile_urls and user_zonefile is not None: urls = user_db.user_zonefile_urls(user_zonefile) user_profile = storage.get_mutable_data( name, user_data_pubkey, blockchain_id=name, data_address=data_address, owner_address=owner_address, urls=urls, drivers=profile_storage_drivers, decode=decode_profile, ) if user_profile is None or json_is_error(user_profile): if user_profile is None: log.error('no user profile for {}'.format(name)) else: log.error('failed to load profile for {}: {}'.format(name, user_profile['error'])) return {'error': 'Failed to load user profile'} # finally, if the caller asked for the name record, and we didn't get a chance to look it up, # then go get it. ret = { 'status': True, 'profile': user_profile, 'zonefile': user_zonefile } if include_name_record: if name_record is None: name_record = get_name_blockchain_record(name, proxy=proxy) if name_record is None or 'error' in name_record: log.error('Failed to look up name record for "{}"'.format(name)) return {'error': 'Failed to look up name record'} ret['name_record'] = name_record if include_raw_zonefile: if raw_zonefile is not None: ret['raw_zonefile'] = raw_zonefile return ret
def get_profile(name, zonefile_storage_drivers=None, profile_storage_drivers=None, proxy=None, user_zonefile=None, name_record=None, include_name_record=False, include_raw_zonefile=False, use_zonefile_urls=True, use_legacy=False, use_legacy_zonefile=True, decode_profile=True): """ Given a name, look up an associated profile. Do so by first looking up the zonefile the name points to, and then loading the profile from that zonefile's public key. Notes on backwards compatibility (activated if use_legacy=True and use_legacy_zonefile=True): * (use_legacy=True) If the user's zonefile is really a legacy profile from Onename, then the profile returned will be the converted legacy profile. The returned zonefile will still be a legacy profile, however. The caller can check this and perform the conversion automatically. * (use_legacy_zonefile=True) If the name points to a current zonefile that does not have a data public key, then the owner address of the name will be used to verify the profile's authenticity. Returns (profile, zonefile) on success. If include_name_record is True, then zonefile['name_record'] will be defined and will contain the user's blockchain information Returns (None, {'error': ...}) on failure """ proxy = get_default_proxy() if proxy is None else proxy raw_zonefile = None if user_zonefile is None: user_zonefile = get_name_zonefile( name, proxy=proxy, name_record=name_record, include_name_record=True, storage_drivers=zonefile_storage_drivers, include_raw_zonefile=include_raw_zonefile, allow_legacy=True) if 'error' in user_zonefile: return None, user_zonefile raw_zonefile = None if include_raw_zonefile: raw_zonefile = user_zonefile.pop('raw_zonefile') user_zonefile = user_zonefile['zonefile'] # is this really a legacy profile? if blockstack_profiles.is_profile_in_legacy_format(user_zonefile): if not use_legacy: return (None, {'error': 'Profile is in legacy format'}) # convert it log.debug('Converting legacy profile to modern profile') user_profile = blockstack_profiles.get_person_from_legacy_format( user_zonefile) elif not user_db.is_user_zonefile(user_zonefile): if not use_legacy: return (None, {'error': 'Name zonefile is non-standard'}) # not a legacy profile, but a custom profile log.debug('Using custom legacy profile') user_profile = copy.deepcopy(user_zonefile) else: # get user's data public key data_address, owner_address = None, None try: user_data_pubkey = user_db.user_zonefile_data_pubkey(user_zonefile) if user_data_pubkey is not None: user_data_pubkey = str(user_data_pubkey) data_address = virtualchain.BitcoinPublicKey( user_data_pubkey).address() except ValueError: # multiple keys defined; we don't know which one to use user_data_pubkey = None if not use_legacy_zonefile and user_data_pubkey is None: # legacy zonefile without a data public key return (None, {'error': 'Name zonefile is missing a public key'}) # find owner address if name_record is None: name_record = get_name_blockchain_record(name, proxy=proxy) if name_record is None or 'error' in name_record: log.error( 'Failed to look up name record for "{}"'.format(name)) return None, {'error': 'Failed to look up name record'} assert 'address' in name_record.keys(), json.dumps(name_record, indent=4, sort_keys=True) owner_address = name_record['address'] # get user's data public key from the zonefile urls = None if use_zonefile_urls and user_zonefile is not None: urls = user_db.user_zonefile_urls(user_zonefile) user_profile = storage.get_mutable_data( name, user_data_pubkey, data_address=data_address, owner_address=owner_address, urls=urls, drivers=profile_storage_drivers, decode=decode_profile, ) if user_profile is None or json_is_error(user_profile): if user_profile is None: log.debug('WARN: no user profile for {}'.format(name)) else: log.debug('WARN: failed to load profile for {}: {}'.format( name, user_profile['error'])) return None, {'error': 'Failed to load user profile'} # finally, if the caller asked for the name record, and we didn't get a chance to look it up, # then go get it. if include_name_record: if name_record is None: name_record = get_name_blockchain_record(name, proxy=proxy) if name_record is None or 'error' in name_record: log.error('Failed to look up name record for "{}"'.format(name)) return None, {'error': 'Failed to look up name record'} user_zonefile['name_record'] = name_record if include_raw_zonefile: if raw_zonefile is not None: user_zonefile['raw_zonefile'] = raw_zonefile return user_profile, user_zonefile