App-Netdisco

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN


  [ENHANCEMENTS]

  * #1044 faster PortVLANMismatch query
  * #1045 custom fields are now indexed in the DB
  * add "phone" to phone_platforms default config
  * swagger-ui allows adding X-REMOTE_USER header when behind proxy

  [BUG FIXES]

  * add missing validate_remote_user default setting to config
  * fix for undef HTTP_SERVER and HTTP_PORT err when browsing swagger-ui

2.062002 - 2023-06-06

  [BUG FIXES]

  * fix mistaken release with device ports partial search default as on

2.062001 - 2023-06-05

Changes  view on Meta::CPAN


  [ENHANCEMENTS]

  * Add documentation note about SNMPv3 configuration on Cisco IOS
  * Update repository and issue tracker links for GitHub

2.034001 - 2016-11-20

  [NEW FEATURES]

  * Add validate_remote_user setting to check proxied users are known

  [ENHANCEMENTS]

  * Add Linux SNMP, LLDP setup docs (S. Hobson)
  * Add note on SLES 11 SP4 install workaround (C. Ramseyer)

  [BUG FIXES]

  * Remove some odd behaviour of cached SNMP config hints (L. Ferguson)

lib/App/Netdisco/Web/API/User.pm  view on Meta::CPAN


  my $provider = Dancer::Plugin::Auth::Extensible::auth_provider('users');

  my $want_permanent = $body->{permanent} && setting('allow_permanent_tokens');
  my $allowed_ips    = (ref $body->{allowed_ips} eq ref []) ? $body->{allowed_ips} : undef;

  $user->update({
    token_from      => time,
    token_no_expire => ($want_permanent ? \"true" : \"false"),
    (defined $allowed_ips ? (token_allowed_ips => $allowed_ips) : ()),
    ($provider->validate_api_token($user->token)
      ? () : (token => \'md5(random()::text)')),
  })->discard_changes();

  return to_json {
    username  => $username,
    api_key   => $user->token,
    permanent => ($user->token_no_expire ? \1 : \0),
    ($user->token_allowed_ips
      ? (allowed_ips => $user->token_allowed_ips) : ()),
  };

lib/App/Netdisco/Web/Auth/Provider/DBIC.pm  view on Meta::CPAN

      $database->resultset($users_table)->find({
          # FIXME: ILIKE to get case insensitive match on username, no wildcards
          $username_column => { -ilike => quotemeta($username) },
      });
    };

    # each of these settings permits no user in the database
    # so create a pseudo user entry instead
    if (not $user and
        (setting('no_auth') or
          (not setting('validate_remote_user')
           and (setting('trust_remote_user') or setting('trust_x_remote_user')) ))) {

        $user = $database->resultset($users_table)
          ->new_result({username => $username});
    }

    return $user;
}

sub validate_api_token {
    my ($self, $token) = @_;
    return unless defined $token;

    my $settings = $self->realm_settings;
    my $database = schema($settings->{schema_name})
        or die "No database connection";

    my $users_table  = $settings->{users_resultset}    || 'User';
    my $token_column = $settings->{users_token_column} || 'token';

lib/App/Netdisco/Web/AuthN.pm  view on Meta::CPAN

  # this works for API calls, too
  elsif (setting('no_auth')) {
      $username = 'guest';
  }

  return unless $username;

  # from the internals of Dancer::Plugin::Auth::Extensible
  my $provider = Dancer::Plugin::Auth::Extensible::auth_provider('users');

  # may synthesize a user if validate_remote_user=false
  return $provider->get_user_details($username);
}

# Dancer will create a session if it sees its own cookie. For the API and also
# various auto login options we need to bootstrap the session instead. If no
# auth data passed, then the hook simply returns, no session is set, and the
# user is redirected to login page.
hook 'before' => sub {
    # return if request is for endpoints not requiring a session
    return if (

lib/App/Netdisco/Web/AuthN.pm  view on Meta::CPAN

      (setting('trust_x_remote_user') or setting('trust_remote_user'))) {
        session->destroy;
        request->path_info('/');
    }
    # API calls must conform strictly to path and header requirements
    elsif (request_is_api and request->header('Authorization')) {
        # from the internals of Dancer::Plugin::Auth::Extensible
        my $provider = Dancer::Plugin::Auth::Extensible::auth_provider('users');

        my $token = request->header('Authorization');
        my $user = $provider->validate_api_token($token)
          or return send_error('{"error":"invalid or expired token"}', 401);

        session(logged_in_user => $user->username);
        session(logged_in_user_realm => 'users');
    }
    elsif ($delegated) {
        session(logged_in_user => $delegated->username);
        session(logged_in_user_realm => 'users');
    }
    else {

lib/App/Netdisco/Web/AuthN.pm  view on Meta::CPAN

    my $provider = Dancer::Plugin::Auth::Extensible::auth_provider('users');

    # get authN data from BasicAuth header used by API, put into params
    my $authheader = request->header('Authorization');
    if (defined $authheader and $authheader =~ /^Basic (.*)$/i) {
        my ($u, $p) = split(m/:/, (MIME::Base64::decode($1) || ":"));
        params->{username} = $u;
        params->{password} = $p;
    }

    # validate authN
    my ($success, $realm) = authenticate_user(param('username'),param('password'));

    # or try to get user from somewhere else
    my $delegated = _get_delegated_authn_user();

    if (($success and not
          # protect against delegated authN config but no valid user (then must ignore params)
          (not $delegated and (setting('trust_x_remote_user') or setting('trust_remote_user'))))
        or $delegated) {

lib/App/Netdisco/Web/AuthN.pm  view on Meta::CPAN


            my $body = try { from_json(request->body) } catch { {} };
            my $want_permanent = $body->{permanent} && setting('allow_permanent_tokens');
            my $allowed_ips    = (ref $body->{allowed_ips} eq ref [])
                                   ? $body->{allowed_ips} : undef;

            $user->update({
              token_from      => time,
              token_no_expire => ($want_permanent ? \"true" : \"false"),
              ($allowed_ips ? (token_allowed_ips => $allowed_ips) : ()),
              ($provider->validate_api_token($user->token)
                ? () : (token => \'md5(random()::text)')),
            })->discard_changes();

            return to_json {
              api_key   => $user->token,
              permanent => ($user->token_no_expire ? \1 : \0),
              ($user->token_allowed_ips
                ? (allowed_ips => $user->token_allowed_ips) : ()),
            };
        }

        redirect ((scalar URI::Based->new(param('return_url'))->path_query) || '/');
    }
    else {
        # invalidate session cookie
        session->destroy;

        schema('netdisco')->resultset('UserLog')->create({
          username => param('username'),
          userip => request->remote_address,
          event => (sprintf 'Login Failure (%s)', ($api ? 'API' : 'WebUI')),
          details => param('return_url'),
        });

        if ($api) {

lib/App/Netdisco/Web/AuthN.pm  view on Meta::CPAN

},
get '/logout' => sub {
    my $api = ((request->accept and request->accept =~ m/(?:json|javascript)/) ? true : false);

    # clear out API token
    my $user = schema('netdisco')->resultset('User')
      ->find({ username => session('logged_in_user')});
    $user->update({token => undef, token_from => undef})->discard_changes()
      if $user and $user->in_storage;

    # invalidate session cookie
    session->destroy;

    schema('netdisco')->resultset('UserLog')->create({
      username => session('logged_in_user'),
      userip => request->remote_address,
      event => (sprintf 'Logout (%s)', ($api ? 'API' : 'WebUI')),
      details => '',
    });

    if ($api) {

lib/App/Netdisco/Web/Plugin/Device/Neighbors.pm  view on Meta::CPAN

    my $vlan = param('vlan');
    undef $vlan if (defined $vlan and $vlan !~ m/^\d+$/);

    my $mapshow = param('mapshow');
    return if !defined $mapshow or $mapshow !~ m/^(?:all|cloud|depth)$/;
    my $depth = param('depth') || 1;
    return if $depth !~ m/^\d+$/;

    # list of groups selected by user and passed in param
    my $hgroup = (ref [] eq ref param('hgroup') ? param('hgroup') : [param('hgroup')]);
    # list of groups validated as real host groups and named host groups
    my @hgrplist = List::MoreUtils::uniq
                   grep { exists setting('host_group_displaynames')->{$_} }
                   grep { exists setting('host_groups')->{$_} }
                   grep { defined } @{ $hgroup };

    # list of locations selected by user and passed in param
    my $lgroup = (ref [] eq ref param('lgroup') ? param('lgroup') : [param('lgroup')]);
    my @lgrplist = List::MoreUtils::uniq grep { defined } @{ $lgroup };

    my %clean = ();

lib/App/Netdisco/Web/Plugin/Device/Neighbors.pm  view on Meta::CPAN

    undef $vlan if (defined $vlan and $vlan !~ m/^\d+$/);

    my $colorby = (param('colorby') || 'speed');
    my $mapshow = (param('mapshow') || 'depth');
    my $depth   = (param('depth')   || 1);
    $mapshow = 'depth' if $mapshow !~ m/^(?:all|cloud|depth)$/;
    $mapshow = 'all' unless $qdev->in_storage;

    # list of groups selected by user and passed in param
    my $hgroup = (ref [] eq ref param('hgroup') ? param('hgroup') : [param('hgroup')]);
    # list of groups validated as real host groups and named host groups
    my @hgrplist = List::MoreUtils::uniq
                   grep { exists setting('host_group_displaynames')->{$_} }
                   grep { exists setting('host_groups')->{$_} }
                   grep { defined } @{ $hgroup };

    # list of locations selected by user and passed in param
    my $lgroup = (ref [] eq ref param('lgroup') ? param('lgroup') : [param('lgroup')]);
    my @lgrplist = List::MoreUtils::uniq grep { defined } @{ $lgroup };

    my %ok_dev = ();

lib/App/Netdisco/Worker/Plugin/GetAPIKey.pm  view on Meta::CPAN

    $user->update({ token => undef, token_from => undef, token_no_expire => \"false" });
    return Status->done(sprintf 'Revoked API token for user %s', $username);
  }

  # from the internals of Dancer::Plugin::Auth::Extensible
  my $provider = Dancer::Plugin::Auth::Extensible::auth_provider('users');

  my %updates = (
    token_from     => time,
    token_no_expire => ($flag eq 'permanent' ? \"true" : \"false"),
    ($provider->validate_api_token($user->token)
      ? () : (token => \'md5(random()::text)')),
  );

  $user->update(\%updates)->discard_changes();

  return Status->done(
    sprintf 'Set %s token for user %s: %s',
      ($flag eq 'permanent' ? 'permanent' : 'session'),
      $username, $user->token);
});

share/config.yml  view on Meta::CPAN


domain_suffix: []
branding_text: Netdisco
no_auth: false
suggest_guest: false
login_banner_message: null
navbar_autocomplete: true
max_typeahead_rows: 50
trust_remote_user: false
trust_x_remote_user: false
validate_remote_user: false
api_token_lifetime: 3600
allow_permanent_tokens: false
tacacs: {}
radius: {}
ldap: {}
#  servers: []
#  user_string: 'MYDOMAIN\%USER%'
#  base: ""
#  proxy_user: ""
#  proxy_pass: ""

share/public/css/bootstrap.min.css  view on Meta::CPAN

@charset "UTF-8";/*!
 * Bootstrap  v5.3.8 (https://getbootstrap.com/)
 * Copyright 2011-2025 The Bootstrap Authors
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
 */:root,[data-bs-theme=light]{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-black:#000;--bs-white:#fff...
/*# sourceMappingURL=bootstrap.min.css.map */

share/public/javascripts/htmx.min.js  view on Meta::CPAN

var htmx=function(){"use strict";const Q={onLoad:null,process:null,on:null,off:null,trigger:null,ajax:null,find:null,findAll:null,closest:null,values:function(e,t){const n=dn(e,t||"post");return n.values},remove:null,addClass:null,removeClass:null,to...

share/public/javascripts/jquery.dataTables.min.js  view on Meta::CPAN

/*! DataTables 2.3.8
 * © SpryMedia Ltd - datatables.net/license
 */
(n=>{var r;"function"==typeof define&&define.amd?define(["jquery"],function(e){return n(e,window,document)}):"object"==typeof exports?(r=require("jquery"),"undefined"==typeof window?module.exports=function(e,t){return e=e||window,t=t||r(e),n(t,e,e.do...

share/public/javascripts/moment.min.js  view on Meta::CPAN

!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.moment=t()}(this,function(){"use strict";var H;function _(){return H.apply(null,arguments)}function y(e){return e...
//# sourceMappingURL=moment.min.js.map

share/public/swagger-ui/swagger-ui-bundle.js  view on Meta::CPAN

/*! For license information please see swagger-ui-bundle.js.LICENSE.txt */
!function webpackUniversalModuleDefinition(s,o){"object"==typeof exports&&"object"==typeof module?module.exports=o():"function"==typeof define&&define.amd?define([],o):"object"==typeof exports?exports.SwaggerUIBundle=o():s.SwaggerUIBundle=o()}(this,(...

share/public/swagger-ui/swagger-ui-bundle.js.map  view on Meta::CPAN

{"version":3,"file":"swagger-ui-bundle.js","mappings":";CAAA,SAAUA,iCAAiCC,EAAMC,GAC1B,iBAAZC,SAA0C,iBAAXC,OACxCA,OAAOD,QAAUD,IACQ,mBAAXG,QAAyBA,OAAOC,IAC9CD,OAAO,GAAIH,GACe,iBAAZC,QACdA,QAAyB,gBAAID,IAE7BD,EAAsB,gBAAIC,GAC3B,CATD,CASGK,MAAM,cCRLC,EA...

share/public/swagger-ui/swagger-ui-standalone-preset.js  view on Meta::CPAN

/*! For license information please see swagger-ui-standalone-preset.js.LICENSE.txt */
!function webpackUniversalModuleDefinition(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.SwaggerUIStandalonePreset=e():t.SwaggerUIStandalo...

share/public/swagger-ui/swagger-ui-standalone-preset.js.map  view on Meta::CPAN

{"version":3,"file":"swagger-ui-standalone-preset.js","mappings":";CAAA,SAAUA,iCAAiCC,EAAMC,GAC1B,iBAAZC,SAA0C,iBAAXC,OACxCA,OAAOD,QAAUD,IACQ,mBAAXG,QAAyBA,OAAOC,IAC9CD,OAAO,GAAIH,GACe,iBAAZC,QACdA,QAAmC,0BAAID,IAEvCD,EAAgC,0BAAIC,GACrC,CATD,CASGK,MA...



( run in 1.177 second using v1.01-cache-2.11-cpan-4ac696b4eb4 )