From d31e0108b43df7c447b68114fc58e5238a7f27e4 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Tue, 7 Jul 2015 14:03:55 -0400 Subject: [PATCH] app: Implement username / password based API access #139 --- libraries/app/api.cpp | 47 ++++++++++++++--- libraries/app/application.cpp | 40 ++++++++++++++- libraries/app/include/graphene/app/api.hpp | 4 +- .../app/include/graphene/app/api_access.hpp | 50 +++++++++++++++++++ .../app/include/graphene/app/application.hpp | 2 + 5 files changed, 133 insertions(+), 10 deletions(-) create mode 100644 libraries/app/include/graphene/app/api_access.hpp diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index bf71f665..e32115d7 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -16,6 +16,7 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include +#include #include #include #include @@ -349,17 +350,47 @@ namespace graphene { namespace app { bool login_api::login(const string& user, const string& password) { - auto db_api = std::make_shared(std::ref(*_app.chain_database())); - auto net_broadcast_api = std::make_shared(std::ref(_app)); - auto hist_api = std::make_shared(_app); - auto net_node_api = std::make_shared(std::ref(_app)); - _database_api = db_api; - _network_broadcast_api = net_broadcast_api; - _history_api = hist_api; - _network_node_api = net_node_api; + optional< api_access_info > acc = _app.get_api_access_info( user ); + if( !acc.valid() ) + return false; + if( acc->password_hash_b64 != "*" ) + { + std::string password_salt = fc::base64_decode( acc->password_salt_b64 ); + std::string acc_password_hash = fc::base64_decode( acc->password_hash_b64 ); + + fc::sha256 hash_obj = fc::sha256::hash( password + password_salt ); + if( hash_obj.data_size() != acc_password_hash.length() ) + return false; + if( memcmp( hash_obj.data(), acc_password_hash.c_str(), hash_obj.data_size() ) != 0 ) + return false; + } + + for( const std::string& api_name : acc->allowed_apis ) + enable_api( api_name ); return true; } + void login_api::enable_api( const std::string& api_name ) + { + if( api_name == "database_api" ) + { + _database_api = std::make_shared< database_api >( std::ref( *_app.chain_database() ) ); + } + else if( api_name == "network_broadcast_api" ) + { + _network_broadcast_api = std::make_shared< network_broadcast_api >( std::ref( _app ) ); + } + else if( api_name == "history_api" ) + { + _history_api = std::make_shared< history_api >( _app ); + } + else if( api_name == "network_node_api" ) + { + _network_node_api = std::make_shared< network_node_api >( std::ref(_app) ); + } + return; + } + network_broadcast_api::network_broadcast_api(application& a):_app(a) { _applied_block_connection = _app.chain_database()->applied_block.connect([this](const signed_block& b){ on_applied_block(b); }); diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 1a9a932c..e92ca5a2 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -15,9 +15,10 @@ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include +#include #include #include -#include #include @@ -200,11 +201,41 @@ namespace detail { _chain_db->reindex(_data_dir / "blockchain", initial_state); } + if( _options->count("apiaccess") ) + _apiaccess = fc::json::from_file( _options->at("apiaccess").as() ) + .as(); + else + { + // TODO: Remove this generous default access policy + // when the UI logs in properly + _apiaccess = api_access(); + api_access_info wild_access; + wild_access.password_hash_b64 = "*"; + wild_access.password_salt_b64 = "*"; + wild_access.allowed_apis.push_back( "database_api" ); + wild_access.allowed_apis.push_back( "network_broadcast_api" ); + wild_access.allowed_apis.push_back( "history_api" ); + _apiaccess.permission_map["*"] = wild_access; + } + reset_p2p_node(_data_dir); reset_websocket_server(); reset_websocket_tls_server(); } FC_CAPTURE_AND_RETHROW() } + optional< api_access_info > get_api_access_info( const string& username )const + { + optional< api_access_info > result; + auto it = _apiaccess.permission_map.find( username ); + if( it == _apiaccess.permission_map.end() ) + { + it = _apiaccess.permission_map.find( "*" ); + if( it == _apiaccess.permission_map.end() ) + return result; + } + return it->second; + } + /** * If delegate has the item, the network has no need to fetch it. */ @@ -410,6 +441,7 @@ namespace detail { fc::path _data_dir; const bpo::variables_map* _options = nullptr; + api_access _apiaccess; std::shared_ptr _chain_db; std::shared_ptr _p2p_network; @@ -449,6 +481,7 @@ void application::set_program_options(boost::program_options::options_descriptio ("server-pem,p", bpo::value()->implicit_value("server.pem"), "The TLS certificate file for this server") ("server-pem-password,P", bpo::value()->implicit_value(""), "Password for this certificate") ("genesis-json", bpo::value(), "File to read Genesis State from") + ("apiaccess", bpo::value(), "JSON file specifying API permissions") ; command_line_options.add(configuration_file_options); command_line_options.add_options() @@ -519,6 +552,11 @@ void application::set_block_production(bool producing_blocks) my->_is_block_producer = producing_blocks; } +optional< api_access_info > application::get_api_access_info( const string& username )const +{ + return my->get_api_access_info( username ); +} + void graphene::app::application::add_plugin(const string& name, std::shared_ptr p) { my->_plugins[name] = p; diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index d57eb2d1..531dadb1 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -29,7 +29,6 @@ #include #include - #include #include @@ -439,6 +438,9 @@ namespace graphene { namespace app { fc::api network_node()const; private: + /// @brief Called to enable an API, not reflected. + void enable_api( const string& api_name ); + application& _app; optional< fc::api > _database_api; optional< fc::api > _network_broadcast_api; diff --git a/libraries/app/include/graphene/app/api_access.hpp b/libraries/app/include/graphene/app/api_access.hpp new file mode 100644 index 00000000..b6f4e2f3 --- /dev/null +++ b/libraries/app/include/graphene/app/api_access.hpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2015, Cryptonomex, Inc. + * All rights reserved. + * + * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and + * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, + * are permitted until September 8, 2015, provided that the following conditions are met: + * + * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once + +#include + +#include +#include +#include + +namespace graphene { namespace app { + +struct api_access_info +{ + std::string password_hash_b64; + std::string password_salt_b64; + std::vector< std::string > allowed_apis; +}; + +struct api_access +{ + std::map< std::string, api_access_info > permission_map; +}; + +} } // graphene::app + +FC_REFLECT( graphene::app::api_access_info, + (password_hash_b64) + (password_salt_b64) + (allowed_apis) + ) + +FC_REFLECT( graphene::app::api_access, + (permission_map) + ) diff --git a/libraries/app/include/graphene/app/application.hpp b/libraries/app/include/graphene/app/application.hpp index f7ee1450..4eccc980 100644 --- a/libraries/app/include/graphene/app/application.hpp +++ b/libraries/app/include/graphene/app/application.hpp @@ -17,6 +17,7 @@ */ #pragma once +#include #include #include @@ -74,6 +75,7 @@ namespace graphene { namespace app { std::shared_ptr chain_database()const; void set_block_production(bool producing_blocks); + fc::optional< api_access_info > get_api_access_info( const string& username )const; private: void add_plugin( const string& name, std::shared_ptr p );