[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#924138: unblock: libnet-duo-perl/1.02-1



Russ Allbery <rra@debian.org> writes:

> This is an unblock request for a package I've not yet uploaded, since I
> wanted to get your guidance on how you'd prefer to get this update.

Here's the full debdiff for the 1.02-1 package, which as warned contains a
lot of extraneous documentation and test suite changes (mostly from
upstream).

-- 
Russ Allbery (rra@debian.org)               <http://www.eyrie.org/~eagle/>

diff -Nru libnet-duo-perl-1.01/.travis.yml libnet-duo-perl-1.02/.travis.yml
--- libnet-duo-perl-1.01/.travis.yml	1969-12-31 16:00:00.000000000 -0800
+++ libnet-duo-perl-1.02/.travis.yml	2019-03-06 22:02:52.000000000 -0800
@@ -0,0 +1,35 @@
+language: perl
+perl:
+  - "5.28"
+  - "5.26"
+  - "5.24"
+  - "5.22"
+  - "5.20"
+  - "5.18"
+  - "5.16"
+  - "5.14"
+
+# Only run the exhaustive tests with heavy dependencies on the latest Perl
+# version.  (These conditionals are written weirdly backwards so that the
+# command line evaluates to true.)
+
+env: AUTHOR_TESTING=1
+before_install:
+  - cpanm HTTP::Message
+  - cpanm JSON::PP
+  - cpanm LWP::UserAgent
+  - cpanm Perl6::Slurp
+  - cpanm Sub::Install
+  - cpanm URI::Escape
+  - test "$TRAVIS_PERL_VERSION" != "5.28" || cpanm Test::MinimumVersion
+  - test "$TRAVIS_PERL_VERSION" != "5.28" || cpanm Test::Perl::Critic
+  - test "$TRAVIS_PERL_VERSION" != "5.28" || cpanm Test::Pod
+  - test "$TRAVIS_PERL_VERSION" != "5.28" || cpanm Test::Pod::Coverage
+  - test "$TRAVIS_PERL_VERSION" != "5.28" || cpanm Test::Strict
+  - test "$TRAVIS_PERL_VERSION" != "5.28" || cpanm Test::Synopsis
+
+branches:
+  except:
+    - /^debian\/.*/
+    - pristine-tar
+    - /^upstream\/.*/
diff -Nru libnet-duo-perl-1.01/Build.PL libnet-duo-perl-1.02/Build.PL
--- libnet-duo-perl-1.01/Build.PL	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/Build.PL	2019-03-06 22:02:52.000000000 -0800
@@ -23,6 +23,8 @@
 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 # DEALINGS IN THE SOFTWARE.
+#
+# SPDX-License-Identifier: MIT
 
 use 5.014;
 use strict;
@@ -35,7 +37,7 @@
     module_name          => 'Net::Duo',
     dist_abstract        => 'API for Duo multifactor authentication service',
     dist_author          => 'Russ Allbery <rra@cpan.org>',
-    dist_version         => '1.01',
+    dist_version         => '1.02',
     license              => 'mit',
     recursive_test_files => 1,
 
diff -Nru libnet-duo-perl-1.01/Changes libnet-duo-perl-1.02/Changes
--- libnet-duo-perl-1.01/Changes	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/Changes	2019-03-06 22:02:52.000000000 -0800
@@ -1,5 +1,24 @@
                       User-Visible Net::Duo Changes
 
+Net::Duo 1.02 (2019-03-06)
+
+    Add basic support for paginated queries via a new call_json_paged()
+    method in Net::Duo.  Enable that support for the users() and
+    integrations() methods in Net::Duo::Admin in advance of the March 15,
+    2019 deadline for the Duo API change requiring paginated calls.  This
+    support makes repeated calls until all objects have been returned and
+    doesn't expose the pagination to the caller.  (#127964)
+
+    Change documentation URLs to point to duo.com instead of
+    duosecurity.com, following upstream redirects.  Remove the reference
+    to the Verify API, which no longer appears to be a separate API.
+
+    Update to rra-c-util 8.0:
+
+    * Add SPDX-License-Identifier information to imported files.
+    * Fix some formatting issues with latest perltidy.
+    * Exclude more files from strictness checks.
+
 Net::Duo 1.01 (2015-08-16)
 
     Adjust for an incompatible change in JSON::XS 3.0 that broke magical
diff -Nru libnet-duo-perl-1.01/LICENSE libnet-duo-perl-1.02/LICENSE
--- libnet-duo-perl-1.01/LICENSE	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/LICENSE	2019-03-06 22:02:52.000000000 -0800
@@ -5,14 +5,15 @@
  .
  For any copyright year range specified as YYYY-ZZZZ in this file, the
  range specifies every single year in that closed interval.
-Copyright: 2014 The Board of Trustees of the Leland Stanford Junior University
+Copyright: 2015, 2018-2019 Russ Allbery <rra@cpan.org>
+  2014 The Board of Trustees of the Leland Stanford Junior University
 License: Expat
 
 Files: *
 Copyright: 2011-2014
     The Board of Trustees of the Leland Stanford Junior University
-  2014 Russ Allbery <eagle@eyrie.org>
-  2015 Russ Allbery <rra@cpan.org>
+  2014-2016, 2018-2019 Russ Allbery <eagle@eyrie.org>
+  2015, 2019 Russ Allbery <rra@cpan.org>
 License: Expat
  Permission is hereby granted, free of charge, to any person obtaining a
  copy of this software and associated documentation files (the
@@ -33,3 +34,12 @@
  OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
  THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
+Files: t/data/perltidyrc
+Copyright: 2012-2013
+    The Board of Trustees of the Leland Stanford Junior University
+License: all-permissive
+ Copying and distribution of this file, with or without modification, are
+ permitted in any medium without royalty provided the copyright notice and
+ this notice are preserved.  This file is offered as-is, without any
+ warranty.
+
diff -Nru libnet-duo-perl-1.01/MANIFEST libnet-duo-perl-1.02/MANIFEST
--- libnet-duo-perl-1.01/MANIFEST	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/MANIFEST	2019-03-06 22:02:52.000000000 -0800
@@ -1,6 +1,14 @@
 .gitignore
+.travis.yml
 Build.PL
 Changes
+docs/metadata/blurb
+docs/metadata/debian/summary
+docs/metadata/description
+docs/metadata/metadata.json
+docs/metadata/orphaned
+docs/metadata/requirements
+docs/metadata/test/suffix
 lib/Net/Duo.pm
 lib/Net/Duo/Admin.pm
 lib/Net/Duo/Admin/Group.pm
@@ -17,6 +25,7 @@
 MANIFEST			This list of files
 MANIFEST.SKIP
 README
+README.md
 t/admin/groups.t
 t/admin/integrations.t
 t/admin/misc.t
@@ -33,7 +42,8 @@
 t/data/perltidyrc
 t/data/responses/group-create.json
 t/data/responses/integration.json
-t/data/responses/integrations.json
+t/data/responses/integrations-1.json
+t/data/responses/integrations-2.json
 t/data/responses/log-admin.json
 t/data/responses/log-auth.json
 t/data/responses/log-telephony.json
@@ -41,19 +51,23 @@
 t/data/responses/token-create.json
 t/data/responses/user-create.json
 t/data/responses/user.json
-t/data/responses/users.json
+t/data/responses/users-1.json
+t/data/responses/users-2.json
 t/docs/pod-coverage.t
 t/docs/pod-spelling.t
 t/docs/pod.t
+t/docs/spdx-license.t
 t/docs/synopsis.t
 t/duo/call.t
 t/duo/exception.t
 t/lib/Test/RRA.pm
 t/lib/Test/RRA/Config.pm
 t/lib/Test/RRA/Duo.pm
+t/lib/Test/RRA/ModuleVersion.pm
 t/style/critic.t
 t/style/minimum-version.t
 t/style/module-version.t
+t/style/obsolete-strings.t
 t/style/strict.t
 TODO
 META.yml
diff -Nru libnet-duo-perl-1.01/META.json libnet-duo-perl-1.02/META.json
--- libnet-duo-perl-1.01/META.json	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/META.json	2019-03-06 22:02:52.000000000 -0800
@@ -4,13 +4,13 @@
       "Russ Allbery <rra@cpan.org>"
    ],
    "dynamic_config" : 1,
-   "generated_by" : "Module::Build version 0.4214",
+   "generated_by" : "Module::Build version 0.4224",
    "license" : [
       "mit"
    ],
    "meta-spec" : {
       "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec";,
-      "version" : "2"
+      "version" : 2
    },
    "name" : "Net-Duo",
    "prereqs" : {
@@ -33,51 +33,51 @@
    "provides" : {
       "Net::Duo" : {
          "file" : "lib/Net/Duo.pm",
-         "version" : "1.01"
+         "version" : "1.02"
       },
       "Net::Duo::Admin" : {
          "file" : "lib/Net/Duo/Admin.pm",
-         "version" : "1.01"
+         "version" : "1.02"
       },
       "Net::Duo::Admin::Group" : {
          "file" : "lib/Net/Duo/Admin/Group.pm",
-         "version" : "1.01"
+         "version" : "1.02"
       },
       "Net::Duo::Admin::Integration" : {
          "file" : "lib/Net/Duo/Admin/Integration.pm",
-         "version" : "1.01"
+         "version" : "1.02"
       },
       "Net::Duo::Admin::Phone" : {
          "file" : "lib/Net/Duo/Admin/Phone.pm",
-         "version" : "1.01"
+         "version" : "1.02"
       },
       "Net::Duo::Admin::Token" : {
          "file" : "lib/Net/Duo/Admin/Token.pm",
-         "version" : "1.01"
+         "version" : "1.02"
       },
       "Net::Duo::Admin::User" : {
          "file" : "lib/Net/Duo/Admin/User.pm",
-         "version" : "1.01"
+         "version" : "1.02"
       },
       "Net::Duo::Auth" : {
          "file" : "lib/Net/Duo/Auth.pm",
-         "version" : "1.01"
+         "version" : "1.02"
       },
       "Net::Duo::Auth::Async" : {
          "file" : "lib/Net/Duo/Auth/Async.pm",
-         "version" : "1.01"
+         "version" : "1.02"
       },
       "Net::Duo::Exception" : {
          "file" : "lib/Net/Duo/Exception.pm",
-         "version" : "1.01"
+         "version" : "1.02"
       },
       "Net::Duo::Mock::Agent" : {
          "file" : "lib/Net/Duo/Mock/Agent.pm",
-         "version" : "1.01"
+         "version" : "1.02"
       },
       "Net::Duo::Object" : {
          "file" : "lib/Net/Duo/Object.pm",
-         "version" : "1.01"
+         "version" : "1.02"
       }
    },
    "release_status" : "stable",
@@ -86,12 +86,12 @@
          "web" : "https://rt.cpan.org/Public/Dist/Display.html?Name=Net-Duo";
       },
       "license" : [
-         "http://opensource.org/licenses/mit-license.php";
+         "http://www.opensource.org/licenses/mit-license.php";
       ],
       "repository" : {
          "url" : "git://git.eyrie.org/perl/duo.git"
       }
    },
-   "version" : "1.01",
-   "x_serialization_backend" : "JSON::PP version 2.27203"
+   "version" : "1.02",
+   "x_serialization_backend" : "JSON::PP version 2.97001"
 }
diff -Nru libnet-duo-perl-1.01/META.yml libnet-duo-perl-1.02/META.yml
--- libnet-duo-perl-1.01/META.yml	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/META.yml	2019-03-06 22:02:52.000000000 -0800
@@ -6,7 +6,7 @@
 configure_requires:
   Module::Build: '0.28'
 dynamic_config: 1
-generated_by: 'Module::Build version 0.4214, CPAN::Meta::Converter version 2.150005'
+generated_by: 'Module::Build version 0.4224, CPAN::Meta::Converter version 2.150010'
 license: mit
 meta-spec:
   url: http://module-build.sourceforge.net/META-spec-v1.4.html
@@ -15,40 +15,40 @@
 provides:
   Net::Duo:
     file: lib/Net/Duo.pm
-    version: '1.01'
+    version: '1.02'
   Net::Duo::Admin:
     file: lib/Net/Duo/Admin.pm
-    version: '1.01'
+    version: '1.02'
   Net::Duo::Admin::Group:
     file: lib/Net/Duo/Admin/Group.pm
-    version: '1.01'
+    version: '1.02'
   Net::Duo::Admin::Integration:
     file: lib/Net/Duo/Admin/Integration.pm
-    version: '1.01'
+    version: '1.02'
   Net::Duo::Admin::Phone:
     file: lib/Net/Duo/Admin/Phone.pm
-    version: '1.01'
+    version: '1.02'
   Net::Duo::Admin::Token:
     file: lib/Net/Duo/Admin/Token.pm
-    version: '1.01'
+    version: '1.02'
   Net::Duo::Admin::User:
     file: lib/Net/Duo/Admin/User.pm
-    version: '1.01'
+    version: '1.02'
   Net::Duo::Auth:
     file: lib/Net/Duo/Auth.pm
-    version: '1.01'
+    version: '1.02'
   Net::Duo::Auth::Async:
     file: lib/Net/Duo/Auth/Async.pm
-    version: '1.01'
+    version: '1.02'
   Net::Duo::Exception:
     file: lib/Net/Duo/Exception.pm
-    version: '1.01'
+    version: '1.02'
   Net::Duo::Mock::Agent:
     file: lib/Net/Duo/Mock/Agent.pm
-    version: '1.01'
+    version: '1.02'
   Net::Duo::Object:
     file: lib/Net/Duo/Object.pm
-    version: '1.01'
+    version: '1.02'
 requires:
   JSON: '0'
   LWP::UserAgent: '6.00'
@@ -58,7 +58,7 @@
   perl: '5.014'
 resources:
   bugtracker: https://rt.cpan.org/Public/Dist/Display.html?Name=Net-Duo
-  license: http://opensource.org/licenses/mit-license.php
+  license: http://www.opensource.org/licenses/mit-license.php
   repository: git://git.eyrie.org/perl/duo.git
-version: '1.01'
-x_serialization_backend: 'CPAN::Meta::YAML version 0.012'
+version: '1.02'
+x_serialization_backend: 'CPAN::Meta::YAML version 0.018'
diff -Nru libnet-duo-perl-1.01/README libnet-duo-perl-1.02/README
--- libnet-duo-perl-1.01/README	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/README	2019-03-06 22:02:52.000000000 -0800
@@ -1,12 +1,20 @@
-                              Net::Duo 1.01
+                              Net::Duo 1.02
              (API for Duo multifactor authentication service)
+                Maintained by Russ Allbery <rra@cpan.org>
 
-                  Written by Russ Allbery <rra@cpan.org>
-
-  Copyright 2014 The Board of Trustees of the Leland Stanford Junior
-  University, 2015 Russ Allbery <rra@cpan.org>.  This software is
-  distributed under a BSD-style license.  Please see the section LICENSE
-  below for more information.
+  Copyright 2015, 2018-2019 Russ Allbery <rra@cpan.org>.  Copyright 2014
+  The Board of Trustees of the Leland Stanford Junior University.  This
+  software is distributed under a BSD-style license.  Please see the
+  section LICENSE below for more information.
+
+WARNING
+
+  This package is orphaned.  You should use the Perl code provided by Duo
+  instead, since it will be more up-to-date with current features.  I no
+  longer maintain a large Duo deployment, and my current company uses
+  Python instead.  That said, I do still think this package provides a
+  nicer object framework for the Duo API in Perl; if you agree, please
+  feel free to adopt it and maintain it!
 
 BLURB
 
@@ -20,7 +28,7 @@
 
 DESCRIPTION
 
-  Duo Security is a cloud second-factor authentication service that
+  Duo Security [1] is a cloud second-factor authentication service that
   supports a wide variety of different mechanisms for a user to establish
   a second authentication factor.  It is intended to supplement local
   authentication systems, such as password-based authentication, with an
@@ -30,6 +38,8 @@
   provides several RESTful APIs that can perform all possible actions on
   Duo accounts, users, and their devices.
 
+  [1] https://duosecurity.com/
+
   This module implements a client for the Duo REST APIs.  It differs from
   the sample Perl implementation provided by Duo in that it attempts to
   wrap the Duo data model and JSON information in Perl objects and provide
@@ -40,9 +50,9 @@
 
   The API implementation is currently incomplete and contains just the
   calls required by Stanford's integration.  Only the Auth and Admin APIs
-  are implemented, and both are partial.  However, the Net::Duo call and
-  call_json methods can be used to make calls to APIs that aren't fully
-  implemented.
+  are implemented, and both are partial.  However, the Net::Duo call,
+  call_json, and call_json_paged methods can be used to make calls to APIs
+  that aren't fully implemented.
 
 REQUIREMENTS
 
@@ -50,23 +60,47 @@
   this module.  The following additional Perl modules are required to use
   it:
 
-      JSON
-      HTTP::Message
-      LWP::UserAgent 6.00 or later (part of libwww-perl)
-      Perl6::Slurp
-      Sub::Install
-      URI::Escape (part of URI)
+  * JSON
+  * HTTP::Message
+  * LWP::UserAgent 6.00 or later (part of libwww-perl)
+  * Perl6::Slurp
+  * Sub::Install
+  * URI::Escape (part of URI)
+
+  All are available on CPAN.
+
+BUILDING AND INSTALLATION
+
+  Net::Duo uses Module::Build and can be installed using the same process
+  as any other Module::Build module:
+
+      perl Build.PL
+      ./Build
+      ./Build install
+
+  You will have to run the last command as root unless you're installing
+  into a local Perl module tree in your home directory.
+
+TESTING
+
+  Net::Duo comes with a test suite, which you can run after building with:
+
+      ./Build test
+
+  If a test fails, you can run a single test with verbose output via:
+
+      ./Build test --test_files <path-to-test>
 
   The following additional Perl modules will be used by the test suite if
   present:
 
-      Test::MinimumVersion
-      Test::Perl::Critic
-      Test::Pod
-      Test::Pod::Coverage
-      Test::Spelling
-      Test::Strict
-      Test::Synopsis
+  * Test::MinimumVersion
+  * Test::Perl::Critic
+  * Test::Pod
+  * Test::Pod::Coverage
+  * Test::Spelling
+  * Test::Strict
+  * Test::Synopsis
 
   All are available on CPAN.  Those tests will be skipped if the modules
   are not available.
@@ -78,60 +112,52 @@
   many problems, set the environment variable AUTHOR_TESTING to a true
   value.
 
-BUILDING AND INSTALLATION
-
-  Net::Duo uses Module::Build and can be installed using the same process
-  as any other Module::Build module:
-
-      perl Build.PL
-      ./Build
-      ./Build test
-      ./Build install
-
-  You will have to run the last command as root unless you're installing
-  into a local Perl module tree in your home directory.
-
 SUPPORT
 
   The Net::Duo web page at:
 
-      http://www.eyrie.org/~eagle/software/net-duo/
+      https://www.eyrie.org/~eagle/software/net-duo/
 
   will always have the current version of this package, the current
   documentation, and pointers to any additional resources.
 
-  For bug tracking, this package uses the CPAN bug tracker at:
+  For bug tracking, use the CPAN bug tracker at:
 
-      https://rt.cpan.org/Public/Dist/Display.html?Name=Net-Duo
+      https://rt.cpan.org/Dist/Display.html?Name=Net-Duo
 
-  I welcome bug reports and patches for this package at rra@cpan.org or
-  via the CPAN bug tracker.  However, please be aware that I tend to be
-  extremely busy and work projects often take priority.  I'll save your
-  mail and get to it as soon as I can, but it may take me a couple of
-  months.
+  However, please be aware that I tend to be extremely busy and work
+  projects often take priority.  I'll save your report and get to it as
+  soon as I can, but it may take me a couple of months.
 
 SOURCE REPOSITORY
 
-  Net::Duo is maintained using Git.  You can access the current source by
-  cloning the repository at:
+  Net::Duo is maintained using Git.  You can access the current source on
+  GitHub at:
+
+      https://github.com/rra/net-duo
+
+  or by cloning the repository at:
 
-      git://git.eyrie.org/perl/net-duo.git
+      https://git.eyrie.org/git/perl/net-duo.git
 
   or view the repository via the web at:
 
-      http://git.eyrie.org/?p=perl/net-duo.git
+      https://git.eyrie.org/?p=perl/net-duo.git
 
-  When contributing modifications, either patches (possibly generated by
-  git format-patch) or Git pull requests are welcome.
+  The eyrie.org repository is the canonical one, maintained by the author,
+  but using GitHub is probably more convenient for most purposes.  Pull
+  requests are gratefully reviewed and normally accepted.  It's probably
+  better to use the CPAN bug tracker than GitHub issues, though, to keep
+  all Perl module issues in the same place.
 
 LICENSE
 
   The Net::Duo package as a whole is covered by the following copyright
   statement and license:
 
+    Copyright 2015, 2018-2019 Russ Allbery <rra@cpan.org>
     Copyright 2014
         The Board of Trustees of the Leland Stanford Junior University
-    Copyright 2015 Russ Allbery <rra@cpan.org>
 
     Permission is hereby granted, free of charge, to any person obtaining
     a copy of this software and associated documentation files (the
@@ -140,10 +166,10 @@
     distribute, sublicense, and/or sell copies of the Software, and to
     permit persons to whom the Software is furnished to do so, subject to
     the following conditions:
-    
+
     The above copyright notice and this permission notice shall be
     included in all copies or substantial portions of the Software.
-    
+
     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
@@ -152,12 +178,16 @@
     TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-  All individual files without an explicit exception below are released
-  under this license.  Some files may have additional copyright holders as
-  noted in those files.  There is detailed information about the licensing
-  of each file in the LICENSE file in this distribution.
-
   Some files in this distribution are individually released under
   different licenses, all of which are compatible with the above general
   package license but which may require preservation of additional
-  notices.  All required notices are preserved in the LICENSE file.
+  notices.  All required notices, and detailed information about the
+  licensing of each file, are recorded in the LICENSE file.
+
+  Files covered by a license with an assigned SPDX License Identifier
+  include SPDX-License-Identifier tags to enable automated processing of
+  license information.  See https://spdx.org/licenses/ for more
+  information.
+
+  For any copyright range specified by files in this package as YYYY-ZZZZ,
+  the range specifies every single year in that closed interval.
diff -Nru libnet-duo-perl-1.01/README.md libnet-duo-perl-1.02/README.md
--- libnet-duo-perl-1.01/README.md	1969-12-31 16:00:00.000000000 -0800
+++ libnet-duo-perl-1.02/README.md	2019-03-06 22:02:52.000000000 -0800
@@ -0,0 +1,191 @@
+# Net::Duo 1.02
+
+[![Build
+status](https://travis-ci.org/rra/net-duo.svg?branch=master)](https://travis-ci.org/rra/net-duo)
+[![CPAN
+version](https://img.shields.io/cpan/v/Net-Duo.svg)](https://metacpan.org/release/Net-Duo)
+
+Copyright 2015, 2018-2019 Russ Allbery <rra@cpan.org>.  Copyright 2014 The
+Board of Trustees of the Leland Stanford Junior University.  This software
+is distributed under a BSD-style license.  Please see the section
+[License](#license) below for more information.
+
+## Warning
+
+**This package is orphaned.** You should use the Perl code provided by Duo
+instead, since it will be more up-to-date with current features.  I no
+longer maintain a large Duo deployment, and my current company uses Python
+instead.  That said, I do still think this package provides a nicer object
+framework for the Duo API in Perl; if you agree, please feel free to adopt
+it and maintain it!
+
+## Blurb
+
+Net::Duo provides an object-oriented Perl interface for the Duo Security
+REST APIs.  It attempts to abstract some of the API details and provide an
+object-oriented view of the returned objects in order to make use of the
+API in Perl code more natural than dealing with JSON data structures
+directly.  Currently, some parts of the Auth and Admin APIs are
+implemented alongside with generic methods to call any of the JSON-based
+APIs.
+
+## Description
+
+[Duo Security](https://duosecurity.com/) is a cloud second-factor
+authentication service that supports a wide variety of different
+mechanisms for a user to establish a second authentication factor.  It is
+intended to supplement local authentication systems, such as
+password-based authentication, with an unrelated second factor: possession
+of a device, or access to a phone.  Duo Security provides direct
+integration with a variety of applications and a self-service web sign-up
+interface, but for more control, it also provides several RESTful APIs
+that can perform all possible actions on Duo accounts, users, and their
+devices.
+
+This module implements a client for the Duo REST APIs.  It differs from
+the sample Perl implementation provided by Duo in that it attempts to wrap
+the Duo data model and JSON information in Perl objects and provide
+logical and convenient methods on those objects to make writing Perl
+clients of Duo simpler and easier.  It tries to abstract portions of the
+API, such as the endpoint URLs, that allow for more natural and less
+cluttered Perl code than manipulating the JSON data structures directly.
+
+The API implementation is currently incomplete and contains just the calls
+required by Stanford's integration.  Only the Auth and Admin APIs are
+implemented, and both are partial.  However, the Net::Duo `call`,
+`call_json`, and `call_json_paged` methods can be used to make calls to
+APIs that aren't fully implemented.
+
+## Requirements
+
+Perl 5.14 or later and Module::Build 0.28 or later are required to build
+this module.  The following additional Perl modules are required to use
+it:
+
+* JSON
+* HTTP::Message
+* LWP::UserAgent 6.00 or later (part of libwww-perl)
+* Perl6::Slurp
+* Sub::Install
+* URI::Escape (part of URI)
+
+All are available on CPAN.
+
+## Building and Installation
+
+Net::Duo uses Module::Build and can be installed using the same process as
+any other Module::Build module:
+
+```
+    perl Build.PL
+    ./Build
+    ./Build install
+```
+
+You will have to run the last command as root unless you're installing
+into a local Perl module tree in your home directory.
+
+## Testing
+
+Net::Duo comes with a test suite, which you can run after building with:
+
+```
+    ./Build test
+```
+
+If a test fails, you can run a single test with verbose output via:
+
+```
+    ./Build test --test_files <path-to-test>
+```
+
+The following additional Perl modules will be used by the test suite if
+present:
+
+* Test::MinimumVersion
+* Test::Perl::Critic
+* Test::Pod
+* Test::Pod::Coverage
+* Test::Spelling
+* Test::Strict
+* Test::Synopsis
+
+All are available on CPAN.  Those tests will be skipped if the modules are
+not available.
+
+To enable tests that don't detect functionality problems but are used to
+sanity-check the release, set the environment variable `RELEASE_TESTING`
+to a true value.  To enable tests that may be sensitive to the local
+environment or that produce a lot of false positives without uncovering
+many problems, set the environment variable `AUTHOR_TESTING` to a true
+value.
+
+## Support
+
+The [Net::Duo web page](https://www.eyrie.org/~eagle/software/net-duo/)
+will always have the current version of this package, the current
+documentation, and pointers to any additional resources.
+
+For bug tracking, use the [CPAN bug
+tracker](https://rt.cpan.org/Dist/Display.html?Name=Net-Duo).  However,
+please be aware that I tend to be extremely busy and work projects often
+take priority.  I'll save your report and get to it as soon as I can, but
+it may take me a couple of months.
+
+## Source Repository
+
+Net::Duo is maintained using Git.  You can access the current source on
+[GitHub](https://github.com/rra/net-duo) or by cloning the repository at:
+
+https://git.eyrie.org/git/perl/net-duo.git
+
+or [view the repository on the
+web](https://git.eyrie.org/?p=perl/net-duo.git).
+
+The eyrie.org repository is the canonical one, maintained by the author,
+but using GitHub is probably more convenient for most purposes.  Pull
+requests are gratefully reviewed and normally accepted.  It's probably
+better to use the CPAN bug tracker than GitHub issues, though, to keep all
+Perl module issues in the same place.
+
+## License
+
+The Net::Duo package as a whole is covered by the following copyright
+statement and license:
+
+> Copyright 2015, 2018-2019
+>     Russ Allbery <rra@cpan.org>
+>
+> Copyright 2014
+>     The Board of Trustees of the Leland Stanford Junior University
+>
+> Permission is hereby granted, free of charge, to any person obtaining a
+> copy of this software and associated documentation files (the "Software"),
+> to deal in the Software without restriction, including without limitation
+> the rights to use, copy, modify, merge, publish, distribute, sublicense,
+> and/or sell copies of the Software, and to permit persons to whom the
+> Software is furnished to do so, subject to the following conditions:
+>
+> The above copyright notice and this permission notice shall be included in
+> all copies or substantial portions of the Software.
+>
+> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+> THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+> FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+> DEALINGS IN THE SOFTWARE.
+
+Some files in this distribution are individually released under different
+licenses, all of which are compatible with the above general package
+license but which may require preservation of additional notices.  All
+required notices, and detailed information about the licensing of each
+file, are recorded in the LICENSE file.
+
+Files covered by a license with an assigned SPDX License Identifier
+include SPDX-License-Identifier tags to enable automated processing of
+license information.  See https://spdx.org/licenses/ for more information.
+
+For any copyright range specified by files in this package as YYYY-ZZZZ,
+the range specifies every single year in that closed interval.
diff -Nru libnet-duo-perl-1.01/debian/changelog libnet-duo-perl-1.02/debian/changelog
--- libnet-duo-perl-1.01/debian/changelog	2018-05-07 16:59:34.000000000 -0700
+++ libnet-duo-perl-1.02/debian/changelog	2019-03-09 12:18:24.000000000 -0800
@@ -1,3 +1,16 @@
+libnet-duo-perl (1.02-1) UNRELEASED; urgency=medium
+
+  * New upstream release.
+    - Use pagination to retrieve users and integrations via the admin
+      API.  (Closes: #924127)
+  * Update to debhelper compatibility level V12.
+    - Depend on debhelper-compat instead of using debian/compat.
+  * Update to standards version 4.3.0 (no changes required).
+  * Stop explicitly setting xz compression in debian/source/options.
+  * Refresh upstream signing key.
+
+ -- Russ Allbery <rra@debian.org>  Sat, 09 Mar 2019 12:18:24 -0800
+
 libnet-duo-perl (1.01-2) unstable; urgency=medium
 
   [ Russ Allbery ]
diff -Nru libnet-duo-perl-1.01/debian/compat libnet-duo-perl-1.02/debian/compat
--- libnet-duo-perl-1.01/debian/compat	2018-05-07 16:59:34.000000000 -0700
+++ libnet-duo-perl-1.02/debian/compat	1969-12-31 16:00:00.000000000 -0800
@@ -1 +0,0 @@
-11
diff -Nru libnet-duo-perl-1.01/debian/control libnet-duo-perl-1.02/debian/control
--- libnet-duo-perl-1.01/debian/control	2018-05-07 16:59:34.000000000 -0700
+++ libnet-duo-perl-1.02/debian/control	2019-03-09 12:18:24.000000000 -0800
@@ -2,7 +2,7 @@
 Section: perl
 Priority: optional
 Build-Depends:
- debhelper (>= 11),
+ debhelper-compat (= 12),
  libjson-perl,
  libmodule-build-perl,
  libperl6-slurp-perl,
@@ -18,7 +18,7 @@
 Uploaders:
  Russ Allbery <rra@debian.org>,
 Rules-Requires-Root: no
-Standards-Version: 4.1.4
+Standards-Version: 4.3.0
 Homepage: https://www.eyrie.org/~eagle/software/net-duo/
 Vcs-Git: https://salsa.debian.org/perl-team/modules/packages/libnet-duo-perl.git
 Vcs-Browser: https://salsa.debian.org/perl-team/modules/packages/libnet-duo-perl
diff -Nru libnet-duo-perl-1.01/debian/copyright libnet-duo-perl-1.02/debian/copyright
--- libnet-duo-perl-1.01/debian/copyright	2018-05-07 16:59:34.000000000 -0700
+++ libnet-duo-perl-1.02/debian/copyright	2019-03-09 12:18:24.000000000 -0800
@@ -5,6 +5,8 @@
 Files: *
 Copyright: 2011-2014
     The Board of Trustees of the Leland Stanford Junior University
+  2014-2016, 2018-2019 Russ Allbery <eagle@eyrie.org>
+  2015, 2019 Russ Allbery <rra@cpan.org>
 License: Expat
  Permission is hereby granted, free of charge, to any person obtaining a
  copy of this software and associated documentation files (the
@@ -24,3 +26,12 @@
  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
  OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
  THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Files: t/data/perltidyrc
+Copyright: 2012-2013
+    The Board of Trustees of the Leland Stanford Junior University
+License: all-permissive
+ Copying and distribution of this file, with or without modification, are
+ permitted in any medium without royalty provided the copyright notice and
+ this notice are preserved.  This file is offered as-is, without any
+ warranty.
diff -Nru libnet-duo-perl-1.01/debian/source/options libnet-duo-perl-1.02/debian/source/options
--- libnet-duo-perl-1.01/debian/source/options	2018-05-07 16:59:34.000000000 -0700
+++ libnet-duo-perl-1.02/debian/source/options	1969-12-31 16:00:00.000000000 -0800
@@ -1 +0,0 @@
-compression = "xz"
diff -Nru libnet-duo-perl-1.01/debian/upstream/signing-key.asc libnet-duo-perl-1.02/debian/upstream/signing-key.asc
--- libnet-duo-perl-1.01/debian/upstream/signing-key.asc	2018-05-07 16:59:34.000000000 -0700
+++ libnet-duo-perl-1.02/debian/upstream/signing-key.asc	2019-03-09 12:18:24.000000000 -0800
@@ -11,128 +11,131 @@
 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
-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
-mE181sJQzenKfTQhsA7/ZV7rvurnfnMd5TMHR27qZLGkB7CyKHv+aSjkklVtTCUW
-0q68RweRJr2/9gojIqPvyUchuGnBlJDx6KAXJxhvYSTJrwA+H78XnA6AeuL+Qn+N
-tIL9veJquayxFq0hpt8tLhs6EAcNHumMafYgf2hqA+uj1i2/Tb25VEUbdpCIssZO
-BTW7tCpSdXNzIEFsbGJlcnkgPGVhZ2xlQHdpbmRsb3JkLnN0YW5mb3JkLmVkdT6J
-AkEEMAEIACsFAlPu130kHSBTeXN0ZW0gYW5kIGFkZHJlc3Mgbm8gbG9uZ2VyIGV4
-aXN0AAoJENFdMTiCAEFz6+cP/0LpJNgtaFQTc6EASrZJepp0qR4jSEj0rAxwuQtK
-iFPRM0t9mBrGRLCwWDGssGpUmH5s2xbrg+f80gMPL85CFO2NzRdAX9vak+J53Di6
-dn4f1MrAQouQPt/53R8txN6cyerp3Fyk66j8KVLZSu17crpLxJ7GHTN8UPjHHhmj
-EFxSI7VifM9/9RlKeGY1Tj6+fn8SKRPDJwEE2HwoSDhmmvFbqDJQe/fk7TaaG/G6
-F+iTASFsiqdwK5ffDT1Yi18K3eaCHcuIbgXb0qXHH7hYxIgXQuAOL6t6LQmRz5HC
-upCjUBx9W25AJLTogenj2hgNbBe8LDbbELI5Foxn4Dhb+6dpepvCEAfUirxYiKHc
-RsZB0E8D6qObQUgXmUZ45Z43jJf9Wk8LPVxnN6Lny3DVrGgE5r7sr/QyzDjRuDQg
-Rsa9uXpv7rX2KYKpyZStvgA5SM+XdYTyaIlDgGiTIVjNWkb+3LAzrHXV8CvDSn7F
-CuvpOax+z1+cJXnFFDFOrHN/tC/8w2U29L+fmJRCeJhj7MKjT/IobkdyuS2juBDg
-r7QDmsHcI07ElbZGp4zjKnZYx42hkgVyjJBIcUdU3UGazTsnmF9t7Ek8JrZNannR
-u1KC6rgOc8tP/FsVJmllgnr57CXazF++kLy7qwqgw2T9EA89WMtkakKLyMoit86C
-E40TtCJSdXNzIEFsbGJlcnkgPHJyYUBjcy5zdGFuZm9yZC5lZHU+iQI9BBMBCAAn
-AhsDBQsJCAcDBRUKCQgLBRYCAwEAAh4BAheABQJX3ZNQBQkTYecHAAoJENFdMTiC
-AEFzV0kP/3qNPGkGvHAq4fwTI9Bvp/OFrALKfsYMeAnYpo86+P5RCwS8kr2YAr/s
-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
-m8LXrdtLpPopB3kcVeW4FOE4hkHt38n/f3o=
-=wYxx
+tB5SdXNzIEFsbGJlcnkgPGVhZ2xlQGV5cmllLm9yZz6JAlcEEwEIAEECGwMFCwkI
+BwMFFQoJCAsFFgIDAQACHgECF4ACGQEWIQTnhDZOjd57s3D72erRXTE4ggBBcwUC
+W6BpnAUJFyS9agAKCRDRXTE4ggBBc4PgEACa9sft6EPV/f43Xl304pBeDEIbWWYs
+gWaJ2A/zT/1WGP/Jvtd/pQ9ShjII42tX+aKoEFdTBN/QHArmYArM+9Rcy8+iL+h+
+uGfSbB0X6PdGjtjuzQWjrzDUf2g8KsP09jl86kWUH9XpReOifL+KntkjRreif/dY
+ALOcUYfbwWBlugCwZC7ZjIrlUqk/X7l1AY+XhVqkVWKcObRTzfjulgYLsO51COre
+SkoRST5tsI1VenoLmAOb154YwEmLVHCBF41kWP38ezYJGx7CvAdM3bQM/jzlleGS
+IXZnCKt/Fratv4E7xNPDX/DCyRGpItqPjyYB7KN5YrIe3xLzJ0f+2hkdCqYHok+k
+HHXoV6Ms7IsM0j2vo1bHqm6UIw8VwhWiSLZkYOmk4P4eWbgiBm7EnjEalySVRftQ
+re5Hz5gK1oVZgI48mJ6ZKR93+YsyNNLY21ngwCpcfeg51H0qp+Q9ScNGMQ5iH0gA
+TIDHtpn2Dl0i7k79l0TjWAeezWXsvYZeXy4k7NlFul9N0Rs7eLcoZdKKeZ3JvWH2
+lqXFo0C+pqTyjYKwsxNK0mRwOUN3LI6S9oFe6vQKWx8M1m/I3GCVXdjnz0fihP3n
+re9aNijOT9UCl+larNiKxktkU4dRLRbERsHN6goK/k7t4ozOUFBPj1DgIrv+eotP
+nVPdQAnei0Z4TbQfUnVzcyBBbGxiZXJ5IDxycmFAc3RhbmZvcmQuZWR1PokCVAQT
+AQgAPgIbAwIeAQIXgAULCQgHAwUVCgkICwUWAgMBABYhBOeENk6N3nuzcPvZ6tFd
+MTiCAEFzBQJboGmpBQkXJL1qAAoJENFdMTiCAEFz37IP/jWSv3F8AsCBcWaBqZNh
+iMhYYWcb8h52Dq0DRb1Pcm6obNcni2R0xPlbczI/oKqZGCd851dPaDpJ5y+vPEfK
+YAbb72dwBLFPpAJHxcNVNb/8fSrmI5Vbh6OfSgm/Dwgz6hS9hFiEROg4IUwKz64o
+0I8BmLgwjAL2gGaRQukeKGxCuO3WsMk8u0b9rpPJKMEWIfzkdJ+gxBgi1oN57Ayd
+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
+QJZ73F2Yn8ijH8iCQE/v7hrRBzs=
+=3p8N
 -----END PGP PUBLIC KEY BLOCK-----
diff -Nru libnet-duo-perl-1.01/docs/metadata/blurb libnet-duo-perl-1.02/docs/metadata/blurb
--- libnet-duo-perl-1.01/docs/metadata/blurb	1969-12-31 16:00:00.000000000 -0800
+++ libnet-duo-perl-1.02/docs/metadata/blurb	2019-03-06 22:02:52.000000000 -0800
@@ -0,0 +1,7 @@
+Net::Duo provides an object-oriented Perl interface for the Duo Security
+REST APIs.  It attempts to abstract some of the API details and provide an
+object-oriented view of the returned objects in order to make use of the
+API in Perl code more natural than dealing with JSON data structures
+directly.  Currently, some parts of the Auth and Admin APIs are
+implemented alongside with generic methods to call any of the JSON-based
+APIs.
diff -Nru libnet-duo-perl-1.01/docs/metadata/debian/summary libnet-duo-perl-1.02/docs/metadata/debian/summary
--- libnet-duo-perl-1.01/docs/metadata/debian/summary	1969-12-31 16:00:00.000000000 -0800
+++ libnet-duo-perl-1.02/docs/metadata/debian/summary	2019-03-06 22:02:52.000000000 -0800
@@ -0,0 +1,2 @@
+A Debian package (as libnet-duo-perl) is included in Debian 8.0 (jessie)
+and later releases.
diff -Nru libnet-duo-perl-1.01/docs/metadata/description libnet-duo-perl-1.02/docs/metadata/description
--- libnet-duo-perl-1.01/docs/metadata/description	1969-12-31 16:00:00.000000000 -0800
+++ libnet-duo-perl-1.02/docs/metadata/description	2019-03-06 22:02:52.000000000 -0800
@@ -0,0 +1,24 @@
+[Duo Security](https://duosecurity.com/) is a cloud second-factor
+authentication service that supports a wide variety of different
+mechanisms for a user to establish a second authentication factor.  It is
+intended to supplement local authentication systems, such as
+password-based authentication, with an unrelated second factor: possession
+of a device, or access to a phone.  Duo Security provides direct
+integration with a variety of applications and a self-service web sign-up
+interface, but for more control, it also provides several RESTful APIs
+that can perform all possible actions on Duo accounts, users, and their
+devices.
+
+This module implements a client for the Duo REST APIs.  It differs from
+the sample Perl implementation provided by Duo in that it attempts to wrap
+the Duo data model and JSON information in Perl objects and provide
+logical and convenient methods on those objects to make writing Perl
+clients of Duo simpler and easier.  It tries to abstract portions of the
+API, such as the endpoint URLs, that allow for more natural and less
+cluttered Perl code than manipulating the JSON data structures directly.
+
+The API implementation is currently incomplete and contains just the calls
+required by Stanford's integration.  Only the Auth and Admin APIs are
+implemented, and both are partial.  However, the Net::Duo `call`,
+`call_json`, and `call_json_paged` methods can be used to make calls to
+APIs that aren't fully implemented.
diff -Nru libnet-duo-perl-1.01/docs/metadata/metadata.json libnet-duo-perl-1.02/docs/metadata/metadata.json
--- libnet-duo-perl-1.01/docs/metadata/metadata.json	1969-12-31 16:00:00.000000000 -0800
+++ libnet-duo-perl-1.02/docs/metadata/metadata.json	2019-03-06 22:02:52.000000000 -0800
@@ -0,0 +1,96 @@
+{
+    "name": "Net::Duo",
+    "version": "1.02",
+    "synopsis": "API for Duo multifactor authentication service",
+    "maintainer": "Russ Allbery <rra@cpan.org>",
+    "orphaned": true,
+    "copyrights": [
+        {
+            "holder": "Russ Allbery <rra@cpan.org>",
+            "years": "2015, 2018-2019",
+        },
+        {
+            "holder": "The Board of Trustees of the Leland Stanford Junior University",
+            "years": "2014",
+        },
+    ],
+    "license": "Expat",
+    "build": {
+        "lancaster": true,
+        "type": "Module::Build",
+    },
+    "support": {
+        "cpan": "Net-Duo",
+        "email": "rra@cpan.org",
+        "web": "https://www.eyrie.org/~eagle/software/net-duo/";,
+    },
+    "vcs": {
+        "type": "Git",
+        "url": "https://git.eyrie.org/git/perl/net-duo.git";,
+        "browse": "https://git.eyrie.org/?p=perl/net-duo.git";,
+        "github": "rra/net-duo",
+        "openhub": "https://www.openhub.net/p/net-duo";,
+        "travis": "rra/net-duo",
+    },
+    "distribution": {
+        "section": "perl",
+        "tarname": "Net-Duo",
+        "version": "net-duo",
+        "cpan": "Net-Duo",
+    },
+    "packaging": {
+        "debian": "libnet-duo-perl",
+    },
+    "docs": {
+        "api": [
+            {
+                "name": "api/duo",
+                "title": "Net::Duo",
+            },
+            {
+                "name": "api/admin",
+                "title": "Net::Duo::Admin",
+            },
+            {
+                "name": "api/admin-group",
+                "title": "Net::Duo::Admin::Group",
+            },
+            {
+                "name": "api/admin-integration",
+                "title": "Net::Duo::Admin::Integration",
+            },
+            {
+                "name": "api/admin-phone",
+                "title": "Net::Duo::Admin::Phone",
+            },
+            {
+                "name": "api/admin-token",
+                "title": "Net::Duo::Admin::Token",
+            },
+            {
+                "name": "api/admin-user",
+                "title": "Net::Duo::Admin::User",
+            },
+            {
+                "name": "api/auth",
+                "title": "Net::Duo::Auth",
+            },
+            {
+                "name": "api/auth-async",
+                "title": "Net::Duo::Auth::Async",
+            },
+            {
+                "name": "api/exception",
+                "title": "Net::Duo::Exception",
+            },
+            {
+                "name": "api/mock-agent",
+                "title": "Net::Duo::Mock::Agent",
+            },
+            {
+                "name": "api/object",
+                "title": "Net::Duo::Object",
+            },
+        ],
+    },
+}
diff -Nru libnet-duo-perl-1.01/docs/metadata/orphaned libnet-duo-perl-1.02/docs/metadata/orphaned
--- libnet-duo-perl-1.01/docs/metadata/orphaned	1969-12-31 16:00:00.000000000 -0800
+++ libnet-duo-perl-1.02/docs/metadata/orphaned	2019-03-06 22:02:52.000000000 -0800
@@ -0,0 +1,5 @@
+You should use the Perl code provided by Duo instead, since it will be
+more up-to-date with current features.  I no longer maintain a large Duo
+deployment, and my current company uses Python instead.  That said, I do
+still think this package provides a nicer object framework for the Duo API
+in Perl; if you agree, please feel free to adopt it and maintain it!
diff -Nru libnet-duo-perl-1.01/docs/metadata/requirements libnet-duo-perl-1.02/docs/metadata/requirements
--- libnet-duo-perl-1.01/docs/metadata/requirements	1969-12-31 16:00:00.000000000 -0800
+++ libnet-duo-perl-1.02/docs/metadata/requirements	2019-03-06 22:02:52.000000000 -0800
@@ -0,0 +1,12 @@
+Perl 5.14 or later and Module::Build 0.28 or later are required to build
+this module.  The following additional Perl modules are required to use
+it:
+
+* JSON
+* HTTP::Message
+* LWP::UserAgent 6.00 or later (part of libwww-perl)
+* Perl6::Slurp
+* Sub::Install
+* URI::Escape (part of URI)
+
+All are available on CPAN.
diff -Nru libnet-duo-perl-1.01/docs/metadata/test/suffix libnet-duo-perl-1.02/docs/metadata/test/suffix
--- libnet-duo-perl-1.01/docs/metadata/test/suffix	1969-12-31 16:00:00.000000000 -0800
+++ libnet-duo-perl-1.02/docs/metadata/test/suffix	2019-03-06 22:02:52.000000000 -0800
@@ -0,0 +1,13 @@
+The following additional Perl modules will be used by the test suite if
+present:
+
+* Test::MinimumVersion
+* Test::Perl::Critic
+* Test::Pod
+* Test::Pod::Coverage
+* Test::Spelling
+* Test::Strict
+* Test::Synopsis
+
+All are available on CPAN.  Those tests will be skipped if the modules are
+not available.
diff -Nru libnet-duo-perl-1.01/lib/Net/Duo/Admin/Group.pm libnet-duo-perl-1.02/lib/Net/Duo/Admin/Group.pm
--- libnet-duo-perl-1.01/lib/Net/Duo/Admin/Group.pm	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/lib/Net/Duo/Admin/Group.pm	2019-03-06 22:02:52.000000000 -0800
@@ -2,8 +2,10 @@
 #
 # This class wraps the Duo representation of a single Duo group, as returned
 # by (for example) the Admin /groups REST endpoint.
+#
+# SPDX-License-Identifier: MIT
 
-package Net::Duo::Admin::Group 1.01;
+package Net::Duo::Admin::Group 1.02;
 
 use 5.014;
 use strict;
@@ -253,10 +255,14 @@
 
 L<Net::Duo::Admin>
 
-L<Duo Admin API for groups|https://www.duosecurity.com/docs/adminapi#groups>
+L<Duo Admin API for groups|https://www.duo.com/docs/adminapi#groups>
 
 This module is part of the Net::Duo distribution.  The current version of
 Net::Duo is available from CPAN, or directly from its web site at
-L<http://www.eyrie.org/~eagle/software/net-duo/>.
+L<https://www.eyrie.org/~eagle/software/net-duo/>.
 
 =cut
+
+# Local Variables:
+# copyright-at-end-flag: t
+# End:
diff -Nru libnet-duo-perl-1.01/lib/Net/Duo/Admin/Integration.pm libnet-duo-perl-1.02/lib/Net/Duo/Admin/Integration.pm
--- libnet-duo-perl-1.01/lib/Net/Duo/Admin/Integration.pm	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/lib/Net/Duo/Admin/Integration.pm	2019-03-06 22:02:52.000000000 -0800
@@ -2,8 +2,10 @@
 #
 # This class wraps the Duo representation of a single Duo integration, as
 # returned by (for example) the Admin /integrations REST endpoint.
+#
+# SPDX-License-Identifier: MIT
 
-package Net::Duo::Admin::Integration 1.01;
+package Net::Duo::Admin::Integration 1.02;
 
 use 5.014;
 use strict;
@@ -442,10 +444,14 @@
 
 L<Net::Duo::Admin>
 
-L<Duo Admin API for integrations|https://www.duosecurity.com/docs/adminapi#integrations>
+L<Duo Admin API for integrations|https://www.duo.com/docs/adminapi#integrations>
 
 This module is part of the Net::Duo distribution.  The current version of
 Net::Duo is available from CPAN, or directly from its web site at
-L<http://www.eyrie.org/~eagle/software/net-duo/>.
+L<https://www.eyrie.org/~eagle/software/net-duo/>.
 
 =cut
+
+# Local Variables:
+# copyright-at-end-flag: t
+# End:
diff -Nru libnet-duo-perl-1.01/lib/Net/Duo/Admin/Phone.pm libnet-duo-perl-1.02/lib/Net/Duo/Admin/Phone.pm
--- libnet-duo-perl-1.01/lib/Net/Duo/Admin/Phone.pm	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/lib/Net/Duo/Admin/Phone.pm	2019-03-06 22:02:52.000000000 -0800
@@ -2,8 +2,10 @@
 #
 # This class wraps the Duo representation of a single Duo phone, as returned
 # by (for example) the Admin /phones REST endpoint.
+#
+# SPDX-License-Identifier: MIT
 
-package Net::Duo::Admin::Phone 1.01;
+package Net::Duo::Admin::Phone 1.02;
 
 use 5.014;
 use strict;
@@ -444,10 +446,14 @@
 
 L<Net::Duo::Admin>
 
-L<Duo Admin API for phones|https://www.duosecurity.com/docs/adminapi#phones>
+L<Duo Admin API for phones|https://www.duo.com/docs/adminapi#phones>
 
 This module is part of the Net::Duo distribution.  The current version of
 Net::Duo is available from CPAN, or directly from its web site at
-L<http://www.eyrie.org/~eagle/software/net-duo/>.
+L<https://www.eyrie.org/~eagle/software/net-duo/>.
 
 =cut
+
+# Local Variables:
+# copyright-at-end-flag: t
+# End:
diff -Nru libnet-duo-perl-1.01/lib/Net/Duo/Admin/Token.pm libnet-duo-perl-1.02/lib/Net/Duo/Admin/Token.pm
--- libnet-duo-perl-1.01/lib/Net/Duo/Admin/Token.pm	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/lib/Net/Duo/Admin/Token.pm	2019-03-06 22:02:52.000000000 -0800
@@ -2,8 +2,10 @@
 #
 # This class wraps the Duo representation of a single Duo token, as returned
 # by (for example) the Admin /tokens REST endpoint.
+#
+# SPDX-License-Identifier: MIT
 
-package Net::Duo::Admin::Token 1.01;
+package Net::Duo::Admin::Token 1.02;
 
 use 5.014;
 use strict;
@@ -251,10 +253,14 @@
 
 L<Net::Duo::Admin>
 
-L<Duo Admin API for tokens|https://www.duosecurity.com/docs/adminapi#tokens>
+L<Duo Admin API for tokens|https://www.duo.com/docs/adminapi#tokens>
 
 This module is part of the Net::Duo distribution.  The current version of
 Net::Duo is available from CPAN, or directly from its web site at
-L<http://www.eyrie.org/~eagle/software/net-duo/>.
+L<https://www.eyrie.org/~eagle/software/net-duo/>.
 
 =cut
+
+# Local Variables:
+# copyright-at-end-flag: t
+# End:
diff -Nru libnet-duo-perl-1.01/lib/Net/Duo/Admin/User.pm libnet-duo-perl-1.02/lib/Net/Duo/Admin/User.pm
--- libnet-duo-perl-1.01/lib/Net/Duo/Admin/User.pm	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/lib/Net/Duo/Admin/User.pm	2019-03-06 22:02:52.000000000 -0800
@@ -2,8 +2,10 @@
 #
 # This class wraps the Duo representation of a single Duo user, as returned by
 # (for example) the Admin /users REST endpoint.
+#
+# SPDX-License-Identifier: MIT
 
-package Net::Duo::Admin::User 1.01;
+package Net::Duo::Admin::User 1.02;
 
 use 5.014;
 use strict;
@@ -459,10 +461,14 @@
 
 L<Net::Duo::Admin>
 
-L<Duo Admin API for users|https://www.duosecurity.com/docs/adminapi#users>
+L<Duo Admin API for users|https://www.duo.com/docs/adminapi#users>
 
 This module is part of the Net::Duo distribution.  The current version of
 Net::Duo is available from CPAN, or directly from its web site at
-L<http://www.eyrie.org/~eagle/software/net-duo/>.
+L<https://www.eyrie.org/~eagle/software/net-duo/>.
 
 =cut
+
+# Local Variables:
+# copyright-at-end-flag: t
+# End:
diff -Nru libnet-duo-perl-1.01/lib/Net/Duo/Admin.pm libnet-duo-perl-1.02/lib/Net/Duo/Admin.pm
--- libnet-duo-perl-1.01/lib/Net/Duo/Admin.pm	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/lib/Net/Duo/Admin.pm	2019-03-06 22:02:52.000000000 -0800
@@ -2,12 +2,14 @@
 #
 # This Perl module collection provides a Perl interface to the Admin API
 # integration for the Duo multifactor authentication service
-# (https://www.duosecurity.com/).  It differs from the Perl API sample code in
-# that it wraps all the returned data structures in objects with method calls,
+# (https://www.duo.com/).  It differs from the Perl API sample code in that it
+# wraps all the returned data structures in objects with method calls,
 # abstracts some of the API details, and throws rich exceptions rather than
 # requiring the caller deal with JSON data structures directly.
+#
+# SPDX-License-Identifier: MIT
 
-package Net::Duo::Admin 1.01;
+package Net::Duo::Admin 1.02;
 
 use 5.014;
 use strict;
@@ -32,7 +34,7 @@
     my ($self) = @_;
 
     # Make the Duo call and get the decoded result.
-    my $result = $self->call_json('GET', '/admin/v1/integrations');
+    my $result = $self->call_json_paged('GET', '/admin/v1/integrations');
 
     # Convert the returned integrations into Net::Duo::Admin::Integration
     # objects.
@@ -135,7 +137,7 @@
     my ($self, $username) = @_;
 
     # Make the Duo call and get the decoded result.
-    my $args = { username => $username };
+    my $args   = { username => $username };
     my $result = $self->call_json('GET', '/admin/v1/users', $args);
 
     # Convert the returned user into a Net::Duo::Admin::User object.
@@ -159,8 +161,8 @@
     my ($self, $username) = @_;
 
     # Make the Duo call and get the decoded result.
-    my $args = $username ? { username => $username } : undef;
-    my $result = $self->call_json('GET', '/admin/v1/users', $args);
+    my $args   = $username ? { username => $username } : undef;
+    my $result = $self->call_json_paged('GET', '/admin/v1/users', $args);
 
     # Convert the returned users into Net::Duo::Admin::User objects.
     my @users;
@@ -386,6 +388,8 @@
 Copyright 2014 The Board of Trustees of the Leland Stanford Junior
 University
 
+Copyright 2019 Russ Allbery <rra@cpan.org>
+
 Permission is hereby granted, free of charge, to any person obtaining a
 copy of this software and associated documentation files (the "Software"),
 to deal in the Software without restriction, including without limitation
@@ -406,10 +410,14 @@
 
 =head1 SEE ALSO
 
-L<Duo Auth API|https://www.duosecurity.com/docs/authapi>
+L<Duo Auth API|https://www.duo.com/docs/authapi>
 
 This module is part of the Net::Duo distribution.  The current version of
 Net::Duo is available from CPAN, or directly from its web site at
-L<http://www.eyrie.org/~eagle/software/net-duo/>.
+L<https://www.eyrie.org/~eagle/software/net-duo/>.
 
 =cut
+
+# Local Variables:
+# copyright-at-end-flag: t
+# End:
diff -Nru libnet-duo-perl-1.01/lib/Net/Duo/Auth/Async.pm libnet-duo-perl-1.02/lib/Net/Duo/Auth/Async.pm
--- libnet-duo-perl-1.01/lib/Net/Duo/Auth/Async.pm	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/lib/Net/Duo/Auth/Async.pm	2019-03-06 22:02:52.000000000 -0800
@@ -3,8 +3,10 @@
 # This class wraps the transaction ID returned by Duo from an asynchronous
 # authentication and provides a method to long-poll the status of that
 # authentication attempt.
+#
+# SPDX-License-Identifier: MIT
 
-package Net::Duo::Auth::Async 1.01;
+package Net::Duo::Auth::Async 1.02;
 
 use 5.014;
 use strict;
@@ -190,10 +192,14 @@
 
 =head1 SEE ALSO
 
-L<Duo Auth API|https://www.duosecurity.com/docs/authapi>
+L<Duo Auth API|https://www.duo.com/docs/authapi>
 
 This module is part of the Net::Duo distribution.  The current version of
 Net::Duo is available from CPAN, or directly from its web site at
-L<http://www.eyrie.org/~eagle/software/net-duo/>.
+L<https://www.eyrie.org/~eagle/software/net-duo/>.
 
 =cut
+
+# Local Variables:
+# copyright-at-end-flag: t
+# End:
diff -Nru libnet-duo-perl-1.01/lib/Net/Duo/Auth.pm libnet-duo-perl-1.02/lib/Net/Duo/Auth.pm
--- libnet-duo-perl-1.01/lib/Net/Duo/Auth.pm	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/lib/Net/Duo/Auth.pm	2019-03-06 22:02:52.000000000 -0800
@@ -2,12 +2,14 @@
 #
 # This Perl module collection provides a Perl interface to the Auth API
 # integration for the Duo multifactor authentication service
-# (https://www.duosecurity.com/).  It differs from the Perl API sample code in
-# that it wraps all the returned data structures in objects with method calls,
+# (https://www.duo.com/).  It differs from the Perl API sample code in that it
+# wraps all the returned data structures in objects with method calls,
 # abstracts some of the API details, and throws rich exceptions rather than
 # requiring the caller deal with JSON data structures directly.
+#
+# SPDX-License-Identifier: MIT
 
-package Net::Duo::Auth 1.01;
+package Net::Duo::Auth 1.02;
 
 use 5.014;
 use strict;
@@ -441,10 +443,14 @@
 
 =head1 SEE ALSO
 
-L<Duo Auth API|https://www.duosecurity.com/docs/authapi>
+L<Duo Auth API|https://www.duo.com/docs/authapi>
 
 This module is part of the Net::Duo distribution.  The current version of
 Net::Duo is available from CPAN, or directly from its web site at
-L<http://www.eyrie.org/~eagle/software/net-duo/>.
+L<https://www.eyrie.org/~eagle/software/net-duo/>.
 
 =cut
+
+# Local Variables:
+# copyright-at-end-flag: t
+# End:
diff -Nru libnet-duo-perl-1.01/lib/Net/Duo/Exception.pm libnet-duo-perl-1.02/lib/Net/Duo/Exception.pm
--- libnet-duo-perl-1.01/lib/Net/Duo/Exception.pm	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/lib/Net/Duo/Exception.pm	2019-03-06 22:02:52.000000000 -0800
@@ -7,8 +7,10 @@
 # recover additional information.  If the caller doesn't care about the
 # details, it provides a stringification that is suitable for simple error
 # messages.
+#
+# SPDX-License-Identifier: MIT
 
-package Net::Duo::Exception 1.01;
+package Net::Duo::Exception 1.02;
 
 use 5.014;
 use strict;
@@ -346,6 +348,10 @@
 
 This module is part of the Net::Duo distribution.  The current version of
 Net::Duo is available from CPAN, or directly from its web site at
-L<http://www.eyrie.org/~eagle/software/net-duo/>.
+L<https://www.eyrie.org/~eagle/software/net-duo/>.
 
 =cut
+
+# Local Variables:
+# copyright-at-end-flag: t
+# End:
diff -Nru libnet-duo-perl-1.01/lib/Net/Duo/Mock/Agent.pm libnet-duo-perl-1.02/lib/Net/Duo/Mock/Agent.pm
--- libnet-duo-perl-1.01/lib/Net/Duo/Mock/Agent.pm	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/lib/Net/Duo/Mock/Agent.pm	2019-03-06 22:02:52.000000000 -0800
@@ -8,8 +8,10 @@
 # All tests are reported by Test::More, and no effort is made to produce a
 # predictable number of test results.  This means that any calling test
 # program should probably not specify a plan and instead use done_testing().
+#
+# SPDX-License-Identifier: MIT
 
-package Net::Duo::Mock::Agent 1.01;
+package Net::Duo::Mock::Agent 1.02;
 
 use 5.014;
 use strict;
@@ -68,7 +70,7 @@
     $args = join(q{&}, sort(split(m{&}xms, $args)));
 
     # Generate the hash of the request and check it.
-    my $data = join("\n", $date, $method, $host, $path, $args);
+    my $data      = join("\n", $date, $method, $host, $path, $args);
     my $signature = hmac_sha1_hex($data, $self->{secret_key});
     is($password, $signature, 'Signature');
     return;
@@ -88,16 +90,17 @@
     my ($self, $request) = @_;
 
     # Throw an exception if we got an unexpected call.
-    if (!$self->{expected}) {
+    if (!@{ $self->{expected} }) {
         croak('saw an unexpected request');
     }
+    my $expected = shift(@{ $self->{expected} });
 
     # Verify the signature on the request.  We continue even if it doesn't
     # verify and check the rest of the results.
     $self->_verify_signature($request);
 
     # Ensure the method and URI match what we expect, and extract the content.
-    is($request->method, $self->{expected}{method}, 'Method');
+    is($request->method, $expected->{method}, 'Method');
     my $uri = $request->uri;
     my $content;
     if ($request->method eq 'GET') {
@@ -109,7 +112,7 @@
     } else {
         $content = $request->content // q{};
     }
-    is($uri, $self->{expected}{uri}, 'URI');
+    is($uri, $expected->{uri}, 'URI');
 
     # Decode the content.
     my @pairs = split(m{&}xms, $content // q{});
@@ -122,15 +125,14 @@
     }
 
     # Check the content.
-    if ($self->{expected}{content}) {
-        is_deeply(\%content, $self->{expected}{content}, 'Content');
+    if ($expected->{content}) {
+        is_deeply(\%content, $expected->{content}, 'Content');
     } else {
         is($content, q{}, 'Content');
     }
 
-    # Return the configured response and clear state.
-    my $response = $self->{expected}{response};
-    delete $self->{expected};
+    # Return the configured response.
+    my $response = $expected->{response};
     return $response;
 }
 
@@ -171,6 +173,9 @@
     # Create the JSON decoder that we'll use for subsequent operations.
     $self->{json} = JSON->new->utf8(1);
 
+    # Create the queue of expected requests.
+    $self->{expected} = [];
+
     # Bless and return the new object.
     bless($self, $class);
     return $self;
@@ -189,6 +194,8 @@
 #   response      - HTTP::Response object to return to the caller
 #   response_data - Partial data structure to add to generic JSON in response
 #   response_file - File containing JSON to return as a respose
+#   next_offset   - Return paging metadata with this next_offset key
+#   total_objects - Value for the paging metadata if next_offset is given
 #
 # Returns: undef
 #  Throws: Text exception on invalid parameters
@@ -197,7 +204,7 @@
     my ($self, $args_ref) = @_;
 
     # Verify consistency of the arguments.
-    my @response_args = qw(response response_data response_file);
+    my @response_args  = qw(response response_data response_file);
     my $response_count = grep { defined($args_ref->{$_}) } @response_args;
     if ($response_count < 1) {
         croak('no response, response_data, or response_file specified');
@@ -221,16 +228,29 @@
             my $data     = $self->{json}->decode($contents);
             $reply = { stat => 'OK', response => $data };
         }
+        if (defined($args_ref->{next_offset})) {
+            $reply->{metadata} = {
+                next_offset   => $args_ref->{next_offset},
+                prev_offset   => 0,
+                total_objects => $args_ref->{total_objects},
+            };
+        } elsif (exists($args_ref->{next_offset})) {
+            $reply->{metadata} = {
+                prev_offset   => 0,
+                total_objects => $args_ref->{total_objects},
+            };
+        }
         $response->content($self->{json}->encode($reply));
     }
 
     # Set the expected information for call verification later.
-    $self->{expected} = {
+    my $expected = {
         method   => uc($args_ref->{method}),
         uri      => 'https://' . $self->{api_hostname} . $args_ref->{uri},
         content  => $args_ref->{content},
         response => $response,
     };
+    push(@{ $self->{expected} }, $expected);
     return;
 }
 
@@ -306,7 +326,8 @@
 
 =item expect(ARGS)
 
-Expect a REST API call from Net::Duo.
+Expect a REST API call from Net::Duo.  This method can be called multiple
+times to build up a queue of expected requests.
 
 ARGS is used to specify both the expected request data and the response
 to return to the caller.  The same response is returned regardless of
@@ -357,6 +378,18 @@
 A file containing JSON that will be included as the value of the
 C<response> key in the returned success response to the client.
 
+=item next_offset
+
+Return paging metadata in the response, setting the C<next_offset> key to
+this value, the C<prev_offset> key to 0, and the C<total_objects> key to
+the value of the I<total_objects> parameter, which must be specified as
+well.  Set this to C<undef> to include pagination information but without
+a C<next_offset> key.
+
+=item total_objects
+
+Value to return in the C<total_objects> key in the paging metadata.
+
 =back
 
 =item request(REQUEST)
@@ -389,6 +422,8 @@
 Copyright 2014 The Board of Trustees of the Leland Stanford Junior
 University
 
+Copyright 2019 Russ Allbery <rra@cpan.org>
+
 Permission is hereby granted, free of charge, to any person obtaining a
 copy of this software and associated documentation files (the "Software"),
 to deal in the Software without restriction, including without limitation
@@ -413,6 +448,10 @@
 
 This module is part of the Net::Duo distribution.  The current version of
 Net::Duo is available from CPAN, or directly from its web site at
-L<http://www.eyrie.org/~eagle/software/net-duo/>.
+L<https://www.eyrie.org/~eagle/software/net-duo/>.
 
 =cut
+
+# Local Variables:
+# copyright-at-end-flag: t
+# End:
diff -Nru libnet-duo-perl-1.01/lib/Net/Duo/Object.pm libnet-duo-perl-1.02/lib/Net/Duo/Object.pm
--- libnet-duo-perl-1.01/lib/Net/Duo/Object.pm	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/lib/Net/Duo/Object.pm	2019-03-06 22:02:52.000000000 -0800
@@ -9,8 +9,10 @@
 # This module serves as a base class for such objects and does the dirty work
 # of constructing an object from decoded JSON data and building the accessors
 # automatically from a field specification.
+#
+# SPDX-License-Identifier: MIT
 
-package Net::Duo::Object 1.01;
+package Net::Duo::Object 1.02;
 
 use 5.014;
 use strict;
@@ -509,6 +511,10 @@
 
 This module is part of the Net::Duo distribution.  The current version of
 Net::Duo is available from CPAN, or directly from its web site at
-L<http://www.eyrie.org/~eagle/software/net-duo/>.
+L<https://www.eyrie.org/~eagle/software/net-duo/>.
 
 =cut
+
+# Local Variables:
+# copyright-at-end-flag: t
+# End:
diff -Nru libnet-duo-perl-1.01/lib/Net/Duo.pm libnet-duo-perl-1.02/lib/Net/Duo.pm
--- libnet-duo-perl-1.01/lib/Net/Duo.pm	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/lib/Net/Duo.pm	2019-03-06 22:02:52.000000000 -0800
@@ -1,16 +1,18 @@
 # Perl interface for the Duo multifactor authentication service.
 #
 # This Perl module collection provides a Perl interface to the Duo multifactor
-# authentication service (https://www.duosecurity.com/).  It differs from the
-# Perl API sample code in that it abstracts some of the API details and throws
-# rich exceptions rather than requiring the caller deal with JSON data
-# structures directly.
+# authentication service (https://www.duo.com/).  It differs from the Perl API
+# sample code in that it abstracts some of the API details and throws rich
+# exceptions rather than requiring the caller deal with JSON data structures
+# directly.
 #
 # This module is intended primarily for use as a base class for more
 # specialized Perl modules implementing the specific Duo APIs, but it can also
 # be used directly to make generic API calls.
+#
+# SPDX-License-Identifier: MIT
 
-package Net::Duo 1.01;
+package Net::Duo 1.02;
 
 use 5.014;
 use strict;
@@ -209,15 +211,15 @@
 # $path     - URL path to the REST endpoint to call
 # $args_ref - Reference to a hash of additional arguments
 #
-# Returns: Reference to hash corresponding to the JSON result
+# Returns: Full raw JSON response
 #  Throws: Net::Duo::Exception on any failure
-sub call_json {
+sub _call_json_internal {
     my ($self, $method, $path, $args_ref) = @_;
 
     # Use the simpler call() method to do most of the work.  This returns the
     # HTTP::Response object.  Retrieve the content of the response as well.
     my $response = $self->call($method, $path, $args_ref);
-    my $content = $response->decoded_content;
+    my $content  = $response->decoded_content;
 
     # If the content was empty, we have a failure of some sort.
     if (!defined($content)) {
@@ -246,21 +248,81 @@
         die Net::Duo::Exception->api($data, $content);
     }
 
-    # Return the response portion of the reply.
+    # Return the response portion of the reply and the metadata if available
+    # and called in a list context.
     if (!defined($data->{response})) {
         my $error = 'no response key in JSON reply';
         die Net::Duo::Exception->protocol($error, $content);
     }
+    return $data;
+}
+
+# Make a generic Duo API call that returns JSON and do the return status
+# checking that's common to most of the Duo API calls.  There are a few
+# exceptions, like /logo, which do not return JSON and therefore cannot be
+# called using this method).
+#
+# $self     - Net::Duo object
+# $method   - HTTP method (GET, PUT, POST, or DELETE)
+# $path     - URL path to the REST endpoint to call
+# $args_ref - Reference to a hash of additional arguments
+#
+# Returns: Full raw JSON response
+#  Throws: Net::Duo::Exception on any failure
+sub call_json {
+    my ($self, $method, $path, $args_ref) = @_;
+    my $data = $self->_call_json_internal($method, $path, $args_ref);
     return $data->{response};
 }
 
+# Make a generic Duo API that returns paginated data.
+#
+# $self     - Net::Duo object
+# $method   - HTTP method (GET, PUT, POST, or DELETE)
+# $path     - URL path to the REST endpoint to call
+# $args_ref - Reference to a hash of additional arguments
+#
+# Returns: Reference to hash corresponding to the JSON result
+#  Throws: Net::Duo::Exception on any failure
+sub call_json_paged {
+    my ($self, $method, $path, $args_ref) = @_;
+    my $offset = 0;
+    my @response;
+    my $more_data = 1;
+
+    # Iterate over repeated calls to get paginated data.
+    while ($more_data) {
+        my $call_args_ref = $args_ref ? { %{$args_ref} } : {};
+        $call_args_ref->{offset} = $offset;
+        $call_args_ref->{limit}  = 500;
+        my $data = $self->_call_json_internal($method, $path, $call_args_ref);
+
+        # For paginated data, the response must be a list.
+        if (ref($data->{response}) ne 'ARRAY') {
+            my $error = 'body of paginated response not an array';
+            die Net::Duo::Exception->protocol($error);
+        }
+        push(@response, @{ $data->{response} });
+
+        # Continue if we have more paginated data.
+        if ($data->{metadata} && exists($data->{metadata}{next_offset})) {
+            $offset = $data->{metadata}{next_offset};
+        } else {
+            $more_data = 0;
+        }
+    }
+
+    # Return accumulated results.
+    return \@response;
+}
+
 1;
 __END__
 
 =for stopwords
 API LWP libwww perl JSON CPAN auth APIs namespace prepended ARGS hostname
 username Auth AUTH sublicense MERCHANTABILITY NONINFRINGEMENT Allbery
-multifactor
+multifactor URI
 
 =head1 NAME
 
@@ -398,6 +460,13 @@
 This method cannot be used with the small handful of API calls that do not
 return JSON, such as the Auth API C</logo> endpoint.
 
+=item call_json_paged(METHOD, PATH[, ARGS])
+
+The same as call_json(), except for endpoints that require pagination.
+This will make repeated calls to call_json() with increasing offsets until
+it has retrieved all of the objects, and then return them all as a single
+response.  It always uses a C<limit> setting of 500.
+
 =back
 
 =head1 AUTHOR
@@ -409,6 +478,8 @@
 Copyright 2014 The Board of Trustees of the Leland Stanford Junior
 University
 
+Copyright 2019 Russ Allbery <rra@cpan.org>
+
 Permission is hereby granted, free of charge, to any person obtaining a
 copy of this software and associated documentation files (the "Software"),
 to deal in the Software without restriction, including without limitation
@@ -429,14 +500,16 @@
 
 =head1 SEE ALSO
 
-L<Duo Auth API|https://www.duosecurity.com/docs/authapi>
+L<Duo Auth API|https://www.duo.com/docs/authapi>
 
-L<Duo Verify API|https://www.duosecurity.com/docs/duoverify>
-
-L<Duo Admin API|https://www.duosecurity.com/docs/adminapi>
+L<Duo Admin API|https://www.duo.com/docs/adminapi>
 
 This module is part of the Net::Duo distribution.  The current version of
 Net::Duo is available from CPAN, or directly from its web site at
-L<http://www.eyrie.org/~eagle/software/net-duo/>.
+L<https://www.eyrie.org/~eagle/software/net-duo/>.
 
 =cut
+
+# Local Variables:
+# copyright-at-end-flag: t
+# End:
diff -Nru libnet-duo-perl-1.01/t/admin/groups.t libnet-duo-perl-1.02/t/admin/groups.t
--- libnet-duo-perl-1.01/t/admin/groups.t	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/t/admin/groups.t	2019-03-06 22:02:52.000000000 -0800
@@ -23,6 +23,8 @@
 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 # IN THE SOFTWARE.
+#
+# SPDX-License-Identifier: MIT
 
 use 5.014;
 use strict;
diff -Nru libnet-duo-perl-1.01/t/admin/integrations.t libnet-duo-perl-1.02/t/admin/integrations.t
--- libnet-duo-perl-1.01/t/admin/integrations.t	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/t/admin/integrations.t	2019-03-06 22:02:52.000000000 -0800
@@ -3,6 +3,7 @@
 # Test suite for integration handling in the Admin API.
 #
 # Written by Russ Allbery <rra@cpan.org>
+# Copyright 2019 Russ Allbery <rra@cpan.org>
 # Copyright 2014
 #     The Board of Trustees of the Leland Stanford Junior University
 #
@@ -23,6 +24,8 @@
 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 # IN THE SOFTWARE.
+#
+# SPDX-License-Identifier: MIT
 
 use 5.014;
 use strict;
@@ -58,20 +61,36 @@
     {
         method        => 'GET',
         uri           => '/admin/v1/integrations',
-        response_file => 't/data/responses/integrations.json',
+        content       => { limit => 500, offset => 0 },
+        response_file => 't/data/responses/integrations-1.json',
+        next_offset   => 1,
+        total_objects => 2,
+    }
+);
+$mock->expect(
+    {
+        method        => 'GET',
+        uri           => '/admin/v1/integrations',
+        content       => { limit => 500, offset => 1 },
+        response_file => 't/data/responses/integrations-2.json',
+        next_offset   => undef,
+        total_objects => 2,
     }
 );
 note('Testing integrations endpoint');
 my @integrations = $duo->integrations;
 
-# Should be an array of a single integration
-is(scalar(@integrations), 1, 'integrations method returns one object');
-my $integration = $integrations[0];
+# Should be an array of two integrations.
+is(scalar(@integrations), 2, 'integrations method returns two objects');
 
 # Verify that the returned integration is correct.
-my $raw      = slurp('t/data/responses/integrations.json');
-my $expected = $json->decode($raw)->[0];
-is_admin_integration($integration, $expected);
+my @expected;
+for my $file ('integrations-1.json', 'integrations-2.json') {
+    my $raw = slurp("t/data/responses/$file");
+    push(@expected, $json->decode($raw)->[0]);
+}
+is_admin_integration($integrations[0], $expected[0]);
+is_admin_integration($integrations[1], $expected[1]);
 
 # Create a new integration.  Make sure we include some zero_or_one fields.
 my $data = {
@@ -97,11 +116,11 @@
 
 # Attempt the create call.
 note('Testing integration create endpoint');
-$integration = Net::Duo::Admin::Integration->create($duo, $data);
+my $integration = Net::Duo::Admin::Integration->create($duo, $data);
 
 # Verify that the returned group is correct.  (Just use the same return data.)
-$raw      = slurp('t/data/responses/integration.json');
-$expected = $json->decode($raw);
+my $raw      = slurp('t/data/responses/integration.json');
+my $expected = $json->decode($raw);
 is_admin_integration($integration, $expected);
 
 # Convert the full integration to JSON and compare that with the expected
diff -Nru libnet-duo-perl-1.01/t/admin/misc.t libnet-duo-perl-1.02/t/admin/misc.t
--- libnet-duo-perl-1.01/t/admin/misc.t	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/t/admin/misc.t	2019-03-06 22:02:52.000000000 -0800
@@ -23,6 +23,8 @@
 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 # IN THE SOFTWARE.
+#
+# SPDX-License-Identifier: MIT
 
 use 5.014;
 use strict;
diff -Nru libnet-duo-perl-1.01/t/admin/phones.t libnet-duo-perl-1.02/t/admin/phones.t
--- libnet-duo-perl-1.01/t/admin/phones.t	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/t/admin/phones.t	2019-03-06 22:02:52.000000000 -0800
@@ -23,6 +23,8 @@
 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 # IN THE SOFTWARE.
+#
+# SPDX-License-Identifier: MIT
 
 use 5.014;
 use strict;
diff -Nru libnet-duo-perl-1.01/t/admin/tokens.t libnet-duo-perl-1.02/t/admin/tokens.t
--- libnet-duo-perl-1.01/t/admin/tokens.t	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/t/admin/tokens.t	2019-03-06 22:02:52.000000000 -0800
@@ -23,6 +23,8 @@
 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 # IN THE SOFTWARE.
+#
+# SPDX-License-Identifier: MIT
 
 use 5.014;
 use strict;
diff -Nru libnet-duo-perl-1.01/t/admin/users.t libnet-duo-perl-1.02/t/admin/users.t
--- libnet-duo-perl-1.01/t/admin/users.t	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/t/admin/users.t	2019-03-06 22:02:52.000000000 -0800
@@ -3,6 +3,7 @@
 # Test suite for user handling in the Admin API.
 #
 # Written by Russ Allbery <rra@cpan.org>
+# Copyright 2019 Russ Allbery <rra@cpan.org>
 # Copyright 2014
 #     The Board of Trustees of the Leland Stanford Junior University
 #
@@ -23,6 +24,8 @@
 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 # IN THE SOFTWARE.
+#
+# SPDX-License-Identifier: MIT
 
 use 5.014;
 use strict;
@@ -58,19 +61,36 @@
     {
         method        => 'GET',
         uri           => '/admin/v1/users',
-        response_file => 't/data/responses/users.json',
+        content       => { limit => 500, offset => 0 },
+        response_file => 't/data/responses/users-1.json',
+        next_offset   => 1,
+        total_objects => 2,
+    }
+);
+$mock->expect(
+    {
+        method        => 'GET',
+        uri           => '/admin/v1/users',
+        content       => { limit => 500, offset => 1 },
+        response_file => 't/data/responses/users-2.json',
+        next_offset   => undef,
+        total_objects => 2,
     }
 );
 note('Testing users endpoint with no search');
 my @users = $duo->users;
 
-# Should be an array of a single user.
-is(scalar(@users), 1, 'users method returns a single user');
+# Should be an array of two users.
+is(scalar(@users), 2, 'users method returns correct number of users');
 
 # Verify that the returned user is correct.
-my $raw      = slurp('t/data/responses/users.json');
-my $expected = $json->decode($raw)->[0];
-is_admin_user($users[0], $expected);
+my @expected;
+for my $file ('users-1.json', 'users-2.json') {
+    my $raw = slurp("t/data/responses/$file");
+    push(@expected, $json->decode($raw)->[0]);
+}
+is_admin_user($users[0], $expected[0]);
+is_admin_user($users[1], $expected[1]);
 
 # Now, try a user call with a specified username.
 $mock->expect(
@@ -85,8 +105,8 @@
 my $user = $duo->user('jdoe');
 
 # Verify that the returned user is correct.
-$raw      = slurp('t/data/responses/user.json');
-$expected = $json->decode($raw)->[0];
+my $raw      = slurp('t/data/responses/user.json');
+my $expected = $json->decode($raw)->[0];
 is_admin_user($user, $expected);
 
 # Convert the full user to JSON and compare that with the expected JSON.
@@ -213,9 +233,9 @@
 # Request bypass codes for a user.
 $mock->expect(
     {
-        method  => 'POST',
-        uri     => "/admin/v1/users/$id/bypass_codes",
-        content => { count => 2, valid_secs => 3600 },
+        method        => 'POST',
+        uri           => "/admin/v1/users/$id/bypass_codes",
+        content       => { count => 2, valid_secs => 3600 },
         response_data => ['567891', '857231'],
     }
 );
diff -Nru libnet-duo-perl-1.01/t/auth/auth.t libnet-duo-perl-1.02/t/auth/auth.t
--- libnet-duo-perl-1.01/t/auth/auth.t	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/t/auth/auth.t	2019-03-06 22:02:52.000000000 -0800
@@ -23,6 +23,8 @@
 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 # IN THE SOFTWARE.
+#
+# SPDX-License-Identifier: MIT
 
 use 5.014;
 use strict;
diff -Nru libnet-duo-perl-1.01/t/auth/basic.t libnet-duo-perl-1.02/t/auth/basic.t
--- libnet-duo-perl-1.01/t/auth/basic.t	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/t/auth/basic.t	2019-03-06 22:02:52.000000000 -0800
@@ -23,6 +23,8 @@
 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 # IN THE SOFTWARE.
+#
+# SPDX-License-Identifier: MIT
 
 use 5.014;
 use strict;
diff -Nru libnet-duo-perl-1.01/t/auth/sms.t libnet-duo-perl-1.02/t/auth/sms.t
--- libnet-duo-perl-1.01/t/auth/sms.t	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/t/auth/sms.t	2019-03-06 22:02:52.000000000 -0800
@@ -23,6 +23,8 @@
 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 # IN THE SOFTWARE.
+#
+# SPDX-License-Identifier: MIT
 
 use 5.014;
 use strict;
diff -Nru libnet-duo-perl-1.01/t/data/perlcriticrc libnet-duo-perl-1.02/t/data/perlcriticrc
--- libnet-duo-perl-1.01/t/data/perlcriticrc	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/t/data/perlcriticrc	2019-03-06 22:02:52.000000000 -0800
@@ -7,10 +7,11 @@
 # This file has been updated to match perlcritic 1.118.
 #
 # The canonical version of this file is maintained in the rra-c-util package,
-# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+# which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
 #
 # Written by Russ Allbery <eagle@eyrie.org>
-# Copyright 2011, 2012, 2013
+# Copyright 2018 Russ Allbery <eagle@eyrie.org>
+# Copyright 2011-2013
 #     The Board of Trustees of the Leland Stanford Junior University
 #
 # Permission is hereby granted, free of charge, to any person obtaining a
@@ -30,6 +31,8 @@
 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 # DEALINGS IN THE SOFTWARE.
+#
+# SPDX-License-Identifier: MIT
 
 severity = 1
 verbose  = %f:%l:%c: [%p] %m (%e, Severity: %s)\n
@@ -75,6 +78,10 @@
 # off.
 [-RegularExpressions::ProhibitComplexRegexes]
 
+# Produces false positives currently with postfix dereferencing (introduced in
+# Perl 5.20).  See https://github.com/Perl-Critic/Perl-Critic/issues/578.
+[-References::ProhibitDoubleSigils]
+
 # I generally don't want to require Readonly as a prerequisite for all my Perl
 # modules.
 [-ValuesAndExpressions::ProhibitConstantPragma]
@@ -89,9 +96,10 @@
 [ValuesAndExpressions::RequireNumberSeparators]
 min_value = 100000
 
+# IO::Uncompress::Gunzip puts the error message in a package variable.
 # Text::Wrap has a broken interface that requires use of package variables.
 [Variables::ProhibitPackageVars]
-add_packages = Text::Wrap
+add_packages = IO::Uncompress::Gunzip Text::Wrap
 
 # use English was one of the worst ideas in the history of Perl.  It makes the
 # code slightly more readable for amateurs at the cost of confusing
diff -Nru libnet-duo-perl-1.01/t/data/perltidyrc libnet-duo-perl-1.02/t/data/perltidyrc
--- libnet-duo-perl-1.01/t/data/perltidyrc	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/t/data/perltidyrc	2019-03-06 22:02:52.000000000 -0800
@@ -3,7 +3,18 @@
 # Default options for perltidy for proper Perl code reformatting.
 #
 # The canonical version of this file is maintained in the rra-c-util package,
-# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+# which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
+#
+# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2012-2013
+#     The Board of Trustees of the Leland Stanford Junior University
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice and
+# this notice are preserved.  This file is offered as-is, without any
+# warranty.
+#
+# SPDX-License-Identifier: FSFAP
 
 -bbao           # put line breaks before any operator
 -nbbc           # don't force blank lines before comments (bad for else blocks)
diff -Nru libnet-duo-perl-1.01/t/data/responses/integrations-1.json libnet-duo-perl-1.02/t/data/responses/integrations-1.json
--- libnet-duo-perl-1.01/t/data/responses/integrations-1.json	1969-12-31 16:00:00.000000000 -0800
+++ libnet-duo-perl-1.02/t/data/responses/integrations-1.json	2019-03-06 22:02:52.000000000 -0800
@@ -0,0 +1,21 @@
+[
+    {
+        "enroll_policy": "enroll",
+        "greeting": "",
+        "groups_allowed": [],
+        "integration_key": "DIRWIH0ZZPV4G88B37VQ",
+        "ip_whitelist": [
+            "192.0.2.8",
+            "198.51.100.0-198.51.100.20",
+            "203.0.113.0/24"
+        ],
+        "ip_whitelist_enroll_policy": "enforce",
+        "name": "Integration for the web server",
+        "notes": "",
+        "secret_key": "QO4ZLqQVRIOZYkHfdPDORfcNf8LeXIbCWwHazY7o",
+        "type": "websdk",
+        "trusted_device_days": 0,
+        "username_normalization_policy": "None",
+        "visual_style": "default"
+    }
+]
diff -Nru libnet-duo-perl-1.01/t/data/responses/integrations-2.json libnet-duo-perl-1.02/t/data/responses/integrations-2.json
--- libnet-duo-perl-1.01/t/data/responses/integrations-2.json	1969-12-31 16:00:00.000000000 -0800
+++ libnet-duo-perl-1.02/t/data/responses/integrations-2.json	2019-03-06 22:02:52.000000000 -0800
@@ -0,0 +1,18 @@
+[
+    {
+        "enroll_policy": "enroll",
+        "greeting": "Welcome to Duo.",
+        "groups_allowed": [],
+        "integration_key": "GHZWAFDZZPV4G99B37VQ",
+        "ip_whitelist": [],
+        "ip_whitelist_enroll_policy": "enforce",
+        "name": "Web Application",
+        "notes": "",
+        "secret_key": "98DFAfvqQVRIADFadfdPDORfcNf8LeXIbCWwHazY7",
+        "self_service_allowed": false,
+        "trusted_device_days": 0,
+        "type": "websdk",
+        "username_normalization_policy": "None",
+        "visual_style": "default"
+    }
+]
diff -Nru libnet-duo-perl-1.01/t/data/responses/integrations.json libnet-duo-perl-1.02/t/data/responses/integrations.json
--- libnet-duo-perl-1.01/t/data/responses/integrations.json	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/t/data/responses/integrations.json	1969-12-31 16:00:00.000000000 -0800
@@ -1,21 +0,0 @@
-[
-    {
-        "enroll_policy": "enroll",
-        "greeting": "",
-        "groups_allowed": [],
-        "integration_key": "DIRWIH0ZZPV4G88B37VQ",
-        "ip_whitelist": [
-            "192.0.2.8",
-            "198.51.100.0-198.51.100.20",
-            "203.0.113.0/24"
-        ],
-        "ip_whitelist_enroll_policy": "enforce",
-        "name": "Integration for the web server",
-        "notes": "",
-        "secret_key": "QO4ZLqQVRIOZYkHfdPDORfcNf8LeXIbCWwHazY7o",
-        "type": "websdk",
-        "trusted_device_days": 0,
-        "username_normalization_policy": "None",
-        "visual_style": "default"
-    }
-]
diff -Nru libnet-duo-perl-1.01/t/data/responses/users-1.json libnet-duo-perl-1.02/t/data/responses/users-1.json
--- libnet-duo-perl-1.01/t/data/responses/users-1.json	1969-12-31 16:00:00.000000000 -0800
+++ libnet-duo-perl-1.02/t/data/responses/users-1.json	2019-03-06 22:02:52.000000000 -0800
@@ -0,0 +1,43 @@
+[
+    {
+        "user_id": "DU3RP9I2WOC59VZX672N",
+        "username": "jsmith",
+        "realname": "Joe Smith",
+        "email": "jsmith@example.com",
+        "status": "active",
+        "groups": [
+            {
+                "desc": "People with hardware tokens",
+                "name": "token_users"
+            }
+        ],
+        "last_login": 1343921403,
+        "notes": "",
+        "phones": [
+            {
+                "phone_id": "DPFZRS9FB0D46QFTM899",
+                "number": "+15555550100",
+                "extension": "",
+                "name": "", 
+                "postdelay": null,
+                "predelay": null,
+                "type": "Mobile",
+                "capabilities": [
+                    "sms",
+                    "phone",
+                    "push"
+                ],
+                "platform": "Apple iOS",
+                "activated": false,
+                "sms_passcodes_sent": false
+            }
+        ],
+        "tokens": [
+            {
+                "serial": "0", 
+                "token_id": "DHIZ34ALBA2445ND4AI2",
+                "type": "d1"
+            }
+        ]
+    }
+]
diff -Nru libnet-duo-perl-1.01/t/data/responses/users-2.json libnet-duo-perl-1.02/t/data/responses/users-2.json
--- libnet-duo-perl-1.01/t/data/responses/users-2.json	1969-12-31 16:00:00.000000000 -0800
+++ libnet-duo-perl-1.02/t/data/responses/users-2.json	2019-03-06 22:02:52.000000000 -0800
@@ -0,0 +1,11 @@
+[
+    {
+        "user_id": "893RT9I2XOD58VZX672N",
+        "username": "jane",
+        "realname": "Jane Doe",
+        "email": "jane@example.com",
+        "status": "active",
+        "last_login": 1379921403,
+        "notes": ""
+    }
+]
diff -Nru libnet-duo-perl-1.01/t/data/responses/users.json libnet-duo-perl-1.02/t/data/responses/users.json
--- libnet-duo-perl-1.01/t/data/responses/users.json	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/t/data/responses/users.json	1969-12-31 16:00:00.000000000 -0800
@@ -1,43 +0,0 @@
-[
-    {
-        "user_id": "DU3RP9I2WOC59VZX672N",
-        "username": "jsmith",
-        "realname": "Joe Smith",
-        "email": "jsmith@example.com",
-        "status": "active",
-        "groups": [
-            {
-                "desc": "People with hardware tokens",
-                "name": "token_users"
-            }
-        ],
-        "last_login": 1343921403,
-        "notes": "",
-        "phones": [
-            {
-                "phone_id": "DPFZRS9FB0D46QFTM899",
-                "number": "+15555550100",
-                "extension": "",
-                "name": "", 
-                "postdelay": null,
-                "predelay": null,
-                "type": "Mobile",
-                "capabilities": [
-                    "sms",
-                    "phone",
-                    "push"
-                ],
-                "platform": "Apple iOS",
-                "activated": false,
-                "sms_passcodes_sent": false
-            }
-        ],
-        "tokens": [
-            {
-                "serial": "0", 
-                "token_id": "DHIZ34ALBA2445ND4AI2",
-                "type": "d1"
-            }
-        ]
-    }
-]
diff -Nru libnet-duo-perl-1.01/t/docs/pod-coverage.t libnet-duo-perl-1.02/t/docs/pod-coverage.t
--- libnet-duo-perl-1.01/t/docs/pod-coverage.t	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/t/docs/pod-coverage.t	2019-03-06 22:02:52.000000000 -0800
@@ -3,10 +3,10 @@
 # Test that all methods are documented in POD.
 #
 # The canonical version of this file is maintained in the rra-c-util package,
-# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+# which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
 #
 # Written by Russ Allbery <eagle@eyrie.org>
-# Copyright 2013, 2014
+# Copyright 2013-2014
 #     The Board of Trustees of the Leland Stanford Junior University
 #
 # Permission is hereby granted, free of charge, to any person obtaining a
@@ -26,6 +26,8 @@
 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 # DEALINGS IN THE SOFTWARE.
+#
+# SPDX-License-Identifier: MIT
 
 use 5.006;
 use strict;
@@ -33,10 +35,11 @@
 
 use lib 't/lib';
 
-use Test::More;
 use Test::RRA qw(skip_unless_automated use_prereq);
 use Test::RRA::Config qw(@POD_COVERAGE_EXCLUDE);
 
+use Test::More;
+
 # Skip for normal user installs since this doesn't affect functionality.
 skip_unless_automated('POD coverage tests');
 
diff -Nru libnet-duo-perl-1.01/t/docs/pod-spelling.t libnet-duo-perl-1.02/t/docs/pod-spelling.t
--- libnet-duo-perl-1.01/t/docs/pod-spelling.t	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/t/docs/pod-spelling.t	2019-03-06 22:02:52.000000000 -0800
@@ -3,10 +3,10 @@
 # Check for spelling errors in POD documentation.
 #
 # The canonical version of this file is maintained in the rra-c-util package,
-# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+# which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
 #
 # Written by Russ Allbery <eagle@eyrie.org>
-# Copyright 2013, 2014
+# Copyright 2013-2014
 #     The Board of Trustees of the Leland Stanford Junior University
 #
 # Permission is hereby granted, free of charge, to any person obtaining a
@@ -26,6 +26,8 @@
 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 # DEALINGS IN THE SOFTWARE.
+#
+# SPDX-License-Identifier: MIT
 
 use 5.006;
 use strict;
@@ -33,9 +35,10 @@
 
 use lib 't/lib';
 
-use Test::More;
 use Test::RRA qw(skip_unless_author use_prereq);
 
+use Test::More;
+
 # Only run this test for the module author since the required stopwords are
 # too sensitive to the exact spell-checking program and dictionary.
 skip_unless_author('Spelling tests');
diff -Nru libnet-duo-perl-1.01/t/docs/pod.t libnet-duo-perl-1.02/t/docs/pod.t
--- libnet-duo-perl-1.01/t/docs/pod.t	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/t/docs/pod.t	2019-03-06 22:02:52.000000000 -0800
@@ -3,10 +3,10 @@
 # Check all POD documents for POD formatting errors.
 #
 # The canonical version of this file is maintained in the rra-c-util package,
-# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+# which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
 #
 # Written by Russ Allbery <eagle@eyrie.org>
-# Copyright 2012, 2013, 2014
+# Copyright 2012-2014
 #     The Board of Trustees of the Leland Stanford Junior University
 #
 # Permission is hereby granted, free of charge, to any person obtaining a
@@ -26,6 +26,8 @@
 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 # DEALINGS IN THE SOFTWARE.
+#
+# SPDX-License-Identifier: MIT
 
 use 5.006;
 use strict;
@@ -33,9 +35,10 @@
 
 use lib 't/lib';
 
-use Test::More;
 use Test::RRA qw(skip_unless_automated use_prereq);
 
+use Test::More;
+
 # Skip this test for normal user installs, although pod2man may still fail.
 skip_unless_automated('POD syntax tests');
 
diff -Nru libnet-duo-perl-1.01/t/docs/spdx-license.t libnet-duo-perl-1.02/t/docs/spdx-license.t
--- libnet-duo-perl-1.01/t/docs/spdx-license.t	1969-12-31 16:00:00.000000000 -0800
+++ libnet-duo-perl-1.02/t/docs/spdx-license.t	2019-03-06 22:02:52.000000000 -0800
@@ -0,0 +1,132 @@
+#!/usr/bin/perl
+#
+# Check source files for SPDX-License-Identifier fields.
+#
+# Examine all source files in a distribution to check that they contain an
+# SPDX-License-Identifier field.  This does not check the syntax or whether
+# the identifiers are valid.
+#
+# The canonical version of this file is maintained in the rra-c-util package,
+# which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
+#
+# Copyright 2018, 2019 Russ Allbery <eagle@eyrie.org>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+#
+# SPDX-License-Identifier: MIT
+
+use 5.006;
+use strict;
+use warnings;
+
+use lib 't/lib';
+
+use Test::RRA qw(skip_unless_automated);
+
+use File::Find qw(find);
+use Test::More;
+
+# File name (the file without any directory component) and path patterns to
+# skip for this check.
+## no critic (RegularExpressions::ProhibitFixedStringMatches)
+my @IGNORE = (
+    qr{ \A Build ( [.] (?!PL) .* )? \z }ixms,    # Generated file from Build.PL
+    qr{ \A LICENSE \z }xms,                 # Generated file, no license itself
+    qr{ \A (Changes|NEWS|THANKS) \z }xms,   # Package license should be fine
+    qr{ \A TODO \z }xms,                    # Package license should be fine
+    qr{ \A MANIFEST ( [.] .* )? \z }xms,    # Package license should be fine
+    qr{ \A Makefile \z }xms,                # Generated file, no license itself
+    qr{ \A (MY)? META [.] .* }xms,          # Generated file, no license itself
+    qr{ [.] output \z }xms,                 # Test data
+    qr{ pod2htm . [.] tmp \z }xms,          # Windows pod2html output
+);
+my @IGNORE_PATHS = (
+    qr{ \A [.] / [.] git/ }xms,               # Version control files
+    qr{ \A [.] /_build/ }xms,                 # Module::Build metadata
+    qr{ \A [.] /blib/ }xms,                   # Perl build system artifacts
+    qr{ \A [.] /cover_db/ }xms,               # Artifacts from coverage testing
+    qr{ \A [.] /debian/ }xms,                 # Found in debian/* branches
+    qr{ \A [.] /docs/metadata/ }xms,          # Package license should be fine
+    qr{ \A [.] /README ( [.] .* )? \z }xms,   # Package license should be fine
+    qr{ \A [.] /share/ }xms,                  # Package license should be fine
+    qr{ \A [.] /t/data .* /metadata/ }xms,    # Test metadata
+    qr{ \A [.] /t/data .* /output/ }xms,      # Test output
+    qr{ \A [.] /t/data .* [.] json \z }xms,   # Test metadata
+);
+## use critic
+
+# Only run this test during automated testing, since failure doesn't indicate
+# any user-noticable flaw in the package itself.
+skip_unless_automated('SPDX identifier tests');
+
+# Check a single file for an occurrence of the string.
+#
+# $path - Path to the file
+#
+# Returns: undef
+sub check_file {
+    my $filename = $_;
+    my $path     = $File::Find::name;
+
+    # Ignore files in the whitelist and binary files.
+    for my $pattern (@IGNORE) {
+        return if $filename =~ $pattern;
+    }
+    for my $pattern (@IGNORE_PATHS) {
+        if ($path =~ $pattern) {
+            $File::Find::prune = 1;
+            return;
+        }
+    }
+    return if -d $filename;
+    return if !-T $filename;
+
+    # Scan the file.
+    my ($saw_legacy_notice, $saw_spdx, $skip_spdx);
+    open(my $file, '<', $filename) or BAIL_OUT("Cannot open $path");
+    while (defined(my $line = <$file>)) {
+        if ($line =~ m{ \b See \s+ LICENSE \s+ for \s+ licensing }xms) {
+            $saw_legacy_notice = 1;
+        }
+        if ($line =~ m{ \b SPDX-License-Identifier: \s+ \S+ }xms) {
+            $saw_spdx = 1;
+            last;
+        }
+        if ($line =~ m{ no \s SPDX-License-Identifier \s registered }xms) {
+            $skip_spdx = 1;
+            last;
+        }
+    }
+    close($file) or BAIL_OUT("Cannot close $path");
+
+    # If there is a legacy license notice, report a failure regardless of file
+    # size.  Otherwise, skip files under 1KB.  They can be rolled up into the
+    # overall project license and the license notice may be a substantial
+    # portion of the file size.
+    if ($saw_legacy_notice) {
+        ok(!$saw_legacy_notice, "$path has legacy license notice");
+    } else {
+        ok($saw_spdx || $skip_spdx || -s $filename < 1024, $path);
+    }
+    return;
+}
+
+# Use File::Find to scan all files from the top of the directory.
+find(\&check_file, q{.});
+done_testing();
diff -Nru libnet-duo-perl-1.01/t/docs/synopsis.t libnet-duo-perl-1.02/t/docs/synopsis.t
--- libnet-duo-perl-1.01/t/docs/synopsis.t	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/t/docs/synopsis.t	2019-03-06 22:02:52.000000000 -0800
@@ -3,10 +3,10 @@
 # Check the SYNOPSIS section of the documentation for syntax errors.
 #
 # The canonical version of this file is maintained in the rra-c-util package,
-# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+# which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
 #
 # Written by Russ Allbery <eagle@eyrie.org>
-# Copyright 2013, 2014
+# Copyright 2013-2014
 #     The Board of Trustees of the Leland Stanford Junior University
 #
 # Permission is hereby granted, free of charge, to any person obtaining a
@@ -26,6 +26,8 @@
 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 # DEALINGS IN THE SOFTWARE.
+#
+# SPDX-License-Identifier: MIT
 
 use 5.006;
 use strict;
@@ -33,9 +35,11 @@
 
 use lib 't/lib';
 
-use Test::More;
 use Test::RRA qw(skip_unless_automated use_prereq);
 
+use File::Spec;
+use Test::More;
+
 # Skip for normal user installs since this doesn't affect functionality.
 skip_unless_automated('Synopsis syntax tests');
 
@@ -43,12 +47,25 @@
 use_prereq('Perl::Critic::Utils');
 use_prereq('Test::Synopsis');
 
+# Helper function that checks to see if a given path starts with blib/script.
+# This is written a bit weirdly so that it's portable to Windows and VMS.
+#
+# $path - Path to a file
+#
+# Returns: True if the file doesn't start with blib/script, false otherwise.
+sub in_blib_script {
+    my ($path) = @_;
+    my ($volume, $dir, $file) = File::Spec->splitpath($path);
+    my @dir = File::Spec->splitdir($dir);
+    return (scalar(@dir) < 2 || $dir[0] ne 'blib' || $dir[1] ne 'script');
+}
+
 # The default Test::Synopsis all_synopsis_ok() function requires that the
 # module be in a lib directory.  Use Perl::Critic::Utils to find the modules
 # in blib, or lib if it doesn't exist.  However, strip out anything in
 # blib/script, since scripts use a different SYNOPSIS syntax.
 my @files = Perl::Critic::Utils::all_perl_files('blib');
-@files = grep { !m{blib/script/}xms } @files;
+@files = grep { in_blib_script($_) } @files;
 if (!@files) {
     @files = Perl::Critic::Utils::all_perl_files('lib');
 }
diff -Nru libnet-duo-perl-1.01/t/duo/call.t libnet-duo-perl-1.02/t/duo/call.t
--- libnet-duo-perl-1.01/t/duo/call.t	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/t/duo/call.t	2019-03-06 22:02:52.000000000 -0800
@@ -23,6 +23,8 @@
 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 # IN THE SOFTWARE.
+#
+# SPDX-License-Identifier: MIT
 
 use 5.014;
 use strict;
diff -Nru libnet-duo-perl-1.01/t/duo/exception.t libnet-duo-perl-1.02/t/duo/exception.t
--- libnet-duo-perl-1.01/t/duo/exception.t	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/t/duo/exception.t	2019-03-06 22:02:52.000000000 -0800
@@ -23,6 +23,8 @@
 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 # IN THE SOFTWARE.
+#
+# SPDX-License-Identifier: MIT
 
 use 5.014;
 use strict;
diff -Nru libnet-duo-perl-1.01/t/lib/Test/RRA/Config.pm libnet-duo-perl-1.02/t/lib/Test/RRA/Config.pm
--- libnet-duo-perl-1.01/t/lib/Test/RRA/Config.pm	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/t/lib/Test/RRA/Config.pm	2019-03-06 22:02:52.000000000 -0800
@@ -4,6 +4,8 @@
 # configuration file to store some package-specific data.  This module loads
 # that configuration and provides the namespace for the configuration
 # settings.
+#
+# SPDX-License-Identifier: MIT
 
 package Test::RRA::Config;
 
@@ -27,22 +29,23 @@
     @ISA       = qw(Exporter);
     @EXPORT_OK = qw(
       $COVERAGE_LEVEL @COVERAGE_SKIP_TESTS @CRITIC_IGNORE $LIBRARY_PATH
-      $MINIMUM_VERSION %MINIMUM_VERSION @POD_COVERAGE_EXCLUDE @STRICT_IGNORE
-      @STRICT_PREREQ
+      $MINIMUM_VERSION %MINIMUM_VERSION @MODULE_VERSION_IGNORE
+      @POD_COVERAGE_EXCLUDE @STRICT_IGNORE @STRICT_PREREQ
     );
 
     # This version should match the corresponding rra-c-util release, but with
     # two digits for the minor version, including a leading zero if necessary,
     # so that it will sort properly.
-    $VERSION = '5.08';
+    $VERSION = '8.00';
 }
 
-# If BUILD or SOURCE are set in the environment, look for data/perl.conf under
-# those paths for a C Automake package.  Otherwise, look in t/data/perl.conf
-# for a standalone Perl module.  Don't use Test::RRA::Automake since it may
-# not exist.
+# If C_TAP_BUILD or C_TAP_SOURCE are set in the environment, look for
+# data/perl.conf under those paths for a C Automake package.  Otherwise, look
+# in t/data/perl.conf for a standalone Perl module or tests/data/perl.conf for
+# Perl tests embedded in a larger distribution.  Don't use Test::RRA::Automake
+# since it may not exist.
 our $PATH;
-for my $base ($ENV{BUILD}, $ENV{SOURCE}, 't') {
+for my $base ($ENV{C_TAP_BUILD}, $ENV{C_TAP_SOURCE}, './t', './tests') {
     next if !defined($base);
     my $path = "$base/data/perl.conf";
     if (-r $path) {
@@ -61,6 +64,7 @@
 our $LIBRARY_PATH;
 our $MINIMUM_VERSION = '5.008';
 our %MINIMUM_VERSION;
+our @MODULE_VERSION_IGNORE;
 our @POD_COVERAGE_EXCLUDE;
 our @STRICT_IGNORE;
 our @STRICT_PREREQ;
@@ -68,15 +72,15 @@
 # Load the configuration.
 if (!do($PATH)) {
     my $error = $@ || $! || 'loading file did not return true';
-    BAIL_OUT("cannot load data/perl.conf: $error");
+    BAIL_OUT("cannot load $PATH: $error");
 }
 
 1;
 __END__
 
 =for stopwords
-Allbery rra-c-util Automake perlcritic .libs namespace subdirectory
-sublicense MERCHANTABILITY NONINFRINGEMENT
+Allbery rra-c-util Automake perlcritic .libs namespace subdirectory sublicense
+MERCHANTABILITY NONINFRINGEMENT regexes
 
 =head1 NAME
 
@@ -89,19 +93,17 @@
 
 =head1 DESCRIPTION
 
-Test::RRA::Config encapsulates per-package configuration for generic Perl
-test programs that are shared between multiple packages using the
-rra-c-util infrastructure.  It handles locating and loading the test
-configuration file for both C Automake packages and stand-alone Perl
-modules.
+Test::RRA::Config encapsulates per-package configuration for generic Perl test
+programs that are shared between multiple packages using the rra-c-util
+infrastructure.  It handles locating and loading the test configuration file
+for both C Automake packages and stand-alone Perl modules.
 
 Test::RRA::Config looks for a file named F<data/perl.conf> relative to the
-root of the test directory.  That root is taken from the environment
-variables BUILD or SOURCE (in that order) if set, which will be the case
-for C Automake packages using C TAP Harness.  If neither is set, it
-expects the root of the test directory to be a directory named F<t>
-relative to the current directory, which will be the case for stand-alone
-Perl modules.
+root of the test directory.  That root is taken from the environment variables
+C_TAP_BUILD or C_TAP_SOURCE (in that order) if set, which will be the case for
+C Automake packages using C TAP Harness.  If neither is set, it expects the
+root of the test directory to be a directory named F<t> relative to the
+current directory, which will be the case for stand-alone Perl modules.
 
 The following variables are supported:
 
@@ -109,70 +111,75 @@
 
 =item $COVERAGE_LEVEL
 
-The coverage level achieved by the test suite for Perl test coverage
-testing using Test::Strict, as a percentage.  The test will fail if test
-coverage less than this percentage is achieved.  If not given, defaults
-to 100.
+The coverage level achieved by the test suite for Perl test coverage testing
+using Test::Strict, as a percentage.  The test will fail if test coverage less
+than this percentage is achieved.  If not given, defaults to 100.
 
 =item @COVERAGE_SKIP_TESTS
 
 Directories under F<t> whose tests should be skipped when doing coverage
-testing.  This can be tests that won't contribute to coverage or tests
-that don't run properly under Devel::Cover for some reason (such as ones
-that use taint checking).  F<docs> and F<style> will always be skipped
-regardless of this setting.
+testing.  This can be tests that won't contribute to coverage or tests that
+don't run properly under Devel::Cover for some reason (such as ones that use
+taint checking).  F<docs> and F<style> will always be skipped regardless of
+this setting.
 
 =item @CRITIC_IGNORE
 
-Additional directories to ignore when doing recursive perlcritic testing.
-The contents of this directory must be either top-level directory names or
+Additional directories to ignore when doing recursive perlcritic testing.  The
+contents of this directory must be either top-level directory names or
 directory names starting with F<tests/>.
 
 =item $LIBRARY_PATH
 
 Add this directory (or a F<.libs> subdirectory) relative to the top of the
-source tree to LD_LIBRARY_PATH when checking the syntax of Perl modules.
-This may be required to pick up libraries that are used by in-tree Perl
-modules so that Perl scripts can pass a syntax check.
+source tree to LD_LIBRARY_PATH when checking the syntax of Perl modules.  This
+may be required to pick up libraries that are used by in-tree Perl modules so
+that Perl scripts can pass a syntax check.
 
 =item $MINIMUM_VERSION
 
-Default minimum version requirement for included Perl scripts.  If not
-given, defaults to 5.008.
+Default minimum version requirement for included Perl scripts.  If not given,
+defaults to 5.008.
 
 =item %MINIMUM_VERSION
 
 Minimum version exceptions for specific directories.  The keys should be
 minimum versions of Perl to enforce.  The value for each key should be a
-reference to an array of either top-level directory names or directory
-names starting with F<tests/>.  All files in those directories will have
-that minimum Perl version constraint imposed instead of $MINIMUM_VERSION.
+reference to an array of either top-level directory names or directory names
+starting with F<tests/>.  All files in those directories will have that
+minimum Perl version constraint imposed instead of $MINIMUM_VERSION.
+
+=item @MODULE_VERSION_IGNORE
+
+File names to ignore when checking that all modules in a distribution have the
+same version.  Sometimes, some specific modules need separate, special version
+handling, such as modules defining database schemata for DBIx::Class, and
+can't follow the version of the larger package.
 
 =item @POD_COVERAGE_EXCLUDE
 
 Regexes that match method names that should be excluded from POD coverage
-testing.  Normally, all methods have to be documented in the POD for a
-Perl module, but methods matching any of these regexes will be considered
-private and won't require documentation.
+testing.  Normally, all methods have to be documented in the POD for a Perl
+module, but methods matching any of these regexes will be considered private
+and won't require documentation.
 
 =item @STRICT_IGNORE
 
-Additional directories to ignore when doing recursive Test::Strict testing
-for C<use strict> and C<use warnings>.  The contents of this directory
-must be either top-level directory names or directory names starting with
-F<tests/>.
+Additional directories to ignore when doing recursive Test::Strict testing for
+C<use strict> and C<use warnings>.  The contents of this directory must be
+either top-level directory names or directory names starting with F<tests/>.
 
 =item @STRICT_PREREQ
 
 A list of Perl modules that have to be available in order to do meaningful
 Test::Strict testing.  If any of the modules cannot be loaded via C<use>,
-Test::Strict checking will be skipped.  There is currently no way to
-require specific versions of the modules.
+Test::Strict checking will be skipped.  There is currently no way to require
+specific versions of the modules.
 
 =back
 
-No variables are exported by default, but the variables can be imported
-into the local namespace to avoid long variable names.
+No variables are exported by default, but the variables can be imported into
+the local namespace to avoid long variable names.
 
 =head1 AUTHOR
 
@@ -180,36 +187,42 @@
 
 =head1 COPYRIGHT AND LICENSE
 
-Copyright 2013, 2014 The Board of Trustees of the Leland Stanford Junior
+Copyright 2015-2016 Russ Allbery <eagle@eyrie.org>
+
+Copyright 2013-2014 The Board of Trustees of the Leland Stanford Junior
 University
 
-Permission is hereby granted, free of charge, to any person obtaining a
-copy of this software and associated documentation files (the "Software"),
-to deal in the Software without restriction, including without limitation
-the rights to use, copy, modify, merge, publish, distribute, sublicense,
-and/or sell copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following conditions:
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
 
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
 
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
-THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
 
 =head1 SEE ALSO
 
-perlcritic(1), Test::MinimumVersion(3), Test::RRA(3),
-Test::RRA::Automake(3), Test::Strict(3)
+perlcritic(1), Test::MinimumVersion(3), Test::RRA(3), Test::RRA::Automake(3),
+Test::Strict(3)
 
-This module is maintained in the rra-c-util package.  The current version
-is available from L<http://www.eyrie.org/~eagle/software/rra-c-util/>.
+This module is maintained in the rra-c-util package.  The current version is
+available from L<https://www.eyrie.org/~eagle/software/rra-c-util/>.
 
 The C TAP Harness test driver and libraries for TAP-based C testing are
-available from L<http://www.eyrie.org/~eagle/software/c-tap-harness/>.
+available from L<https://www.eyrie.org/~eagle/software/c-tap-harness/>.
 
 =cut
+
+# Local Variables:
+# copyright-at-end-flag: t
+# End:
diff -Nru libnet-duo-perl-1.01/t/lib/Test/RRA/Duo.pm libnet-duo-perl-1.02/t/lib/Test/RRA/Duo.pm
--- libnet-duo-perl-1.01/t/lib/Test/RRA/Duo.pm	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/t/lib/Test/RRA/Duo.pm	2019-03-06 22:02:52.000000000 -0800
@@ -14,23 +14,7 @@
 # Copyright 2014
 #     The Board of Trustees of the Leland Stanford Junior University
 #
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to
-# deal in the Software without restriction, including without limitation the
-# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-# sell copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-# IN THE SOFTWARE.
+# SPDX-License-Identifier: MIT
 
 package Test::RRA::Duo 1.00;
 
diff -Nru libnet-duo-perl-1.01/t/lib/Test/RRA/ModuleVersion.pm libnet-duo-perl-1.02/t/lib/Test/RRA/ModuleVersion.pm
--- libnet-duo-perl-1.01/t/lib/Test/RRA/ModuleVersion.pm	1969-12-31 16:00:00.000000000 -0800
+++ libnet-duo-perl-1.02/t/lib/Test/RRA/ModuleVersion.pm	2019-03-06 22:02:52.000000000 -0800
@@ -0,0 +1,303 @@
+# Check Perl module versions for consistency.
+#
+# This module contains the common code for testing and updating Perl module
+# versions for consistency within a Perl module distribution and within a
+# larger package that contains both Perl modules and other code.
+#
+# SPDX-License-Identifier: MIT
+
+package Test::RRA::ModuleVersion;
+
+use 5.006;
+use strict;
+use warnings;
+
+use Exporter;
+use File::Find qw(find);
+use Test::More;
+use Test::RRA::Config qw(@MODULE_VERSION_IGNORE);
+
+# For Perl 5.006 compatibility.
+## no critic (ClassHierarchies::ProhibitExplicitISA)
+
+# Declare variables that should be set in BEGIN for robustness.
+our (@EXPORT_OK, @ISA, $VERSION);
+
+# Set $VERSION and everything export-related in a BEGIN block for robustness
+# against circular module loading (not that we load any modules, but
+# consistency is good).
+BEGIN {
+    @ISA       = qw(Exporter);
+    @EXPORT_OK = qw(test_module_versions update_module_versions);
+
+    # This version should match the corresponding rra-c-util release, but with
+    # two digits for the minor version, including a leading zero if necessary,
+    # so that it will sort properly.
+    $VERSION = '8.00';
+}
+
+# A regular expression matching the version string for a module using the
+# package syntax from Perl 5.12 and later.  $1 will contain all of the line
+# contents prior to the actual version string, $2 will contain the version
+# itself, and $3 will contain the rest of the line.
+our $REGEX_VERSION_PACKAGE = qr{
+    (                           # prefix ($1)
+        \A \s*                  # whitespace
+        package \s+             # package keyword
+        [\w\:\']+ \s+           # package name
+    )
+    ( v? [\d._]+ )              # the version number itself ($2)
+    (                           # suffix ($3)
+        \s* ;
+    )
+}xms;
+
+# A regular expression matching a $VERSION string in a module.  $1 will
+# contain all of the line contents prior to the actual version string, $2 will
+# contain the version itself, and $3 will contain the rest of the line.
+our $REGEX_VERSION_OLD = qr{
+    (                           # prefix ($1)
+        \A .*                   # any prefix, such as "our"
+        [\$*]                   # scalar or typeglob
+        [\w\:\']*\b             # optional package name
+        VERSION\b               # version variable
+        \s* = \s*               # assignment
+    )
+    [\"\']?                     # optional leading quote
+    ( v? [\d._]+ )              # the version number itself ($2)
+    [\"\']?                     # optional trailing quote
+    (                           # suffix ($3)
+        \s*
+        ;
+    )
+}xms;
+
+# Find all the Perl modules shipped in this package, if any, and returns the
+# list of file names.
+#
+# $dir - The root directory to search
+#
+# Returns: List of file names
+sub _module_files {
+    my ($dir) = @_;
+    return if !-d $dir;
+    my @files;
+    my %ignore = map { $_ => 1 } @MODULE_VERSION_IGNORE;
+    my $wanted = sub {
+        if ($_ eq 'blib') {
+            $File::Find::prune = 1;
+            return;
+        }
+        if (m{ [.] pm \z }xms && !$ignore{$File::Find::name}) {
+            push(@files, $File::Find::name);
+        }
+        return;
+    };
+    find($wanted, $dir);
+    return @files;
+}
+
+# Given a module file, read it for the version value and return the value.
+#
+# $file - File to check, which should be a Perl module
+#
+# Returns: The version of the module
+#  Throws: Text exception on I/O failure or inability to find version
+sub _module_version {
+    my ($file) = @_;
+    open(my $data, q{<}, $file) or die "$0: cannot open $file: $!\n";
+    while (defined(my $line = <$data>)) {
+        if (   $line =~ $REGEX_VERSION_PACKAGE
+            || $line =~ $REGEX_VERSION_OLD)
+        {
+            my ($prefix, $version, $suffix) = ($1, $2, $3);
+            close($data) or die "$0: error reading from $file: $!\n";
+            return $version;
+        }
+    }
+    close($data) or die "$0: error reading from $file: $!\n";
+    die "$0: cannot find version number in $file\n";
+}
+
+# Given a module file and the new version for that module, update the version
+# in that module to the new one.
+#
+# $file    - Perl module file whose version should be updated
+# $version - The new version number
+#
+# Returns: undef
+#  Throws: Text exception on I/O failure or inability to find version
+sub _update_module_version {
+    my ($file, $version) = @_;
+
+    # The old-style syntax may require different quoting.  If the version
+    # starts with v, use it without quotes.  Otherwise, quote it to prevent
+    # removal of trailing zeroes.
+    my $old_version = $version;
+    if ($old_version !~ m{ \A v }xms) {
+        $old_version = "'$old_version'";
+    }
+
+    # Scan for the version and replace it.
+    open(my $in, q{<}, $file) or die "$0: cannot open $file: $!\n";
+    open(my $out, q{>}, "$file.new")
+      or die "$0: cannot create $file.new: $!\n";
+  SCAN:
+    while (defined(my $line = <$in>)) {
+        if (   $line =~ s{ $REGEX_VERSION_PACKAGE }{$1$version$3}xms
+            || $line =~ s{ $REGEX_VERSION_OLD     }{$1$old_version$3}xms)
+        {
+            print {$out} $line or die "$0: cannot write to $file.new: $!\n";
+            last SCAN;
+        }
+        print {$out} $line or die "$0: cannot write to $file.new: $!\n";
+    }
+
+    # Copy the rest of the input file to the output file.
+    print {$out} <$in> or die "$0: cannot write to $file.new: $!\n";
+    close($out) or die "$0: cannot flush $file.new: $!\n";
+    close($in)  or die "$0: error reading from $file: $!\n";
+
+    # All done.  Rename the new file over top of the old file.
+    rename("$file.new", $file)
+      or die "$0: cannot rename $file.new to $file: $!\n";
+    return;
+}
+
+# Act as a test suite.  Find all of the Perl modules under the provided root,
+# if any, and check that the version for each module matches the version.
+# Reports results with Test::More and sets up a plan based on the number of
+# modules found.
+#
+# $root    - Directory under which to look for Perl modules
+# $version - The version all those modules should have
+#
+# Returns: undef
+#  Throws: Text exception on fatal errors
+sub test_module_versions {
+    my ($root, $version) = @_;
+    my @modules = _module_files($root);
+
+    # Output the plan.  Skip the test if there were no modules found.
+    if (@modules) {
+        plan tests => scalar(@modules);
+    } else {
+        plan skip_all => 'No Perl modules found';
+        return;
+    }
+
+    # For each module, get the module version and compare.
+    for my $module (@modules) {
+        my $module_version = _module_version($module);
+        is($module_version, $version, "Version for $module");
+    }
+    return;
+}
+
+# Update the versions of all modules to the current distribution version.
+#
+# $root    - Directory under which to look for Perl modules
+# $version - The version all those modules should have
+#
+# Returns: undef
+#  Throws: Text exception on fatal errors
+sub update_module_versions {
+    my ($root, $version) = @_;
+    my @modules = _module_files($root);
+    for my $module (@modules) {
+        _update_module_version($module, $version);
+    }
+    return;
+}
+
+1;
+__END__
+
+=for stopwords
+Allbery sublicense MERCHANTABILITY NONINFRINGEMENT rra-c-util versioning
+
+=head1 NAME
+
+Test::RRA::ModuleVersion - Check Perl module versions for consistency
+
+=head1 SYNOPSIS
+
+    use Test::RRA::ModuleVersion
+      qw(test_module_versions update_module_versions);
+
+    # Ensure all modules under perl/lib have a version of 3.12.
+    test_module_versions('perl/lib', '3.12');
+
+    # Update the version of those modules to 3.12.
+    update_module_versions('perl/lib', 3.12');
+
+=head1 DESCRIPTION
+
+This module provides functions to test and update the versions of Perl
+modules.  It helps with enforcing consistency of versioning across all modules
+in a Perl distribution or embedded in a larger project containing non-Perl
+code.  The calling script provides the version with which to be consistent
+and the root directory under which modules are found.
+
+=head1 FUNCTIONS
+
+None of these functions are imported by default.  The ones used by a script
+should be explicitly imported.
+
+=over 4
+
+=item test_module_versions(ROOT, VERSION)
+
+Tests the version of all Perl modules under ROOT to ensure they match VERSION,
+reporting the results with Test::More.  If the test configuration loaded by
+Test::RRA::Config contains a @MODULE_VERSION_EXCLUDE variable, the module
+files listed there will be ignored for this test.  This function also sets up
+a plan based on the number of modules, so should be the only testing function
+called in a test script.
+
+=item update_module_versions(ROOT, VERSION)
+
+Update the version of all Perl modules found under ROOT to VERSION, except for
+any listed in a @MODULE_VERSION_EXCLUDE variable set in the test configuration
+loaded by Test::RRA::Config.
+
+=back
+
+=head1 AUTHOR
+
+Russ Allbery <eagle@eyrie.org>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2016, 2018 Russ Allbery <eagle@eyrie.org>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+=head1 SEE ALSO
+
+Test::More(3), Test::RRA::Config(3)
+
+This module is maintained in the rra-c-util package.  The current version
+is available from L<https://www.eyrie.org/~eagle/software/rra-c-util/>.
+
+=cut
+
+# Local Variables:
+# copyright-at-end-flag: t
+# End:
diff -Nru libnet-duo-perl-1.01/t/lib/Test/RRA.pm libnet-duo-perl-1.02/t/lib/Test/RRA.pm
--- libnet-duo-perl-1.01/t/lib/Test/RRA.pm	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/t/lib/Test/RRA.pm	2019-03-06 22:02:52.000000000 -0800
@@ -5,6 +5,8 @@
 # by both C packages with Automake and by stand-alone Perl modules.  See
 # Test::RRA::Automake for additional functions specifically for C Automake
 # distributions.
+#
+# SPDX-License-Identifier: MIT
 
 package Test::RRA;
 
@@ -12,8 +14,29 @@
 use strict;
 use warnings;
 
+use Carp qw(croak);
 use Exporter;
-use Test::More;
+use File::Temp;
+
+# Abort if Test::More was loaded before Test::RRA to be sure that we get the
+# benefits of the Test::More probing below.
+if ($INC{'Test/More.pm'}) {
+    croak('Test::More loaded before Test::RRA');
+}
+
+# Red Hat's base perl package doesn't include Test::More (one has to install
+# the perl-core package in addition).  Try to detect this and skip any Perl
+# tests if Test::More is not present.  This relies on Test::RRA being included
+# before Test::More.
+eval {
+    require Test::More;
+    Test::More->import();
+};
+if ($@) {
+    print "1..0 # SKIP Test::More required for test\n"
+      or croak('Cannot write to stdout');
+    exit 0;
+}
 
 # For Perl 5.006 compatibility.
 ## no critic (ClassHierarchies::ProhibitExplicitISA)
@@ -26,12 +49,56 @@
 # consistency is good).
 BEGIN {
     @ISA       = qw(Exporter);
-    @EXPORT_OK = qw(skip_unless_author skip_unless_automated use_prereq);
+    @EXPORT_OK = qw(
+      is_file_contents skip_unless_author skip_unless_automated use_prereq
+    );
 
     # This version should match the corresponding rra-c-util release, but with
     # two digits for the minor version, including a leading zero if necessary,
     # so that it will sort properly.
-    $VERSION = '5.08';
+    $VERSION = '8.00';
+}
+
+# Compare a string to the contents of a file, similar to the standard is()
+# function, but to show the line-based unified diff between them if they
+# differ.
+#
+# $got      - The output that we received
+# $expected - The path to the file containing the expected output
+# $message  - The message to use when reporting the test results
+#
+# Returns: undef
+#  Throws: Exception on failure to read or write files or run diff
+sub is_file_contents {
+    my ($got, $expected, $message) = @_;
+
+    # If they're equal, this is simple.
+    open(my $fh, '<', $expected) or BAIL_OUT("Cannot open $expected: $!\n");
+    my $data = do { local $/ = undef; <$fh> };
+    close($fh) or BAIL_OUT("Cannot close $expected: $!\n");
+    if ($got eq $data) {
+        is($got, $data, $message);
+        return;
+    }
+
+    # Otherwise, we show a diff, but only if we have IPC::System::Simple.
+    eval { require IPC::System::Simple };
+    if ($@) {
+        ok(0, $message);
+        return;
+    }
+
+    # They're not equal.  Write out what we got so that we can run diff.
+    my $tmp     = File::Temp->new();
+    my $tmpname = $tmp->filename;
+    print {$tmp} $got or BAIL_OUT("Cannot write to $tmpname: $!\n");
+    my @command = ('diff', '-u', $expected, $tmpname);
+    my $diff    = IPC::System::Simple::capturex([0 .. 1], @command);
+    diag($diff);
+
+    # Remove the temporary file and report failure.
+    ok(0, $message);
+    return;
 }
 
 # Skip this test unless author tests are requested.  Takes a short description
@@ -44,7 +111,7 @@
 sub skip_unless_author {
     my ($description) = @_;
     if (!$ENV{AUTHOR_TESTING}) {
-        plan skip_all => "$description only run for author";
+        plan(skip_all => "$description only run for author");
     }
     return;
 }
@@ -63,7 +130,7 @@
     for my $env (qw(AUTOMATED_TESTING RELEASE_TESTING AUTHOR_TESTING)) {
         return if $ENV{$env};
     }
-    plan skip_all => "$description normally skipped";
+    plan(skip_all => "$description normally skipped");
     return;
 }
 
@@ -105,14 +172,14 @@
             use $module $version \@imports;
             1;
         };
-        $error = $@;
+        $error  = $@;
         $sigdie = $SIG{__DIE__} || undef;
     }
 
     # If the use failed for any reason, skip the test.
     if (!$result || $error) {
         my $name = length($version) > 0 ? "$module $version" : $module;
-        plan skip_all => "$name required for test";
+        plan(skip_all => "$name required for test");
     }
 
     # If the module set $SIG{__DIE__}, we cleared that via local.  Restore it.
@@ -128,7 +195,7 @@
 
 =for stopwords
 Allbery Allbery's DESC bareword sublicense MERCHANTABILITY NONINFRINGEMENT
-rra-c-util
+rra-c-util CPAN
 
 =head1 NAME
 
@@ -151,46 +218,53 @@
 
 =head1 DESCRIPTION
 
-This module collects utility functions that are useful for Perl test
-scripts.  It assumes Russ Allbery's Perl module layout and test
-conventions and will only be useful for other people if they use the
-same conventions.
+This module collects utility functions that are useful for Perl test scripts.
+It assumes Russ Allbery's Perl module layout and test conventions and will
+only be useful for other people if they use the same conventions.
+
+This module B<must> be loaded before Test::More or it will abort during
+import.  It will skip the test (by printing a skip message to standard output
+and exiting with status 0, equivalent to C<plan skip_all>) during import if
+Test::More is not available.  This allows tests written in Perl using this
+module to be skipped if run on a system with Perl but not Test::More, such as
+Red Hat systems with the C<perl> package but not the C<perl-core> package
+installed.
 
 =head1 FUNCTIONS
 
-None of these functions are imported by default.  The ones used by a
-script should be explicitly imported.
+None of these functions are imported by default.  The ones used by a script
+should be explicitly imported.
 
 =over 4
 
 =item skip_unless_author(DESC)
 
-Checks whether AUTHOR_TESTING is set in the environment and skips the
-whole test (by calling C<plan skip_all> from Test::More) if it is not.
-DESC is a description of the tests being skipped.  A space and C<only run
-for author> will be appended to it and used as the skip reason.
+Checks whether AUTHOR_TESTING is set in the environment and skips the whole
+test (by calling C<plan skip_all> from Test::More) if it is not.  DESC is a
+description of the tests being skipped.  A space and C<only run for author>
+will be appended to it and used as the skip reason.
 
 =item skip_unless_automated(DESC)
 
-Checks whether AUTHOR_TESTING, AUTOMATED_TESTING, or RELEASE_TESTING are
-set in the environment and skips the whole test (by calling C<plan
-skip_all> from Test::More) if they are not.  This should be used by tests
-that should not run during end-user installs of the module, but which
-should run as part of CPAN smoke testing and release testing.
+Checks whether AUTHOR_TESTING, AUTOMATED_TESTING, or RELEASE_TESTING are set
+in the environment and skips the whole test (by calling C<plan skip_all> from
+Test::More) if they are not.  This should be used by tests that should not run
+during end-user installs of the module, but which should run as part of CPAN
+smoke testing and release testing.
 
 DESC is a description of the tests being skipped.  A space and C<normally
 skipped> will be appended to it and used as the skip reason.
 
 =item use_prereq(MODULE[, VERSION][, IMPORT ...])
 
-Attempts to load MODULE with the given VERSION and import arguments.  If
-this fails for any reason, the test will be skipped (by calling C<plan
-skip_all> from Test::More) with a skip reason saying that MODULE is
-required for the test.
+Attempts to load MODULE with the given VERSION and import arguments.  If this
+fails for any reason, the test will be skipped (by calling C<plan skip_all>
+from Test::More) with a skip reason saying that MODULE is required for the
+test.
 
 VERSION will be passed to C<use> as a version bareword if it looks like a
-version number.  The remaining IMPORT arguments will be passed as the
-value of an array.
+version number.  The remaining IMPORT arguments will be passed as the value of
+an array.
 
 =back
 
@@ -200,36 +274,42 @@
 
 =head1 COPYRIGHT AND LICENSE
 
-Copyright 2013, 2014 The Board of Trustees of the Leland Stanford Junior
+Copyright 2016, 2018 Russ Allbery <eagle@eyrie.org>
+
+Copyright 2013-2014 The Board of Trustees of the Leland Stanford Junior
 University
 
-Permission is hereby granted, free of charge, to any person obtaining a
-copy of this software and associated documentation files (the "Software"),
-to deal in the Software without restriction, including without limitation
-the rights to use, copy, modify, merge, publish, distribute, sublicense,
-and/or sell copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following conditions:
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
 
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
 
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
-THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
 
 =head1 SEE ALSO
 
 Test::More(3), Test::RRA::Automake(3), Test::RRA::Config(3)
 
-This module is maintained in the rra-c-util package.  The current version
-is available from L<http://www.eyrie.org/~eagle/software/rra-c-util/>.
+This module is maintained in the rra-c-util package.  The current version is
+available from L<https://www.eyrie.org/~eagle/software/rra-c-util/>.
 
-The functions to control when tests are run use environment variables
-defined by the L<Lancaster
+The functions to control when tests are run use environment variables defined
+by the L<Lancaster
 Consensus|https://github.com/Perl-Toolchain-Gang/toolchain-site/blob/master/lancaster-consensus.md>.
 
 =cut
+
+# Local Variables:
+# copyright-at-end-flag: t
+# End:
diff -Nru libnet-duo-perl-1.01/t/style/critic.t libnet-duo-perl-1.02/t/style/critic.t
--- libnet-duo-perl-1.01/t/style/critic.t	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/t/style/critic.t	2019-03-06 22:02:52.000000000 -0800
@@ -7,7 +7,7 @@
 # files, junk, and any files explicitly configured to be ignored.
 #
 # Written by Russ Allbery <eagle@eyrie.org>
-# Copyright 2013, 2014
+# Copyright 2013-2014
 #     The Board of Trustees of the Leland Stanford Junior University
 #
 # Permission is hereby granted, free of charge, to any person obtaining a
@@ -27,6 +27,8 @@
 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 # DEALINGS IN THE SOFTWARE.
+#
+# SPDX-License-Identifier: MIT
 
 use 5.006;
 use strict;
@@ -34,10 +36,11 @@
 
 use lib 't/lib';
 
-use Test::More;
 use Test::RRA qw(skip_unless_author use_prereq);
 use Test::RRA::Config qw(@CRITIC_IGNORE);
 
+use Test::More;
+
 # Skip tests unless we're running author tests since this test is too
 # sensitive to the exact version of Perl::Critic to be generally useful.
 skip_unless_author('Coding style tests');
diff -Nru libnet-duo-perl-1.01/t/style/minimum-version.t libnet-duo-perl-1.02/t/style/minimum-version.t
--- libnet-duo-perl-1.01/t/style/minimum-version.t	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/t/style/minimum-version.t	2019-03-06 22:02:52.000000000 -0800
@@ -3,10 +3,10 @@
 # Check that too-new features of Perl are not being used.
 #
 # The canonical version of this file is maintained in the rra-c-util package,
-# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+# which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
 #
 # Written by Russ Allbery <eagle@eyrie.org>
-# Copyright 2013, 2014
+# Copyright 2013-2014
 #     The Board of Trustees of the Leland Stanford Junior University
 #
 # Permission is hereby granted, free of charge, to any person obtaining a
@@ -26,6 +26,8 @@
 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 # DEALINGS IN THE SOFTWARE.
+#
+# SPDX-License-Identifier: MIT
 
 use 5.006;
 use strict;
@@ -33,10 +35,11 @@
 
 use lib 't/lib';
 
-use Test::More;
 use Test::RRA qw(skip_unless_automated use_prereq);
 use Test::RRA::Config qw($MINIMUM_VERSION);
 
+use Test::More;
+
 # Skip for normal user installs since this doesn't affect functionality.
 skip_unless_automated('Minimum version tests');
 
diff -Nru libnet-duo-perl-1.01/t/style/module-version.t libnet-duo-perl-1.02/t/style/module-version.t
--- libnet-duo-perl-1.01/t/style/module-version.t	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/t/style/module-version.t	2019-03-06 22:02:52.000000000 -0800
@@ -8,6 +8,8 @@
 #
 # When given the --update option, instead fixes all of the Perl modules found
 # to have the correct version.
+#
+# SPDX-License-Identifier: MIT
 
 use 5.006;
 use strict;
@@ -15,11 +17,10 @@
 
 use lib 't/lib';
 
-use Carp qw(croak);
-use File::Find qw(find);
-use Getopt::Long qw(GetOptions);
-use Test::More;
 use Test::RRA qw(skip_unless_automated use_prereq);
+use Test::RRA::ModuleVersion qw(test_module_versions update_module_versions);
+
+use Getopt::Long qw(GetOptions);
 
 # If we have options, we're being run from the command line and always load
 # our prerequisite modules.  Otherwise, check if we have necessary
@@ -34,89 +35,6 @@
     use_prereq('Perl6::Slurp');
 }
 
-# A regular expression matching the version string for a module using the
-# package syntax from Perl 5.12 and later.  $1 will contain all of the line
-# contents prior to the actual version string, $2 will contain the version
-# itself, and $3 will contain the rest of the line.
-our $REGEX_VERSION_PACKAGE = qr{
-    (                           # prefix ($1)
-        \A \s*                  # whitespace
-        package \s+             # package keyword
-        [\w\:\']+ \s+           # package name
-    )
-    ( v? [\d._]+ )              # the version number itself ($2)
-    (                           # suffix ($3)
-        \s* ;
-    )
-}xms;
-
-# A regular expression matching a $VERSION string in a module.  $1 will
-# contain all of the line contents prior to the actual version string, $2 will
-# contain the version itself, and $3 will contain the rest of the line.
-our $REGEX_VERSION_OLD = qr{
-    (                           # prefix ($1)
-        \A .*                   # any prefix, such as "our"
-        [\$*]                   # scalar or typeglob
-        [\w\:\']*\b             # optional package name
-        VERSION\b               # version variable
-        \s* = \s*               # assignment
-    )
-    [\"\']?                     # optional leading quote
-    ( v? [\d._]+ )              # the version number itself ($2)
-    [\"\']?                     # optional trailing quote
-    (                           # suffix ($3)
-        \s*
-        ;
-    )
-}xms;
-
-# Find all the Perl modules shipped in this package, if any, and returns the
-# list of file names.
-#
-# $dir - The root directory to search, lib by default
-#
-# Returns: List of file names
-sub module_files {
-    my ($dir) = @_;
-    $dir ||= 'lib';
-    return if !-d $dir;
-    my @files;
-    my $wanted = sub {
-        if ($_ eq 'blib') {
-            $File::Find::prune = 1;
-            return;
-        }
-        if (m{ [.] pm \z }xms) {
-            push(@files, $File::Find::name);
-        }
-        return;
-    };
-    find($wanted, $dir);
-    return @files;
-}
-
-# Given a module file, read it for the version value and return the value.
-#
-# $file - File to check, which should be a Perl module
-#
-# Returns: The version of the module
-#  Throws: Text exception on I/O failure or inability to find version
-sub module_version {
-    my ($file) = @_;
-    open(my $data, q{<}, $file) or die "$0: cannot open $file: $!\n";
-    while (defined(my $line = <$data>)) {
-        if (   $line =~ $REGEX_VERSION_PACKAGE
-            || $line =~ $REGEX_VERSION_OLD)
-        {
-            my ($prefix, $version, $suffix) = ($1, $2, $3);
-            close($data) or die "$0: error reading from $file: $!\n";
-            return $version;
-        }
-    }
-    close($data) or die "$0: error reading from $file: $!\n";
-    die "$0: cannot find version number in $file\n";
-}
-
 # Return the current version of the distribution from MYMETA.json in the
 # current directory.
 #
@@ -133,88 +51,8 @@
     return $version;
 }
 
-# Given a module file and the new version for that module, update the version
-# in that module to the new one.
-#
-# $file    - Perl module file whose version should be updated
-# $version - The new version number
-#
-# Returns: undef
-#  Throws: Text exception on I/O failure or inability to find version
-sub update_module_version {
-    my ($file, $version) = @_;
-    open(my $in, q{<}, $file) or die "$0: cannot open $file: $!\n";
-    open(my $out, q{>}, "$file.new")
-      or die "$0: cannot create $file.new: $!\n";
-
-    # If the version starts with v, use it without quotes.  Otherwise, quote
-    # it to prevent removal of trailing zeroes.
-    if ($version !~ m{ \A v }xms) {
-        $version = "'$version'";
-    }
-
-    # Scan for the version and replace it.
-  SCAN:
-    while (defined(my $line = <$in>)) {
-        if (   $line =~ s{ $REGEX_VERSION_PACKAGE }{$1$version$3}xms
-            || $line =~ s{ $REGEX_VERSION_OLD     }{$1$version$3}xms)
-        {
-            print {$out} $line or die "$0: cannot write to $file.new: $!\n";
-            last SCAN;
-        }
-        print {$out} $line or die "$0: cannot write to $file.new: $!\n";
-    }
-
-    # Copy the rest of the input file to the output file.
-    print {$out} <$in> or die "$0: cannot write to $file.new: $!\n";
-    close($out) or die "$0: cannot flush $file.new: $!\n";
-    close($in)  or die "$0: error reading from $file: $!\n";
-
-    # All done.  Rename the new file over top of the old file.
-    rename("$file.new", $file)
-      or die "$0: cannot rename $file.new to $file: $!\n";
-    return;
-}
-
-# Act as a test suite.  Find all of the Perl modules in the package, if any,
-# and check that the version for each module matches the version of the
-# distribution.  Reports results with Test::More and sets up a plan based on
-# the number of modules found.
-#
-# Returns: undef
-#  Throws: Text exception on fatal errors
-sub test_versions {
-    my $dist_version = dist_version();
-    my @modules      = module_files();
-
-    # Output the plan.  Skip the test if there were no modules found.
-    if (@modules) {
-        plan tests => scalar(@modules);
-    } else {
-        plan skip_all => 'No Perl modules found';
-        return;
-    }
-
-    # For each module, get the module version and compare.
-    for my $module (@modules) {
-        my $module_version = module_version($module);
-        is($module_version, $dist_version, "Version for $module");
-    }
-    return;
-}
-
-# Update the versions of all modules to the current distribution version.
-#
-# Returns: undef
-#  Throws: Text exception on fatal errors
-sub update_versions {
-    my $version = dist_version();
-    my @modules = module_files();
-    for my $module (@modules) {
-        update_module_version($module, $version);
-    }
-    return;
-}
+# Get the version of the overall distribution.
+my $version = dist_version();
 
 # Main routine.  We run as either a test suite or as a script to update all of
 # the module versions, selecting based on whether we got the -u / --update
@@ -223,15 +61,15 @@
 Getopt::Long::config('bundling', 'no_ignore_case');
 GetOptions('update|u' => \$update) or exit 1;
 if ($update) {
-    update_versions();
+    update_module_versions('lib', $version);
 } else {
-    test_versions();
+    test_module_versions('lib', $version);
 }
 exit 0;
 __END__
 
 =for stopwords
-Allbery sublicense MERCHANTABILITY NONINFRINGEMENT CPAN
+Allbery sublicense MERCHANTABILITY NONINFRINGEMENT CPAN rra-c-util
 
 =head1 NAME
 
@@ -243,28 +81,27 @@
 
 =head1 REQUIREMENTS
 
-Perl 5.6.0 or later, the Perl6::Slurp module, and the JSON::PP Perl
-module, both of which are available from CPAN.  JSON::PP is also included
-in Perl core in Perl 5.14 and later.
+Perl 5.6.0 or later, the Perl6::Slurp module, and the JSON::PP Perl module,
+both of which are available from CPAN.  JSON::PP is also included in Perl core
+in Perl 5.14 and later.
 
 =head1 DESCRIPTION
 
-This script has a dual purpose as either a test script or a utility
-script.  The intent is to assist with maintaining consistent versions in a
-Perl distribution, supporting both the package keyword syntax introduced
-in Perl 5.12 or the older explicit setting of a $VERSION variable.
-
-As a test, it reads the current version of a package from the
-F<MYMETA.json> file in the current directory (which should be the root of
-the distribution) and then looks for any Perl modules in F<lib>.  If it
-finds any, it checks that the version number of the Perl module matches
-the version number of the package from the F<MYMETA.json> file.  These
-test results are reported with Test::More, suitable for any TAP harness.
-
-As a utility script, when run with the B<--update> option, it similarly
-finds all Perl modules in F<lib> and then rewrites their version setting
-to match the version of the package as determined from the F<MYMETA.json>
-file.
+This script has a dual purpose as either a test script or a utility script.
+The intent is to assist with maintaining consistent versions in a Perl
+distribution, supporting both the package keyword syntax introduced in Perl
+5.12 or the older explicit setting of a $VERSION variable.
+
+As a test, it reads the current version of a package from the F<MYMETA.json>
+file in the current directory (which should be the root of the distribution)
+and then looks for any Perl modules in F<lib>.  If it finds any, it checks
+that the version number of the Perl module matches the version number of the
+package from the F<MYMETA.json> file.  These test results are reported with
+Test::More, suitable for any TAP harness.
+
+As a utility script, when run with the B<--update> option, it similarly finds
+all Perl modules in F<lib> and then rewrites their version setting to match
+the version of the package as determined from the F<MYMETA.json> file.
 
 =head1 OPTIONS
 
@@ -272,9 +109,9 @@
 
 =item B<-u>, B<--update>
 
-Rather than test the Perl modules for the correct version, update all
-Perl modules found in the tree under F<lib> to the current version
-from the C<MYMETA.json> file.
+Rather than test the Perl modules for the correct version, update all Perl
+modules found in the tree under F<lib> to the current version from the
+C<MYMETA.json> file.
 
 =back
 
@@ -284,32 +121,36 @@
 
 =head1 COPYRIGHT AND LICENSE
 
-Copyright 2013, 2014 The Board of Trustees of the Leland Stanford Junior
-University
+Copyright 2014-2016 Russ Allbery <eagle@eyrie.org>
 
-Copyright 2014 Russ Allbery <eagle@eyrie.org>
+Copyright 2013-2014 The Board of Trustees of the Leland Stanford Junior
+University
 
-Permission is hereby granted, free of charge, to any person obtaining a
-copy of this software and associated documentation files (the "Software"),
-to deal in the Software without restriction, including without limitation
-the rights to use, copy, modify, merge, publish, distribute, sublicense,
-and/or sell copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following conditions:
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
 
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
 
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
-THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
 
 =head1 SEE ALSO
 
 This module is maintained in the rra-c-util package.  The current version
-is available from L<http://www.eyrie.org/~eagle/software/rra-c-util/>.
+is available from L<https://www.eyrie.org/~eagle/software/rra-c-util/>.
 
 =cut
+
+# Local Variables:
+# copyright-at-end-flag: t
+# End:
diff -Nru libnet-duo-perl-1.01/t/style/obsolete-strings.t libnet-duo-perl-1.02/t/style/obsolete-strings.t
--- libnet-duo-perl-1.01/t/style/obsolete-strings.t	1969-12-31 16:00:00.000000000 -0800
+++ libnet-duo-perl-1.02/t/style/obsolete-strings.t	2019-03-06 22:02:52.000000000 -0800
@@ -0,0 +1,96 @@
+#!/usr/bin/perl
+#
+# Check for obsolete strings in source files.
+#
+# Examine all source files in a distribution for obsolete strings and report
+# on files that fail this check.  This catches various transitions I want to
+# do globally in all my packages, like changing my personal URLs to https.
+#
+# The canonical version of this file is maintained in the rra-c-util package,
+# which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
+#
+# Copyright 2016, 2018 Russ Allbery <eagle@eyrie.org>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+#
+# SPDX-License-Identifier: MIT
+
+use 5.006;
+use strict;
+use warnings;
+
+use lib 't/lib';
+
+use Test::RRA qw(skip_unless_automated);
+
+use File::Find qw(find);
+use Test::More;
+
+# Bad patterns to search for.
+my @BAD_REGEXES = (qr{ http:// \S+ [.]eyrie[.]org }xms);
+my @BAD_STRINGS = qw(rra@stanford.edu RRA_MAINTAINER_TESTS);
+
+# File or directory names to always skip.
+my %SKIP = map { $_ => 1 } qw(
+  .git Changes _build blib cover_db obsolete-strings.t
+);
+
+# Only run this test during automated testing, since failure doesn't indicate
+# any user-noticable flaw in the package itself.
+skip_unless_automated('Obsolete strings tests');
+
+# Scan files for bad URL patterns.  This is meant to be run as the wanted
+# function from File::Find.
+sub check_file {
+    my $filename = $_;
+
+    # Ignore and prune any skipped files.  Ignore directories and binaries.
+    if ($SKIP{$filename}) {
+        $File::Find::prune = 1;
+        return;
+    }
+    return if -d $filename;
+    return if !-T $filename;
+
+    # Scan the file.
+    open(my $fh, '<', $filename) or BAIL_OUT("Cannot open $File::Find::name");
+    while (defined(my $line = <$fh>)) {
+        for my $regex (@BAD_REGEXES) {
+            if ($line =~ $regex) {
+                ok(0, "$File::Find::name contains $regex");
+                close($fh) or BAIL_OUT("Cannot close $File::Find::name");
+                return;
+            }
+        }
+        for my $string (@BAD_STRINGS) {
+            if (index($line, $string) != -1) {
+                ok(0, "$File::Find::name contains $string");
+                close($fh) or BAIL_OUT("Cannot close $File::Find::name");
+                return;
+            }
+        }
+    }
+    close($fh) or BAIL_OUT("Cannot close $File::Find::name");
+    ok(1, $File::Find::name);
+    return;
+}
+
+# Use File::Find to scan all files from the top of the directory.
+find(\&check_file, q{.});
+done_testing();
diff -Nru libnet-duo-perl-1.01/t/style/strict.t libnet-duo-perl-1.02/t/style/strict.t
--- libnet-duo-perl-1.01/t/style/strict.t	2015-08-16 11:46:06.000000000 -0700
+++ libnet-duo-perl-1.02/t/style/strict.t	2019-03-06 22:02:52.000000000 -0800
@@ -3,10 +3,11 @@
 # Test Perl code for strict, warnings, and syntax.
 #
 # The canonical version of this file is maintained in the rra-c-util package,
-# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+# which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
 #
 # Written by Russ Allbery <eagle@eyrie.org>
-# Copyright 2013, 2014
+# Copyright 2016, 2018 Russ Allbery <eagle@eyrie.org>
+# Copyright 2013-2014
 #     The Board of Trustees of the Leland Stanford Junior University
 #
 # Permission is hereby granted, free of charge, to any person obtaining a
@@ -26,6 +27,8 @@
 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 # DEALINGS IN THE SOFTWARE.
+#
+# SPDX-License-Identifier: MIT
 
 use 5.006;
 use strict;
@@ -33,24 +36,51 @@
 
 use lib 't/lib';
 
-use File::Spec;
 use Test::RRA qw(skip_unless_automated use_prereq);
 
+use File::Spec;
+
 # Skip for normal user installs since this doesn't affect functionality.
 skip_unless_automated('Strictness tests');
 
-# Load prerequisite modules.
-use_prereq('Test::Strict');
+# Load prerequisite modules.  At least 0.25 is needed to recognize that having
+# use 5.012 or later automatically implies use strict.
+use_prereq('Test::Strict', '0.25');
+
+# Directories to exclude from checks.
+my %EXCLUDE = map { $_ => 1 } qw(.git blib);
+
+# Determine whether we want to check the given file or top-level directory.
+# Assume that the only interesting files at the top level are directories or
+# files ending in *.PL.
+#
+# $file - Name of the file or directory
+#
+# Returns: 1 if it should be checked, undef otherwise.
+sub should_check {
+    my ($file) = @_;
+    return   if $EXCLUDE{$file};
+    return 1 if -d $file;
+    return 1 if $file =~ m{ [.] PL \z }xms;
+    return;
+}
 
-# Test everything in the distribution directory except the Build and
-# Makefile.PL scripts generated by Module::Build.  We also want to check use
-# warnings.
-$Test::Strict::TEST_SKIP = ['Build', 'Makefile.PL'];
+# Test::Strict (as of 0.47) doesn't have a way of excluding whole directories
+# from all_perl_files_ok and doesn't exclude .git, which results in false
+# positives if there are Perl files unpacked under .git (which is often the
+# case when using dgit).  We therefore can't just point it at the root of the
+# module distribution and instead have to manually construct a list of
+# interesting files.
+opendir(my $rootdir, File::Spec->curdir)
+  or die "$0: cannot open current directory: $!\n";
+my @files = File::Spec->no_upwards(readdir($rootdir));
+closedir($rootdir) or die "$0: cannot close current directory: $!\n";
+my @to_check = grep { should_check($_) } @files;
+
+# Test the files and top-level directories we found, including checking for
+# use warnings.
 $Test::Strict::TEST_WARNINGS = 1;
-all_perl_files_ok(File::Spec->curdir);
+all_perl_files_ok(@to_check);
 
 # Hack to suppress "used only once" warnings.
-END {
-    $Test::Strict::TEST_SKIP     = [];
-    $Test::Strict::TEST_WARNINGS = 0;
-}
+END { $Test::Strict::TEST_WARNINGS = 0 }

Reply to: