From 2bf4fe1e92e64330c7ac83c836bd5c290ee94744 Mon Sep 17 00:00:00 2001 From: Andreas Granig Date: Tue, 29 Apr 2014 11:31:02 +0200 Subject: [PATCH] MT#6831 Implement global password policy. --- lib/NGCP/Panel/Controller/Administrator.pm | 8 ++--- lib/NGCP/Panel/Controller/Subscriber.pm | 8 ++--- lib/NGCP/Panel/Form/Administrator/Reseller.pm | 11 +++++- lib/NGCP/Panel/Form/Customer/PbxSubscriber.pm | 17 +++++++++ lib/NGCP/Panel/Form/Customer/Subscriber.pm | 17 +++++++++ lib/NGCP/Panel/Form/Faxserver/Password.pm | 9 +++++ lib/NGCP/Panel/Form/Subscriber.pm | 17 +++++++++ lib/NGCP/Panel/Form/Subscriber/EditWebpass.pm | 9 +++++ .../Panel/Form/Subscriber/ResetPassword.pm | 9 +++++ lib/NGCP/Panel/Form/SubscriberEdit.pm | 17 +++++++++ lib/NGCP/Panel/Utils/Form.pm | 35 +++++++++++++++++++ ngcp_panel.conf | 6 ++++ 12 files changed, 154 insertions(+), 9 deletions(-) create mode 100644 lib/NGCP/Panel/Utils/Form.pm diff --git a/lib/NGCP/Panel/Controller/Administrator.pm b/lib/NGCP/Panel/Controller/Administrator.pm index 9464079268..946de1c78d 100644 --- a/lib/NGCP/Panel/Controller/Administrator.pm +++ b/lib/NGCP/Panel/Controller/Administrator.pm @@ -81,9 +81,9 @@ sub create :Chained('list_admin') :PathPart('create') :Args(0) { my $params = {}; $params = $params->merge($c->session->{created_objects}); if($c->user->is_superuser) { - $form = NGCP::Panel::Form::Administrator::Admin->new; + $form = NGCP::Panel::Form::Administrator::Admin->new(ctx => $c); } else { - $form = NGCP::Panel::Form::Administrator::Reseller->new; + $form = NGCP::Panel::Form::Administrator::Reseller->new(ctx => $c); } $form->process( posted => ($c->request->method eq 'POST'), @@ -150,9 +150,9 @@ sub edit :Chained('base') :PathPart('edit') :Args(0) { $params->{reseller}{id} = delete $params->{reseller_id}; $params = $params->merge($c->session->{created_objects}); if($c->user->is_superuser) { - $form = NGCP::Panel::Form::Administrator::Admin->new; + $form = NGCP::Panel::Form::Administrator::Admin->new(ctx => $c); } else { - $form = NGCP::Panel::Form::Administrator::Reseller->new; + $form = NGCP::Panel::Form::Administrator::Reseller->new(ctx => $c); } $form->field('md5pass')->{required} = 0; diff --git a/lib/NGCP/Panel/Controller/Subscriber.pm b/lib/NGCP/Panel/Controller/Subscriber.pm index dca7d7a68c..10aa4daca0 100644 --- a/lib/NGCP/Panel/Controller/Subscriber.pm +++ b/lib/NGCP/Panel/Controller/Subscriber.pm @@ -133,7 +133,7 @@ sub create_list :Chained('sub_list') :PathPart('create') :Args(0) :Does(ACL) :AC my $posted = ($c->request->method eq 'POST'); my $params = {}; $params = $params->merge($c->session->{created_objects}); - my $form = NGCP::Panel::Form::Subscriber->new; + my $form = NGCP::Panel::Form::Subscriber->new(ctx => $c); $form->process( posted => $posted, params => $c->request->params, @@ -678,7 +678,7 @@ sub recover_webpassword :Chained('/') :PathPart('recoverwebpassword') :Args(0) { } } - my $form = NGCP::Panel::Form::Subscriber::ResetPassword->new; + my $form = NGCP::Panel::Form::Subscriber::ResetPassword->new(ctx => $c); my $params = { uuid => $uuid_string, }; @@ -2455,7 +2455,7 @@ sub webpass_edit :Chained('base') :PathPart('webpass/edit') :Args(0) { if(($c->user->roles eq "admin" || $c->user->roles eq "reseller") && $c->user->read_only); - my $form = NGCP::Panel::Form::Subscriber::EditWebpass->new; + my $form = NGCP::Panel::Form::Subscriber::EditWebpass->new(ctx => $c); my $posted = ($c->request->method eq 'POST'); @@ -2631,7 +2631,7 @@ sub edit_fax :Chained('base') :PathPart('preferences/fax/edit') :Args(1) { } } when('password') { - $form = NGCP::Panel::Form::Faxserver::Password->new; + $form = NGCP::Panel::Form::Faxserver::Password->new(ctx => $c); $params = { 'password' => $faxpref->password }; $form->process(params => $posted ? $c->req->params : $params); NGCP::Panel::Utils::Navigation::check_form_buttons( diff --git a/lib/NGCP/Panel/Form/Administrator/Reseller.pm b/lib/NGCP/Panel/Form/Administrator/Reseller.pm index 05a2451fd5..addfefe8b5 100644 --- a/lib/NGCP/Panel/Form/Administrator/Reseller.pm +++ b/lib/NGCP/Panel/Form/Administrator/Reseller.pm @@ -2,6 +2,7 @@ package NGCP::Panel::Form::Administrator::Reseller; use HTML::FormHandler::Moose; use HTML::FormHandler::Widget::Block::Bootstrap; use Moose::Util::TypeConstraints; +use NGCP::Panel::Utils::Form; extends 'HTML::FormHandler'; has '+widget_wrapper' => (default => 'Bootstrap'); @@ -10,7 +11,7 @@ sub build_render_list {[qw/submitid fields actions/]} sub build_form_element_class {[qw(form-horizontal)]} has_field 'login' => (type => 'Text', required => 1, minlength => 5); -has_field 'md5pass' => (type => 'Password', required => 1, label => 'Password', minlength => 6); +has_field 'md5pass' => (type => 'Password', required => 1, label => 'Password'); for (qw(is_active show_passwords call_data)) { has_field $_ => (type => 'Boolean', default => 1); } @@ -27,4 +28,12 @@ has_block 'fields' => ( ); has_block 'actions' => (tag => 'div', class => [qw(modal-footer)], render_list => [qw(save)],); +sub validate_md5pass { + my ($self, $field) = @_; + my $c = $self->form->ctx; + return unless $c; + + NGCP::Panel::Utils::Form::validate_password(c => $c, field => $field); +} + 1; diff --git a/lib/NGCP/Panel/Form/Customer/PbxSubscriber.pm b/lib/NGCP/Panel/Form/Customer/PbxSubscriber.pm index 6a6d14cd06..9630e7e490 100644 --- a/lib/NGCP/Panel/Form/Customer/PbxSubscriber.pm +++ b/lib/NGCP/Panel/Form/Customer/PbxSubscriber.pm @@ -7,6 +7,7 @@ use Moose::Util::TypeConstraints; use HTML::FormHandler::Widget::Block::Bootstrap; use NGCP::Panel::Field::PbxGroup; +use NGCP::Panel::Utils::Form; with 'NGCP::Panel::Render::RepeatableJs'; has '+widget_wrapper' => ( default => 'Bootstrap' ); @@ -163,6 +164,22 @@ sub field_list { } +sub validate_password { + my ($self, $field) = @_; + my $c = $self->form->ctx; + return unless $c; + + NGCP::Panel::Utils::Form::validate_password(c => $c, field => $field); +} + +sub validate_webpassword { + my ($self, $field) = @_; + my $c = $self->form->ctx; + return unless $c; + + NGCP::Panel::Utils::Form::validate_password(c => $c, field => $field); +} + 1; =head1 NAME diff --git a/lib/NGCP/Panel/Form/Customer/Subscriber.pm b/lib/NGCP/Panel/Form/Customer/Subscriber.pm index 913aff7ead..77c797d9e2 100644 --- a/lib/NGCP/Panel/Form/Customer/Subscriber.pm +++ b/lib/NGCP/Panel/Form/Customer/Subscriber.pm @@ -9,6 +9,7 @@ use HTML::FormHandler::Widget::Block::Bootstrap; use NGCP::Panel::Field::Domain; use NGCP::Panel::Field::PosInteger; use NGCP::Panel::Field::Identifier; +use NGCP::Panel::Utils::Form; has '+widget_wrapper' => ( default => 'Bootstrap' ); has_field 'submitid' => ( type => 'Hidden' ); @@ -151,6 +152,22 @@ sub update_fields { } } +sub validate_password { + my ($self, $field) = @_; + my $c = $self->form->ctx; + return unless $c; + + NGCP::Panel::Utils::Form::validate_password(c => $c, field => $field); +} + +sub validate_webpassword { + my ($self, $field) = @_; + my $c = $self->form->ctx; + return unless $c; + + NGCP::Panel::Utils::Form::validate_password(c => $c, field => $field); +} + 1; =head1 NAME diff --git a/lib/NGCP/Panel/Form/Faxserver/Password.pm b/lib/NGCP/Panel/Form/Faxserver/Password.pm index 5a19c13e23..447def6a73 100644 --- a/lib/NGCP/Panel/Form/Faxserver/Password.pm +++ b/lib/NGCP/Panel/Form/Faxserver/Password.pm @@ -3,6 +3,7 @@ package NGCP::Panel::Form::Faxserver::Password; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; use Moose::Util::TypeConstraints; +use NGCP::Panel::Utils::Form; use HTML::FormHandler::Widget::Block::Bootstrap; @@ -36,5 +37,13 @@ has_block 'actions' => ( render_list => [qw/save/], ); +sub validate_password { + my ($self, $field) = @_; + my $c = $self->form->ctx; + return unless $c; + + NGCP::Panel::Utils::Form::validate_password(c => $c, field => $field); +} + 1; # vim: set tabstop=4 expandtab: diff --git a/lib/NGCP/Panel/Form/Subscriber.pm b/lib/NGCP/Panel/Form/Subscriber.pm index 0ccdbe4887..cfcc8b1c09 100644 --- a/lib/NGCP/Panel/Form/Subscriber.pm +++ b/lib/NGCP/Panel/Form/Subscriber.pm @@ -10,6 +10,7 @@ use NGCP::Panel::Field::Domain; use NGCP::Panel::Field::CustomerContract; use NGCP::Panel::Field::PosInteger; use NGCP::Panel::Field::Identifier; +use NGCP::Panel::Utils::Form; has '+widget_wrapper' => ( default => 'Bootstrap' ); has_field 'submitid' => ( type => 'Hidden' ); @@ -163,6 +164,22 @@ has_block 'actions' => ( render_list => [qw/save/], ); +sub validate_password { + my ($self, $field) = @_; + my $c = $self->form->ctx; + return unless $c; + + NGCP::Panel::Utils::Form::validate_password(c => $c, field => $field); +} + +sub validate_webpassword { + my ($self, $field) = @_; + my $c = $self->form->ctx; + return unless $c; + + 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) diff --git a/lib/NGCP/Panel/Form/Subscriber/EditWebpass.pm b/lib/NGCP/Panel/Form/Subscriber/EditWebpass.pm index b44ebe9ce4..37d750d2f7 100644 --- a/lib/NGCP/Panel/Form/Subscriber/EditWebpass.pm +++ b/lib/NGCP/Panel/Form/Subscriber/EditWebpass.pm @@ -3,6 +3,7 @@ package NGCP::Panel::Form::Subscriber::EditWebpass; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; use Moose::Util::TypeConstraints; +use NGCP::Panel::Utils::Form; use HTML::FormHandler::Widget::Block::Bootstrap; @@ -42,6 +43,14 @@ has_block 'actions' => ( render_list => [qw/save/], ); +sub validate_webpassword { + my ($self, $field) = @_; + my $c = $self->form->ctx; + return unless $c; + + NGCP::Panel::Utils::Form::validate_password(c => $c, field => $field); +} + 1; # vim: set tabstop=4 expandtab: diff --git a/lib/NGCP/Panel/Form/Subscriber/ResetPassword.pm b/lib/NGCP/Panel/Form/Subscriber/ResetPassword.pm index 35f6d0d1b0..9c9ca94dc7 100644 --- a/lib/NGCP/Panel/Form/Subscriber/ResetPassword.pm +++ b/lib/NGCP/Panel/Form/Subscriber/ResetPassword.pm @@ -6,6 +6,7 @@ use Moose::Util::TypeConstraints; use HTML::FormHandler::Widget::Block::Bootstrap; use NGCP::Panel::Field::PosInteger; +use NGCP::Panel::Utils::Form; has '+widget_wrapper' => ( default => 'Bootstrap' ); has_field 'submitid' => ( type => 'Hidden' ); @@ -44,5 +45,13 @@ has_block 'actions' => ( render_list => [qw/save/], ); +sub validate_password { + my ($self, $field) = @_; + my $c = $self->form->ctx; + return unless $c; + + NGCP::Panel::Utils::Form::validate_password(c => $c, field => $field); +} + 1; # vim: set tabstop=4 expandtab: diff --git a/lib/NGCP/Panel/Form/SubscriberEdit.pm b/lib/NGCP/Panel/Form/SubscriberEdit.pm index e006ddef1d..96af1bb6df 100644 --- a/lib/NGCP/Panel/Form/SubscriberEdit.pm +++ b/lib/NGCP/Panel/Form/SubscriberEdit.pm @@ -10,6 +10,7 @@ use NGCP::Panel::Field::Domain; use NGCP::Panel::Field::CustomerContract; use NGCP::Panel::Field::Reseller; use NGCP::Panel::Field::PosInteger; +use NGCP::Panel::Utils::Form; with 'NGCP::Panel::Render::RepeatableJs'; @@ -181,6 +182,22 @@ sub field_list { } } +sub validate_password { + my ($self, $field) = @_; + my $c = $self->form->ctx; + return unless $c; + + NGCP::Panel::Utils::Form::validate_password(c => $c, field => $field); +} + +sub validate_webpassword { + my ($self, $field) = @_; + my $c = $self->form->ctx; + return unless $c; + + NGCP::Panel::Utils::Form::validate_password(c => $c, field => $field); +} + 1; # vim: set tabstop=4 expandtab: diff --git a/lib/NGCP/Panel/Utils/Form.pm b/lib/NGCP/Panel/Utils/Form.pm new file mode 100644 index 0000000000..1405da1503 --- /dev/null +++ b/lib/NGCP/Panel/Utils/Form.pm @@ -0,0 +1,35 @@ +package NGCP::Panel::Utils::Form; + +use Sipwise::Base; + +sub validate_password { + my %params = @_; + my $c = $params{c}; + my $field = $params{field}; + my $r = $c->config->{security}; + my $pass = $field->value; + + if($r->{password_min_length} && length($pass) < $r->{password_min_length}) { + $field->add_error($c->loc('Must be at minimum ' . $r->{password_min_length} . ' characters long')); + } + if($r->{password_max_length} && length($pass) > $r->{password_max_length}) { + $field->add_error($c->loc('Must be at maximum ' . $r->{password_max_length} . ' characters long')); + } + if($r->{password_musthave_lowercase} && $pass !~ /[a-z]/) { + $field->add_error($c->loc('Must contain lower-case characters')); + } + if($r->{password_musthave_uppercase} && $pass !~ /[A-Z]/) { + $field->add_error($c->loc('Must contain upper-case characters')); + } + if($r->{password_musthave_digit} && $pass !~ /[0-9]/) { + $field->add_error($c->loc('Must contain digits')); + } + if($r->{password_musthave_specialchar} && $pass !~ /[^0-9a-zA-Z]/) { + $field->add_error($c->loc('Must contain special characters')); + } +} + + +1; + +# vim: set tabstop=4 expandtab: diff --git a/ngcp_panel.conf b/ngcp_panel.conf index 16d4ca1d30..c6bdcb1f74 100644 --- a/ngcp_panel.conf +++ b/ngcp_panel.conf @@ -70,6 +70,12 @@ log4perl.appender.Default.layout.ConversionPattern=%d{ISO8601} [%p] [%F +%L] %m{ failed_auth_attempts 3 + password_min_length 6 + password_max_length 40 + password_musthave_lowercase 1 + password_musthave_uppercase 0 + password_musthave_digit 0 + password_musthave_specialchar 0