diff --git a/Build.PL b/Build.PL index 632ab6ab16..40dd7de4e0 100644 --- a/Build.PL +++ b/Build.PL @@ -81,6 +81,7 @@ my $builder = Local::Module::Build->new( 'Scalar::Util' => 0, 'Sereal::Decoder' => 0, 'Sereal::Encoder' => 0, + 'String::MkPasswd' => 0, 'Sipwise::Base' => 0, 'strict' => 0, 'Template' => 0, diff --git a/debian/control b/debian/control index 78a6334133..0bd00e8952 100644 --- a/debian/control +++ b/debian/control @@ -55,6 +55,7 @@ Build-Depends: debhelper (>= 8), libsereal-encoder-perl, libsipwise-base-perl, libstrictures-perl, + libstring-mkpasswd-perl, libsys-sig-perl, libtext-csv-xs-perl, libtrycatch-perl, @@ -129,6 +130,7 @@ Depends: gettext, libregexp-parser-perl, libsereal-decoder-perl, libsereal-encoder-perl, + libstring-mkpasswd-perl, libsys-sig-perl, libtemplate-perl, libtext-csv-xs-perl, diff --git a/lib/NGCP/Panel/Controller/Login.pm b/lib/NGCP/Panel/Controller/Login.pm index f2c7286345..2668813fe0 100644 --- a/lib/NGCP/Panel/Controller/Login.pm +++ b/lib/NGCP/Panel/Controller/Login.pm @@ -35,6 +35,7 @@ sub index :Path Form { $form->process( posted => $posted, params => $c->request->params, + item => { username => $c->stash->{username} }, ); if($posted && $form->validated) { diff --git a/lib/NGCP/Panel/Controller/Subscriber.pm b/lib/NGCP/Panel/Controller/Subscriber.pm index 774dd4625d..5351243cd0 100644 --- a/lib/NGCP/Panel/Controller/Subscriber.pm +++ b/lib/NGCP/Panel/Controller/Subscriber.pm @@ -153,150 +153,34 @@ sub create_list :Chained('sub_list') :PathPart('create') :Args(0) :Does(ACL) :AC my $schema = $c->model('DB'); try { $schema->txn_do(sub { - my ($uuid_bin, $uuid_string); - UUID::generate($uuid_bin); - UUID::unparse($uuid_bin, $uuid_string); - - my $contract = $schema->resultset('contracts') - ->find($form->params->{contract}{id}); - my $billing_domain = $schema->resultset('domains') - ->find($form->params->{domain}{id}); - my $prov_domain = $schema->resultset('voip_domains') - ->find({domain => $billing_domain->domain}); - - my $reseller = $contract->contact->reseller; - - my ($profile_set, $profile); - if($form->values->{profile_set}{id}) { - my $profile_set_rs = $c->model('DB')->resultset('voip_subscriber_profile_sets'); - if($c->user->roles eq "admin") { - } elsif($c->user->roles eq "reseller") { - $profile_set_rs = $profile_set_rs->search({ - reseller_id => $c->user->reseller_id, - }); - } - - $profile_set = $profile_set_rs->find($form->values->{profile_set}{id}); - unless($profile_set) { - NGCP::Panel::Utils::Message->error( - c => $c, - error => 'invalid subscriber profile set id ' . $form->values->{profile_set}{id}, - desc => $c->loc('Invalid subscriber profile set id'), - ); - return; - } - delete $form->values->{profile_set}; - } - if($form->values->{profile}{id}) { - $profile = $profile_set->voip_subscriber_profiles->find({ - id => $form->values->{profile}{id}, - }); - delete $form->values->{profile}; - } - if($profile_set && !$profile) { - $profile = $profile_set->voip_subscriber_profiles->find({ - set_default => 1, - }); - } - - my $billing_subscriber = $contract->voip_subscribers->create({ - uuid => $uuid_string, - username => $c->request->params->{username}, - domain_id => $billing_domain->id, - status => $c->request->params->{status}, - external_id => $form->field('external_id')->value, # null if empty - }); - - my $prov_subscriber = $schema->resultset('provisioning_voip_subscribers')->create({ - uuid => $uuid_string, - username => $c->request->params->{username}, - password => $c->request->params->{password}, - webusername => $c->request->params->{webusername} || $c->request->params->{username}, - webpassword => $c->request->params->{webpassword}, - admin => $c->request->params->{administrative} || 0, - account_id => $contract->id, - domain_id => $prov_domain->id, - profile_set_id => $profile_set ? $profile_set->id : undef, - profile_id => $profile ? $profile->id : undef, - create_timestamp => NGCP::Panel::Utils::DateTime::current_local, - }); - - NGCP::Panel::Utils::Subscriber::update_subscriber_numbers( - schema => $schema, - primary_number => $form->values->{e164}, - reseller_id => $reseller->id, - subscriber_id => $billing_subscriber->id, - ); - - my $cli = 0; - my $voip_preferences = $schema->resultset('voip_preferences')->search({ - 'usr_pref' => 1, + my $preferences = {}; + my $contract_rs = NGCP::Panel::Utils::Contract::get_contracts_rs_sippbx( c => $c ); + $contract_rs = $contract_rs->search({ + 'me.id' => $form->params->{contract}{id}, + }, { + '+select' => 'billing_mappings.id', + '+as' => 'bmid', }); + my $contract = $contract_rs->first; + my $billing_mapping = $contract->billing_mappings->find($contract->get_column('bmid')); - my $rs = NGCP::Panel::Utils::Contract::get_contract_rs( - schema => $c->model('DB')); - my $billing_contract = $rs->find($contract->id); - my $billing_mapping = $c->model('DB')->resultset('billing_mappings')->find( - $billing_contract->get_column('billing_mapping_id')); - - if($billing_mapping->billing_profile->prepaid) { - $voip_preferences->find({ 'attribute' => 'prepaid' }) - ->voip_usr_preferences->create({ - 'subscriber_id' => $prov_subscriber->id, - 'value' => 1, - }); - } - if(defined $billing_subscriber->external_id) { - $voip_preferences->find({ 'attribute' => 'ext_subscriber_id' }) - ->voip_usr_preferences->create({ - 'subscriber_id' => $prov_subscriber->id, - 'value' => $billing_subscriber->external_id, - }); - } - $voip_preferences->find({ 'attribute' => 'account_id' }) - ->voip_usr_preferences->create({ - 'subscriber_id' => $prov_subscriber->id, - 'value' => $prov_subscriber->contract->id, - }); if($contract->external_id) { - $voip_preferences->find({ 'attribute' => 'ext_contract_id' }) - ->voip_usr_preferences->create({ - 'subscriber_id' => $prov_subscriber->id, - 'value' => $contract->external_id, - }); + $preferences->{ext_contract_id} = $c->stash->{contract}->external_id; } - $voip_preferences->find({ 'attribute' => 'ac' }) - ->voip_usr_preferences->create({ - 'subscriber_id' => $prov_subscriber->id, - 'value' => $c->request->params->{'e164.ac'}, - }) if (defined $c->request->params->{'e164.ac'} && - length($c->request->params->{'e164.ac'}) > 0); - if(defined $c->request->params->{'e164.cc'} && - length($c->request->params->{'e164.cc'}) > 0) { - - $voip_preferences->find({ 'attribute' => 'cc' }) - ->voip_usr_preferences->create({ - 'subscriber_id' => $prov_subscriber->id, - 'value' => $c->request->params->{'e164.cc'}, - }); - $cli = $c->request->params->{'e164.cc'} . - (defined $c->request->params->{'e164.ac'} && - length($c->request->params->{'e164.ac'}) > 0 ? - $c->request->params->{'e164.ac'} : '' - ) . - $c->request->params->{'e164.sn'}; - $voip_preferences->find({ 'attribute' => 'cli' }) - ->voip_usr_preferences->create({ - 'subscriber_id' => $prov_subscriber->id, - 'value' => $cli, - }); + if(defined $form->params->{external_id}) { + $preferences->{ext_subscriber_id} = $form->params->{external_id}; } - $schema->resultset('voicemail_users')->create({ - customer_id => $uuid_string, - mailbox => $cli, - password => sprintf("%04d", int(rand 10000)), - email => '', - }); + if($billing_mapping->billing_profile->prepaid) { + $preferences->{prepaid} = 1; + } + my $billing_subscriber = NGCP::Panel::Utils::Subscriber::create_subscriber( + c => $c, + schema => $schema, + contract => $contract, + params => $form->params, + admin_default => 0, + preferences => $preferences, + ); delete $c->session->{created_objects}->{reseller}; delete $c->session->{created_objects}->{contract}; @@ -563,7 +447,7 @@ sub reset_webpassword :Chained('base') :PathPart('resetwebpassword') :Args(0) { $subscriber->password_resets->delete; # clear any old entries of this subscriber $subscriber->password_resets->create({ uuid => $uuid_string, - timestamp => NGCP::Panel::Utils::DateTime::current_local->epoch, + timestamp => NGCP::Panel::Utils::DateTime::current_local->epoch + 86400, }); my $url = $c->uri_for_action('/subscriber/recover_webpassword')->as_string . '?uuid=' . $uuid_string; NGCP::Panel::Utils::Email::password_reset($c, $subscriber, $url); @@ -668,7 +552,7 @@ sub recover_webpassword :Chained('/') :PathPart('recoverwebpassword') :Args(0) { my $rs = $c->model('DB')->resultset('password_resets')->search({ uuid => $uuid_string, - timestamp => { '>=' => (NGCP::Panel::Utils::DateTime::current_local->epoch - 86400) }, + timestamp => { '>=' => NGCP::Panel::Utils::DateTime::current_local->epoch }, }); my $subscriber = $rs->first ? $rs->first->voip_subscriber : undef; @@ -700,7 +584,7 @@ sub recover_webpassword :Chained('/') :PathPart('recoverwebpassword') :Args(0) { $schema->txn_do(sub { my $rs = $c->model('DB')->resultset('password_resets')->search({ uuid => $uuid_string, - timestamp => { '>=' => (NGCP::Panel::Utils::DateTime::current_local->epoch - 86400) }, + timestamp => { '>=' => NGCP::Panel::Utils::DateTime::current_local->epoch }, }); $subscriber = $rs->first ? $rs->first->voip_subscriber : undef; @@ -720,6 +604,7 @@ sub recover_webpassword :Chained('/') :PathPart('recoverwebpassword') :Args(0) { $c->log->debug("+++++++++++++++++++++++ successfully recovered subscriber " . $subscriber->username . '@' . $subscriber->domain->domain); $c->flash(messages => [{type => 'success', text => $c->loc('Web password successfully recovered, please re-login.') }]); + $c->flash(username => $subscriber->username . '@' . $subscriber->domain->domain); $c->res->redirect($c->uri_for('/login/subscriber')); return; diff --git a/lib/NGCP/Panel/Form/Customer/PbxExtensionSubscriber.pm b/lib/NGCP/Panel/Form/Customer/PbxExtensionSubscriber.pm index 11f83cb31b..b7f30c12d6 100644 --- a/lib/NGCP/Panel/Form/Customer/PbxExtensionSubscriber.pm +++ b/lib/NGCP/Panel/Form/Customer/PbxExtensionSubscriber.pm @@ -31,8 +31,6 @@ sub field_list { my $c = $self->ctx; return unless $c; - print "+++++++++++++++++++++++++++++ PbxExtensionSubscribert field_list\n"; - my $group = $self->field('group'); $group->field('id')->ajax_src( $c->uri_for_action('/customer/pbx_group_ajax', [$c->stash->{customer_id}])->as_string @@ -46,6 +44,16 @@ sub field_list { ); } } + + if($c->config->{security}->{password_sip_autogenerate}) { + $self->field('password')->inactive(1); + $self->field('password')->required(0); + } + if($c->config->{security}->{password_web_autogenerate}) { + $self->field('webpassword')->inactive(1); + $self->field('webpassword')->required(0); + } + } diff --git a/lib/NGCP/Panel/Form/Customer/PbxExtensionSubscriberEditSubadmin.pm b/lib/NGCP/Panel/Form/Customer/PbxExtensionSubscriberEditSubadmin.pm index d0be4c93a0..9fcd7d20bc 100644 --- a/lib/NGCP/Panel/Form/Customer/PbxExtensionSubscriberEditSubadmin.pm +++ b/lib/NGCP/Panel/Form/Customer/PbxExtensionSubscriberEditSubadmin.pm @@ -37,16 +37,22 @@ sub update_fields { $c->uri_for_action('/customer/pbx_group_ajax', [$c->stash->{customer_id}])->as_string ); - print ">>>>>>>>>>>>>>>>>>>>>> PbxExtensionSubscriberEditSubadmin update_fields\n"; my $profile_set = $c->stash->{subscriber}->provisioning_voip_subscriber->voip_subscriber_profile_set; - print ">>>>>>>>>>>>>>>>>>>>>> got profile set?\n"; if($profile_set && $self->field('profile')) { - print ">>>>>>>>>>>>>>>>>>>>>> yes, got profile set\n"; $self->field('profile')->field('id')->ajax_src( $c->uri_for_action('/subscriberprofile/profile_ajax', [$profile_set->id])->as_string ); } + if($c->user->roles eq "subscriberadmin") { + if(!$c->config->{security}->{password_sip_expose_subadmin}) { + $self->field('password')->inactive(1); + } + if(!$c->config->{security}->{password_web_expose_subadmin}) { + $self->field('webpassword')->inactive(1); + } + } + $self->field('password')->required(0); # optional on edit } diff --git a/lib/NGCP/Panel/Form/Customer/PbxExtensionSubscriberEditSubadminNoGroup.pm b/lib/NGCP/Panel/Form/Customer/PbxExtensionSubscriberEditSubadminNoGroup.pm index 99b363bd07..4525ec1452 100644 --- a/lib/NGCP/Panel/Form/Customer/PbxExtensionSubscriberEditSubadminNoGroup.pm +++ b/lib/NGCP/Panel/Form/Customer/PbxExtensionSubscriberEditSubadminNoGroup.pm @@ -19,7 +19,7 @@ has_field 'alias_select' => ( has_block 'fields' => ( tag => 'div', class => [qw/modal-body/], - render_list => [qw/email webusername webpassword password external_id alias_select profile/ ], + render_list => [qw/email webusername webpassword password alias_select profile/ ], ); sub update_fields { @@ -39,6 +39,15 @@ sub update_fields { ); } + if($c->user->roles eq "subscriberadmin") { + if(!$c->config->{security}->{password_sip_expose_subadmin}) { + $self->field('password')->inactive(1); + } + if(!$c->config->{security}->{password_web_expose_subadmin}) { + $self->field('webpassword')->inactive(1); + } + } + $self->field('password')->required(0); # optional on edit } diff --git a/lib/NGCP/Panel/Form/Customer/PbxExtensionSubscriberSubadmin.pm b/lib/NGCP/Panel/Form/Customer/PbxExtensionSubscriberSubadmin.pm index 12c9ff9f8a..047861a3ac 100644 --- a/lib/NGCP/Panel/Form/Customer/PbxExtensionSubscriberSubadmin.pm +++ b/lib/NGCP/Panel/Form/Customer/PbxExtensionSubscriberSubadmin.pm @@ -42,6 +42,15 @@ sub field_list { $c->uri_for_action('/subscriberprofile/profile_ajax', [$profile_set->id])->as_string ); } + + if($c->config->{security}->{password_sip_autogenerate}) { + $self->field('password')->inactive(1); + $self->field('password')->required(0); + } + if($c->config->{security}->{password_web_autogenerate}) { + $self->field('webpassword')->inactive(1); + $self->field('webpassword')->required(0); + } } diff --git a/lib/NGCP/Panel/Form/Customer/PbxSubscriber.pm b/lib/NGCP/Panel/Form/Customer/PbxSubscriber.pm index 9630e7e490..2a2b6a5558 100644 --- a/lib/NGCP/Panel/Form/Customer/PbxSubscriber.pm +++ b/lib/NGCP/Panel/Form/Customer/PbxSubscriber.pm @@ -153,8 +153,6 @@ sub field_list { my $c = $self->ctx; return unless($c); - print "++++++++++++++++++++++++++++++++++++++++ PbxSubscriber field_list\n"; - my $profile_set = $self->field('profile_set'); if($profile_set) { $profile_set->field('id')->ajax_src( @@ -162,6 +160,14 @@ sub field_list { ); } + if($c->config->{security}->{password_sip_autogenerate}) { + $self->field('password')->inactive(1); + $self->field('password')->required(0); + } + if($c->config->{security}->{password_web_autogenerate}) { + $self->field('webpassword')->inactive(1); + $self->field('webpassword')->required(0); + } } sub validate_password { diff --git a/lib/NGCP/Panel/Form/Customer/Subscriber.pm b/lib/NGCP/Panel/Form/Customer/Subscriber.pm index 77c797d9e2..60cccb0e47 100644 --- a/lib/NGCP/Panel/Form/Customer/Subscriber.pm +++ b/lib/NGCP/Panel/Form/Customer/Subscriber.pm @@ -150,6 +150,15 @@ sub update_fields { $c->uri_for_action('/subscriberprofile/set_ajax_reseller', [$c->stash->{contract}->contact->reseller_id])->as_string ); } + + if($c->config->{security}->{password_sip_autogenerate}) { + $self->field('password')->inactive(1); + $self->field('password')->required(0); + } + if($c->config->{security}->{password_web_autogenerate}) { + $self->field('webpassword')->inactive(1); + $self->field('webpassword')->required(0); + } } sub validate_password { diff --git a/lib/NGCP/Panel/Form/EmailTemplate/Reseller.pm b/lib/NGCP/Panel/Form/EmailTemplate/Reseller.pm index 6b2a2f9747..7009ef69da 100644 --- a/lib/NGCP/Panel/Form/EmailTemplate/Reseller.pm +++ b/lib/NGCP/Panel/Form/EmailTemplate/Reseller.pm @@ -42,9 +42,9 @@ has_field 'body' => ( default => 'Dear Customer, -A new subscriber [% subscriber %] has been created. +A new subscriber [% subscriber %] has been created for you. -Go to [% url %] to log into your self-care interface. +Please go to [% url %] to set your password and log into your self-care interface. Your faithful Sipwise system diff --git a/lib/NGCP/Panel/Form/Subscriber.pm b/lib/NGCP/Panel/Form/Subscriber.pm index cfcc8b1c09..4579ceab9d 100644 --- a/lib/NGCP/Panel/Form/Subscriber.pm +++ b/lib/NGCP/Panel/Form/Subscriber.pm @@ -180,21 +180,30 @@ sub validate_webpassword { NGCP::Panel::Utils::Form::validate_password(c => $c, field => $field); } -=pod -# we don't have a contract here, so we can't filter on it yet -# (would only be possible via javascript, no framework for that yet) sub update_fields { my $self = shift; my $c = $self->ctx; return unless $c; + if($c->config->{security}->{password_sip_autogenerate}) { + $self->field('password')->inactive(1); + $self->field('password')->required(0); + } + if($c->config->{security}->{password_web_autogenerate}) { + $self->field('webpassword')->inactive(1); + $self->field('webpassword')->required(0); + } + +=pod +# we don't have a contract here, so we can't filter on it yet +# (would only be possible via javascript, no framework for that yet) if($self->field('profile_set')) { $self->field('profile_set')->field('id')->ajax_src( $c->uri_for_action('/subscriberprofile/set_ajax_reseller', [$c->stash->{contract}->contact->reseller_id])->as_string ); } -} =cut +} 1; diff --git a/lib/NGCP/Panel/Utils/Subscriber.pm b/lib/NGCP/Panel/Utils/Subscriber.pm index b2126ec639..c74157a673 100644 --- a/lib/NGCP/Panel/Utils/Subscriber.pm +++ b/lib/NGCP/Panel/Utils/Subscriber.pm @@ -4,6 +4,7 @@ use warnings; use Sipwise::Base; use DBIx::Class::Exception; +use String::MkPasswd; use NGCP::Panel::Utils::DateTime; use NGCP::Panel::Utils::Preferences; use NGCP::Panel::Utils::Email; @@ -158,6 +159,22 @@ sub create_subscriber { }); } } + + my $passlen = $c->config->{security}->{password_min_length} || 8; + if($c->config->{security}->{password_sip_autogenerate}) { + $params->{password} = String::MkPasswd::mkpasswd( + -length => $passlen, + -minnum => 1, -minlower => 1, -minupper => 1, -minspecial => 1, + -distribute => 1, -fatal => 1, + ); + } + if($c->config->{security}->{password_web_autogenerate}) { + $params->{webpassword} = String::MkPasswd::mkpasswd( + -length => $passlen, + -minnum => 1, -minlower => 1, -minupper => 1, -minspecial => 1, + -distribute => 1, -fatal => 1, + ); + } $schema->txn_do(sub { my ($uuid_bin, $uuid_string); @@ -252,7 +269,16 @@ sub create_subscriber { } if($contract->subscriber_email_template_id) { - NGCP::Panel::Utils::Email::new_subscriber($c, $billing_subscriber); + my ($uuid_bin, $uuid_string); + UUID::generate($uuid_bin); + UUID::unparse($uuid_bin, $uuid_string); + $billing_subscriber->password_resets->create({ + uuid => $uuid_string, + # for new subs, let the link be valid for a year + timestamp => NGCP::Panel::Utils::DateTime::current_local->epoch + 31536000, + }); + my $url = $c->uri_for_action('/subscriber/recover_webpassword')->as_string . '?uuid=' . $uuid_string; + NGCP::Panel::Utils::Email::new_subscriber($c, $billing_subscriber, $url); } return $billing_subscriber; diff --git a/ngcp_panel.conf b/ngcp_panel.conf index 25433a2122..72cc6e096c 100644 --- a/ngcp_panel.conf +++ b/ngcp_panel.conf @@ -77,6 +77,10 @@ log4perl.appender.Default.layout.ConversionPattern=%d{ISO8601} [%p] [%F +%L] %m{ password_musthave_digit 0 password_musthave_specialchar 0 password_allow_recovery 0 + password_sip_autogenerate 1 + password_web_autogenerate 1 + password_sip_expose_subadmin 0 + password_web_expose_subadmin 0 diff --git a/share/templates/subscriber/master.tt b/share/templates/subscriber/master.tt index 2003d4c025..c62e7e395d 100644 --- a/share/templates/subscriber/master.tt +++ b/share/templates/subscriber/master.tt @@ -117,6 +117,7 @@ [% subscriber.provisioning_voip_subscriber.pbx_extension %] [% END -%] + [% UNLESS subscriber.provisioning_voip_subscriber.is_pbx_group -%] [% c.loc('Subscriber Profile Set') %] @@ -129,6 +130,7 @@ [% subscriber.provisioning_voip_subscriber.voip_subscriber_profile.name %] + [% END -%] [% IF subscriber.provisioning_voip_subscriber.is_pbx_group -%] [% c.loc('Hunt Policy') %]