2013-04-11 08:48:01 +04:00
package PVE::HTTPServer ;
use strict ;
use warnings ;
2013-04-12 14:51:28 +04:00
2017-01-16 12:59:31 +03:00
use PVE::SafeSyslog ;
use PVE::INotify ;
use PVE::Tools ;
use PVE::APIServer::AnyEvent ;
2017-01-16 12:59:38 +03:00
use PVE::Exception qw( raise_param_exc ) ;
2013-04-11 09:47:33 +04:00
2017-01-16 12:59:31 +03:00
use PVE::RPCEnvironment ;
use PVE::AccessControl ;
use PVE::Cluster ;
2013-04-11 09:47:33 +04:00
2017-01-16 12:59:31 +03:00
use Data::Dumper ;
2013-04-11 08:48:01 +04:00
2017-01-16 12:59:31 +03:00
use base ( 'PVE::APIServer::AnyEvent' ) ;
2013-04-11 10:31:42 +04:00
2017-01-16 12:59:31 +03:00
use HTTP::Status qw( :constants ) ;
2013-04-11 10:31:42 +04:00
2013-04-11 08:48:01 +04:00
sub new {
my ( $ this , % args ) = @ _ ;
my $ class = ref ( $ this ) || $ this ;
2017-01-16 12:59:31 +03:00
my $ self = $ class - > SUPER:: new ( % args ) ;
2013-04-11 10:17:34 +04:00
$ self - > { rpcenv } = PVE::RPCEnvironment - > init (
2013-04-11 10:31:42 +04:00
$ self - > { trusted_env } ? 'priv' : 'pub' , atfork = > sub { $ self - > atfork_handler ( ) } ) ;
2013-04-11 10:17:34 +04:00
2017-01-16 12:59:31 +03:00
return $ self ;
}
2013-04-16 11:09:41 +04:00
2017-01-16 12:59:31 +03:00
sub verify_spice_connect_url {
my ( $ self , $ connect_str ) = @ _ ;
2013-04-11 08:48:01 +04:00
2017-01-16 12:59:35 +03:00
my $ rpcenv = $ self - > { rpcenv } ;
$ rpcenv - > init_request ( ) ;
2017-01-16 12:59:31 +03:00
my ( $ vmid , $ node , $ port ) = PVE::AccessControl:: verify_spice_connect_url ( $ connect_str ) ;
2013-04-11 08:48:01 +04:00
2017-01-16 12:59:31 +03:00
return ( $ vmid , $ node , $ port ) ;
}
2013-04-11 08:48:01 +04:00
2017-01-16 12:59:31 +03:00
sub generate_csrf_prevention_token {
my ( $ username ) = @ _ ;
2013-04-11 10:17:34 +04:00
2017-01-16 12:59:31 +03:00
return PVE::AccessControl:: assemble_csrf_prevention_token ( $ username ) ;
2013-04-11 08:48:01 +04:00
}
2017-01-10 19:05:59 +03:00
sub auth_handler {
2017-01-16 12:59:35 +03:00
my ( $ self , $ method , $ rel_uri , $ ticket , $ token , $ peer_host ) = @ _ ;
2017-01-10 19:05:59 +03:00
my $ rpcenv = $ self - > { rpcenv } ;
2017-01-16 12:59:35 +03:00
# set environment variables
$ rpcenv - > set_user ( undef ) ;
$ rpcenv - > set_language ( 'C' ) ;
$ rpcenv - > set_client_ip ( $ peer_host ) ;
2017-01-16 12:59:36 +03:00
$ rpcenv - > init_request ( ) ;
2017-01-10 19:05:59 +03:00
my $ require_auth = 1 ;
# explicitly allow some calls without auth
if ( ( $ rel_uri eq '/access/domains' && $ method eq 'GET' ) ||
( $ rel_uri eq '/access/ticket' && ( $ method eq 'GET' || $ method eq 'POST' ) ) ) {
$ require_auth = 0 ;
}
my ( $ username , $ age ) ;
my $ isUpload = 0 ;
if ( $ require_auth ) {
die "No ticket\n" if ! $ ticket ;
( $ username , $ age ) = PVE::AccessControl:: verify_ticket ( $ ticket ) ;
$ rpcenv - > set_user ( $ username ) ;
if ( $ method eq 'POST' && $ rel_uri =~ m | ^ /nodes/ ( [ ^ /]+)/s torage /([^/ ] + ) / upload $| ) {
my ( $ node , $ storeid ) = ( $ 1 , $ 2 ) ;
# we disable CSRF checks if $isUpload is set,
# to improve security we check user upload permission here
my $ perm = { check = > [ 'perm' , "/storage/$storeid" , [ 'Datastore.AllocateTemplate' ] ] } ;
$ rpcenv - > check_api2_permissions ( $ perm , $ username , { } ) ;
$ isUpload = 1 ;
}
# we skip CSRF check for file upload, because it is
# difficult to pass CSRF HTTP headers with native html forms,
# and it should not be necessary at all.
my $ euid = $> ;
PVE::AccessControl:: verify_csrf_prevention_token ( $ username , $ token )
if ! $ isUpload && ( $ euid != 0 ) && ( $ method ne 'GET' ) ;
}
return {
ticket = > $ ticket ,
token = > $ token ,
userid = > $ username ,
age = > $ age ,
isUpload = > $ isUpload ,
} ;
}
2017-01-10 19:06:01 +03:00
sub rest_handler {
my ( $ self , $ clientip , $ method , $ rel_uri , $ auth , $ params ) = @ _ ;
my $ rpcenv = $ self - > { rpcenv } ;
2017-01-16 12:59:38 +03:00
my $ resp = {
status = > HTTP_NOT_IMPLEMENTED ,
message = > "Method '$method $rel_uri' not implemented" ,
} ;
my ( $ handler , $ info ) ;
eval {
my $ uri_param = { } ;
( $ handler , $ info ) = PVE::API2 - > find_handler ( $ method , $ rel_uri , $ uri_param ) ;
return if ! $ handler || ! $ info ;
2017-01-10 19:06:01 +03:00
2017-01-16 12:59:38 +03:00
foreach my $ p ( keys % { $ params } ) {
if ( defined ( $ uri_param - > { $ p } ) ) {
raise_param_exc ( { $ p = > "duplicate parameter (already defined in URI)" } ) ;
}
$ uri_param - > { $ p } = $ params - > { $ p } ;
2017-01-10 19:06:01 +03:00
}
2017-01-16 12:59:38 +03:00
# check access permissions
$ rpcenv - > check_api2_permissions ( $ info - > { permissions } , $ auth - > { userid } , $ uri_param ) ;
2017-01-10 19:06:01 +03:00
2017-01-16 12:59:38 +03:00
if ( $ info - > { proxyto } ) {
2017-01-10 19:06:01 +03:00
my $ pn = $ info - > { proxyto } ;
2017-01-16 12:59:38 +03:00
my $ node = $ uri_param - > { $ pn } ;
raise_param_exc ( { $ pn = > "proxy parameter '$pn' does not exists" } ) if ! $ node ;
2017-01-10 19:06:01 +03:00
if ( $ node ne 'localhost' && $ node ne PVE::INotify:: nodename ( ) ) {
die "unable to proxy file uploads" if $ auth - > { isUpload } ;
2017-01-16 12:59:38 +03:00
my $ remip = $ self - > remote_node_ip ( $ node ) ;
$ resp = { proxy = > $ remip , proxynode = > $ node , proxy_params = > $ params } ;
return ;
2017-01-10 19:06:01 +03:00
}
}
2017-01-16 12:59:38 +03:00
my $ euid = $> ;
if ( $ info - > { protected } && ( $ euid != 0 ) ) {
$ resp = { proxy = > 'localhost' , proxy_params = > $ params } ;
return ;
}
2017-01-10 19:06:01 +03:00
2017-01-16 12:59:38 +03:00
$ resp = {
data = > $ handler - > handle ( $ info , $ uri_param ) ,
info = > $ info , # useful to format output
status = > HTTP_OK ,
} ;
2017-01-10 19:06:01 +03:00
if ( my $ count = $ rpcenv - > get_result_attrib ( 'total' ) ) {
$ resp - > { total } = $ count ;
}
2017-01-16 12:59:38 +03:00
2017-01-10 19:06:01 +03:00
if ( my $ diff = $ rpcenv - > get_result_attrib ( 'changes' ) ) {
$ resp - > { changes } = $ diff ;
}
} ;
2017-01-16 12:59:38 +03:00
my $ err = $@ ;
2017-01-10 19:06:01 +03:00
2017-01-16 12:59:35 +03:00
$ rpcenv - > set_user ( undef ) ; # clear after request
2017-01-16 12:59:38 +03:00
if ( $ err ) {
$ resp = { info = > $ info } ;
if ( ref ( $ err ) eq "PVE::Exception" ) {
$ resp - > { status } = $ err - > { code } || HTTP_INTERNAL_SERVER_ERROR ;
$ resp - > { errors } = $ err - > { errors } if $ err - > { errors } ;
$ resp - > { message } = $ err - > { msg } ;
} else {
$ resp - > { status } = HTTP_INTERNAL_SERVER_ERROR ;
$ resp - > { message } = $ err ;
}
}
2017-01-10 19:06:01 +03:00
return $ resp ;
}
2017-01-10 19:05:59 +03:00
2017-01-12 14:14:53 +03:00
sub check_cert_fingerprint {
my ( $ self , $ cert ) = @ _ ;
return PVE::Cluster:: check_cert_fingerprint ( $ cert ) ;
}
sub initialize_cert_cache {
my ( $ self , $ node ) = @ _ ;
PVE::Cluster:: initialize_cert_cache ( $ node ) ;
}
sub remote_node_ip {
my ( $ self , $ node ) = @ _ ;
my $ remip = PVE::Cluster:: remote_node_ip ( $ node ) ;
die "unable to get remote IP address for node '$node'\n" if ! $ remip ;
return $ remip ;
}
2013-04-11 08:48:01 +04:00
1 ;