Exemple #1
0
def get_datastore_creds(master_data_privkey=None,
                        app_domain=None,
                        app_user_privkey=None,
                        config_path=CONFIG_PATH):
    """
    Get datastore credentials
    Return {'status': True, 'datastore_privkey': ..., 'datastore_id': ...} on success
    Return {'error': ...} on error
    """

    assert app_user_privkey is not None or (
        master_data_privkey is not None and app_domain is not None
    ), "Invalid creds: need app_domain and master_data_privkey, or app_user_privkey"

    if app_user_privkey is None:
        app_user_privkey = data.datastore_get_privkey(master_data_privkey,
                                                      app_domain,
                                                      config_path=CONFIG_PATH)
        if app_user_privkey is None:
            return {
                'error': 'Failed to load app user private key',
                'errno': errno.EPERM
            }

    app_user_pubkey = get_pubkey_hex(app_user_privkey)
    datastore_id = data.datastore_get_id(app_user_pubkey)

    ret = {
        'status': True,
        'datastore_privkey': app_user_privkey,
        'datastore_pubkey': app_user_pubkey,
        'datastore_id': datastore_id
    }

    return ret
Exemple #2
0
def app_make_session(blockchain_id,
                     app_public_key,
                     app_domain,
                     methods,
                     app_public_keys,
                     requester_device_id,
                     master_data_privkey,
                     session_lifetime=None,
                     config_path=CONFIG_PATH):
    """
    Make a session JWT for this application.
    Verify with user private key
    Sign with master private key

    Return {'session': session jwt, 'session_token': session token} on success
    Return {'error': ...} on error
    """
    conf = get_config(path=config_path)
    assert conf

    if session_lifetime is None:
        session_lifetime = conf.get('default_session_lifetime', 1e80)

    # ysi-storage.js assumes it needs to use an
    #  uncompressed address. let's do that if we need to

    app_datastore_public_key = keylib.key_formatting.decompress(app_public_key)

    app_user_id = data.datastore_get_id(app_datastore_public_key)

    api_endpoint_host = conf.get('api_endpoint_host', DEFAULT_API_HOST)
    api_endpoint_port = conf.get('api_endpoint_port', DEFAULT_API_PORT)

    api_endpoint = '{}:{}'.format(api_endpoint_host, api_endpoint_port)

    ses = {
        'version': 1,
        'blockchain_id': blockchain_id,
        'app_domain': app_domain,
        'methods': methods,
        'app_public_keys': app_public_keys,
        'app_user_id': app_user_id,
        'api_endpoint': api_endpoint,
        'device_id': requester_device_id,
        'storage': {
            'classes': classify_storage_drivers(),
            'preferences': {}
        },
        'timestamp': int(time.time()),
        'expires': int(time.time() + session_lifetime),
    }

    jsonschema.validate(ses, APP_SESSION_SCHEMA)

    signer = jsontokens.TokenSigner()
    session_token = signer.sign(ses, master_data_privkey)
    session = jsontokens.decode_token(session_token)

    return {'session': session, 'session_token': session_token}
Exemple #3
0
def get_datastore_info(datastore_id=None,
                       app_user_privkey=None,
                       master_data_privkey=None,
                       app_domain=None,
                       config_path=CONFIG_PATH,
                       proxy=None):
    """
    Get information about an account datastore.
    At least, get the user and account owner.

    Return {'status': True, 'datastore': ..., 'datastore_id': ..., ['datastore_privkey': ...}] on success.

    Return {'error': ...} on failure
    """

    datastore_privkey = None

    if app_user_privkey is not None or (master_data_privkey is not None
                                        and app_domain is not None):
        creds = get_datastore_creds(master_data_privkey,
                                    app_domain=app_domain,
                                    app_user_privkey=app_user_privkey,
                                    config_path=config_path)
        if 'error' in creds:
            return creds

        if datastore_id is not None:
            assert datastore_id == creds[
                'datastore_id'], "Datastore mismatch: {} != {}".format(
                    datastore_id, creds['datastore_id'])

        datastore_privkey = creds['datastore_privkey']
        datastore_id = creds['datastore_id']

    assert datastore_id, 'No datastore ID given'

    res = data.get_datastore(datastore_id,
                             config_path=config_path,
                             proxy=proxy)
    if 'error' in res:
        return res

    if datastore_privkey is not None:
        res['datastore_privkey'] = datastore_privkey

    if datastore_id is not None:
        res['datastore_id'] = datastore_id
    else:
        res['datastore_id'] = data.datastore_get_id(res['datastore']['pubkey'])

    return res
def app_make_session( blockchain_id, app_private_key, app_domain, methods, app_public_keys, requester_device_id, master_data_privkey, session_lifetime=None, config_path=CONFIG_PATH ):
    """
    Make a session JWT for this application.
    Verify with user private key
    Sign with master private key

    Return {'session': session jwt, 'session_token': session token} on success
    Return {'error': ...} on error
    """
    conf = get_config(path=config_path)
    assert conf

    if session_lifetime is None:
        session_lifetime = conf.get('default_session_lifetime', 1e80)

    app_public_key = get_pubkey_hex(app_private_key)
    app_user_id = data.datastore_get_id(app_public_key)

    api_endpoint_host = conf.get('api_endpoint_host', DEFAULT_API_HOST)
    api_endpoint_port = conf.get('api_endpoint_port', DEFAULT_API_PORT)

    api_endpoint = '{}:{}'.format(api_endpoint_host, api_endpoint_port)

    ses = {
        'version': 1,
        'blockchain_id': blockchain_id,
        'app_domain': app_domain,
        'methods': methods,
        'app_public_keys': app_public_keys,
        'app_user_id': app_user_id,
        'api_endpoint': api_endpoint,
        'device_id': requester_device_id,
        'storage': {
            'classes': classify_storage_drivers(),
            'preferences': {}
        },
        'timestamp': int(time.time()),
        'expires': int(time.time() + session_lifetime),
    }

    jsonschema.validate(ses, APP_SESSION_SCHEMA)

    signer = jsontokens.TokenSigner()
    session_token = signer.sign( ses, master_data_privkey )
    session = jsontokens.decode_token(session_token)

    return {'session': session, 'session_token': session_token}
Exemple #5
0
def app_make_session( app_domain, methods, master_data_privkey_hex, app_user_id=None, app_user_privkey=None, session_lifetime=None, blockchain_ids=None, config_path=CONFIG_PATH ):
    """
    Make a session JWT for this application.
    Verify with user private key
    Sign with master private key
    Return {'session': session jwt, 'session_token': session token} on success
    Return {'error': ...} on error
    """
    if session_lifetime is None:
        conf = get_config(path=config_path)
        assert conf
        session_lifetime = conf.get('default_session_lifetime', 1e80)

    if app_user_id is None:
        if app_user_privkey is None:
            if master_data_privkey_hex is not None:
                assert app_domain is not None, "need app domain to derive app key"
                app_user_privkey = data.datastore_get_privkey(master_data_privkey_hex, app_domain, config_path=config_path)

            else:
                # TODO: load from disk
                raise NotImplemented("Local app user private keys are not supported at this time")

        app_user_pubkey = get_pubkey_hex(app_user_privkey)
        app_user_id = data.datastore_get_id(app_user_pubkey)

    ses = {
        'app_domain': app_domain,
        'methods': methods,
        'app_user_id': app_user_id,
        'timestamp': int(time.time()),
        'expires': int(time.time() + session_lifetime),
    }

    if blockchain_ids is not None:
        ses['blockchain_ids'] = blockchain_ids

    jsonschema.validate(ses, APP_SESSION_SCHEMA)

    signer = jsontokens.TokenSigner()
    session_token = signer.sign( ses, master_data_privkey_hex )
    session = jsontokens.decode_token(session_token)

    return {'session': session, 'session_token': session_token}
Exemple #6
0
def blockstack_url_fetch(url,
                         proxy=None,
                         config_path=CONFIG_PATH,
                         wallet_keys=None):
    """
    Given a blockstack:// url, fetch its data.
    If the data is an immutable data url, and the hash is not given, then look up the hash first.
    If the data is a mutable data url, and the version is not given, then look up the version as well.

    Data from datastores requires wallet_keys

    Return {"data": data} on success
    Return {"error": error message} on error
    """
    mutable = False
    immutable = False
    blockchain_id = None
    data_id = None
    version = None
    data_hash = None

    url_info = blockstack_data_url_parse(url)
    if url_info is None:
        return {'error': 'Failed to parse {}'.format(url)}

    data_id = url_info['data_id']
    blockchain_id = url_info['blockchain_id']
    url_type = url_info['type']
    fields = url_info['fields']

    if url_type == 'mutable':
        datastore_id = fields.get('datastore_id')
        version = fields.get('version')
        app_domain = fields.get('app_domain')
        mutable = True

    else:
        data_hash = fields.get('data_hash')
        immutable = True

    if mutable:
        if app_domain is not None:
            # get from datastore
            datastore_info = get_datastore_info(datastore_id=datastore_id,
                                                config_path=config_path,
                                                proxy=proxy)
            if 'error' in datastore_info:
                return datastore_info

            datastore = datastore_info['datastore']
            if data.datastore_get_id(datastore['pubkey']) != datastore_id:
                return {'error': 'Invalid datastore ID'}

            # file or directory?
            is_dir = data_id.endswith('/')
            if is_dir:
                return data.datastore_listdir(datastore,
                                              data_id,
                                              config_path=config_path,
                                              proxy=proxy)
            else:
                return data.datastore_getfile(datastore,
                                              data_id,
                                              config_path=config_path,
                                              proxy=proxy)

        elif blockchain_id is not None:
            # get single data
            if version is not None:
                return data.get_mutable(data_id,
                                        proxy=proxy,
                                        ver_min=version,
                                        ver_max=version + 1,
                                        blockchain_id=blockchain_id,
                                        fully_qualified_data_id=True)
            else:
                return data.get_mutable(data_id,
                                        proxy=proxy,
                                        blockchain_id=blockchain_id,
                                        fully_qualified_data_id=True)

        else:
            return {'error': 'Invalid URL'}

    else:
        if data_id is not None:
            # get single data
            if data_hash is not None:
                return data.get_immutable(blockchain_id,
                                          data_hash,
                                          data_id=data_id,
                                          proxy=proxy)

            else:
                return data.get_immutable_by_name(blockchain_id,
                                                  data_id,
                                                  proxy=proxy)

        else:
            # list data
            return data.list_immutable_data(blockchain_id,
                                            proxy=proxy,
                                            config_path=config_path)