11 use HTTP
::Request
::Common
;
12 use HTTP
::Status
qw(:constants :is status_message);
14 use PVE
::Exception
qw(raise raise_perm_exc);
16 use PVE
::AccessControl
;
17 use PVE
::RPCEnvironment
;
20 use Data
::Dumper
; # fixme: remove
22 my $cookie_name = 'PVEAuthCookie';
24 my $base_handler_class;
26 sub set_base_handler_class
{
29 die "base_handler_class already defined" if $base_handler_class;
31 $base_handler_class = $class;
34 sub extract_auth_cookie
{
37 return undef if !$cookie;
39 my $ticket = ($cookie =~ /(?:^|\s)$cookie_name=([^;]*)/)[0];
41 if ($ticket && $ticket =~ m/^PVE%3A/) {
42 $ticket = uri_unescape
($ticket);
48 sub create_auth_cookie
{
51 my $encticket = uri_escape
($ticket);
52 return "${cookie_name}=$encticket; path=/; secure;";
55 my $exc_to_res = sub {
56 my ($info, $err, $status) = @_;
58 $status = $status || HTTP_INTERNAL_SERVER_ERROR
;
60 my $resp = { info
=> $info };
61 if (ref($err) eq "PVE::Exception") {
62 $resp->{status
} = $err->{code
} || $status;
63 $resp->{errors
} = $err->{errors
} if $err->{errors
};
64 $resp->{message
} = $err->{msg
};
66 $resp->{status
} = $status;
67 $resp->{message
} = $err;
74 my ($rpcenv, $clientip, $method, $rel_uri, $ticket, $token) = @_;
76 # set environment variables
77 $rpcenv->set_user(undef);
78 $rpcenv->set_language('C'); # fixme:
79 $rpcenv->set_client_ip($clientip);
83 # explicitly allow some calls without auth
84 if (($rel_uri eq '/access/domains' && $method eq 'GET') ||
85 ($rel_uri eq '/access/ticket' && ($method eq 'GET' || $method eq 'POST'))) {
95 die "No ticket\n" if !$ticket;
97 ($username, $age) = PVE
::AccessControl
::verify_ticket
($ticket);
99 $rpcenv->set_user($username);
101 if ($method eq 'POST' && $rel_uri =~ m
|^/nodes/([^/]+)/storage
/([^/]+)/upload
$|) {
102 my ($node, $storeid) = ($1, $2);
103 # we disable CSRF checks if $isUpload is set,
104 # to improve security we check user upload permission here
105 my $perm = { check
=> ['perm', "/storage/$storeid", ['Datastore.AllocateTemplate']] };
106 $rpcenv->check_api2_permissions($perm, $username, {});
110 # we skip CSRF check for file upload, because it is
111 # difficult to pass CSRF HTTP headers with native html forms,
112 # and it should not be necessary at all.
113 PVE
::AccessControl
::verify_csrf_prevention_token
($username, $token)
114 if !$isUpload && ($EUID != 0) && ($method ne 'GET');
122 isUpload
=> $isUpload,
127 my ($rpcenv, $clientip, $method, $rel_uri, $auth, $params) = @_;
129 die "no base handler - internal error" if !$base_handler_class;
132 my ($handler, $info) = $base_handler_class->find_handler($method, $rel_uri, $uri_param);
133 if (!$handler || !$info) {
135 status
=> HTTP_NOT_IMPLEMENTED
,
136 message
=> "Method '$method $rel_uri' not implemented",
140 foreach my $p (keys %{$params}) {
141 if (defined($uri_param->{$p})) {
143 status
=> HTTP_BAD_REQUEST
,
144 message
=> "Parameter verification failed - duplicate parameter '$p'",
147 $uri_param->{$p} = $params->{$p};
150 # check access permissions
151 eval { $rpcenv->check_api2_permissions($info->{permissions
}, $auth->{userid
}, $uri_param); };
153 return &$exc_to_res($info, $err, HTTP_FORBIDDEN
);
156 if ($info->{proxyto
}) {
159 my $pn = $info->{proxyto
};
160 my $node = $uri_param->{$pn};
161 die "proxy parameter '$pn' does not exists" if !$node;
163 if ($node ne 'localhost' && $node ne PVE
::INotify
::nodename
()) {
164 die "unable to proxy file uploads" if $auth->{isUpload
};
165 $remip = PVE
::Cluster
::remote_node_ip
($node);
169 return &$exc_to_res($info, $err);
172 return { proxy
=> $remip, proxy_params
=> $params };
176 if ($info->{protected
} && ($EUID != 0)) {
177 return { proxy
=> 'localhost' , proxy_params
=> $params }
181 info
=> $info, # useful to format output
186 $resp->{data
} = $handler->handle($info, $uri_param);
188 if (my $count = $rpcenv->get_result_attrib('total')) {
189 $resp->{total
} = $count;
191 if (my $diff = $rpcenv->get_result_attrib('changes')) {
192 $resp->{changes
} = $diff;
196 return &$exc_to_res($info, $err);