You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
397 lines
13 KiB
397 lines
13 KiB
package NGCP::Panel;
|
|
|
|
use Moose;
|
|
use Catalyst::Runtime 5.80;
|
|
use File::Slurp qw();
|
|
use Config::General qw();
|
|
use IO::Socket::UNIX qw(SOCK_DGRAM);
|
|
|
|
# Set flags and add plugins for the application.
|
|
#
|
|
# Note that ORDERING IS IMPORTANT here as plugins are initialized in order,
|
|
# therefore you almost certainly want to keep ConfigLoader at the head of the
|
|
# list if you're using it.
|
|
#
|
|
# -Debug: activates the debug mode for very useful log messages
|
|
# ConfigLoader: will load the configuration from a Config::General file in the
|
|
# application's home directory
|
|
# Static::Simple: will serve static files from the application's root
|
|
# directory
|
|
|
|
use Catalyst qw/
|
|
ConfigLoader
|
|
Static::Simple
|
|
Authentication
|
|
Authorization::Roles
|
|
Session
|
|
Session::Store::Redis
|
|
NGCP::RealmCookie
|
|
NGCP::EscapeSensitiveValue
|
|
NGCP::EscapeJs
|
|
I18N
|
|
/;
|
|
use Log::Log4perl::Catalyst qw();
|
|
extends 'Catalyst';
|
|
|
|
our $VERSION = '0.01';
|
|
|
|
my $panel_config;
|
|
for my $path(qw#/etc/ngcp-panel/ngcp_panel.conf etc/ngcp_panel.conf ngcp_panel.conf#) {
|
|
if(-f $path) {
|
|
$panel_config = $path;
|
|
last;
|
|
}
|
|
}
|
|
$panel_config //= 'etc/ngcp_panel.conf';
|
|
|
|
sub get_panel_config{
|
|
my $config;
|
|
if( -f $panel_config ){
|
|
my $catalyst_config = Config::General->new($panel_config);
|
|
my %config = $catalyst_config->getall();
|
|
$config = \%config;
|
|
}
|
|
return $config;
|
|
}
|
|
|
|
my $logger_config;
|
|
for my $path(qw#./logging.conf /etc/ngcp-panel/logging.conf#) {
|
|
if(-f $path) {
|
|
$logger_config = $path;
|
|
last;
|
|
}
|
|
}
|
|
$logger_config = $panel_config unless(defined $logger_config);
|
|
|
|
sub handle_unicode_encoding_exception {
|
|
my ($self, $exception_context) = @_;
|
|
$self->log->debug(">> handle_unicode_encoding_exception: " . $exception_context->{error_msg});
|
|
return $exception_context->{param_value};
|
|
}
|
|
|
|
__PACKAGE__->config(
|
|
name => 'NGCP::Panel',
|
|
# Disable deprecated behavior needed by old applications
|
|
disable_component_resolution_regex_fallback => 1,
|
|
enable_catalyst_header => 1, # Send X-Catalyst header
|
|
encoding => 'UTF-8',
|
|
'Plugin::ConfigLoader' => {
|
|
file => $panel_config,
|
|
},
|
|
'View::HTML' => {
|
|
INCLUDE_PATH => [
|
|
'/usr/share/ngcp-panel/templates',
|
|
'/usr/share/ngcp-panel/layout',
|
|
'/usr/share/ngcp-panel/static',
|
|
__PACKAGE__->path_to('share', 'templates'),
|
|
__PACKAGE__->path_to('share', 'layout'),
|
|
__PACKAGE__->path_to('share', 'static'),
|
|
],
|
|
ABSOLUTE => 1,
|
|
EVAL_PERL => 1,
|
|
},
|
|
'View::JSON' => {
|
|
#Set the stash keys to be exposed to a JSON response
|
|
#(sEcho iTotalRecords iTotalDisplayRecords aaData) for datatables
|
|
expose_stash => [ qw(sEcho iTotalRecords iTotalDisplayRecords iTotalRecordCountClipped iTotalDisplayRecordCountClipped aaData dt_custom_footer widget_data timeline_data) ],
|
|
},
|
|
'View::TT' => {
|
|
INCLUDE_PATH => [
|
|
'/usr/share/ngcp-panel/templates',
|
|
'/usr/share/ngcp-panel/layout',
|
|
'/usr/share/ngcp-panel/static',
|
|
__PACKAGE__->path_to('share', 'templates'),
|
|
__PACKAGE__->path_to('share', 'layout'),
|
|
__PACKAGE__->path_to('share', 'static'),
|
|
],
|
|
ABSOLUTE => 1,
|
|
EVAL_PERL => 1,
|
|
},
|
|
|
|
'Plugin::Static::Simple' => {
|
|
include_path => [
|
|
'/usr/share/ngcp-panel/static',
|
|
__PACKAGE__->path_to('share', 'static'),
|
|
],
|
|
mime_types => {
|
|
woff => 'application/x-font-woff',
|
|
},
|
|
},
|
|
|
|
'Plugin::Session' => {
|
|
flash_to_stash => 1,
|
|
expires => 3600,
|
|
cookie_secure => 1,
|
|
cookie_name => 'ngcp-panel',
|
|
},
|
|
|
|
'Plugin::Authentication' => {
|
|
default_realm => 'subscriber',
|
|
admin => {
|
|
credential => {
|
|
class => 'Password',
|
|
password_field => 'md5pass',
|
|
password_type => 'hashed',
|
|
password_hash_type => 'MD5'
|
|
},
|
|
store => {
|
|
class => 'DBIx::Class',
|
|
user_model => 'DB::admins',
|
|
id_field => 'id',
|
|
store_user_class => 'NGCP::Panel::Authentication::Store::RoleFromRealm',
|
|
use_userdata_from_session => 1,
|
|
}
|
|
},
|
|
admin_bcrypt => {
|
|
credential => {
|
|
class => 'Password',
|
|
password_field => 'saltedpass',
|
|
# we handle the salt and hash management manually in Login.pm
|
|
password_type => 'clear',
|
|
},
|
|
store => {
|
|
class => 'DBIx::Class',
|
|
user_model => 'DB::admins',
|
|
id_field => 'id',
|
|
store_user_class => 'NGCP::Panel::Authentication::Store::RoleFromRealm',
|
|
use_userdata_from_session => 1,
|
|
}
|
|
},
|
|
admin_jwt => {
|
|
credential => {
|
|
class => '+NGCP::Panel::Authentication::Credential::JWT',
|
|
username_jwt => 'username',
|
|
username_field => 'login',
|
|
id_jwt => 'id',
|
|
id_field => 'id',
|
|
jwt_key => _get_jwt_key(),
|
|
debug => 1,
|
|
alg => 'HS256',
|
|
},
|
|
store => {
|
|
class => 'DBIx::Class',
|
|
user_model => 'DB::admins',
|
|
store_user_class => 'NGCP::Panel::Authentication::Store::RoleFromRealm',
|
|
},
|
|
},
|
|
api_admin => {
|
|
credential => {
|
|
class => 'Password',
|
|
password_field => 'md5pass',
|
|
password_type => 'hashed',
|
|
password_hash_type => 'MD5'
|
|
},
|
|
store => {
|
|
class => 'DBIx::Class',
|
|
user_model => 'DB::admins',
|
|
id_field => 'id',
|
|
store_user_class => 'NGCP::Panel::Authentication::Store::RoleFromRealm',
|
|
#use_userdata_from_session => 1,
|
|
},
|
|
use_session => 0,
|
|
},
|
|
api_admin_bcrypt => {
|
|
credential => {
|
|
class => 'Password',
|
|
password_field => 'saltedpass',
|
|
# we handle the salt and hash management manually in Login.pm
|
|
password_type => 'clear',
|
|
},
|
|
store => {
|
|
class => 'DBIx::Class',
|
|
user_model => 'DB::admins',
|
|
id_field => 'id',
|
|
store_user_class => 'NGCP::Panel::Authentication::Store::RoleFromRealm',
|
|
#use_userdata_from_session => 1,
|
|
},
|
|
use_session => 0,
|
|
},
|
|
api_admin_cert => {
|
|
# TODO: should be NoPassword, but it's not available in our catalyst version yet
|
|
credential => {
|
|
class => 'Password',
|
|
password_field => 'is_active',
|
|
password_type => 'clear',
|
|
},
|
|
store => {
|
|
class => 'DBIx::Class',
|
|
user_model => 'DB::admins',
|
|
id_field => 'id',
|
|
store_user_class => 'NGCP::Panel::Authentication::Store::RoleFromRealm',
|
|
},
|
|
use_session => 0,
|
|
},
|
|
api_admin_http => {
|
|
credential => {
|
|
class => 'HTTP',
|
|
#type => 'digest',
|
|
type => 'basic',
|
|
username_field => 'login',
|
|
password_field => 'md5pass',
|
|
password_type => 'hashed',
|
|
password_hash_type => 'MD5'
|
|
},
|
|
store => {
|
|
class => 'DBIx::Class',
|
|
user_model => 'DB::admins',
|
|
id_field => 'id',
|
|
store_user_class => 'NGCP::Panel::Authentication::Store::RoleFromRealm',
|
|
},
|
|
use_session => 0,
|
|
},
|
|
api_subscriber_http => {
|
|
credential => {
|
|
class => 'HTTP',
|
|
#type => 'digest',
|
|
type => 'basic',
|
|
username_field => 'webusername',
|
|
password_field => 'webpassword',
|
|
password_type => 'clear',
|
|
},
|
|
store => {
|
|
class => 'DBIx::Class',
|
|
user_model => 'DB::provisioning_voip_subscribers',
|
|
store_user_class => 'NGCP::Panel::Authentication::Store::RoleFromRealm',
|
|
# use_userdata_from_session => 1,
|
|
},
|
|
use_session => 0,
|
|
},
|
|
api_subscriber_jwt => {
|
|
credential => {
|
|
class => '+NGCP::Panel::Authentication::Credential::JWT',
|
|
username_jwt => 'username',
|
|
username_field => 'webusername',
|
|
id_jwt => 'subscriber_uuid',
|
|
id_field => 'uuid',
|
|
jwt_key => _get_jwt_key(),
|
|
debug => 1,
|
|
alg => 'HS256',
|
|
},
|
|
store => {
|
|
class => 'DBIx::Class',
|
|
user_model => 'DB::provisioning_voip_subscribers',
|
|
store_user_class => 'NGCP::Panel::Authentication::Store::RoleFromRealm',
|
|
},
|
|
use_session => 0,
|
|
},
|
|
api_admin_jwt => {
|
|
credential => {
|
|
class => '+NGCP::Panel::Authentication::Credential::JWT',
|
|
username_jwt => 'username',
|
|
username_field => 'login',
|
|
id_jwt => 'id',
|
|
id_field => 'id',
|
|
jwt_key => _get_jwt_key(),
|
|
debug => 1,
|
|
alg => 'HS256',
|
|
},
|
|
store => {
|
|
class => 'DBIx::Class',
|
|
user_model => 'DB::admins',
|
|
store_user_class => 'NGCP::Panel::Authentication::Store::RoleFromRealm',
|
|
},
|
|
},
|
|
api_admin_system => {
|
|
credential => {
|
|
class => 'HTTP',
|
|
type => 'basic',
|
|
username_field => 'login',
|
|
password_field => 'password',
|
|
password_type => 'clear',
|
|
},
|
|
store => {
|
|
class => '+NGCP::Panel::Authentication::Store::System',
|
|
file => '/etc/default/ngcp-api',
|
|
group => 'auth_system',
|
|
},
|
|
use_session => 0,
|
|
},
|
|
subscriber => {
|
|
credential => {
|
|
class => 'Password',
|
|
password_field => 'webpassword',
|
|
password_type => 'clear',
|
|
},
|
|
store => {
|
|
class => 'DBIx::Class',
|
|
user_model => 'DB::provisioning_voip_subscribers',
|
|
id_field => 'id',
|
|
store_user_class => 'NGCP::Panel::Authentication::Store::RoleFromRealm',
|
|
use_userdata_from_session => 1,
|
|
}
|
|
}
|
|
},
|
|
ngcp_version => get_ngcp_version(),
|
|
);
|
|
__PACKAGE__->config( default_view => 'HTML' );
|
|
|
|
__PACKAGE__->log(Log::Log4perl::Catalyst->new($logger_config));
|
|
|
|
# configure Data::HAL depending on our config
|
|
{
|
|
my $panel_config = get_panel_config;
|
|
if ($panel_config->{appearance}{api_embedded_forcearray} || $panel_config->{appearance}{api_links_forcearray}) {
|
|
require Data::HAL;
|
|
*{Data::HAL::forcearray_policy} = sub {
|
|
my ($self, $root, $property_type, $relation, $property) = @_;
|
|
my $embedded = $root->embedded ? $root->embedded->[0] : undef;
|
|
if ($embedded
|
|
&& ( ( $property_type eq 'links' && $panel_config->{appearance}{api_links_forcearray} )
|
|
|| ( $property_type eq 'embedded' && $panel_config->{appearance}{api_embedded_forcearray}) )
|
|
&& $relation =~/^ngcp:[a-z0-9]+$/
|
|
) {
|
|
return 1;
|
|
}
|
|
if (!$embedded
|
|
&& ( ( $property_type eq 'links' && $panel_config->{appearance}{api_links_forcearray} ) )
|
|
&& $relation =~/^ngcp:[a-z0-9]+$/
|
|
) {
|
|
return 1;
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
after setup_finalize => sub {
|
|
|
|
my $app = shift;
|
|
|
|
if ($ENV{NOTIFY_SOCKET}) {
|
|
my $addr = $ENV{NOTIFY_SOCKET} =~ s/^@/\0/r;
|
|
my $client = IO::Socket::UNIX->new(
|
|
Type => SOCK_DGRAM(),
|
|
Peer => $addr,
|
|
) or warn("can't connect to socket $ENV{NOTIFY_SOCKET}: $!\n");
|
|
if ($client) {
|
|
$client->autoflush(1);
|
|
print $client "READY=1\n" or warn("can't send to socket $ENV{NOTIFY_SOCKET}: $!\n");
|
|
close $client;
|
|
}
|
|
} else {
|
|
warn("NOTIFY_SOCKET not set\n");
|
|
}
|
|
|
|
};
|
|
|
|
# Start the application
|
|
__PACKAGE__->setup();
|
|
|
|
sub get_ngcp_version {
|
|
my $content = File::Slurp::read_file("/etc/ngcp_version", err_mode => 'quiet');
|
|
$content //= '(unavailable)';
|
|
chomp($content);
|
|
return $content;
|
|
}
|
|
|
|
sub _get_jwt_key {
|
|
my $content = File::Slurp::read_file("/etc/ngcp-panel/jwt_secret", err_mode => 'quiet');
|
|
$content //= '';
|
|
$content =~ s/\n//; # remove newline before
|
|
chomp($content);
|
|
return $content;
|
|
}
|
|
|
|
1;
|
|
|
|
# vim: set tabstop=4 expandtab:
|