Neil Bowers

NAME

PAUSE::Permissions - interface to PAUSE's module permissions file (06perms.txt)

SYNOPSIS

  use PAUSE::Permissions 0.08;
  
  my $pp = PAUSE::Permissions->new;
  my $mp = $pp->module_permissions('HTTP::Client');
  
  my $owner    = $mp->owner;
  my @comaints = $mp->co_maintainers;

  my $iterator = $pp->module_iterator();
  while (my $mp = $iterator->next_module) {
    print "module = ", $mp->name, "\n";
    print "  owner = ", $mp->owner // 'none', "\n";
  }

DESCRIPTION

PAUSE::Permissions provides an interface to the 06perms.txt file produced by the Perl Authors Upload Server (PAUSE). The file records who has what permissions for every module on CPAN. The format and interpretation of this file are covered in "The 06perms.txt file" below.

By default, the module will mirror 06perms.txt from CPAN, using HTTP::Tiny to request it and store it locally. By default it will get the file from http://www.cpan.org, but you can pass an alternate URI to the constructor:

  $perms_uri = "http://$CPAN_MIRROR/modules/06perms.txt";
  $pp = PAUSE::Permissions->new(uri => $perms_uri);

If you've already got a copy lying around, you can tell the module to use that:

  $pp = PAUSE::Permissions->new( filename => '/tmp/06perms.txt' );

Having created an instance of PAUSE::Permissions, you can then call the module_permissions method to get the permissions for a particular module. The SYNOPSIS gives the basic usage.

Note: you should make sure you're using version 0.08 or later. PAUSE now treats package names case insensitively with respect to permissions, so this module does now as well.

METHODS

There are only four methods you need to know: the constructor (new), getting an iterator over individual entries (entry_iterator), getting an iterator over modules (module_iterator), and module_permissions().

new

The constructor takes a hash of options:

  • path: the path to a local copy of 06perms.txt. The constructor will die() if the file doesn't exist, or isn't readable. If you don't provide this parameter, then we'll try and get 06perms.txt from the url parameter, and store it in a local directory, determined by File::HomeDir->my_dist_data.

  • url: the URL for 06perms.txt; defaults to http://www.cpan.org/modules/06perms.txt

So you might use the following, to get 06perms.txt from your 'local' CPAN mirror and store it somewhere of your choosing:

  $pp = PAUSE::Permissions->new(
                uri     => 'http://cpan.inode.at/modules/06perms.txt',
                cachdir => '/tmp/pause',
            );

module_iterator

This is a method that returns an instance of PAUSE::Permissions::ModuleIterator, which provides a simple mechanism for iterating over the whole permissions file, module by module:

  $pp       = PAUSE::Permissions->new();
  $iterator = $pp->module_iterator();
  
  while (my $module = $iterator->next_module) {
    print "module    = ", $module->name,           "\n";
    print "owner     = ", $module->owner,          "\n";
    print "co-maints = ", $module->co_maintainers, "\n";
  }

The next_module() method returns either an instance of PAUSE::Permissions::Module, or undef when the end of the file is reached.

entry_iterator

This is a method that returns an instance of PAUSE::Permissions::EntryIterator, which provides a simple mechanism for iterating over the whole permissions file, line by line:

  $pp       = PAUSE::Permissions->new();
  $iterator = $pp->entry_iterator();
  while (my $entry = $iterator->next) {
    print "module = ", $entry->module,     "\n";
    print "user   = ", $entry->user,       "\n";
    print "perm   = ", $entry->permission, "\n";
  }

The module method returns a module name; user returns the PAUSE id of a PAUSE user; perm is one of the three permission identifiers ('m', 'f', or 'c').

module_permissions

The module_permissions method takes a single module name, and returns an instance of PAUSE::Permissions::Module:

  $mp = $pp->module_permissions( $module_name );

Refer to the documentation for PAUSE::Permissions::Module, but the key methods are:

  • owner() returns the PAUSE id of the owner (see "The 06perms.txt file" below), or undef if there isn't a defined owner.

  • co_maintainers() returns a list of PAUSE ids, or an empty list if the module has no co-maintainers.

module_permissions() returns undef if the module wasn't found in the permissions list. If you've only just registered your new module, or only just uploaded the first release, then it might not have made it into the file yet.

The 06perms.txt file

You can find the file on CPAN:

As of October 2012 this file is 8.4M in size.

The file starts with a header, followed by one blank line, then the body. The body contains one line per module per user:

  Config::Properties,CMANLEY,c
  Config::Properties,RANDY,f
  Config::Properties,SALVA,m

Each line has three values, separated by commas:

  • The name of a module.

  • A PAUSE user id, which by convention is always given in upper case.

  • A single character that specifies what permissions the user has with respect to the module. See below.

Note that this file lists modules, not distributions. Every module in a CPAN distribution will be listed separately in this file. Modules are listed in alphabetical order, and for a given module, the PAUSE ids are listed in alphabetical order.

There are three characters that can appear in the permissions column:

  • 'm' identifies the user as the registered maintainer of the module. A module can only ever have zero or one user listed with the 'm' permission. For more details on registering a module, see 04pause.html.

  • 'f' identifies the user as the first person to upload the module to CPAN. You don't have to register a module before uploading it, and ownership in this case is first-come-first-served. A module can only ever have zero or one user listed with the 'f' permission.

  • 'c' identifies the user as a co-maintainer of the module. A module can have any number of co-maintainers.

If you first upload a module, you'll get an 'f' against you in the file. If you subsequently register the module, you'll get an 'm' against you. Internally PAUSE will have you recorded with both an 'm' and an 'f', but 06perms.txt only lists the highest precedence permission for each user.

What do the permissions mean?

  • Various places refer to the 'owner' of the module. This will be either the 'm' or 'f' permission, with 'm' taking precedence. If a module has both an 'm' and an 'f' user listed, then the 'm' user is considered the owner, and the 'f' user isn't. If a module has a user with 'f' listed, but no 'm', then the 'f' user is considered the owner.

  • If a module is listed in 06perms.txt, then only the people listed (m, f, or c) are allowed to upload (new) versions of the module. If anyone else uploads a version of the module, then the offending distribution will not be indexed: it will appear in the uploader's directory on CPAN, but won't be indexed under the module.

  • Only the owner for a module can grant co-maintainer status for a module. I.e. if you have the 'm' permission, you can always do it. If you have the 'f' permission, you can only do it if no-one else has the 'm' permission. You can grant co-maintainer status using the PAUSE web interface.

  • Regardless of your permissions, you can only remove things from CPAN that you uploaded. If you're the owner, you can't delete a version uploaded by a co-maintainer. If you weren't happy with it, you could revoke their co-maintainer status and then upload a superseding version. But we'd recommend you talk to them (first).

  • If you upload a distribution containing a number of previously unseen modules, and haven't pre-registered them, then you'll get an 'f' permission for all of the modules. Let's say you upload a second release of the distribution, which doesn't include one of the modules, and then delete the first release from CPAN (via the PAUSE web interface). After some time the module will no longer be on CPAN, but you'll still have the 'f' permission in 06perms.txt. You can free up the namespace using the PAUSE interface ("Change Permissions").

  • If your first upload of a module is a Developer Release, then you won't get permissions for the module. You don't get permissions for a module until you've uploaded a non-developer release containing the module, that was accepted for indexing.

  • If you take over maintenance of a module, then you'll generally be given the permissions of the previous maintainer. So if the previous maintainer had 'm', then you'll get 'm', and (s)he will be downgraded to 'c'. If the previous maintainer had 'f', then you'll get 'f', and the previous owner will be downgraded to 'c'.

SEE ALSO

App::PAUSE::CheckPerms checks whether all modules in (your) CPAN distributions have the same permissions.

tmpdir() in File::Spec::Functions is used to get a local directory for caching 06perms.txt.

HTTP::Tiny is used to mirror 06perms.txt from CPAN.

TODO

  • Request the file gzip'd, if we've got an appropriate module that can be used to gunzip it.

  • At construct time we currently mirror the file; should do this lazily, triggering it the first time you want a module's perms.

  • Every time you ask for a module, I scan the file from the start, then close it once I've got the details for the requested module. Would be a lot more efficient to keep the file open and start the search from there, as the file is sorted. A binary chop on the file would be much more efficient as well.

  • The 06perms.txt file is currently mirrored with an If-Modified-Since request. We should probably also support a mechanism for saying things like "only get it if my copy is more than N days old". And consider using rsync as well.

  • A command-line script.

REPOSITORY

https://github.com/neilbowers/PAUSE-Permissions

AUTHOR

Neil Bowers <neilb@cpan.org>

Thanks to Andreas König, for patiently answering many questions on how this stuff all works.

COPYRIGHT AND LICENSE

This software is copyright (c) 2012-2013 by Neil Bowers <neilb@cpan.org>.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.