TT#13249 Implement API to create admin client cert

Admins can use this to generate a new ssl client certificate package
in ZIP format containing a PEM and a P12 cert, the same way you can
do it on the admin panel.

Admins can generate that for themselves in all cases, or for others
if they are master or superuser. If role is reseller, then masters
can only do this within their own reseller.

Change-Id: I32d5c1b5af5324d1c80b34bacecd7f2665cd91c7
changes/88/12088/1
Andreas Granig 9 years ago
parent 5918c162e1
commit b60b439220

@ -0,0 +1,76 @@
package NGCP::Panel::Controller::API::AdminCerts;
use Sipwise::Base;
use parent qw/NGCP::Panel::Role::Entities NGCP::Panel::Role::API::AdminCerts/;
use HTTP::Status qw(:constants);
use NGCP::Panel::Utils::Admin;
__PACKAGE__->set_config();
sub allowed_roles {
return qw/admin reseller/;
}
sub allowed_methods {
return [qw/POST OPTIONS HEAD/];
}
sub api_description {
return 'Creates a new SSL client certificate package in ZIP format containing a PEM and a P12 certificate.';
}
sub query_params {
return [
{},
];
}
# avoid automatic creation of HAL response, since we're
# taking care of returning the body ourselves.
sub return_representation_post {}
sub create_item {
my ($self, $c, $resource, $form, $process_extras) = @_;
my $login = $resource->{login} // $c->user->login;
my $item_rs = $self->get_list($c);
my $admin = $item_rs->search({
login => $login,
})->first;
unless($admin) {
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid login, administrator does not exist");
return;
}
# only allow to generate a certificate if
# a. you're doing it for yourself
# b. you're a master
# c. you're a superuser
unless($c->user->login eq $login || $c->user->is_master || $c->user->is_superuser) {
$self->error($c, HTTP_FORBIDDEN, "Insufficient privileges to create certificate for this administrator");
return;
}
my $err;
my $res = NGCP::Panel::Utils::Admin::generate_client_cert($c, $admin, sub {
my $e = shift;
$self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to generate client certificate");
$err = 1;
});
return if $err;
my $serial = $res->{serial};
my $zipped_file = $res->{file};
$c->res->headers(HTTP::Headers->new(
'Content-Type' => 'application/zip',
'Content-Disposition' => sprintf('attachment; filename=%s', "NGCP-API-client-certificate-$serial.zip")
));
$c->res->body($zipped_file);
$c->response->status(HTTP_CREATED);
}
1;
# vim: set tabstop=4 expandtab:

@ -3,7 +3,6 @@ use NGCP::Panel::Utils::Generic qw(:all);
use Sipwise::Base;
use parent 'Catalyst::Controller';
use HTTP::Headers qw();
use IO::Compress::Zip qw/zip/;
use NGCP::Panel::Form::Administrator::Reseller;
use NGCP::Panel::Form::Administrator::Admin;
use NGCP::Panel::Form::Administrator::APIGenerate qw();
@ -259,54 +258,23 @@ sub api_key :Chained('base') :PathPart('api_key') :Args(0) {
my $serial = $c->stash->{administrator}->ssl_client_m_serial;
my ($pem, $p12);
if ($c->req->body_parameters->{'gen.generate'}) {
my $updated;
while (!$updated) {
$serial = time;
try {
$pem = $c->model('CA')->make_client($c, $serial);
$p12 = $c->model('CA')->make_pkcs12($c, $serial, $pem, 'sipwise');
} catch ($e) {
NGCP::Panel::Utils::Message::error(
c => $c,
error => $e,
data => { $c->stash->{administrator}->get_inflated_columns },
desc => $c->loc("Failed to generate client certificate."),
);
NGCP::Panel::Utils::Navigation::back_or($c, $c->uri_for('/administrator'));
}
try {
$c->stash->{administrator}->update({
ssl_client_m_serial => $serial,
ssl_client_certificate => undef, # not used anymore, clear it just in case
});
$updated = 1;
} catch(DBIx::Class::Exception $e where { "$_" =~ qr'Duplicate entry' }) {
$serial++;
}
}
my $input = {
"NGCP-API-client-certificate-$serial.pem" => $pem,
"NGCP-API-client-certificate-$serial.p12" => $p12,
};
my $zip_opts = {
AutoClose => 0,
Append => 0,
Name => "README.txt",
CanonicalName => 1,
Stream => 1,
};
my $zipped_file;
my $zip = IO::Compress::Zip->new(\$zipped_file, %{ $zip_opts });
$zip->write("Use the PEM file for programmatical clients like java, perl, php or curl, and the P12 file for browsers like Firefox or Chrome. The password for the P12 import is 'sipwise'. Handle this file with care, as it cannot be downloaded for a second time! Only a new certificate can be generated if the certificate is lost.\n");
foreach my $k(keys %{ $input } ) {
$zip_opts->{Name} = $k;
$zip_opts->{Append} = 1;
$zip->newStream(%{ $zip_opts });
$zip->write($input->{$k});
my $err;
my $res = NGCP::Panel::Utils::Admin::generate_client_cert($c, $c->stash->{administrator}, sub {
my $e = shift;
NGCP::Panel::Utils::Message::error(
c => $c,
error => $e,
data => { $c->stash->{administrator}->get_inflated_columns },
desc => $c->loc("Failed to generate client certificate."),
);
$err = 1;
});
if($err) {
NGCP::Panel::Utils::Navigation::back_or($c, $c->uri_for('/administrator'));
}
$zip->close();
$serial = $res->{serial};
my $zipped_file = $res->{file};
$c->res->headers(HTTP::Headers->new(
'Content-Type' => 'application/zip',
'Content-Disposition' => sprintf('attachment; filename=%s', "NGCP-API-client-certificate-$serial.zip")

@ -0,0 +1,9 @@
package NGCP::Panel::Form::Administrator::APICert;
use HTML::FormHandler::Moose;
extends 'HTML::FormHandler';
has_field 'login' => (type => 'Text', required => 0, minlength => 5);
1;
# vim: set tabstop=4 expandtab:

@ -0,0 +1,50 @@
package NGCP::Panel::Role::API::AdminCerts;
use Sipwise::Base;
use parent 'NGCP::Panel::Role::API';
use NGCP::Panel::Utils::DataHalLink qw();
use HTTP::Status qw(:constants);
use NGCP::Panel::Form::Administrator::APICert;
sub item_name {
return 'admincerts';
}
sub resource_name{
return 'admincerts';
}
sub get_form {
my ($self, $c) = @_;
return NGCP::Panel::Form::Administrator::APICert->new(c => $c);
}
sub hal_links {
my($self, $c, $item, $resource, $form) = @_;
return [];
}
sub process_hal_resource {
my($self, $c, $item, $resource, $form) = @_;
return $resource;
}
sub _item_rs {
my ($self, $c) = @_;
my $item_rs;
if($c->user->roles eq "admin") {
$item_rs = $c->model('DB')->resultset('admins');
} elsif($c->user->roles eq "reseller") {
$item_rs = $c->model('DB')->resultset('admins')->search({
reseller_id => $c->user->reseller_id,
});
}
return $item_rs;
}
1;
# vim: set tabstop=4 expandtab:

@ -3,6 +3,7 @@ package NGCP::Panel::Utils::Admin;
use Sipwise::Base;
use Crypt::Eksblowfish::Bcrypt qw/bcrypt_hash en_base64 de_base64/;
use Data::Entropy::Algorithms qw/rand_bits/;
use IO::Compress::Zip qw/zip/;
sub get_bcrypt_cost {
return 13;
@ -85,4 +86,54 @@ sub perform_auth {
return $res;
}
sub generate_client_cert {
my ($c, $admin, $error_cb) = @_;
my $updated;
my ($serial, $pem, $p12);
while (!$updated) {
$serial = time;
try {
$pem = $c->model('CA')->make_client($c, $serial);
$p12 = $c->model('CA')->make_pkcs12($c, $serial, $pem, 'sipwise');
} catch ($e) {
$error_cb->($e);
return;
}
try {
$admin->update({
ssl_client_m_serial => $serial,
ssl_client_certificate => undef, # not used anymore, clear it just in case
});
$updated = 1;
} catch(DBIx::Class::Exception $e where { "$_" =~ qr'Duplicate entry' }) {
$serial++;
}
}
my $input = {
"NGCP-API-client-certificate-$serial.pem" => $pem,
"NGCP-API-client-certificate-$serial.p12" => $p12,
};
my $zip_opts = {
AutoClose => 0,
Append => 0,
Name => "README.txt",
CanonicalName => 1,
Stream => 1,
};
my $zipped_file;
my $zip = IO::Compress::Zip->new(\$zipped_file, %{ $zip_opts });
$zip->write("Use the PEM file for programmatical clients like java, perl, php or curl, and the P12 file for browsers like Firefox or Chrome. The password for the P12 import is 'sipwise'. Handle this file with care, as it cannot be downloaded for a second time! Only a new certificate can be generated if the certificate is lost.\n");
foreach my $k(keys %{ $input } ) {
$zip_opts->{Name} = $k;
$zip_opts->{Append} = 1;
$zip->newStream(%{ $zip_opts });
$zip->write($input->{$k});
}
$zip->close();
return { serial => $serial, file => $zipped_file };
}
1;

Loading…
Cancel
Save