app: Implement username / password based API access #139

This commit is contained in:
theoreticalbts 2015-07-07 14:03:55 -04:00
parent 1dd7316d44
commit d31e0108b4
5 changed files with 133 additions and 10 deletions

View file

@ -16,6 +16,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <graphene/app/api.hpp>
#include <graphene/app/api_access.hpp>
#include <graphene/app/application.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/utilities/key_conversion.hpp>
@ -349,17 +350,47 @@ namespace graphene { namespace app {
bool login_api::login(const string& user, const string& password)
{
auto db_api = std::make_shared<database_api>(std::ref(*_app.chain_database()));
auto net_broadcast_api = std::make_shared<network_broadcast_api>(std::ref(_app));
auto hist_api = std::make_shared<history_api>(_app);
auto net_node_api = std::make_shared<network_node_api>(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); });

View file

@ -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 <graphene/app/api.hpp>
#include <graphene/app/api_access.hpp>
#include <graphene/app/application.hpp>
#include <graphene/app/plugin.hpp>
#include <graphene/app/api.hpp>
#include <graphene/net/core_messages.hpp>
@ -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<boost::filesystem::path>() )
.as<api_access>();
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<graphene::chain::database> _chain_db;
std::shared_ptr<graphene::net::node> _p2p_network;
@ -449,6 +481,7 @@ void application::set_program_options(boost::program_options::options_descriptio
("server-pem,p", bpo::value<string>()->implicit_value("server.pem"), "The TLS certificate file for this server")
("server-pem-password,P", bpo::value<string>()->implicit_value(""), "Password for this certificate")
("genesis-json", bpo::value<boost::filesystem::path>(), "File to read Genesis State from")
("apiaccess", bpo::value<boost::filesystem::path>(), "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<graphene::app::abstract_plugin> p)
{
my->_plugins[name] = p;

View file

@ -29,7 +29,6 @@
#include <graphene/chain/balance_object.hpp>
#include <graphene/net/node.hpp>
#include <graphene/market_history/market_history_plugin.hpp>
#include <fc/api.hpp>
@ -439,6 +438,9 @@ namespace graphene { namespace app {
fc::api<network_node_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> > _database_api;
optional< fc::api<network_broadcast_api> > _network_broadcast_api;

View file

@ -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 <fc/reflect/reflect.hpp>
#include <map>
#include <string>
#include <vector>
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)
)

View file

@ -17,6 +17,7 @@
*/
#pragma once
#include <graphene/app/api_access.hpp>
#include <graphene/net/node.hpp>
#include <graphene/chain/database.hpp>
@ -74,6 +75,7 @@ namespace graphene { namespace app {
std::shared_ptr<chain::database> 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<abstract_plugin> p );