MT#3925 Use forms for validation only; other fixes

Don't use JE for data validation, as it adds lots of processing overhead.
Properly use Formhandler for data validation.
Catch DB errors properly.
Write basic auth test.
agranig/rest
Andreas Granig 13 years ago
parent eb01bbde40
commit a7ae65ca82

@ -17,10 +17,12 @@ use HTTP::Status qw(
HTTP_OK
HTTP_UNPROCESSABLE_ENTITY
HTTP_UNSUPPORTED_MEDIA_TYPE
HTTP_INTERNAL_SERVER_ERROR
);
use JE qw();
use JSON qw();
#use JE qw();
use JSON qw(from_json);
use MooseX::ClassAttribute qw(class_has);
use NGCP::Panel::Form::Contact::Admin qw();
use NGCP::Panel::Form::Contact::Reseller qw();
use NGCP::Panel::ValidateJSON qw();
use Path::Tiny qw(path);
@ -123,55 +125,43 @@ sub OPTIONS : Allow {
sub POST : Allow {
my ($self, $c) = @_;
my $media_type = 'application/hal+json';
my $media_type = 'application/json';
{
last unless $self->forbid_link_header($c);
last unless $self->valid_media_type($c, $media_type);
last unless $self->require_body($c);
my $json = do { local $/; $c->request->body->getline }; # slurp
last unless $self->require_wellformed_json($c, $media_type, $json);
last unless $self->valid_entity($c, $json);
my $hal = Data::HAL->from_json($json);
#last unless $self->valid_entity($c, $json);
my $r_id;
{
my $reseller_link = ($hal->links // [])->grep(sub {
$_->relation->eq('http://purl.org/sipwise/ngcp-api/#rel-resellers')
});
if ($reseller_link->size) {
my $reseller_uri = URI->new_abs($reseller_link->at(0)->href->as_string, $c->req->uri)->canonical;
my $resellers_uri = URI->new_abs('/api/resellers/', $c->req->uri)->canonical;
if (0 != index $reseller_uri, $resellers_uri) {
$c->response->status(HTTP_UNPROCESSABLE_ENTITY);
$c->response->header('Content-Language' => 'en');
$c->response->content_type('application/xhtml+xml');
$c->stash(
template => 'api/unprocessable_entity.tt',
error_message => "The link $reseller_uri cannot express a reseller relationship.",
);
last;
}
$r_id = $reseller_uri->rel($resellers_uri)->query_param('id');
last unless $self->valid_id($c, $r_id);
}
my $contact_form;
my $resource = from_json($json);
$resource->{reseller}{id} = delete $resource->{reseller_id};
if($c->user->roles eq "api_admin") {
$c->log->debug("+++++++++++++++ using NGCP::Panel::Form::Contact::Admin for validation");
$contact_form = NGCP::Panel::Form::Contact::Admin->new;
} else {
$contact_form = NGCP::Panel::Form::Contact::Reseller->new;
$resource->{reseller}{id} = $c->user->reseller_id;
}
my $resource = $hal->resource;
my $contact_form = NGCP::Panel::Form::Contact::Reseller->new;
my %fields = map { $_->name => undef } grep { 'Text' eq $_->type || 'Email' eq $_->type } $contact_form->fields;
my %fields = map { $_->name => undef } $contact_form->fields;
for my $k (keys %{ $resource }) {
delete $resource->{$k} unless exists $fields{$k};
unless(exists $fields{$k}) {
$c->log->debug("+++++++++++++ deleting unknown key '$k'");
delete $resource->{$k};
}
$resource->{$k} = DateTime::Format::RFC3339->format_datetime($resource->{$k})
if $resource->{$k}->$_isa('DateTime');
}
my $result = $contact_form->run(params => $resource);
if ($result->error_results->size) {
$c->response->status(HTTP_UNPROCESSABLE_ENTITY);
$c->response->header('Content-Language' => 'en');
# TODO: return error in json!
$c->response->content_type('application/xhtml+xml');
my $e = $result->error_results->map(sub {
sprintf '%s: %s - %s', $_->name, $_->input, $_->errors->join(q())
sprintf 'field: \'%s\', input: \'%s\', errors: %s', $_->name, $_->input // '', $_->errors->join(q())
})->join("\n");
$c->stash(
template => 'api/unprocessable_entity.tt',
@ -180,11 +170,23 @@ sub POST : Allow {
last;
}
$resource->{reseller_id} = $r_id;
$resource->{reseller_id} = $resource->{reseller}{id}; delete $resource->{reseller};
my $now = DateTime->now;
$resource->{create_timestamp} = $now;
$resource->{modify_timestamp} = $now;
my $contact = $c->model('DB')->resultset('contacts')->create($resource);
my $contact;
try {
$contact = $c->model('DB')->resultset('contacts')->create($resource);
} catch($e) {
$c->log->error("failed to create contact: $e"); # TODO: log user, input etc
$c->response->status(HTTP_INTERNAL_SERVER_ERROR);
# TODO: that one is not rendered, rather than our "normal" 500 template!
$c->stash(
template => 'api/internal_server_error.tt',
error_message => "DB query faild: $e",
);
last;
}
$c->cache->remove($c->request->uri->canonical->as_string);
$c->response->status(HTTP_CREATED);
@ -258,6 +260,7 @@ sub hal_from_contact : Private {
my %resource = $contact->get_inflated_columns;
my $id = delete $resource{id};
my $hal = Data::HAL->new(
links => [
Data::HAL::Link->new(
@ -331,7 +334,7 @@ sub valid_media_type : Private {
$c->stash(template => 'api/valid_media_type.tt', media_type => $media_type);
return;
}
=pod
sub valid_entity : Private {
my ($self, $c, $entity) = @_;
my $js
@ -358,6 +361,7 @@ sub valid_entity : Private {
}
return 1;
}
=cut
sub end : Private {
my ($self, $c) = @_;
@ -390,3 +394,5 @@ use Carp qw(longmess); use DateTime::Format::RFC3339 qw(); use Data::Dumper qw(D
$c->detach($c->view);
}
}
# vim: set tabstop=4 expandtab:

@ -7,6 +7,7 @@ $ua->ssl_opts(
SSL_cert_file => '/etc/ssl/ngcp/api/NGCP-API-client-certificate-1385650532.pem',
SSL_key_file => '/etc/ssl/ngcp/api/NGCP-API-client-certificate-1385650532.pem',
SSL_ca_file => '/etc/ssl/ngcp/api/ca-cert.pem',
);
my $can_accept = HTTP::Message::decodable;
my $res = $ua->get(
'https://serenity:4443/api/contacts/?id=10',

@ -3,6 +3,7 @@
"description": "contact information for a contract",
"required": ["city", "company", "country", "email", "firstname", "lastname", "phonenumber", "postcode", "street"],
"properties": {
"reseller_id": { "type": ["null", "number"], "description": "The id of the contact's reseller." },
"city": { "type": ["null", "string"], "description": "The contact's city." },
"company": { "type": ["null", "string"], "description": "The contact's company." },
"country": { "type": ["null", "string"], "description": "The contact's country (ISO-3166, e.g. \"AT\" for Austria, \"DE\" for Germany)." },

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>Internal Server Error</title>
</head>
<body>
<p>An error occured during processing the request.</p>
<p><samp>[% error_message | html %]</samp></p>
</body>
</html>

@ -0,0 +1,61 @@
use Sipwise::Base;
use Net::Domain qw(hostfqdn);
use LWP::UserAgent;
use Test::More;
my $uri = $ENV{CATALYST_SERVER} || ('https://'.hostfqdn.':4443');
my $valid_ssl_client_cert = $ENV{API_SSL_CLIENT_CERT} ||
"/etc/ssl/ngcp/api/NGCP-API-client-certificate.pem";
my $valid_ssl_client_key = $ENV{API_SSL_CLIENT_KEY} ||
$valid_ssl_client_cert;
my $invalid_ssl_client_cert = $ENV{API_SSL_INVALID_CLIENT_CERT} ||
"/etc/ssl/ngcp/api/NGCP-API-client-certificate.invalid.pem";
my $invalid_ssl_client_key = $ENV{API_SSL_INVALID_CLIENT_KEY} ||
$invalid_ssl_client_cert;
my $unauth_ssl_client_cert = $ENV{API_SSL_UNAUTH_CLIENT_CERT} ||
"/etc/ssl/ngcp/api/NGCP-API-client-certificate.unauth.pem";
my $unauth_ssl_client_key = $ENV{API_SSL_UNAUTH_CLIENT_KEY} ||
$unauth_ssl_client_cert;
my $ssl_ca_cert = $ENV{API_SSL_CA_CERT} || "/etc/ssl/ngcp/api/ca-cert.pem";
my ($ua, $res);
$ua = LWP::UserAgent->new;
# invalid cert
$ua->ssl_opts(
SSL_cert_file => $invalid_ssl_client_cert,
SSL_key_file => $invalid_ssl_client_key,
SSL_ca_file => $ssl_ca_cert,
);
$res = $ua->get($uri.'/api/');
ok($res->code == 400, "check invalid client certificate");
# unauth cert
$ua->ssl_opts(
SSL_cert_file => $unauth_ssl_client_cert,
SSL_key_file => $unauth_ssl_client_key,
SSL_ca_file => $ssl_ca_cert,
);
$res = $ua->get($uri.'/api/');
ok($res->code == 403, "check unauthorized client certificate");
# successful auth
$ua->ssl_opts(
SSL_cert_file => $valid_ssl_client_cert,
SSL_key_file => $valid_ssl_client_key,
SSL_ca_file => $ssl_ca_cert,
);
$res = $ua->get($uri.'/api/');
ok($res->code == 200, "check valid client certificate");
#my @links = $res->header('Link');
#ok(grep /^<\/api\/contacts\/>; rel="collection /, @links);
#ok(grep /^<\/api\/contracts\/>; rel="collection /, @links);
done_testing;
# vim: set tabstop=4 expandtab:
Loading…
Cancel
Save