mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-04-02 17:07:37 +08:00
195 lines
5.3 KiB
Python
195 lines
5.3 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
Blockstack-client
|
|
~~~~~
|
|
copyright: (c) 2014-2015 by Halfmoon Labs, Inc.
|
|
copyright: (c) 2016 by Blockstack.org
|
|
|
|
This file is part of Blockstack-client.
|
|
|
|
Blockstack-client is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Blockstack-client is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
You should have received a copy of the GNU General Public License
|
|
along with Blockstack-client. If not, see <http://www.gnu.org/licenses/>.
|
|
"""
|
|
|
|
# use Blockstack Labs as a read-only reader for zonefile/profile data
|
|
|
|
import os
|
|
import sys
|
|
import traceback
|
|
import logging
|
|
import json
|
|
import requests
|
|
import pybitcoin
|
|
from ConfigParser import SafeConfigParser
|
|
|
|
import blockstack_zones
|
|
|
|
from common import get_logger, DEBUG
|
|
|
|
log = get_logger("blockstack-storage-driver-blockstack-s3-readonly")
|
|
|
|
if os.environ.get("BLOCKSTACK_TEST") is None:
|
|
RESOLVER_URL = "https://onename.com"
|
|
STORAGE_URL = "https://blockstack.s3.amazonaws.com"
|
|
else:
|
|
RESOLVER_URL = "http://localhost:8081"
|
|
STORAGE_URL = "http://localhost:8081"
|
|
|
|
|
|
def get_zonefile( fqu, zonefile_hash ):
|
|
"""
|
|
Try to get a zonefile, from onename.com
|
|
Return the zonefile (serialized as a string) on success
|
|
Return None on error
|
|
"""
|
|
fqu_json = fqu.split(".")[0] + ".json"
|
|
url = "%s/%s" % (RESOLVER_URL, fqu_json)
|
|
req = requests.get(url)
|
|
if req.status_code != 200:
|
|
log.debug("GET %s status code %s" % (url, req.status_code))
|
|
return None
|
|
|
|
try:
|
|
res = json.loads(req.content)
|
|
except Exception, e:
|
|
log.exception(e)
|
|
log.debug("Failed to parse request to %s" % url)
|
|
return None
|
|
|
|
if 'zone_file' not in res.keys():
|
|
log.debug("No zonefile in request to %s" % url)
|
|
return None
|
|
|
|
zone_file_str = str(res['zone_file'])
|
|
if pybitcoin.hex_hash160( zone_file_str ) != zonefile_hash:
|
|
log.debug("Hash mismatch: expected %s, got %s" % (zonefile_hash, pybitcoin.hex_hash160(zone_file_str)))
|
|
return None
|
|
|
|
return zone_file_str
|
|
|
|
|
|
def get_profile( fqu ):
|
|
"""
|
|
Try to get a profile, from S3
|
|
"""
|
|
url = "%s/%s" % (STORAGE_URL, fqu)
|
|
req = requests.get(url)
|
|
if req.status_code != 200:
|
|
log.debug("GET %s status code %s" % (url, req.status_code))
|
|
return None
|
|
|
|
try:
|
|
res = json.loads(req.content)
|
|
except Exception, e:
|
|
log.exception(e)
|
|
log.debug("Failed to parse request to %s" % url)
|
|
return None
|
|
|
|
try:
|
|
res = res[0]
|
|
except:
|
|
log.debug("JWT is not a list")
|
|
return None
|
|
|
|
for k in ['token', 'parentPublicKey']:
|
|
if k not in res.keys():
|
|
log.debug("Missing required key '%s' in request to %s" % (k, url))
|
|
return None
|
|
|
|
ret = {}
|
|
ret['token'] = res['token']
|
|
ret['parentPublicKey'] = res['parentPublicKey']
|
|
return json.dumps([ret])
|
|
|
|
|
|
def storage_init(conf):
|
|
# read config options from the config file, if given
|
|
global STORAGE_URL, RESOLVER_URL
|
|
|
|
config_path = conf['path']
|
|
if os.path.exists( config_path ):
|
|
|
|
parser = SafeConfigParser()
|
|
|
|
try:
|
|
parser.read(config_path)
|
|
except Exception, e:
|
|
log.exception(e)
|
|
return False
|
|
|
|
if parser.has_section('blockstack-resolver-storage'):
|
|
|
|
if parser.has_option('blockstack-resolver-storage', 'storage_url'):
|
|
SERVER_NAME = parser.get('blockstack-resolver-storage', 'storage_url')
|
|
|
|
if parser.has_option('blockstack-resolver-storage', 'resolver_url'):
|
|
SERVER_PORT = int(parser.get('blockstack-resolver-storage', 'resolver_url'))
|
|
|
|
|
|
# we can't proceed unless we have them.
|
|
if STORAGE_URL is None or RESOLVER_URL is None:
|
|
log.error("Config file '%s': section 'blockstack_resolver_storage' is missing 'resolver_url' and/or 'storage_url'")
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
def handles_url( url ):
|
|
if RESOLVER_URL in url or STORAGE_URL in url:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def make_mutable_url( data_id ):
|
|
# only deal with names
|
|
if ':' in data_id:
|
|
return None
|
|
else:
|
|
return "%s/%s" % (STORAGE_URL, data_id)
|
|
|
|
|
|
def get_immutable_handler( key, **kw ):
|
|
# only works for zonefiles, and we need the name
|
|
if 'zonefile' in kw and not kw['zonefile']:
|
|
return None
|
|
|
|
fqu = kw.get('fqu', None)
|
|
if fqu is None:
|
|
return None
|
|
|
|
return get_zonefile( fqu, key )
|
|
|
|
|
|
def get_mutable_handler( url, **kw ):
|
|
# only works for profiles
|
|
fqu = kw.get('fqu', None)
|
|
if fqu is None:
|
|
return None
|
|
|
|
return get_profile( fqu )
|
|
|
|
|
|
def put_immutable_handler( key, data, txid, **kw ):
|
|
# read only
|
|
return False
|
|
|
|
def put_mutable_handler( data_id, data_bin, **kw ):
|
|
# read only
|
|
return False
|
|
|
|
def delete_immutable_handler( key, txid, sig_key_txid, **kw ):
|
|
return True
|
|
|
|
def delete_mutable_handler( data_id, signature, **kw ):
|
|
return True
|