diff --git a/Netdisco/Changes b/Netdisco/Changes index 191a9079..234929b5 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -1,3 +1,20 @@ +2.012000 - 2013-08-06 + + [ENHANCEMENTS] + + * Use icons for port status and archive presentation + * Always offer to discover device on site homepage + * Device re-discover/arpnip/macsuck is async with toastr notify + * Implement extensible authN and role based access control + * RW snmp community strings now in the "community" table + + [BUG FIXES] + + * Floating table headers not compatible with in-header drop-down menus + * Fix blur events on editable cells + * Pseudo device ports being counted properly since Port Utilization report + * Manual topology is preserved when re-discovering neighbors + 2.011000 - 2013-07-29 [NEW FEATURES] diff --git a/Netdisco/MANIFEST b/Netdisco/MANIFEST index c43326a2..2658dc65 100644 --- a/Netdisco/MANIFEST +++ b/Netdisco/MANIFEST @@ -40,6 +40,7 @@ lib/App/Netdisco/Daemon/Worker/Scheduler.pm lib/App/Netdisco/DB.pm lib/App/Netdisco/DB/ExplicitLocking.pm lib/App/Netdisco/DB/Result/Admin.pm +lib/App/Netdisco/DB/Result/Community.pm lib/App/Netdisco/DB/Result/Device.pm lib/App/Netdisco/DB/Result/DeviceIp.pm lib/App/Netdisco/DB/Result/DeviceModule.pm @@ -73,6 +74,7 @@ lib/App/Netdisco/DB/Result/Virtual/DevicePortVlanTagged.pm lib/App/Netdisco/DB/Result/Virtual/DuplexMismatch.pm lib/App/Netdisco/DB/Result/Virtual/NodeWithAge.pm lib/App/Netdisco/DB/Result/Virtual/PortUtilization.pm +lib/App/Netdisco/DB/Result/Virtual/UserRole.pm lib/App/Netdisco/DB/ResultSet/Admin.pm lib/App/Netdisco/DB/ResultSet/Device.pm lib/App/Netdisco/DB/ResultSet/DevicePort.pm @@ -100,6 +102,8 @@ lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-20-21-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-21-22-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-22-23-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-23-24-PostgreSQL.sql +lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-24-25-PostgreSQL.sql +lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-25-26-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-3-4-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-4-5-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-5-6-PostgreSQL.sql @@ -123,6 +127,7 @@ lib/App/Netdisco/Util/SNMP.pm lib/App/Netdisco/Util/Web.pm lib/App/Netdisco/Web.pm lib/App/Netdisco/Web/AdminTask.pm +lib/App/Netdisco/Web/Auth/Provider/DBIC.pm lib/App/Netdisco/Web/AuthN.pm lib/App/Netdisco/Web/Device.pm lib/App/Netdisco/Web/Plugin.pm diff --git a/Netdisco/META.yml b/Netdisco/META.yml index b0a38186..ad15512c 100644 --- a/Netdisco/META.yml +++ b/Netdisco/META.yml @@ -60,4 +60,4 @@ resources: homepage: http://netdisco.org/ license: http://opensource.org/licenses/bsd-license.php repository: git://git.code.sf.net/p/netdisco/netdisco-ng -version: 2.011000 +version: 2.012000 diff --git a/Netdisco/lib/App/Netdisco.pm b/Netdisco/lib/App/Netdisco.pm index 44ed3d75..2573cac8 100644 --- a/Netdisco/lib/App/Netdisco.pm +++ b/Netdisco/lib/App/Netdisco.pm @@ -7,7 +7,7 @@ use 5.010_000; use File::ShareDir 'dist_dir'; use Path::Class; -our $VERSION = '2.011000'; +our $VERSION = '2.012000'; BEGIN { if (not ($ENV{DANCER_APPDIR} || '') @@ -58,6 +58,7 @@ if (ref {} eq ref setting('database')) { options => { AutoCommit => 1, RaiseError => 1, + auto_savepoint => 1, }, schema_class => 'App::Netdisco::DB', }; diff --git a/Netdisco/lib/App/Netdisco/Core/Discover.pm b/Netdisco/lib/App/Netdisco/Core/Discover.pm index cd17a897..17b5b728 100644 --- a/Netdisco/lib/App/Netdisco/Core/Discover.pm +++ b/Netdisco/lib/App/Netdisco/Core/Discover.pm @@ -754,7 +754,7 @@ sub _set_manual_topology { return unless ($left->in_storage and $right->in_storage); $left->ports - ->single({port => $link->port1}, {for => 'update'}) + ->single({port => $link->port1}) ->update({ remote_ip => $right->ip, remote_port => $link->port2, @@ -765,7 +765,7 @@ sub _set_manual_topology { }); $right->ports - ->single({port => $link->port2}, {for => 'update'}) + ->single({port => $link->port2}) ->update({ remote_ip => $left->ip, remote_port => $link->port1, diff --git a/Netdisco/lib/App/Netdisco/DB.pm b/Netdisco/lib/App/Netdisco/DB.pm index 7663003e..67f1ecb5 100644 --- a/Netdisco/lib/App/Netdisco/DB.pm +++ b/Netdisco/lib/App/Netdisco/DB.pm @@ -8,7 +8,7 @@ use base 'DBIx::Class::Schema'; __PACKAGE__->load_namespaces; -our $VERSION = 24; # schema version used for upgrades, keep as integer +our $VERSION = 26; # schema version used for upgrades, keep as integer use Path::Class; use File::Basename; diff --git a/Netdisco/lib/App/Netdisco/DB/Result/Community.pm b/Netdisco/lib/App/Netdisco/DB/Result/Community.pm new file mode 100644 index 00000000..1a390756 --- /dev/null +++ b/Netdisco/lib/App/Netdisco/DB/Result/Community.pm @@ -0,0 +1,17 @@ +use utf8; +package App::Netdisco::DB::Result::Community; + +use strict; +use warnings; + +use base 'DBIx::Class::Core'; +__PACKAGE__->table("community"); +__PACKAGE__->add_columns( + "ip", + { data_type => "inet", is_nullable => 0 }, + "snmp_comm_rw", + { data_type => "text", is_nullable => 1 }, +); +__PACKAGE__->set_primary_key("ip"); + +1; diff --git a/Netdisco/lib/App/Netdisco/DB/Result/Device.pm b/Netdisco/lib/App/Netdisco/DB/Result/Device.pm index dc9b4cd1..3035b28e 100644 --- a/Netdisco/lib/App/Netdisco/DB/Result/Device.pm +++ b/Netdisco/lib/App/Netdisco/DB/Result/Device.pm @@ -180,6 +180,15 @@ __PACKAGE__->has_many( 'ip', { join_type => 'RIGHT' } ); +=head2 community + +Returns the row from the community string table, if one exists. + +=cut + +__PACKAGE__->might_have( + community => 'App::Netdisco::DB::Result::Community', 'ip'); + =head1 ADDITIONAL COLUMNS =head2 port_count diff --git a/Netdisco/lib/App/Netdisco/DB/Result/User.pm b/Netdisco/lib/App/Netdisco/DB/Result/User.pm index 8aec3792..ba70ff9a 100644 --- a/Netdisco/lib/App/Netdisco/DB/Result/User.pm +++ b/Netdisco/lib/App/Netdisco/DB/Result/User.pm @@ -40,6 +40,7 @@ __PACKAGE__->set_primary_key("username"); # Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02 # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:2awpSJkzXP7+8eyT4vGjfw +__PACKAGE__->has_many( roles => 'App::Netdisco::DB::Result::Virtual::UserRole', 'username' ); # You can replace this text with custom code or comments, and it will be preserved on regeneration 1; diff --git a/Netdisco/lib/App/Netdisco/DB/Result/Virtual/UserRole.pm b/Netdisco/lib/App/Netdisco/DB/Result/Virtual/UserRole.pm new file mode 100644 index 00000000..0a2694a1 --- /dev/null +++ b/Netdisco/lib/App/Netdisco/DB/Result/Virtual/UserRole.pm @@ -0,0 +1,27 @@ +use utf8; +package App::Netdisco::DB::Result::Virtual::UserRole; + +use strict; +use warnings; + +use base 'DBIx::Class::Core'; + +__PACKAGE__->table_class('DBIx::Class::ResultSource::View'); + +__PACKAGE__->table("user_role"); +__PACKAGE__->result_source_instance->is_virtual(1); +__PACKAGE__->result_source_instance->view_definition(<add_columns( + 'username' => { data_type => 'text' }, + 'role' => { data_type => 'text' }, +); + +1; diff --git a/Netdisco/lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-24-25-PostgreSQL.sql b/Netdisco/lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-24-25-PostgreSQL.sql new file mode 100644 index 00000000..ad21f8a5 --- /dev/null +++ b/Netdisco/lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-24-25-PostgreSQL.sql @@ -0,0 +1,5 @@ +BEGIN; + +ALTER TABLE device DROP COLUMN snmp_comm_rw; + +COMMIT; diff --git a/Netdisco/lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-25-26-PostgreSQL.sql b/Netdisco/lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-25-26-PostgreSQL.sql new file mode 100644 index 00000000..de4f2818 --- /dev/null +++ b/Netdisco/lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-25-26-PostgreSQL.sql @@ -0,0 +1,9 @@ +BEGIN; + +CREATE TABLE "community" ( + "ip" inet NOT NULL, + "snmp_comm_rw" text, + PRIMARY KEY ("ip") +); + +COMMIT; diff --git a/Netdisco/lib/App/Netdisco/Daemon/Worker/Scheduler.pm b/Netdisco/lib/App/Netdisco/Daemon/Worker/Scheduler.pm index b827ed83..cce5f282 100644 --- a/Netdisco/lib/App/Netdisco/Daemon/Worker/Scheduler.pm +++ b/Netdisco/lib/App/Netdisco/Daemon/Worker/Scheduler.pm @@ -73,7 +73,7 @@ sub worker_body { # due to a table constraint, this will (intentionally) fail if a # similar job is already queued. try { - debug "sched ($wid): queueing $a job"; + info "sched ($wid): queueing $a job"; schema('netdisco')->resultset('Admin')->create({ action => $a, device => ($sched->{device} || undef), diff --git a/Netdisco/lib/App/Netdisco/Manual/Configuration.pod b/Netdisco/lib/App/Netdisco/Manual/Configuration.pod index b01ca311..a5061551 100644 --- a/Netdisco/lib/App/Netdisco/Manual/Configuration.pod +++ b/Netdisco/lib/App/Netdisco/Manual/Configuration.pod @@ -387,7 +387,12 @@ hour fields (which accept same types as C notation). For example: min: 30 macwalk: when: + min: 15 hour: '*/2' + wday: 'mon-fri' + +Note that the fields default to "all" (i.e. "C<*>") when not specified. See +L for further details. =head2 Dancer Internal diff --git a/Netdisco/lib/App/Netdisco/Util/SNMP.pm b/Netdisco/lib/App/Netdisco/Util/SNMP.pm index 3c7ddaf4..2e40efbc 100644 --- a/Netdisco/lib/App/Netdisco/Util/SNMP.pm +++ b/Netdisco/lib/App/Netdisco/Util/SNMP.pm @@ -90,8 +90,8 @@ sub _snmp_connect_generic { unshift @communities, $device->snmp_comm if defined $device->snmp_comm and defined $comm_type and $comm_type eq 'community'; - unshift @communities, $device->snmp_comm_rw - if defined $device->snmp_comm_rw + unshift @communities, $device->community->snmp_comm_rw + if eval { $device->community->snmp_comm_rw } and defined $comm_type and $comm_type eq 'community_rw'; my $info = undef; @@ -126,7 +126,7 @@ sub _try_write { debug sprintf '[%s] try_write with comm: %s', $device->ip, $comm; $info->clear_cache; my $rv = $info->set_location( $info->location ); - $device->update({snmp_comm_rw => $comm}) + $device->update_or_create_related('community', {snmp_comm_rw => $comm}) if $device->in_storage; $happy = 1 if $rv; } diff --git a/Netdisco/lib/App/Netdisco/Web.pm b/Netdisco/lib/App/Netdisco/Web.pm index 5e1a8d77..d9252a62 100644 --- a/Netdisco/lib/App/Netdisco/Web.pm +++ b/Netdisco/lib/App/Netdisco/Web.pm @@ -4,6 +4,7 @@ use Dancer ':syntax'; use Dancer::Plugin::Ajax; use Dancer::Plugin::DBIC; +use Dancer::Plugin::Auth::Extensible; use Socket6 (); # to ensure dependency is met use HTML::Entities (); # to ensure dependency is met @@ -56,6 +57,9 @@ hook 'before_template' => sub { # allow portable dynamic content $tokens->{uri_for} = sub { uri_for(@_)->path_query }; + # access to logged in user's roles + $tokens->{user_has_role} = sub { user_has_role(@_) }; + # allow very long lists of ports $Template::Directive::WHILE_MAX = 10_000; @@ -63,13 +67,7 @@ hook 'before_template' => sub { $Template::Stash::PRIVATE = undef; }; -get '/' => sub { - if (var('user') and var('user')->admin) { - if (schema('netdisco')->resultset('Device')->count == 0) { - var('nodevices' => true); - } - } - +get qr{^/(?:login(?:/denied)?)?} => sub { template 'index'; }; diff --git a/Netdisco/lib/App/Netdisco/Web/AdminTask.pm b/Netdisco/lib/App/Netdisco/Web/AdminTask.pm index 55f8924d..1e0dfa39 100644 --- a/Netdisco/lib/App/Netdisco/Web/AdminTask.pm +++ b/Netdisco/lib/App/Netdisco/Web/AdminTask.pm @@ -3,6 +3,7 @@ package App::Netdisco::Web::AdminTask; use Dancer ':syntax'; use Dancer::Plugin::Ajax; use Dancer::Plugin::DBIC; +use Dancer::Plugin::Auth::Extensible; use Try::Tiny; @@ -21,7 +22,7 @@ sub add_job { ($device ? (device => $device->addr) : ()), action => $jobtype, status => 'queued', - username => session('user'), + username => session('logged_in_user'), userip => request->remote_address, }); @@ -30,14 +31,14 @@ sub add_job { action => 'macwalk', subaction => 'after-discoverall', status => 'queued', - username => session('user'), + username => session('logged_in_user'), userip => request->remote_address, }); schema('netdisco')->resultset('Admin')->create({ action => 'arpwalk', subaction => 'after-discoverall', status => 'queued', - username => session('user'), + username => session('logged_in_user'), userip => request->remote_address, }); } @@ -58,18 +59,14 @@ my %jobs_all = map {$_ => 1} qw/ /; foreach my $jobtype (keys %jobs_all, keys %jobs) { - ajax "/ajax/control/admin/$jobtype" => sub { - send_error('Forbidden', 403) - unless var('user')->admin; + ajax "/ajax/control/admin/$jobtype" => require_role admin => sub { send_error('Missing device', 400) if exists $jobs{$jobtype} and not param('device'); add_job($jobtype, param('device')); }; - post "/admin/$jobtype" => sub { - send_error('Forbidden', 403) - unless var('user')->admin; + post "/admin/$jobtype" => require_role admin => sub { send_error('Missing device', 400) if exists $jobs{$jobtype} and not param('device'); @@ -78,13 +75,9 @@ foreach my $jobtype (keys %jobs_all, keys %jobs) { }; } -get '/admin/*' => sub { +get '/admin/*' => require_role admin => sub { my ($tag) = splat; - if (! eval { var('user')->admin }) { - return redirect uri_for('/')->as_string; - } - # trick the ajax into working as if this were a tabbed page params->{tab} = $tag; diff --git a/Netdisco/lib/App/Netdisco/Web/Auth/Provider/DBIC.pm b/Netdisco/lib/App/Netdisco/Web/Auth/Provider/DBIC.pm new file mode 100644 index 00000000..375ef58f --- /dev/null +++ b/Netdisco/lib/App/Netdisco/Web/Auth/Provider/DBIC.pm @@ -0,0 +1,72 @@ +package App::Netdisco::Web::Auth::Provider::DBIC; + +use strict; +use warnings FATAL => 'all'; + +use base 'Dancer::Plugin::Auth::Extensible::Provider::Base'; + +# with thanks to yanick's patch at +# https://github.com/bigpresh/Dancer-Plugin-Auth-Extensible/pull/24 + +use Dancer qw(:syntax); +use Dancer::Plugin::DBIC; + +use Digest::MD5; + +sub authenticate_user { + my ($self, $username, $password) = @_; + return unless defined $username; + + my $user = $self->get_user_details($username) or return; + return $self->match_password($password, $user); +} + +sub match_password { + my( $self, $password, $user ) = @_; + return unless $user and $password and $user->password; + + my $settings = $self->realm_settings; + my $password_column = $settings->{users_password_column} || 'password'; + + my $sum = Digest::MD5::md5_hex($password); + return ($sum eq $user->$password_column ? 1 : 0); +} + + +sub get_user_details { + my ($self, $username) = @_; + + my $settings = $self->realm_settings; + my $database = schema($settings->{schema_name}) + or die "No database connection"; + + my $users_table = $settings->{users_resultset} || 'User'; + my $username_column = $settings->{users_username_column} || 'username'; + + my $user = $database->resultset($users_table)->find({ + $username_column => $username + }) or debug("No such user $username"); + + return $user; +} + +sub get_user_roles { + my ($self, $username) = @_; + return unless defined $username; + + my $settings = $self->realm_settings; + my $database = schema($settings->{schema_name}) + or die "No database connection"; + + # Get details of the user first; both to check they exist, and so we have + # their ID to use. + my $user = $self->get_user_details($username) + or return; + + my $roles = $settings->{roles_relationship} || 'roles'; + my $role_column = $settings->{role_column} || 'role'; + + return [ $user->$roles->get_column( $role_column )->all ]; +} + +1; diff --git a/Netdisco/lib/App/Netdisco/Web/AuthN.pm b/Netdisco/lib/App/Netdisco/Web/AuthN.pm index bad88259..6805904e 100644 --- a/Netdisco/lib/App/Netdisco/Web/AuthN.pm +++ b/Netdisco/lib/App/Netdisco/Web/AuthN.pm @@ -2,56 +2,26 @@ package App::Netdisco::Web::AuthN; use Dancer ':syntax'; use Dancer::Plugin::DBIC; - -use Digest::MD5 (); +use Dancer::Plugin::Auth::Extensible; hook 'before' => sub { - if (! session('user') && request->path ne uri_for('/login')->path) { + params->{return_url} ||= '/inventory'; + + if (! session('logged_in_user') && request->path ne uri_for('/login')->path) { if (setting('trust_x_remote_user') and scalar request->header('X-REMOTE_USER')) { - session(user => scalar request->header('X-REMOTE_USER')); + session(logged_in_user => scalar request->header('X-REMOTE_USER')); } elsif (setting('trust_remote_user') and $ENV{REMOTE_USER}) { - session(user => $ENV{REMOTE_USER}); + session(logged_in_user => $ENV{REMOTE_USER}); } elsif (setting('no_auth')) { - session(user => 'guest'); + session(logged_in_user => 'guest'); } else { # user has no AuthN - force to handler for '/' request->path_info('/'); } } - - if (session('user') && session->id) { - var(user => schema('netdisco')->resultset('User') - ->find(session('user'))); - - # really just for dev work, to quieten the logs - var('user')->port_control(0) - if var('user') and setting('no_port_control'); - } -}; - -post '/login' => sub { - if (param('username') and param('password')) { - my $user = schema('netdisco')->resultset('User') - ->find(param('username')); - - if ($user) { - my $sum = Digest::MD5::md5_hex(param('password')); - if (($sum and $user->password) and ($sum eq $user->password)) { - session(user => $user->username); - return redirect uri_for('/inventory')->as_string; - } - } - } - - redirect uri_for('/', {failed => 1})->as_string; -}; - -get '/logout' => sub { - session->destroy; - redirect uri_for('/', {logout => 1})->as_string; }; true; diff --git a/Netdisco/lib/App/Netdisco/Web/Device.pm b/Netdisco/lib/App/Netdisco/Web/Device.pm index 1bae16c3..68240692 100644 --- a/Netdisco/lib/App/Netdisco/Web/Device.pm +++ b/Netdisco/lib/App/Netdisco/Web/Device.pm @@ -3,6 +3,7 @@ package App::Netdisco::Web::Device; use Dancer ':syntax'; use Dancer::Plugin::Ajax; use Dancer::Plugin::DBIC; +use Dancer::Plugin::Auth::Extensible; hook 'before' => sub { my @default_port_columns_left = ( @@ -106,7 +107,7 @@ hook 'before_template' => sub { $tokens->{self_options} = $self_uri->query_form_hash; }; -get '/device' => sub { +get '/device' => require_login sub { my $q = param('q'); my $dev = schema('netdisco')->resultset('Device')->single({ -or => [ diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/JobQueue.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/JobQueue.pm index b539a18e..88a011a7 100644 --- a/Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/JobQueue.pm +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/JobQueue.pm @@ -3,6 +3,7 @@ package App::Netdisco::Web::Plugin::AdminTask::JobQueue; use Dancer ':syntax'; use Dancer::Plugin::Ajax; use Dancer::Plugin::DBIC; +use Dancer::Plugin::Auth::Extensible; use App::Netdisco::Web::Plugin; @@ -11,8 +12,7 @@ register_admin_task({ label => 'Job Queue', }); -ajax '/ajax/control/admin/jobqueue/del' => sub { - send_error('Forbidden', 403) unless var('user')->admin; +ajax '/ajax/control/admin/jobqueue/del' => require_role admin => sub { send_error('Missing job', 400) unless param('job'); schema('netdisco')->txn_do(sub { @@ -21,17 +21,13 @@ ajax '/ajax/control/admin/jobqueue/del' => sub { }); }; -ajax '/ajax/control/admin/jobqueue/delall' => sub { - send_error('Forbidden', 403) unless var('user')->admin; - +ajax '/ajax/control/admin/jobqueue/delall' => require_role admin => sub { schema('netdisco')->txn_do(sub { my $device = schema('netdisco')->resultset('Admin')->delete; }); }; -ajax '/ajax/content/admin/jobqueue' => sub { - send_error('Forbidden', 403) unless var('user')->admin; - +ajax '/ajax/content/admin/jobqueue' => require_role admin => sub { my $set = schema('netdisco')->resultset('Admin') ->with_times ->search({}, { diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/PseudoDevice.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/PseudoDevice.pm index 00c9ab57..3a2dd0a5 100644 --- a/Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/PseudoDevice.pm +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/PseudoDevice.pm @@ -3,6 +3,7 @@ package App::Netdisco::Web::Plugin::AdminTask::PseudoDevice; use Dancer ':syntax'; use Dancer::Plugin::Ajax; use Dancer::Plugin::DBIC; +use Dancer::Plugin::Auth::Extensible; use App::Netdisco::Web::Plugin; use NetAddr::IP::Lite ':lower'; @@ -13,8 +14,6 @@ register_admin_task({ }); sub _sanity_ok { - return 0 unless var('user') and var('user')->admin; - return 0 unless param('dns') and param('dns') =~ m/^[[:print:]]+$/ and param('dns') !~ m/[[:space:]]/; @@ -28,7 +27,7 @@ sub _sanity_ok { return 1; } -ajax '/ajax/control/admin/pseudodevice/add' => sub { +ajax '/ajax/control/admin/pseudodevice/add' => require_role admin => sub { send_error('Bad Request', 400) unless _sanity_ok(); schema('netdisco')->txn_do(sub { @@ -42,13 +41,20 @@ ajax '/ajax/control/admin/pseudodevice/add' => sub { return unless $device; $device->ports->populate([ - ['port'], - map {["Port$_"]} @{[1 .. param('ports')]}, + [qw/port type/], + map {["Port$_", 'other']} @{[1 .. param('ports')]}, ]); + + # device_ip table is used to show whether topo is "broken" + schema('netdisco')->resultset('DeviceIp') + ->create({ + ip => param('ip'), + alias => param('ip'), + }); }); }; -ajax '/ajax/control/admin/pseudodevice/del' => sub { +ajax '/ajax/control/admin/pseudodevice/del' => require_role admin => sub { send_error('Bad Request', 400) unless _sanity_ok(); schema('netdisco')->txn_do(sub { @@ -60,7 +66,7 @@ ajax '/ajax/control/admin/pseudodevice/del' => sub { }); }; -ajax '/ajax/control/admin/pseudodevice/update' => sub { +ajax '/ajax/control/admin/pseudodevice/update' => require_role admin => sub { send_error('Bad Request', 400) unless _sanity_ok(); schema('netdisco')->txn_do(sub { @@ -72,8 +78,8 @@ ajax '/ajax/control/admin/pseudodevice/update' => sub { if (param('ports') > $count) { my $start = $count + 1; $device->ports->populate([ - ['port'], - map {["Port$_"]} @{[$start .. param('ports')]}, + [qw/port type/], + map {["Port$_", 'other']} @{[$start .. param('ports')]}, ]); } elsif (param('ports') < $count) { @@ -85,9 +91,7 @@ ajax '/ajax/control/admin/pseudodevice/update' => sub { }); }; -ajax '/ajax/content/admin/pseudodevice' => sub { - send_error('Forbidden', 403) unless var('user')->admin; - +ajax '/ajax/content/admin/pseudodevice' => require_role admin => sub { my $set = schema('netdisco')->resultset('Device') ->search( {vendor => 'netdisco'}, diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/Topology.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/Topology.pm index 75533869..04fcf404 100644 --- a/Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/Topology.pm +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/Topology.pm @@ -3,6 +3,7 @@ package App::Netdisco::Web::Plugin::AdminTask::Topology; use Dancer ':syntax'; use Dancer::Plugin::Ajax; use Dancer::Plugin::DBIC; +use Dancer::Plugin::Auth::Extensible; use App::Netdisco::Web::Plugin; use App::Netdisco::Util::Device 'get_device'; @@ -16,8 +17,6 @@ register_admin_task({ }); sub _sanity_ok { - return 0 unless var('user') and var('user')->admin; - my $dev1 = NetAddr::IP::Lite->new(param('dev1')); return 0 unless ($dev1 and $dev1->addr ne '0.0.0.0'); @@ -30,7 +29,7 @@ sub _sanity_ok { return 1; } -ajax '/ajax/control/admin/topology/add' => sub { +ajax '/ajax/control/admin/topology/add' => require_role admin => sub { send_error('Bad Request', 400) unless _sanity_ok(); my $device = schema('netdisco')->resultset('Topology') @@ -79,7 +78,7 @@ ajax '/ajax/control/admin/topology/add' => sub { }; }; -ajax '/ajax/control/admin/topology/del' => sub { +ajax '/ajax/control/admin/topology/del' => require_role admin => sub { send_error('Bad Request', 400) unless _sanity_ok(); schema('netdisco')->txn_do(sub { @@ -93,9 +92,7 @@ ajax '/ajax/control/admin/topology/del' => sub { }); }; -ajax '/ajax/content/admin/topology' => sub { - send_error('Forbidden', 403) unless var('user')->admin; - +ajax '/ajax/content/admin/topology' => require_role admin => sub { my $set = schema('netdisco')->resultset('Topology') ->search({},{order_by => [qw/dev1 dev2 port1/]}); diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/Users.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/Users.pm index 39746033..af2c591b 100644 --- a/Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/Users.pm +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/Users.pm @@ -3,6 +3,7 @@ package App::Netdisco::Web::Plugin::AdminTask::Users; use Dancer ':syntax'; use Dancer::Plugin::Ajax; use Dancer::Plugin::DBIC; +use Dancer::Plugin::Auth::Extensible; use App::Netdisco::Web::Plugin; use Digest::MD5 (); @@ -13,8 +14,6 @@ register_admin_task({ }); sub _sanity_ok { - return 0 unless var('user') and var('user')->admin; - return 0 unless param('username') and param('username') =~ m/^[[:print:]]+$/ and param('username') !~ m/[[:space:]]/; @@ -22,7 +21,7 @@ sub _sanity_ok { return 1; } -ajax '/ajax/control/admin/users/add' => sub { +ajax '/ajax/control/admin/users/add' => require_role admin => sub { send_error('Bad Request', 400) unless _sanity_ok(); schema('netdisco')->txn_do(sub { @@ -37,7 +36,7 @@ ajax '/ajax/control/admin/users/add' => sub { }); }; -ajax '/ajax/control/admin/users/del' => sub { +ajax '/ajax/control/admin/users/del' => require_role admin => sub { send_error('Bad Request', 400) unless _sanity_ok(); schema('netdisco')->txn_do(sub { @@ -46,7 +45,7 @@ ajax '/ajax/control/admin/users/del' => sub { }); }; -ajax '/ajax/control/admin/users/update' => sub { +ajax '/ajax/control/admin/users/update' => require_role admin => sub { send_error('Bad Request', 400) unless _sanity_ok(); schema('netdisco')->txn_do(sub { @@ -65,9 +64,7 @@ ajax '/ajax/control/admin/users/update' => sub { }); }; -ajax '/ajax/content/admin/users' => sub { - send_error('Forbidden', 403) unless var('user')->admin; - +ajax '/ajax/content/admin/users' => require_role admin => sub { my $set = schema('netdisco')->resultset('User') ->search(undef, { order_by => [qw/fullname username/]}); diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Addresses.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Addresses.pm index 66013836..8fc175d2 100644 --- a/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Addresses.pm +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Addresses.pm @@ -3,13 +3,14 @@ package App::Netdisco::Web::Plugin::Device::Addresses; use Dancer ':syntax'; use Dancer::Plugin::Ajax; use Dancer::Plugin::DBIC; +use Dancer::Plugin::Auth::Extensible; use App::Netdisco::Web::Plugin; register_device_tab({ tag => 'addresses', label => 'Addresses' }); # device interface addresses -ajax '/ajax/content/device/addresses' => sub { +ajax '/ajax/content/device/addresses' => require_login sub { my $q = param('q'); my $device = schema('netdisco')->resultset('Device') diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Details.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Details.pm index e40113fb..7288fdfc 100644 --- a/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Details.pm +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Details.pm @@ -3,13 +3,14 @@ package App::Netdisco::Web::Plugin::Device::Details; use Dancer ':syntax'; use Dancer::Plugin::Ajax; use Dancer::Plugin::DBIC; +use Dancer::Plugin::Auth::Extensible; use App::Netdisco::Web::Plugin; register_device_tab({ tag => 'details', label => 'Details' }); # device details table -ajax '/ajax/content/device/details' => sub { +ajax '/ajax/content/device/details' => require_login sub { my $q = param('q'); my $device = schema('netdisco')->resultset('Device') ->with_times()->search_for_device($q) or send_error('Bad device', 400); diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Modules.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Modules.pm index 47341216..79f07046 100644 --- a/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Modules.pm +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Modules.pm @@ -2,12 +2,13 @@ package App::Netdisco::Web::Plugin::Device::Modules; use Dancer ':syntax'; use Dancer::Plugin::Ajax; +use Dancer::Plugin::Auth::Extensible; use App::Netdisco::Web::Plugin; register_device_tab({ tag => 'modules', label => 'Modules' }); -ajax '/ajax/content/device/:thing' => sub { +ajax '/ajax/content/device/:thing' => require_login sub { return "

Hello, this is where the ". param('thing') ." content goes.

"; }; diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Neighbors.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Neighbors.pm index e43fb1da..466b391c 100644 --- a/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Neighbors.pm +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Neighbors.pm @@ -3,12 +3,13 @@ package App::Netdisco::Web::Plugin::Device::Neighbors; use Dancer ':syntax'; use Dancer::Plugin::Ajax; use Dancer::Plugin::DBIC; +use Dancer::Plugin::Auth::Extensible; use App::Netdisco::Web::Plugin; register_device_tab({ tag => 'netmap', label => 'Neighbors' }); -ajax '/ajax/content/device/netmap' => sub { +ajax '/ajax/content/device/netmap' => require_login sub { content_type('text/html'); template 'ajax/device/netmap.tt', {}, { layout => undef }; }; @@ -39,7 +40,7 @@ sub _add_children { } # d3 seems not to use proper ajax semantics, so get instead of ajax -get '/ajax/data/device/netmap' => sub { +get '/ajax/data/device/netmap' => require_login sub { my $q = param('q'); my $device = schema('netdisco')->resultset('Device') @@ -75,7 +76,7 @@ get '/ajax/data/device/netmap' => sub { to_json(\%tree); }; -ajax '/ajax/data/device/alldevicelinks' => sub { +ajax '/ajax/data/device/alldevicelinks' => require_login sub { my @devices = schema('netdisco')->resultset('Device')->search({}, { result_class => 'DBIx::Class::ResultClass::HashRefInflator', columns => ['ip', 'dns'], diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Ports.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Ports.pm index fea0e5fd..d683eb59 100644 --- a/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Ports.pm +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Ports.pm @@ -3,6 +3,7 @@ package App::Netdisco::Web::Plugin::Device::Ports; use Dancer ':syntax'; use Dancer::Plugin::Ajax; use Dancer::Plugin::DBIC; +use Dancer::Plugin::Auth::Extensible; use App::Netdisco::Util::Web (); # for sort_port use App::Netdisco::Web::Plugin; @@ -10,7 +11,7 @@ use App::Netdisco::Web::Plugin; register_device_tab({ tag => 'ports', label => 'Ports' }); # device ports with a description (er, name) matching -ajax '/ajax/content/device/ports' => sub { +ajax '/ajax/content/device/ports' => require_login sub { my $q = param('q'); my $device = schema('netdisco')->resultset('Device') diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/Inventory.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/Inventory.pm index bafe2074..4dc93679 100644 --- a/Netdisco/lib/App/Netdisco/Web/Plugin/Inventory.pm +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/Inventory.pm @@ -2,6 +2,7 @@ package App::Netdisco::Web::Plugin::Inventory; use Dancer ':syntax'; use Dancer::Plugin::DBIC; +use Dancer::Plugin::Auth::Extensible; use App::Netdisco::Web::Plugin; @@ -11,7 +12,7 @@ register_navbar_item({ label => 'Inventory', }); -get '/inventory' => sub { +get '/inventory' => require_login sub { my $models = schema('netdisco')->resultset('Device')->get_models(); my $releases = schema('netdisco')->resultset('Device')->get_releases(); diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/Report/DuplexMismatch.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/Report/DuplexMismatch.pm index a87bebf8..7cb026f4 100644 --- a/Netdisco/lib/App/Netdisco/Web/Plugin/Report/DuplexMismatch.pm +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/Report/DuplexMismatch.pm @@ -3,6 +3,7 @@ package App::Netdisco::Web::Plugin::Report::DuplexMismatch; use Dancer ':syntax'; use Dancer::Plugin::Ajax; use Dancer::Plugin::DBIC; +use Dancer::Plugin::Auth::Extensible; use App::Netdisco::Web::Plugin; @@ -12,7 +13,7 @@ register_report({ label => 'Duplex Mismatches Between Devices', }); -ajax '/ajax/content/report/duplexmismatch' => sub { +ajax '/ajax/content/report/duplexmismatch' => require_login sub { my $set = schema('netdisco')->resultset('Virtual::DuplexMismatch'); return unless $set->count; diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/Report/PortUtilization.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/Report/PortUtilization.pm index b7a70431..defa9beb 100644 --- a/Netdisco/lib/App/Netdisco/Web/Plugin/Report/PortUtilization.pm +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/Report/PortUtilization.pm @@ -3,6 +3,7 @@ package App::Netdisco::Web::Plugin::Report::PortUtilization; use Dancer ':syntax'; use Dancer::Plugin::Ajax; use Dancer::Plugin::DBIC; +use Dancer::Plugin::Auth::Extensible; use App::Netdisco::Web::Plugin; @@ -12,7 +13,7 @@ register_report({ label => 'Port Utilization', }); -ajax '/ajax/content/report/portutilization' => sub { +ajax '/ajax/content/report/portutilization' => require_login sub { return unless schema('netdisco')->resultset('Device')->count; my $set = schema('netdisco')->resultset('Virtual::PortUtilization'); diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/Search/Device.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/Search/Device.pm index 58c933a1..9c9e498c 100644 --- a/Netdisco/lib/App/Netdisco/Web/Plugin/Search/Device.pm +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/Search/Device.pm @@ -3,6 +3,7 @@ package App::Netdisco::Web::Plugin::Search::Device; use Dancer ':syntax'; use Dancer::Plugin::Ajax; use Dancer::Plugin::DBIC; +use Dancer::Plugin::Auth::Extensible; use List::MoreUtils (); @@ -11,7 +12,7 @@ use App::Netdisco::Web::Plugin; register_search_tab({ tag => 'device', label => 'Device' }); # device with various properties or a default match-all -ajax '/ajax/content/search/device' => sub { +ajax '/ajax/content/search/device' => require_login sub { my $has_opt = List::MoreUtils::any {param($_)} qw/name location dns ip description model os_ver vendor/; my $set; diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/Search/Node.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/Search/Node.pm index 126f9172..4d130871 100644 --- a/Netdisco/lib/App/Netdisco/Web/Plugin/Search/Node.pm +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/Search/Node.pm @@ -3,6 +3,7 @@ package App::Netdisco::Web::Plugin::Search::Node; use Dancer ':syntax'; use Dancer::Plugin::Ajax; use Dancer::Plugin::DBIC; +use Dancer::Plugin::Auth::Extensible; use NetAddr::IP::Lite ':lower'; use Net::MAC (); @@ -12,7 +13,7 @@ use App::Netdisco::Web::Plugin; register_search_tab({ tag => 'node', label => 'Node' }); # nodes matching the param as an IP or DNS hostname or MAC -ajax '/ajax/content/search/node' => sub { +ajax '/ajax/content/search/node' => require_login sub { my $node = param('q'); send_error('Missing node', 400) unless $node; content_type('text/html'); diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/Search/Port.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/Search/Port.pm index 2415d359..41792154 100644 --- a/Netdisco/lib/App/Netdisco/Web/Plugin/Search/Port.pm +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/Search/Port.pm @@ -3,13 +3,14 @@ package App::Netdisco::Web::Plugin::Search::Port; use Dancer ':syntax'; use Dancer::Plugin::Ajax; use Dancer::Plugin::DBIC; +use Dancer::Plugin::Auth::Extensible; use App::Netdisco::Web::Plugin; register_search_tab({ tag => 'port', label => 'Port' }); # device ports with a description (er, name) matching -ajax '/ajax/content/search/port' => sub { +ajax '/ajax/content/search/port' => require_login sub { my $q = param('q'); send_error('Missing query', 400) unless $q; my $set; diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/Search/VLAN.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/Search/VLAN.pm index f204c4e5..4ecec8ce 100644 --- a/Netdisco/lib/App/Netdisco/Web/Plugin/Search/VLAN.pm +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/Search/VLAN.pm @@ -3,13 +3,14 @@ package App::Netdisco::Web::Plugin::Search::VLAN; use Dancer ':syntax'; use Dancer::Plugin::Ajax; use Dancer::Plugin::DBIC; +use Dancer::Plugin::Auth::Extensible; use App::Netdisco::Web::Plugin; register_search_tab({ tag => 'vlan', label => 'VLAN' }); # devices carrying vlan xxx -ajax '/ajax/content/search/vlan' => sub { +ajax '/ajax/content/search/vlan' => require_login sub { my $q = param('q'); send_error('Missing query', 400) unless $q; my $set; diff --git a/Netdisco/lib/App/Netdisco/Web/PortControl.pm b/Netdisco/lib/App/Netdisco/Web/PortControl.pm index 8ba697ed..3e3c0242 100644 --- a/Netdisco/lib/App/Netdisco/Web/PortControl.pm +++ b/Netdisco/lib/App/Netdisco/Web/PortControl.pm @@ -3,10 +3,9 @@ package App::Netdisco::Web::PortControl; use Dancer ':syntax'; use Dancer::Plugin::Ajax; use Dancer::Plugin::DBIC; +use Dancer::Plugin::Auth::Extensible; -ajax '/ajax/portcontrol' => sub { - send_error('Forbidden', 403) - unless var('user')->port_control; +ajax '/ajax/portcontrol' => require_role port_control => sub { send_error('No device/port/field', 400) unless param('device') and (param('port') or param('field')); @@ -37,7 +36,7 @@ ajax '/ajax/portcontrol' => sub { action => $action, subaction => $subaction, status => 'queued', - username => session('user'), + username => session('logged_in_user'), userip => request->remote_address, log => $log, }); @@ -46,13 +45,11 @@ ajax '/ajax/portcontrol' => sub { to_json({}); }; -ajax '/ajax/userlog' => sub { - my $user = session('user'); - send_error('No username', 400) unless $user; - +ajax '/ajax/userlog' => require_login sub { my $rs = schema('netdisco')->resultset('Admin')->search({ - username => $user, - action => [qw/location contact portcontrol portname vlan power/], + username => session('logged_in_user'), + action => [qw/location contact portcontrol portname vlan power + discover macsuck arpnip/], finished => { '>' => \"(now() - interval '5 seconds')" }, }); diff --git a/Netdisco/lib/App/Netdisco/Web/Report.pm b/Netdisco/lib/App/Netdisco/Web/Report.pm index e9ab7bb4..29ab91c1 100644 --- a/Netdisco/lib/App/Netdisco/Web/Report.pm +++ b/Netdisco/lib/App/Netdisco/Web/Report.pm @@ -1,8 +1,9 @@ package App::Netdisco::Web::Report; use Dancer ':syntax'; +use Dancer::Plugin::Auth::Extensible; -get '/report/*' => sub { +get '/report/*' => require_login sub { my ($tag) = splat; # trick the ajax into working as if this were a tabbed page diff --git a/Netdisco/lib/App/Netdisco/Web/Search.pm b/Netdisco/lib/App/Netdisco/Web/Search.pm index 4898a635..3f60cb3d 100644 --- a/Netdisco/lib/App/Netdisco/Web/Search.pm +++ b/Netdisco/lib/App/Netdisco/Web/Search.pm @@ -3,6 +3,7 @@ package App::Netdisco::Web::Search; use Dancer ':syntax'; use Dancer::Plugin::Ajax; use Dancer::Plugin::DBIC; +use Dancer::Plugin::Auth::Extensible; hook 'before' => sub { # view settings for node options @@ -59,7 +60,7 @@ hook 'before_template' => sub { } }; -get '/search' => sub { +get '/search' => require_login sub { my $q = param('q'); my $s = schema('netdisco'); diff --git a/Netdisco/lib/App/Netdisco/Web/TypeAhead.pm b/Netdisco/lib/App/Netdisco/Web/TypeAhead.pm index 18433e22..32caf950 100644 --- a/Netdisco/lib/App/Netdisco/Web/TypeAhead.pm +++ b/Netdisco/lib/App/Netdisco/Web/TypeAhead.pm @@ -3,10 +3,11 @@ package App::Netdisco::Web::TypeAhead; use Dancer ':syntax'; use Dancer::Plugin::Ajax; use Dancer::Plugin::DBIC; +use Dancer::Plugin::Auth::Extensible; use App::Netdisco::Util::Web (); # for sort_port -ajax '/ajax/data/devicename/typeahead' => sub { +ajax '/ajax/data/devicename/typeahead' => require_login sub { my $q = param('query') || param('term'); my $set = schema('netdisco')->resultset('Device')->search_fuzzy($q); @@ -14,7 +15,7 @@ ajax '/ajax/data/devicename/typeahead' => sub { to_json [map {$_->dns || $_->name || $_->ip} $set->all]; }; -ajax '/ajax/data/deviceip/typeahead' => sub { +ajax '/ajax/data/deviceip/typeahead' => require_login sub { my $q = param('query') || param('term'); my $set = schema('netdisco')->resultset('Device')->search_fuzzy($q); @@ -32,7 +33,7 @@ ajax '/ajax/data/deviceip/typeahead' => sub { to_json \@data; }; -ajax '/ajax/data/port/typeahead' => sub { +ajax '/ajax/data/port/typeahead' => require_login sub { my $dev = param('dev1') || param('dev2'); my $port = param('port1') || param('port2'); send_error('Missing device', 400) unless $dev; diff --git a/Netdisco/share/config.yml b/Netdisco/share/config.yml index 97770a9a..b18071d5 100644 --- a/Netdisco/share/config.yml +++ b/Netdisco/share/config.yml @@ -40,6 +40,15 @@ web_plugins: - Device::Addresses web_plugins_extra: [] +# authN and authZ controls from the database +plugins: + Auth::Extensible: + no_default_pages: true + realms: + users: + provider: 'App::Netdisco::Web::Auth::Provider::DBIC' + schema_name: 'netdisco' + # ------------- # NETDISCO CORE # ------------- diff --git a/Netdisco/share/public/css/font-awesome.min.css b/Netdisco/share/public/css/font-awesome.min.css index d4e45b3c..866437fa 100644 --- a/Netdisco/share/public/css/font-awesome.min.css +++ b/Netdisco/share/public/css/font-awesome.min.css @@ -1,33 +1,403 @@ -/*! - * Font Awesome 3.0.2 - * the iconic font designed for use with Twitter Bootstrap - * ------------------------------------------------------- - * The full suite of pictographic icons, examples, and documentation - * can be found at: http://fortawesome.github.com/Font-Awesome/ - * - * License - * ------------------------------------------------------- - * - The Font Awesome font is licensed under the SIL Open Font License - http://scripts.sil.org/OFL - * - Font Awesome CSS, LESS, and SASS files are licensed under the MIT License - - * http://opensource.org/licenses/mit-license.html - * - The Font Awesome pictograms are licensed under the CC BY 3.0 License - http://creativecommons.org/licenses/by/3.0/ - * - Attribution is no longer required in Font Awesome 3.0, but much appreciated: - * "Font Awesome by Dave Gandy - http://fortawesome.github.com/Font-Awesome" - - * Contact - * ------------------------------------------------------- - * Email: dave@davegandy.com - * Twitter: http://twitter.com/fortaweso_me - * Work: Lead Product Designer @ http://kyruus.com - */ - -@font-face{ - font-family:'FontAwesome'; - src:url('../font/fontawesome-webfont.eot?v=3.0.1'); - src:url('../font/fontawesome-webfont.eot?#iefix&v=3.0.1') format('embedded-opentype'), - url('../font/fontawesome-webfont.woff?v=3.0.1') format('woff'), - url('../font/fontawesome-webfont.ttf?v=3.0.1') format('truetype'); - font-weight:normal; - font-style:normal } - -[class^="icon-"],[class*=" icon-"]{font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;display:inline;width:auto;height:auto;line-height:normal;vertical-align:baseline;background-image:none;background-position:0 0;background-repeat:repeat;margin-top:0}.icon-white,.nav-pills>.active>a>[class^="icon-"],.nav-pills>.active>a>[class*=" icon-"],.nav-list>.active>a>[class^="icon-"],.nav-list>.active>a>[class*=" icon-"],.navbar-inverse .nav>.active>a>[class^="icon-"],.navbar-inverse .nav>.active>a>[class*=" icon-"],.dropdown-menu>li>a:hover>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"],.dropdown-submenu:hover>a>[class^="icon-"],.dropdown-submenu:hover>a>[class*=" icon-"]{background-image:none}[class^="icon-"]:before,[class*=" icon-"]:before{text-decoration:inherit;display:inline-block;speak:none}a [class^="icon-"],a [class*=" icon-"]{display:inline-block}.icon-large:before{vertical-align:-10%;font-size:1.3333333333333333em}.btn [class^="icon-"],.nav [class^="icon-"],.btn [class*=" icon-"],.nav [class*=" icon-"]{display:inline}.btn [class^="icon-"].icon-large,.nav [class^="icon-"].icon-large,.btn [class*=" icon-"].icon-large,.nav [class*=" icon-"].icon-large{line-height:.9em}.btn [class^="icon-"].icon-spin,.nav [class^="icon-"].icon-spin,.btn [class*=" icon-"].icon-spin,.nav [class*=" icon-"].icon-spin{display:inline-block}.nav-tabs [class^="icon-"],.nav-pills [class^="icon-"],.nav-tabs [class*=" icon-"],.nav-pills [class*=" icon-"],.nav-tabs [class^="icon-"].icon-large,.nav-pills [class^="icon-"].icon-large,.nav-tabs [class*=" icon-"].icon-large,.nav-pills [class*=" icon-"].icon-large{line-height:.9em}li [class^="icon-"],.nav li [class^="icon-"],li [class*=" icon-"],.nav li [class*=" icon-"]{display:inline-block;width:1.25em;text-align:center}li [class^="icon-"].icon-large,.nav li [class^="icon-"].icon-large,li [class*=" icon-"].icon-large,.nav li [class*=" icon-"].icon-large{width:1.5625em}ul.icons{list-style-type:none;text-indent:-0.75em}ul.icons li [class^="icon-"],ul.icons li [class*=" icon-"]{width:.75em}.icon-muted{color:#eee}.icon-border{border:solid 1px #eee;padding:.2em .25em .15em;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.icon-2x{font-size:2em}.icon-2x.icon-border{border-width:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.icon-3x{font-size:3em}.icon-3x.icon-border{border-width:3px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.icon-4x{font-size:4em}.icon-4x.icon-border{border-width:4px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.pull-right{float:right}.pull-left{float:left}[class^="icon-"].pull-left,[class*=" icon-"].pull-left{margin-right:.3em}[class^="icon-"].pull-right,[class*=" icon-"].pull-right{margin-left:.3em}.btn [class^="icon-"].pull-left.icon-2x,.btn [class*=" icon-"].pull-left.icon-2x,.btn [class^="icon-"].pull-right.icon-2x,.btn [class*=" icon-"].pull-right.icon-2x{margin-top:.18em}.btn [class^="icon-"].icon-spin.icon-large,.btn [class*=" icon-"].icon-spin.icon-large{line-height:.8em}.btn.btn-small [class^="icon-"].pull-left.icon-2x,.btn.btn-small [class*=" icon-"].pull-left.icon-2x,.btn.btn-small [class^="icon-"].pull-right.icon-2x,.btn.btn-small [class*=" icon-"].pull-right.icon-2x{margin-top:.25em}.btn.btn-large [class^="icon-"],.btn.btn-large [class*=" icon-"]{margin-top:0}.btn.btn-large [class^="icon-"].pull-left.icon-2x,.btn.btn-large [class*=" icon-"].pull-left.icon-2x,.btn.btn-large [class^="icon-"].pull-right.icon-2x,.btn.btn-large [class*=" icon-"].pull-right.icon-2x{margin-top:.05em}.btn.btn-large [class^="icon-"].pull-left.icon-2x,.btn.btn-large [class*=" icon-"].pull-left.icon-2x{margin-right:.2em}.btn.btn-large [class^="icon-"].pull-right.icon-2x,.btn.btn-large [class*=" icon-"].pull-right.icon-2x{margin-left:.2em}.icon-spin{display:inline-block;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;-webkit-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)}100%{-o-transform:rotate(359deg)}}@-ms-keyframes spin{0%{-ms-transform:rotate(0deg)}100%{-ms-transform:rotate(359deg)}}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}@-moz-document url-prefix(){.icon-spin{height:.9em}.btn .icon-spin{height:auto}.icon-spin.icon-large{height:1.25em}.btn .icon-spin.icon-large{height:.75em}}.icon-glass:before{content:"\f000"}.icon-music:before{content:"\f001"}.icon-search:before{content:"\f002"}.icon-envelope:before{content:"\f003"}.icon-heart:before{content:"\f004"}.icon-star:before{content:"\f005"}.icon-star-empty:before{content:"\f006"}.icon-user:before{content:"\f007"}.icon-film:before{content:"\f008"}.icon-th-large:before{content:"\f009"}.icon-th:before{content:"\f00a"}.icon-th-list:before{content:"\f00b"}.icon-ok:before{content:"\f00c"}.icon-remove:before{content:"\f00d"}.icon-zoom-in:before{content:"\f00e"}.icon-zoom-out:before{content:"\f010"}.icon-off:before{content:"\f011"}.icon-signal:before{content:"\f012"}.icon-cog:before{content:"\f013"}.icon-trash:before{content:"\f014"}.icon-home:before{content:"\f015"}.icon-file:before{content:"\f016"}.icon-time:before{content:"\f017"}.icon-road:before{content:"\f018"}.icon-download-alt:before{content:"\f019"}.icon-download:before{content:"\f01a"}.icon-upload:before{content:"\f01b"}.icon-inbox:before{content:"\f01c"}.icon-play-circle:before{content:"\f01d"}.icon-repeat:before{content:"\f01e"}.icon-refresh:before{content:"\f021"}.icon-list-alt:before{content:"\f022"}.icon-lock:before{content:"\f023"}.icon-flag:before{content:"\f024"}.icon-headphones:before{content:"\f025"}.icon-volume-off:before{content:"\f026"}.icon-volume-down:before{content:"\f027"}.icon-volume-up:before{content:"\f028"}.icon-qrcode:before{content:"\f029"}.icon-barcode:before{content:"\f02a"}.icon-tag:before{content:"\f02b"}.icon-tags:before{content:"\f02c"}.icon-book:before{content:"\f02d"}.icon-bookmark:before{content:"\f02e"}.icon-print:before{content:"\f02f"}.icon-camera:before{content:"\f030"}.icon-font:before{content:"\f031"}.icon-bold:before{content:"\f032"}.icon-italic:before{content:"\f033"}.icon-text-height:before{content:"\f034"}.icon-text-width:before{content:"\f035"}.icon-align-left:before{content:"\f036"}.icon-align-center:before{content:"\f037"}.icon-align-right:before{content:"\f038"}.icon-align-justify:before{content:"\f039"}.icon-list:before{content:"\f03a"}.icon-indent-left:before{content:"\f03b"}.icon-indent-right:before{content:"\f03c"}.icon-facetime-video:before{content:"\f03d"}.icon-picture:before{content:"\f03e"}.icon-pencil:before{content:"\f040"}.icon-map-marker:before{content:"\f041"}.icon-adjust:before{content:"\f042"}.icon-tint:before{content:"\f043"}.icon-edit:before{content:"\f044"}.icon-share:before{content:"\f045"}.icon-check:before{content:"\f046"}.icon-move:before{content:"\f047"}.icon-step-backward:before{content:"\f048"}.icon-fast-backward:before{content:"\f049"}.icon-backward:before{content:"\f04a"}.icon-play:before{content:"\f04b"}.icon-pause:before{content:"\f04c"}.icon-stop:before{content:"\f04d"}.icon-forward:before{content:"\f04e"}.icon-fast-forward:before{content:"\f050"}.icon-step-forward:before{content:"\f051"}.icon-eject:before{content:"\f052"}.icon-chevron-left:before{content:"\f053"}.icon-chevron-right:before{content:"\f054"}.icon-plus-sign:before{content:"\f055"}.icon-minus-sign:before{content:"\f056"}.icon-remove-sign:before{content:"\f057"}.icon-ok-sign:before{content:"\f058"}.icon-question-sign:before{content:"\f059"}.icon-info-sign:before{content:"\f05a"}.icon-screenshot:before{content:"\f05b"}.icon-remove-circle:before{content:"\f05c"}.icon-ok-circle:before{content:"\f05d"}.icon-ban-circle:before{content:"\f05e"}.icon-arrow-left:before{content:"\f060"}.icon-arrow-right:before{content:"\f061"}.icon-arrow-up:before{content:"\f062"}.icon-arrow-down:before{content:"\f063"}.icon-share-alt:before{content:"\f064"}.icon-resize-full:before{content:"\f065"}.icon-resize-small:before{content:"\f066"}.icon-plus:before{content:"\f067"}.icon-minus:before{content:"\f068"}.icon-asterisk:before{content:"\f069"}.icon-exclamation-sign:before{content:"\f06a"}.icon-gift:before{content:"\f06b"}.icon-leaf:before{content:"\f06c"}.icon-fire:before{content:"\f06d"}.icon-eye-open:before{content:"\f06e"}.icon-eye-close:before{content:"\f070"}.icon-warning-sign:before{content:"\f071"}.icon-plane:before{content:"\f072"}.icon-calendar:before{content:"\f073"}.icon-random:before{content:"\f074"}.icon-comment:before{content:"\f075"}.icon-magnet:before{content:"\f076"}.icon-chevron-up:before{content:"\f077"}.icon-chevron-down:before{content:"\f078"}.icon-retweet:before{content:"\f079"}.icon-shopping-cart:before{content:"\f07a"}.icon-folder-close:before{content:"\f07b"}.icon-folder-open:before{content:"\f07c"}.icon-resize-vertical:before{content:"\f07d"}.icon-resize-horizontal:before{content:"\f07e"}.icon-bar-chart:before{content:"\f080"}.icon-twitter-sign:before{content:"\f081"}.icon-facebook-sign:before{content:"\f082"}.icon-camera-retro:before{content:"\f083"}.icon-key:before{content:"\f084"}.icon-cogs:before{content:"\f085"}.icon-comments:before{content:"\f086"}.icon-thumbs-up:before{content:"\f087"}.icon-thumbs-down:before{content:"\f088"}.icon-star-half:before{content:"\f089"}.icon-heart-empty:before{content:"\f08a"}.icon-signout:before{content:"\f08b"}.icon-linkedin-sign:before{content:"\f08c"}.icon-pushpin:before{content:"\f08d"}.icon-external-link:before{content:"\f08e"}.icon-signin:before{content:"\f090"}.icon-trophy:before{content:"\f091"}.icon-github-sign:before{content:"\f092"}.icon-upload-alt:before{content:"\f093"}.icon-lemon:before{content:"\f094"}.icon-phone:before{content:"\f095"}.icon-check-empty:before{content:"\f096"}.icon-bookmark-empty:before{content:"\f097"}.icon-phone-sign:before{content:"\f098"}.icon-twitter:before{content:"\f099"}.icon-facebook:before{content:"\f09a"}.icon-github:before{content:"\f09b"}.icon-unlock:before{content:"\f09c"}.icon-credit-card:before{content:"\f09d"}.icon-rss:before{content:"\f09e"}.icon-hdd:before{content:"\f0a0"}.icon-bullhorn:before{content:"\f0a1"}.icon-bell:before{content:"\f0a2"}.icon-certificate:before{content:"\f0a3"}.icon-hand-right:before{content:"\f0a4"}.icon-hand-left:before{content:"\f0a5"}.icon-hand-up:before{content:"\f0a6"}.icon-hand-down:before{content:"\f0a7"}.icon-circle-arrow-left:before{content:"\f0a8"}.icon-circle-arrow-right:before{content:"\f0a9"}.icon-circle-arrow-up:before{content:"\f0aa"}.icon-circle-arrow-down:before{content:"\f0ab"}.icon-globe:before{content:"\f0ac"}.icon-wrench:before{content:"\f0ad"}.icon-tasks:before{content:"\f0ae"}.icon-filter:before{content:"\f0b0"}.icon-briefcase:before{content:"\f0b1"}.icon-fullscreen:before{content:"\f0b2"}.icon-group:before{content:"\f0c0"}.icon-link:before{content:"\f0c1"}.icon-cloud:before{content:"\f0c2"}.icon-beaker:before{content:"\f0c3"}.icon-cut:before{content:"\f0c4"}.icon-copy:before{content:"\f0c5"}.icon-paper-clip:before{content:"\f0c6"}.icon-save:before{content:"\f0c7"}.icon-sign-blank:before{content:"\f0c8"}.icon-reorder:before{content:"\f0c9"}.icon-list-ul:before{content:"\f0ca"}.icon-list-ol:before{content:"\f0cb"}.icon-strikethrough:before{content:"\f0cc"}.icon-underline:before{content:"\f0cd"}.icon-table:before{content:"\f0ce"}.icon-magic:before{content:"\f0d0"}.icon-truck:before{content:"\f0d1"}.icon-pinterest:before{content:"\f0d2"}.icon-pinterest-sign:before{content:"\f0d3"}.icon-google-plus-sign:before{content:"\f0d4"}.icon-google-plus:before{content:"\f0d5"}.icon-money:before{content:"\f0d6"}.icon-caret-down:before{content:"\f0d7"}.icon-caret-up:before{content:"\f0d8"}.icon-caret-left:before{content:"\f0d9"}.icon-caret-right:before{content:"\f0da"}.icon-columns:before{content:"\f0db"}.icon-sort:before{content:"\f0dc"}.icon-sort-down:before{content:"\f0dd"}.icon-sort-up:before{content:"\f0de"}.icon-envelope-alt:before{content:"\f0e0"}.icon-linkedin:before{content:"\f0e1"}.icon-undo:before{content:"\f0e2"}.icon-legal:before{content:"\f0e3"}.icon-dashboard:before{content:"\f0e4"}.icon-comment-alt:before{content:"\f0e5"}.icon-comments-alt:before{content:"\f0e6"}.icon-bolt:before{content:"\f0e7"}.icon-sitemap:before{content:"\f0e8"}.icon-umbrella:before{content:"\f0e9"}.icon-paste:before{content:"\f0ea"}.icon-lightbulb:before{content:"\f0eb"}.icon-exchange:before{content:"\f0ec"}.icon-cloud-download:before{content:"\f0ed"}.icon-cloud-upload:before{content:"\f0ee"}.icon-user-md:before{content:"\f0f0"}.icon-stethoscope:before{content:"\f0f1"}.icon-suitcase:before{content:"\f0f2"}.icon-bell-alt:before{content:"\f0f3"}.icon-coffee:before{content:"\f0f4"}.icon-food:before{content:"\f0f5"}.icon-file-alt:before{content:"\f0f6"}.icon-building:before{content:"\f0f7"}.icon-hospital:before{content:"\f0f8"}.icon-ambulance:before{content:"\f0f9"}.icon-medkit:before{content:"\f0fa"}.icon-fighter-jet:before{content:"\f0fb"}.icon-beer:before{content:"\f0fc"}.icon-h-sign:before{content:"\f0fd"}.icon-plus-sign-alt:before{content:"\f0fe"}.icon-double-angle-left:before{content:"\f100"}.icon-double-angle-right:before{content:"\f101"}.icon-double-angle-up:before{content:"\f102"}.icon-double-angle-down:before{content:"\f103"}.icon-angle-left:before{content:"\f104"}.icon-angle-right:before{content:"\f105"}.icon-angle-up:before{content:"\f106"}.icon-angle-down:before{content:"\f107"}.icon-desktop:before{content:"\f108"}.icon-laptop:before{content:"\f109"}.icon-tablet:before{content:"\f10a"}.icon-mobile-phone:before{content:"\f10b"}.icon-circle-blank:before{content:"\f10c"}.icon-quote-left:before{content:"\f10d"}.icon-quote-right:before{content:"\f10e"}.icon-spinner:before{content:"\f110"}.icon-circle:before{content:"\f111"}.icon-reply:before{content:"\f112"}.icon-github-alt:before{content:"\f113"}.icon-folder-close-alt:before{content:"\f114"}.icon-folder-open-alt:before{content:"\f115"} \ No newline at end of file +@font-face{font-family:'FontAwesome';src:url('../font/fontawesome-webfont.eot?v=3.2.1');src:url('../font/fontawesome-webfont.eot?#iefix&v=3.2.1') format('embedded-opentype'),url('../font/fontawesome-webfont.woff?v=3.2.1') format('woff'),url('../font/fontawesome-webfont.ttf?v=3.2.1') format('truetype'),url('../font/fontawesome-webfont.svg#fontawesomeregular?v=3.2.1') format('svg');font-weight:normal;font-style:normal;}[class^="icon-"],[class*=" icon-"]{font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;} +[class^="icon-"]:before,[class*=" icon-"]:before{text-decoration:inherit;display:inline-block;speak:none;} +.icon-large:before{vertical-align:-10%;font-size:1.3333333333333333em;} +a [class^="icon-"],a [class*=" icon-"]{display:inline;} +[class^="icon-"].icon-fixed-width,[class*=" icon-"].icon-fixed-width{display:inline-block;width:1.1428571428571428em;text-align:right;padding-right:0.2857142857142857em;}[class^="icon-"].icon-fixed-width.icon-large,[class*=" icon-"].icon-fixed-width.icon-large{width:1.4285714285714286em;} +.icons-ul{margin-left:2.142857142857143em;list-style-type:none;}.icons-ul>li{position:relative;} +.icons-ul .icon-li{position:absolute;left:-2.142857142857143em;width:2.142857142857143em;text-align:center;line-height:inherit;} +[class^="icon-"].hide,[class*=" icon-"].hide{display:none;} +.icon-muted{color:#eeeeee;} +.icon-light{color:#ffffff;} +.icon-dark{color:#333333;} +.icon-border{border:solid 1px #eeeeee;padding:.2em .25em .15em;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} +.icon-2x{font-size:2em;}.icon-2x.icon-border{border-width:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} +.icon-3x{font-size:3em;}.icon-3x.icon-border{border-width:3px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;} +.icon-4x{font-size:4em;}.icon-4x.icon-border{border-width:4px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;} +.icon-5x{font-size:5em;}.icon-5x.icon-border{border-width:5px;-webkit-border-radius:7px;-moz-border-radius:7px;border-radius:7px;} +.pull-right{float:right;} +.pull-left{float:left;} +[class^="icon-"].pull-left,[class*=" icon-"].pull-left{margin-right:.3em;} +[class^="icon-"].pull-right,[class*=" icon-"].pull-right{margin-left:.3em;} +[class^="icon-"],[class*=" icon-"]{display:inline;width:auto;height:auto;line-height:normal;vertical-align:baseline;background-image:none;background-position:0% 0%;background-repeat:repeat;margin-top:0;} +.icon-white,.nav-pills>.active>a>[class^="icon-"],.nav-pills>.active>a>[class*=" icon-"],.nav-list>.active>a>[class^="icon-"],.nav-list>.active>a>[class*=" icon-"],.navbar-inverse .nav>.active>a>[class^="icon-"],.navbar-inverse .nav>.active>a>[class*=" icon-"],.dropdown-menu>li>a:hover>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"],.dropdown-submenu:hover>a>[class^="icon-"],.dropdown-submenu:hover>a>[class*=" icon-"]{background-image:none;} +.btn [class^="icon-"].icon-large,.nav [class^="icon-"].icon-large,.btn [class*=" icon-"].icon-large,.nav [class*=" icon-"].icon-large{line-height:.9em;} +.btn [class^="icon-"].icon-spin,.nav [class^="icon-"].icon-spin,.btn [class*=" icon-"].icon-spin,.nav [class*=" icon-"].icon-spin{display:inline-block;} +.nav-tabs [class^="icon-"],.nav-pills [class^="icon-"],.nav-tabs [class*=" icon-"],.nav-pills [class*=" icon-"],.nav-tabs [class^="icon-"].icon-large,.nav-pills [class^="icon-"].icon-large,.nav-tabs [class*=" icon-"].icon-large,.nav-pills [class*=" icon-"].icon-large{line-height:.9em;} +.btn [class^="icon-"].pull-left.icon-2x,.btn [class*=" icon-"].pull-left.icon-2x,.btn [class^="icon-"].pull-right.icon-2x,.btn [class*=" icon-"].pull-right.icon-2x{margin-top:.18em;} +.btn [class^="icon-"].icon-spin.icon-large,.btn [class*=" icon-"].icon-spin.icon-large{line-height:.8em;} +.btn.btn-small [class^="icon-"].pull-left.icon-2x,.btn.btn-small [class*=" icon-"].pull-left.icon-2x,.btn.btn-small [class^="icon-"].pull-right.icon-2x,.btn.btn-small [class*=" icon-"].pull-right.icon-2x{margin-top:.25em;} +.btn.btn-large [class^="icon-"],.btn.btn-large [class*=" icon-"]{margin-top:0;}.btn.btn-large [class^="icon-"].pull-left.icon-2x,.btn.btn-large [class*=" icon-"].pull-left.icon-2x,.btn.btn-large [class^="icon-"].pull-right.icon-2x,.btn.btn-large [class*=" icon-"].pull-right.icon-2x{margin-top:.05em;} +.btn.btn-large [class^="icon-"].pull-left.icon-2x,.btn.btn-large [class*=" icon-"].pull-left.icon-2x{margin-right:.2em;} +.btn.btn-large [class^="icon-"].pull-right.icon-2x,.btn.btn-large [class*=" icon-"].pull-right.icon-2x{margin-left:.2em;} +.nav-list [class^="icon-"],.nav-list [class*=" icon-"]{line-height:inherit;} +.icon-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:-35%;}.icon-stack [class^="icon-"],.icon-stack [class*=" icon-"]{display:block;text-align:center;position:absolute;width:100%;height:100%;font-size:1em;line-height:inherit;*line-height:2em;} +.icon-stack .icon-stack-base{font-size:2em;*line-height:1em;} +.icon-spin{display:inline-block;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;-webkit-animation:spin 2s infinite linear;animation:spin 2s infinite linear;} +a .icon-stack,a .icon-spin{display:inline-block;text-decoration:none;} +@-moz-keyframes spin{0%{-moz-transform:rotate(0deg);} 100%{-moz-transform:rotate(359deg);}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg);} 100%{-webkit-transform:rotate(359deg);}}@-o-keyframes spin{0%{-o-transform:rotate(0deg);} 100%{-o-transform:rotate(359deg);}}@-ms-keyframes spin{0%{-ms-transform:rotate(0deg);} 100%{-ms-transform:rotate(359deg);}}@keyframes spin{0%{transform:rotate(0deg);} 100%{transform:rotate(359deg);}}.icon-rotate-90:before{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg);filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);} +.icon-rotate-180:before{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg);filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);} +.icon-rotate-270:before{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg);filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);} +.icon-flip-horizontal:before{-webkit-transform:scale(-1, 1);-moz-transform:scale(-1, 1);-ms-transform:scale(-1, 1);-o-transform:scale(-1, 1);transform:scale(-1, 1);} +.icon-flip-vertical:before{-webkit-transform:scale(1, -1);-moz-transform:scale(1, -1);-ms-transform:scale(1, -1);-o-transform:scale(1, -1);transform:scale(1, -1);} +a .icon-rotate-90:before,a .icon-rotate-180:before,a .icon-rotate-270:before,a .icon-flip-horizontal:before,a .icon-flip-vertical:before{display:inline-block;} +.icon-glass:before{content:"\f000";} +.icon-music:before{content:"\f001";} +.icon-search:before{content:"\f002";} +.icon-envelope-alt:before{content:"\f003";} +.icon-heart:before{content:"\f004";} +.icon-star:before{content:"\f005";} +.icon-star-empty:before{content:"\f006";} +.icon-user:before{content:"\f007";} +.icon-film:before{content:"\f008";} +.icon-th-large:before{content:"\f009";} +.icon-th:before{content:"\f00a";} +.icon-th-list:before{content:"\f00b";} +.icon-ok:before{content:"\f00c";} +.icon-remove:before{content:"\f00d";} +.icon-zoom-in:before{content:"\f00e";} +.icon-zoom-out:before{content:"\f010";} +.icon-power-off:before,.icon-off:before{content:"\f011";} +.icon-signal:before{content:"\f012";} +.icon-gear:before,.icon-cog:before{content:"\f013";} +.icon-trash:before{content:"\f014";} +.icon-home:before{content:"\f015";} +.icon-file-alt:before{content:"\f016";} +.icon-time:before{content:"\f017";} +.icon-road:before{content:"\f018";} +.icon-download-alt:before{content:"\f019";} +.icon-download:before{content:"\f01a";} +.icon-upload:before{content:"\f01b";} +.icon-inbox:before{content:"\f01c";} +.icon-play-circle:before{content:"\f01d";} +.icon-rotate-right:before,.icon-repeat:before{content:"\f01e";} +.icon-refresh:before{content:"\f021";} +.icon-list-alt:before{content:"\f022";} +.icon-lock:before{content:"\f023";} +.icon-flag:before{content:"\f024";} +.icon-headphones:before{content:"\f025";} +.icon-volume-off:before{content:"\f026";} +.icon-volume-down:before{content:"\f027";} +.icon-volume-up:before{content:"\f028";} +.icon-qrcode:before{content:"\f029";} +.icon-barcode:before{content:"\f02a";} +.icon-tag:before{content:"\f02b";} +.icon-tags:before{content:"\f02c";} +.icon-book:before{content:"\f02d";} +.icon-bookmark:before{content:"\f02e";} +.icon-print:before{content:"\f02f";} +.icon-camera:before{content:"\f030";} +.icon-font:before{content:"\f031";} +.icon-bold:before{content:"\f032";} +.icon-italic:before{content:"\f033";} +.icon-text-height:before{content:"\f034";} +.icon-text-width:before{content:"\f035";} +.icon-align-left:before{content:"\f036";} +.icon-align-center:before{content:"\f037";} +.icon-align-right:before{content:"\f038";} +.icon-align-justify:before{content:"\f039";} +.icon-list:before{content:"\f03a";} +.icon-indent-left:before{content:"\f03b";} +.icon-indent-right:before{content:"\f03c";} +.icon-facetime-video:before{content:"\f03d";} +.icon-picture:before{content:"\f03e";} +.icon-pencil:before{content:"\f040";} +.icon-map-marker:before{content:"\f041";} +.icon-adjust:before{content:"\f042";} +.icon-tint:before{content:"\f043";} +.icon-edit:before{content:"\f044";} +.icon-share:before{content:"\f045";} +.icon-check:before{content:"\f046";} +.icon-move:before{content:"\f047";} +.icon-step-backward:before{content:"\f048";} +.icon-fast-backward:before{content:"\f049";} +.icon-backward:before{content:"\f04a";} +.icon-play:before{content:"\f04b";} +.icon-pause:before{content:"\f04c";} +.icon-stop:before{content:"\f04d";} +.icon-forward:before{content:"\f04e";} +.icon-fast-forward:before{content:"\f050";} +.icon-step-forward:before{content:"\f051";} +.icon-eject:before{content:"\f052";} +.icon-chevron-left:before{content:"\f053";} +.icon-chevron-right:before{content:"\f054";} +.icon-plus-sign:before{content:"\f055";} +.icon-minus-sign:before{content:"\f056";} +.icon-remove-sign:before{content:"\f057";} +.icon-ok-sign:before{content:"\f058";} +.icon-question-sign:before{content:"\f059";} +.icon-info-sign:before{content:"\f05a";} +.icon-screenshot:before{content:"\f05b";} +.icon-remove-circle:before{content:"\f05c";} +.icon-ok-circle:before{content:"\f05d";} +.icon-ban-circle:before{content:"\f05e";} +.icon-arrow-left:before{content:"\f060";} +.icon-arrow-right:before{content:"\f061";} +.icon-arrow-up:before{content:"\f062";} +.icon-arrow-down:before{content:"\f063";} +.icon-mail-forward:before,.icon-share-alt:before{content:"\f064";} +.icon-resize-full:before{content:"\f065";} +.icon-resize-small:before{content:"\f066";} +.icon-plus:before{content:"\f067";} +.icon-minus:before{content:"\f068";} +.icon-asterisk:before{content:"\f069";} +.icon-exclamation-sign:before{content:"\f06a";} +.icon-gift:before{content:"\f06b";} +.icon-leaf:before{content:"\f06c";} +.icon-fire:before{content:"\f06d";} +.icon-eye-open:before{content:"\f06e";} +.icon-eye-close:before{content:"\f070";} +.icon-warning-sign:before{content:"\f071";} +.icon-plane:before{content:"\f072";} +.icon-calendar:before{content:"\f073";} +.icon-random:before{content:"\f074";} +.icon-comment:before{content:"\f075";} +.icon-magnet:before{content:"\f076";} +.icon-chevron-up:before{content:"\f077";} +.icon-chevron-down:before{content:"\f078";} +.icon-retweet:before{content:"\f079";} +.icon-shopping-cart:before{content:"\f07a";} +.icon-folder-close:before{content:"\f07b";} +.icon-folder-open:before{content:"\f07c";} +.icon-resize-vertical:before{content:"\f07d";} +.icon-resize-horizontal:before{content:"\f07e";} +.icon-bar-chart:before{content:"\f080";} +.icon-twitter-sign:before{content:"\f081";} +.icon-facebook-sign:before{content:"\f082";} +.icon-camera-retro:before{content:"\f083";} +.icon-key:before{content:"\f084";} +.icon-gears:before,.icon-cogs:before{content:"\f085";} +.icon-comments:before{content:"\f086";} +.icon-thumbs-up-alt:before{content:"\f087";} +.icon-thumbs-down-alt:before{content:"\f088";} +.icon-star-half:before{content:"\f089";} +.icon-heart-empty:before{content:"\f08a";} +.icon-signout:before{content:"\f08b";} +.icon-linkedin-sign:before{content:"\f08c";} +.icon-pushpin:before{content:"\f08d";} +.icon-external-link:before{content:"\f08e";} +.icon-signin:before{content:"\f090";} +.icon-trophy:before{content:"\f091";} +.icon-github-sign:before{content:"\f092";} +.icon-upload-alt:before{content:"\f093";} +.icon-lemon:before{content:"\f094";} +.icon-phone:before{content:"\f095";} +.icon-unchecked:before,.icon-check-empty:before{content:"\f096";} +.icon-bookmark-empty:before{content:"\f097";} +.icon-phone-sign:before{content:"\f098";} +.icon-twitter:before{content:"\f099";} +.icon-facebook:before{content:"\f09a";} +.icon-github:before{content:"\f09b";} +.icon-unlock:before{content:"\f09c";} +.icon-credit-card:before{content:"\f09d";} +.icon-rss:before{content:"\f09e";} +.icon-hdd:before{content:"\f0a0";} +.icon-bullhorn:before{content:"\f0a1";} +.icon-bell:before{content:"\f0a2";} +.icon-certificate:before{content:"\f0a3";} +.icon-hand-right:before{content:"\f0a4";} +.icon-hand-left:before{content:"\f0a5";} +.icon-hand-up:before{content:"\f0a6";} +.icon-hand-down:before{content:"\f0a7";} +.icon-circle-arrow-left:before{content:"\f0a8";} +.icon-circle-arrow-right:before{content:"\f0a9";} +.icon-circle-arrow-up:before{content:"\f0aa";} +.icon-circle-arrow-down:before{content:"\f0ab";} +.icon-globe:before{content:"\f0ac";} +.icon-wrench:before{content:"\f0ad";} +.icon-tasks:before{content:"\f0ae";} +.icon-filter:before{content:"\f0b0";} +.icon-briefcase:before{content:"\f0b1";} +.icon-fullscreen:before{content:"\f0b2";} +.icon-group:before{content:"\f0c0";} +.icon-link:before{content:"\f0c1";} +.icon-cloud:before{content:"\f0c2";} +.icon-beaker:before{content:"\f0c3";} +.icon-cut:before{content:"\f0c4";} +.icon-copy:before{content:"\f0c5";} +.icon-paperclip:before,.icon-paper-clip:before{content:"\f0c6";} +.icon-save:before{content:"\f0c7";} +.icon-sign-blank:before{content:"\f0c8";} +.icon-reorder:before{content:"\f0c9";} +.icon-list-ul:before{content:"\f0ca";} +.icon-list-ol:before{content:"\f0cb";} +.icon-strikethrough:before{content:"\f0cc";} +.icon-underline:before{content:"\f0cd";} +.icon-table:before{content:"\f0ce";} +.icon-magic:before{content:"\f0d0";} +.icon-truck:before{content:"\f0d1";} +.icon-pinterest:before{content:"\f0d2";} +.icon-pinterest-sign:before{content:"\f0d3";} +.icon-google-plus-sign:before{content:"\f0d4";} +.icon-google-plus:before{content:"\f0d5";} +.icon-money:before{content:"\f0d6";} +.icon-caret-down:before{content:"\f0d7";} +.icon-caret-up:before{content:"\f0d8";} +.icon-caret-left:before{content:"\f0d9";} +.icon-caret-right:before{content:"\f0da";} +.icon-columns:before{content:"\f0db";} +.icon-sort:before{content:"\f0dc";} +.icon-sort-down:before{content:"\f0dd";} +.icon-sort-up:before{content:"\f0de";} +.icon-envelope:before{content:"\f0e0";} +.icon-linkedin:before{content:"\f0e1";} +.icon-rotate-left:before,.icon-undo:before{content:"\f0e2";} +.icon-legal:before{content:"\f0e3";} +.icon-dashboard:before{content:"\f0e4";} +.icon-comment-alt:before{content:"\f0e5";} +.icon-comments-alt:before{content:"\f0e6";} +.icon-bolt:before{content:"\f0e7";} +.icon-sitemap:before{content:"\f0e8";} +.icon-umbrella:before{content:"\f0e9";} +.icon-paste:before{content:"\f0ea";} +.icon-lightbulb:before{content:"\f0eb";} +.icon-exchange:before{content:"\f0ec";} +.icon-cloud-download:before{content:"\f0ed";} +.icon-cloud-upload:before{content:"\f0ee";} +.icon-user-md:before{content:"\f0f0";} +.icon-stethoscope:before{content:"\f0f1";} +.icon-suitcase:before{content:"\f0f2";} +.icon-bell-alt:before{content:"\f0f3";} +.icon-coffee:before{content:"\f0f4";} +.icon-food:before{content:"\f0f5";} +.icon-file-text-alt:before{content:"\f0f6";} +.icon-building:before{content:"\f0f7";} +.icon-hospital:before{content:"\f0f8";} +.icon-ambulance:before{content:"\f0f9";} +.icon-medkit:before{content:"\f0fa";} +.icon-fighter-jet:before{content:"\f0fb";} +.icon-beer:before{content:"\f0fc";} +.icon-h-sign:before{content:"\f0fd";} +.icon-plus-sign-alt:before{content:"\f0fe";} +.icon-double-angle-left:before{content:"\f100";} +.icon-double-angle-right:before{content:"\f101";} +.icon-double-angle-up:before{content:"\f102";} +.icon-double-angle-down:before{content:"\f103";} +.icon-angle-left:before{content:"\f104";} +.icon-angle-right:before{content:"\f105";} +.icon-angle-up:before{content:"\f106";} +.icon-angle-down:before{content:"\f107";} +.icon-desktop:before{content:"\f108";} +.icon-laptop:before{content:"\f109";} +.icon-tablet:before{content:"\f10a";} +.icon-mobile-phone:before{content:"\f10b";} +.icon-circle-blank:before{content:"\f10c";} +.icon-quote-left:before{content:"\f10d";} +.icon-quote-right:before{content:"\f10e";} +.icon-spinner:before{content:"\f110";} +.icon-circle:before{content:"\f111";} +.icon-mail-reply:before,.icon-reply:before{content:"\f112";} +.icon-github-alt:before{content:"\f113";} +.icon-folder-close-alt:before{content:"\f114";} +.icon-folder-open-alt:before{content:"\f115";} +.icon-expand-alt:before{content:"\f116";} +.icon-collapse-alt:before{content:"\f117";} +.icon-smile:before{content:"\f118";} +.icon-frown:before{content:"\f119";} +.icon-meh:before{content:"\f11a";} +.icon-gamepad:before{content:"\f11b";} +.icon-keyboard:before{content:"\f11c";} +.icon-flag-alt:before{content:"\f11d";} +.icon-flag-checkered:before{content:"\f11e";} +.icon-terminal:before{content:"\f120";} +.icon-code:before{content:"\f121";} +.icon-reply-all:before{content:"\f122";} +.icon-mail-reply-all:before{content:"\f122";} +.icon-star-half-full:before,.icon-star-half-empty:before{content:"\f123";} +.icon-location-arrow:before{content:"\f124";} +.icon-crop:before{content:"\f125";} +.icon-code-fork:before{content:"\f126";} +.icon-unlink:before{content:"\f127";} +.icon-question:before{content:"\f128";} +.icon-info:before{content:"\f129";} +.icon-exclamation:before{content:"\f12a";} +.icon-superscript:before{content:"\f12b";} +.icon-subscript:before{content:"\f12c";} +.icon-eraser:before{content:"\f12d";} +.icon-puzzle-piece:before{content:"\f12e";} +.icon-microphone:before{content:"\f130";} +.icon-microphone-off:before{content:"\f131";} +.icon-shield:before{content:"\f132";} +.icon-calendar-empty:before{content:"\f133";} +.icon-fire-extinguisher:before{content:"\f134";} +.icon-rocket:before{content:"\f135";} +.icon-maxcdn:before{content:"\f136";} +.icon-chevron-sign-left:before{content:"\f137";} +.icon-chevron-sign-right:before{content:"\f138";} +.icon-chevron-sign-up:before{content:"\f139";} +.icon-chevron-sign-down:before{content:"\f13a";} +.icon-html5:before{content:"\f13b";} +.icon-css3:before{content:"\f13c";} +.icon-anchor:before{content:"\f13d";} +.icon-unlock-alt:before{content:"\f13e";} +.icon-bullseye:before{content:"\f140";} +.icon-ellipsis-horizontal:before{content:"\f141";} +.icon-ellipsis-vertical:before{content:"\f142";} +.icon-rss-sign:before{content:"\f143";} +.icon-play-sign:before{content:"\f144";} +.icon-ticket:before{content:"\f145";} +.icon-minus-sign-alt:before{content:"\f146";} +.icon-check-minus:before{content:"\f147";} +.icon-level-up:before{content:"\f148";} +.icon-level-down:before{content:"\f149";} +.icon-check-sign:before{content:"\f14a";} +.icon-edit-sign:before{content:"\f14b";} +.icon-external-link-sign:before{content:"\f14c";} +.icon-share-sign:before{content:"\f14d";} +.icon-compass:before{content:"\f14e";} +.icon-collapse:before{content:"\f150";} +.icon-collapse-top:before{content:"\f151";} +.icon-expand:before{content:"\f152";} +.icon-euro:before,.icon-eur:before{content:"\f153";} +.icon-gbp:before{content:"\f154";} +.icon-dollar:before,.icon-usd:before{content:"\f155";} +.icon-rupee:before,.icon-inr:before{content:"\f156";} +.icon-yen:before,.icon-jpy:before{content:"\f157";} +.icon-renminbi:before,.icon-cny:before{content:"\f158";} +.icon-won:before,.icon-krw:before{content:"\f159";} +.icon-bitcoin:before,.icon-btc:before{content:"\f15a";} +.icon-file:before{content:"\f15b";} +.icon-file-text:before{content:"\f15c";} +.icon-sort-by-alphabet:before{content:"\f15d";} +.icon-sort-by-alphabet-alt:before{content:"\f15e";} +.icon-sort-by-attributes:before{content:"\f160";} +.icon-sort-by-attributes-alt:before{content:"\f161";} +.icon-sort-by-order:before{content:"\f162";} +.icon-sort-by-order-alt:before{content:"\f163";} +.icon-thumbs-up:before{content:"\f164";} +.icon-thumbs-down:before{content:"\f165";} +.icon-youtube-sign:before{content:"\f166";} +.icon-youtube:before{content:"\f167";} +.icon-xing:before{content:"\f168";} +.icon-xing-sign:before{content:"\f169";} +.icon-youtube-play:before{content:"\f16a";} +.icon-dropbox:before{content:"\f16b";} +.icon-stackexchange:before{content:"\f16c";} +.icon-instagram:before{content:"\f16d";} +.icon-flickr:before{content:"\f16e";} +.icon-adn:before{content:"\f170";} +.icon-bitbucket:before{content:"\f171";} +.icon-bitbucket-sign:before{content:"\f172";} +.icon-tumblr:before{content:"\f173";} +.icon-tumblr-sign:before{content:"\f174";} +.icon-long-arrow-down:before{content:"\f175";} +.icon-long-arrow-up:before{content:"\f176";} +.icon-long-arrow-left:before{content:"\f177";} +.icon-long-arrow-right:before{content:"\f178";} +.icon-apple:before{content:"\f179";} +.icon-windows:before{content:"\f17a";} +.icon-android:before{content:"\f17b";} +.icon-linux:before{content:"\f17c";} +.icon-dribbble:before{content:"\f17d";} +.icon-skype:before{content:"\f17e";} +.icon-foursquare:before{content:"\f180";} +.icon-trello:before{content:"\f181";} +.icon-female:before{content:"\f182";} +.icon-male:before{content:"\f183";} +.icon-gittip:before{content:"\f184";} +.icon-sun:before{content:"\f185";} +.icon-moon:before{content:"\f186";} +.icon-archive:before{content:"\f187";} +.icon-bug:before{content:"\f188";} +.icon-vk:before{content:"\f189";} +.icon-weibo:before{content:"\f18a";} +.icon-renren:before{content:"\f18b";} diff --git a/Netdisco/share/public/css/netdisco.css b/Netdisco/share/public/css/netdisco.css index 997e733b..816dbf30 100644 --- a/Netdisco/share/public/css/netdisco.css +++ b/Netdisco/share/public/css/netdisco.css @@ -381,12 +381,6 @@ td > form.nd_inline-form { display: inline; } -/* fixup for placing the Archived "A" inside the prepended checkbox */ -.nd_legendlabel { - float: right; - line-height: 1.2; -} - /* placement of form field in sidebar */ .nd_side-input { margin-left: -3px; @@ -472,30 +466,20 @@ form .clearfix.success input { background-color: #A9DBA9; } -/* when we use font-awesome icons, override the size */ -#nd_legend i { - width: 9px; -} -.table i { - width: 9px; -} - /* bring sidebar items closer together */ .nd_inputs-list label { margin-bottom: 1px; } -/* compact icons for the sidebar legend */ -.nd_inputs-list i { - margin-right: 5px; - margin-left: 2px; -} - /* nudge content closer to the header labels in the sidebar */ .nd_inputs-list li:first-child { padding-top: 3px !important; } +.icons-ul { + margin-left: 22px; +} + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /* D3 SVG */ diff --git a/Netdisco/share/public/font/FontAwesome.otf b/Netdisco/share/public/font/FontAwesome.otf index 64049bf2..70125459 100644 Binary files a/Netdisco/share/public/font/FontAwesome.otf and b/Netdisco/share/public/font/FontAwesome.otf differ diff --git a/Netdisco/share/public/font/fontawesome-webfont.eot b/Netdisco/share/public/font/fontawesome-webfont.eot index 7d81019e..0662cb96 100755 Binary files a/Netdisco/share/public/font/fontawesome-webfont.eot and b/Netdisco/share/public/font/fontawesome-webfont.eot differ diff --git a/Netdisco/share/public/font/fontawesome-webfont.svg b/Netdisco/share/public/font/fontawesome-webfont.svg index ba0afe5e..2edb4ec3 100755 --- a/Netdisco/share/public/font/fontawesome-webfont.svg +++ b/Netdisco/share/public/font/fontawesome-webfont.svg @@ -52,25 +52,25 @@ - - + + - - - + + + - - + + - + - + @@ -89,11 +89,11 @@ - + - + @@ -115,12 +115,12 @@ - - + + - - - + + + @@ -132,13 +132,13 @@ - + - + @@ -152,8 +152,8 @@ - - + + @@ -163,22 +163,22 @@ - + - - + + - + - - - - + + + + @@ -186,18 +186,18 @@ - + - + - + - + @@ -230,8 +230,8 @@ - - + + @@ -250,13 +250,13 @@ - + - - + + @@ -271,7 +271,7 @@ - + @@ -280,5 +280,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Netdisco/share/public/font/fontawesome-webfont.ttf b/Netdisco/share/public/font/fontawesome-webfont.ttf index d4617247..d3659246 100755 Binary files a/Netdisco/share/public/font/fontawesome-webfont.ttf and b/Netdisco/share/public/font/fontawesome-webfont.ttf differ diff --git a/Netdisco/share/public/font/fontawesome-webfont.woff b/Netdisco/share/public/font/fontawesome-webfont.woff index 3c89ae09..b9bd17e1 100755 Binary files a/Netdisco/share/public/font/fontawesome-webfont.woff and b/Netdisco/share/public/font/fontawesome-webfont.woff differ diff --git a/Netdisco/share/public/javascripts/netdisco_portcontrol.js b/Netdisco/share/public/javascripts/netdisco_portcontrol.js index 56b6fda8..81c1be86 100644 --- a/Netdisco/share/public/javascripts/netdisco_portcontrol.js +++ b/Netdisco/share/public/javascripts/netdisco_portcontrol.js @@ -50,23 +50,92 @@ function port_control (e) { }); } -// for growl-like functionality, check for notifications periodically -(function worker() { - $.ajax({ - url: uri_base + '/ajax/userlog' - ,success: function(data) { - for (var i = 0; i < data['error'].length; i++) { - toastr.error(data['error'][i], 'Failed Change Request'); - } - for (var i = 0; i < data['done'].length; i++) { - toastr.success(data['done'][i], 'Successful Change Request'); - } - // Schedule next request when the current one's complete - setTimeout(worker, 5000); - } - ,error: function() { - // after one failure, don't try again - toastr.warning('Unable to retrieve change request log') +// on load, establish global delegations for now and future +$(document).ready(function() { + // for growl-like functionality, check for notifications periodically + if (nd_port_control) { + (function worker() { + $.ajax({ + url: uri_base + '/ajax/userlog' + ,success: function(data) { + for (var i = 0; i < data['error'].length; i++) { + toastr.error(data['error'][i], 'Failed Job:'); + } + for (var i = 0; i < data['done'].length; i++) { + toastr.success(data['done'][i], 'Successful Job:'); + } + // Schedule next request when the current one's complete + setTimeout(worker, 5000); + } + ,error: function() { + // after one failure, don't try again + toastr.warning('Unable to retrieve change request log') + } + }); + })(); + } + + // toggle visibility of port up/down and edit controls + $('.tab-content').on('mouseenter', '.nd_editable-cell', function() { + $(this).children('.nd_hand-icon').show(); + if (! $(this).is(':focus')) { + $(this).children('.nd_edit-icon').show(); // ports + $(this).siblings('td').find('.nd_device-details-edit').show(); // details } }); -})(); + $('.tab-content').on('mouseleave', '.nd_editable-cell', function() { + $(this).children('.nd_hand-icon').hide(); + if (! $(this).is(':focus')) { + $(this).children('.nd_edit-icon').hide(); // ports + $(this).siblings('td').find('.nd_device-details-edit').hide(); // details + } + }); + $('.tab-content').on('focus', '[contenteditable=true]', function() { + $(this).children('.nd_edit-icon').hide(); // ports + $(this).siblings('td').find('.nd_device-details-edit').hide(); // details + }); + + // activity for port up/down control + $('#ports_pane').on('click', '.icon-hand-up', function() { + port_control(this); // save + }); + $('#ports_pane').on('click', '.icon-hand-down', function() { + port_control(this); // save + }); + + // activity for power enable/disable control + $('#ports_pane').on('click', '.nd_power-icon', function() { + port_control(this); // save + }); + + var dirty = false; + + // activity for contenteditable control + $('.tab-content').on('keydown', '[contenteditable=true]', function(event) { + var esc = event.which == 27, + nl = event.which == 13; + + if (esc) { + $(this).blur(); + } + else if (nl) { + event.preventDefault(); + port_control(this); // save + dirty = false; + $(this).blur(); + } + else { + dirty = true; + } + }); + + // activity for contenteditable control + $('.tab-content').on('blur', '[contenteditable=true]', function(event) { + if (dirty) { + document.execCommand('undo'); + dirty = false; + $(this).blur(); + } + }); + +}); diff --git a/Netdisco/share/views/admintask.tt b/Netdisco/share/views/admintask.tt index aabc01a7..2baad94e 100644 --- a/Netdisco/share/views/admintask.tt +++ b/Netdisco/share/views/admintask.tt @@ -30,6 +30,7 @@ href="#[% task.tag %]_pane">[% task.label %] [% IF task.tag == 'jobqueue' %] + diff --git a/Netdisco/share/views/ajax/device/details.tt b/Netdisco/share/views/ajax/device/details.tt index 6fc4fd8c..acaf8edb 100644 --- a/Netdisco/share/views/ajax/device/details.tt +++ b/Netdisco/share/views/ajax/device/details.tt @@ -1,3 +1,4 @@ +[% SET user_can_port_control = user_has_role('port_control') %] @@ -6,11 +7,11 @@ - [% IF vars.user.port_control %] + [% IF user_can_port_control %] - [% IF vars.user.port_control %] + [% IF user_can_port_control %] - [% IF vars.user.admin %] - + [% IF user_has_role('admin') %] + [% END %] diff --git a/Netdisco/share/views/ajax/device/ports.tt b/Netdisco/share/views/ajax/device/ports.tt index c3d9bc68..f2752802 100644 --- a/Netdisco/share/views/ajax/device/ports.tt +++ b/Netdisco/share/views/ajax/device/ports.tt @@ -1,3 +1,4 @@ +[% SET user_can_port_control = user_has_role('port_control') %]
Location - [% IF vars.user.port_control %] + [% IF user_can_port_control %] [% END %] [% d.location | html_entity %] @@ -24,11 +25,11 @@
Contact - [% IF vars.user.port_control %] + [% IF user_can_port_control %] [% END %] [% d.contact | html_entity %] @@ -93,22 +94,16 @@ VTP Domain [% d.vtp_domain | html_entity %]
Admin Tasks -
- - -
-
- - -
-
- - -
+ + + + + +
@@ -7,7 +8,7 @@ [% NEXT IF item.name == 'c_nodes' AND params.c_nodes AND params.c_neighbors %] [% NEXT UNLESS params.${item.name} %] + (user_can_port_control AND params.c_admin AND (item.name == 'c_port' OR item.name == 'c_name')) %]> [% item.label | html_entity %] [% END %] @@ -16,15 +17,17 @@ [% FOREACH row IN results %] - @@ -40,7 +43,7 @@ [% END %] [% IF params.c_port %] - [% IF vars.user.port_control AND params.c_admin %] + [% IF user_can_port_control AND params.c_admin %] [% IF row.up_admin == 'up' %] [% IF params.stamps %] @@ -42,7 +42,7 @@ [% IF params.stamps %] diff --git a/Netdisco/share/views/ajax/search/node_by_mac.tt b/Netdisco/share/views/ajax/search/node_by_mac.tt index 4206dea7..7c6e489e 100644 --- a/Netdisco/share/views/ajax/search/node_by_mac.tt +++ b/Netdisco/share/views/ajax/search/node_by_mac.tt @@ -37,7 +37,7 @@ [% END %] [% IF params.stamps %] @@ -71,7 +71,7 @@
+ [% IF row.up_admin == 'down' %] - S + [% ELSIF row.stp == 'blocking' %] - B + [% ELSIF params.free OR row.is_free %] - F + [% ELSIF row.up_admin == 'up' AND row.up == 'down' %] - D + + [% ELSE %] + [% END %] @@ -95,7 +98,7 @@ [% END %] [% IF params.c_name %] - [% IF vars.user.port_control AND params.c_admin %] + [% IF user_can_port_control AND params.c_admin %] @@ -121,7 +124,7 @@ [% END %] [% IF params.c_vlan %] - [% IF vars.user.port_control AND params.c_admin %] + [% IF user_can_port_control AND params.c_admin %] @@ -164,7 +167,7 @@ [% IF params.c_power %] [% IF row.power %] [% IF row.power.admin == 'true' %] - [% IF vars.user.port_control AND params.c_admin %] + [% IF user_can_port_control AND params.c_admin %] @@ -184,7 +187,7 @@ [% END %] [% ELSE %] - [% IF vars.user.port_control AND params.c_admin %] + [% IF user_can_port_control AND params.c_admin %] @@ -214,25 +217,25 @@ [% ' id: '_ row.remote_id IF row.remote_id %] [% ' type: '_ row.remote_type IF row.remote_type %]) [% ELSIF row.remote_ip AND row.remote_port %] - N +   [% row.remote_ip | html_entity %] (port: [% row.remote_port | html_entity %] [% ' id: '_ row.remote_id IF row.remote_id %] [% ' type: '_ row.remote_type IF row.remote_type %]) [% ELSE %] - N (possible uplink) +   (possible uplink) [% END %] [% END %] [% IF params.c_nodes %] [% FOREACH node IN row.$nodes %] [% '
' IF (row.remote_ip OR row.is_uplink) OR NOT loop.first %] - [% 'A  ' IF NOT node.active %] + [% '  ' IF NOT node.active %] [% node.net_mac.$mac_format_call | html_entity %] [% ' (' _ node.time_last_age _ ')' IF params.n_age %] [% IF params.n_ip %] [% FOREACH ip IN node.ips %] -
  [% 'A  ' IF NOT ip.active %] +
  [% '  ' IF NOT ip.active %] [% SET dns = ip.dns %] [% IF dns %] [% dns %] ([% ip.ip | html_entity %]) diff --git a/Netdisco/share/views/ajax/search/node_by_ip.tt b/Netdisco/share/views/ajax/search/node_by_ip.tt index 04122eaf..fbe15b58 100644 --- a/Netdisco/share/views/ajax/search/node_by_ip.tt +++ b/Netdisco/share/views/ajax/search/node_by_ip.tt @@ -24,7 +24,7 @@ [% END %]
IP → MAC [% row.ip | html_entity %] - [% ' A' IF NOT row.active %] + [% '  ' IF NOT row.active %] [% ' (' _ row.dns.remove(settings.domain_suffix) _ ')' IF row.dns %] [% node.switch | html_entity %] - [% node.port | html_entity %] - [% ' A' IF NOT node.active %] + [% '  ' IF NOT node.active %] [% IF node.device.dns AND node.device_port AND node.device_port.name %] ([% node.device.dns | html_entity %] - [% node.device_port.name | html_entity %]) [% END %] @@ -61,7 +61,7 @@ [% END %] MAC → IP [% nodeip.ip | html_entity %] - [% ' A' IF NOT nodeip.active %] + [% '  ' IF NOT nodeip.active %] [% ' (' _ nodeip.dns.remove(settings.domain_suffix) _ ')' IF nodeip.dns %] MAC → IP [% row.ip | html_entity %] - [% ' A' IF NOT row.active %] + [% '  ' IF NOT row.active %] [% ' (' _ row.dns.remove(settings.domain_suffix) _ ')' IF row.dns %] [% node.switch | html_entity %] - [% node.port | html_entity %] - [% ' A' IF NOT node.active %] + [% '  ' IF NOT node.active %] [% IF node.device.dns AND node.device_port AND node.device_port.name %] ([% node.device.dns | html_entity %] - [% node.device_port.name | html_entity %]) [% END %] diff --git a/Netdisco/share/views/index.tt b/Netdisco/share/views/index.tt index 76e1f934..0f663c3c 100644 --- a/Netdisco/share/views/index.tt +++ b/Netdisco/share/views/index.tt @@ -1,7 +1,7 @@
- [% IF params.failed %] + [% IF params.login_failed %]
× Incorrect username or password, please try again. @@ -22,42 +22,37 @@ [% IF vars.notfound %]
× - Sorry, page not found. - Report a Bug? + Sorry, page not found.  +  Report a Bug?
[% END %]

Welcome to Netdisco

Netdisco is an Open Source web-based network management tool. - [% IF NOT session.user %] + [% IF NOT session.logged_in_user %] [% ELSE %] + [% IF user_has_role('admin') %] + + [% END %] [% END %]
- [% IF vars.nodevices %] -
-

Initial Discovery

-

You haven't discovered any devices yet.

-

Enter a network device name or IP to queue the first discovery:

-
-
- - - -
-
-
- [% END %]
diff --git a/Netdisco/share/views/js/admintask.js b/Netdisco/share/views/js/admintask.js index 02737eb9..4c542ce7 100644 --- a/Netdisco/share/views/js/admintask.js +++ b/Netdisco/share/views/js/admintask.js @@ -107,7 +107,7 @@ // activity for admin task tables // dynamically bind to all forms in the table - $(target).on('click', '.nd_adminbutton', function(event) { + $('.content').on('click', '.nd_adminbutton', function(event) { // stop form from submitting normally event.preventDefault(); @@ -117,7 +117,7 @@ } // what purpose - add/update/del - var mode = $(this).attr('name') + var mode = $(this).attr('name'); // submit the query and put results into the tab pane $.ajax({ diff --git a/Netdisco/share/views/js/device.js b/Netdisco/share/views/js/device.js index ff10a979..3899738e 100644 --- a/Netdisco/share/views/js/device.js +++ b/Netdisco/share/views/js/device.js @@ -86,58 +86,30 @@ } }); - // toggle visibility of port up/down and edit controls - $('.tab-content').on('mouseenter', '.nd_editable-cell', function() { - $(this).children('.nd_hand-icon').show(); - if (! $(this).is(':focus')) { - $(this).children('.nd_edit-icon').show(); // ports - $(this).siblings('td').find('.nd_device-details-edit').show(); // details - } - }); - $('.tab-content').on('mouseleave', '.nd_editable-cell', function() { - $(this).children('.nd_hand-icon').hide(); - if (! $(this).is(':focus')) { - $(this).children('.nd_edit-icon').hide(); // ports - $(this).siblings('td').find('.nd_device-details-edit').hide(); // details - } - }); - $('.tab-content').on('focus', '[contenteditable=true]', function() { - $(this).children('.nd_edit-icon').hide(); // ports - $(this).siblings('td').find('.nd_device-details-edit').hide(); // details - }); + // activity for admin tasks in device details + $('#details_pane').on('click', '.nd_adminbutton', function(event) { + // stop form from submitting normally + event.preventDefault(); - // activity for port up/down control - $('#ports_pane').on('click', '.icon-hand-up', function() { - port_control(this); // save - }); - $('#ports_pane').on('click', '.icon-hand-down', function() { - port_control(this); // save - }); + // what purpose - discover/macsuck/arpnip + var mode = $(this).attr('name'); + var tr = $(this).closest('tr'); - // activity for power enable/disable control - $('#ports_pane').on('click', '.nd_power-icon', function() { - port_control(this); // save - }); - - // activity for contenteditable control - $('.tab-content').on('keydown', '[contenteditable=true]', function(event) { - var esc = event.which == 27, - nl = event.which == 13; - - if (esc) { - if (dirty) { document.execCommand('undo') } - $(this).blur(); - dirty = false; - - } - else if (nl) { - $(this).blur(); - event.preventDefault(); - dirty = false; - port_control(this); // save - } - else { - dirty = true; - } + // submit the query + $.ajax({ + type: 'POST' + ,async: true + ,dataType: 'html' + ,url: uri_base + '/ajax/control/admin/' + mode + ,data: tr.find('input[data-form="' + mode + '"]').serializeArray() + ,success: function() { + toastr.info('Queued '+ mode +' for device '+ tr.attr('data-for-device')); + } + // skip any error reporting for now + // TODO: fix sanity_ok in Netdisco Web + ,error: function() { + toastr.error('Failed to queue '+ mode +' for device '+ tr.attr('data-for-device')); + } + }); }); }); diff --git a/Netdisco/share/views/layouts/main.tt b/Netdisco/share/views/layouts/main.tt index 9f752fd4..ff68c56a 100644 --- a/Netdisco/share/views/layouts/main.tt +++ b/Netdisco/share/views/layouts/main.tt @@ -25,10 +25,11 @@ - [% IF vars.user.port_control %] + [% IF user_has_role('port_control') %] [% END %] @@ -54,7 +55,7 @@