summaryrefslogtreecommitdiff
path: root/perl/Wallet/Report.pm
diff options
context:
space:
mode:
Diffstat (limited to 'perl/Wallet/Report.pm')
-rw-r--r--perl/Wallet/Report.pm680
1 files changed, 0 insertions, 680 deletions
diff --git a/perl/Wallet/Report.pm b/perl/Wallet/Report.pm
deleted file mode 100644
index 1085546..0000000
--- a/perl/Wallet/Report.pm
+++ /dev/null
@@ -1,680 +0,0 @@
-# Wallet::Report -- Wallet system reporting interface.
-#
-# Written by Russ Allbery <eagle@eyrie.org>
-# Copyright 2008, 2009, 2010, 2013
-# The Board of Trustees of the Leland Stanford Junior University
-#
-# See LICENSE for licensing terms.
-
-##############################################################################
-# Modules and declarations
-##############################################################################
-
-package Wallet::Report;
-require 5.006;
-
-use strict;
-use vars qw($VERSION);
-
-use Wallet::ACL;
-use Wallet::Schema;
-
-# This version should be increased on any code change to this module. Always
-# use two digits for the minor version with a leading zero if necessary so
-# that it will sort properly.
-$VERSION = '0.04';
-
-##############################################################################
-# Constructor, destructor, and accessors
-##############################################################################
-
-# Create a new wallet report object. Opens a connection to the database that
-# will be used for all of the wallet configuration information. Throw an
-# exception if anything goes wrong.
-sub new {
- my ($class) = @_;
- my $schema = Wallet::Schema->connect;
- my $self = { schema => $schema };
- bless ($self, $class);
- return $self;
-}
-
-# Returns the database handle (used mostly for testing).
-sub dbh {
- my ($self) = @_;
- return $self->{schema}->storage->dbh;
-}
-
-# Returns the DBIx::Class-based database schema object.
-sub schema {
- my ($self) = @_;
- return $self->{schema};
-}
-
-# Set or return the error stashed in the object.
-sub error {
- my ($self, @error) = @_;
- if (@error) {
- my $error = join ('', @error);
- chomp $error;
- 1 while ($error =~ s/ at \S+ line \d+\.?\z//);
- $self->{error} = $error;
- }
- return $self->{error};
-}
-
-# Disconnect the database handle on object destruction to avoid warnings.
-sub DESTROY {
- my ($self) = @_;
- $self->{schema}->storage->dbh->disconnect;
-}
-
-##############################################################################
-# Object reports
-##############################################################################
-
-# Return the SQL statement to find every object in the database.
-sub objects_all {
- my ($self) = @_;
- my @objects;
-
- my %search = ();
- my %options = (order_by => [ qw/ob_type ob_name/ ],
- select => [ qw/ob_type ob_name/ ]);
-
- return (\%search, \%options);
-}
-
-# Return the SQL statement and the search field required to find all objects
-# matching a specific type.
-sub objects_type {
- my ($self, $type) = @_;
- my @objects;
-
- my %search = (ob_type => $type);
- my %options = (order_by => [ qw/ob_type ob_name/ ],
- select => [ qw/ob_type ob_name/ ]);
-
- return (\%search, \%options);
-}
-
-# Return the SQL statement and search field required to find all objects owned
-# by a given ACL. If the requested owner is null, we ignore this and do a
-# different search for IS NULL. If the requested owner does not actually
-# match any ACLs, set an error and return undef.
-sub objects_owner {
- my ($self, $owner) = @_;
- my @objects;
-
- my %search;
- my %options = (order_by => [ qw/ob_type ob_name/ ],
- select => [ qw/ob_type ob_name/ ]);
-
- if (lc ($owner) eq 'null') {
- %search = (ob_owner => undef);
- } else {
- my $acl = eval { Wallet::ACL->new ($owner, $self->{schema}) };
- return unless $acl;
- %search = (ob_owner => $acl->id);
- }
-
- return (\%search, \%options);
-}
-
-# Return the SQL statement and search field required to find all objects that
-# have a specific flag set.
-sub objects_flag {
- my ($self, $flag) = @_;
- my @objects;
-
- my %search = ('flags.fl_flag' => $flag);
- my %options = (join => 'flags',
- prefetch => 'flags',
- order_by => [ qw/ob_type ob_name/ ],
- select => [ qw/ob_type ob_name/ ]);
-
- return (\%search, \%options);
-}
-
-# Return the SQL statement and search field required to find all objects that
-# a given ACL has any permissions on. This expands from objects_owner in that
-# it will also match any records that have the ACL set for get, store, show,
-# destroy, or flags. If the requested owner does not actually match any ACLs,
-# set an error and return the empty string.
-sub objects_acl {
- my ($self, $search) = @_;
- my @objects;
-
- my $schema = $self->{schema};
- my $acl = eval { Wallet::ACL->new ($search, $schema) };
- return unless $acl;
-
- my @search = ({ ob_owner => $acl->id },
- { ob_acl_get => $acl->id },
- { ob_acl_store => $acl->id },
- { ob_acl_show => $acl->id },
- { ob_acl_destroy => $acl->id },
- { ob_acl_flags => $acl->id });
- my %options = (order_by => [ qw/ob_type ob_name/ ],
- select => [ qw/ob_type ob_name/ ]);
-
- return (\@search, \%options);
-}
-
-# Return the SQL statement to find all objects that have been created but
-# have never been retrieved (via get).
-sub objects_unused {
- my ($self) = @_;
- my @objects;
-
- my %search = (ob_downloaded_on => undef);
- my %options = (order_by => [ qw/ob_type ob_name/ ],
- select => [ qw/ob_type ob_name/ ]);
-
- return (\%search, \%options);
-}
-
-# Returns a list of all objects stored in the wallet database in the form of
-# type and name pairs. On error and for an empty database, the empty list
-# will be returned. To distinguish between an empty list and an error, call
-# error(), which will return undef if there was no error. Farms out specific
-# statement to another subroutine for specific search types, but each case
-# should return ob_type and ob_name in that order.
-sub objects {
- my ($self, $type, @args) = @_;
- undef $self->{error};
-
- # Get the search and options array refs from specific functions.
- my ($search_ref, $options_ref);
- if (!defined $type || $type eq '') {
- ($search_ref, $options_ref) = $self->objects_all;
- } else {
- if ($type ne 'unused' && @args != 1) {
- $self->error ("object searches require one argument to search");
- } elsif ($type eq 'type') {
- ($search_ref, $options_ref) = $self->objects_type (@args);
- } elsif ($type eq 'owner') {
- ($search_ref, $options_ref) = $self->objects_owner (@args);
- } elsif ($type eq 'flag') {
- ($search_ref, $options_ref) = $self->objects_flag (@args);
- } elsif ($type eq 'acl') {
- ($search_ref, $options_ref) = $self->objects_acl (@args);
- } elsif ($type eq 'unused') {
- ($search_ref, $options_ref) = $self->objects_unused (@args);
- } else {
- $self->error ("do not know search type: $type");
- }
- return unless $search_ref;
- }
-
- # Perform the search and return on any errors.
- my @objects;
- my $schema = $self->{schema};
- eval {
- my @objects_rs = $schema->resultset('Object')->search ($search_ref,
- $options_ref);
- for my $object_rs (@objects_rs) {
- push (@objects, [ $object_rs->ob_type, $object_rs->ob_name ]);
- }
- };
- if ($@) {
- $self->error ("cannot list objects: $@");
- return;
- }
-
- return @objects;
-}
-
-##############################################################################
-# ACL reports
-##############################################################################
-
-# Returns the SQL statement required to find and return all ACLs in the
-# database.
-sub acls_all {
- my ($self) = @_;
- my @acls;
-
- my $schema = $self->{schema};
- my %search = ();
- my %options = (order_by => [ qw/ac_id/ ],
- select => [ qw/ac_id ac_name/ ]);
-
- eval {
- my @acls_rs = $schema->resultset('Acl')->search (\%search, \%options);
- for my $acl_rs (@acls_rs) {
- push (@acls, [ $acl_rs->ac_id, $acl_rs->ac_name ]);
- }
- };
-
- if ($@) {
- $self->error ("cannot list ACLs: $@");
- return;
- }
- return (@acls);
-}
-
-# Returns the SQL statement required to find all empty ACLs in the database.
-sub acls_empty {
- my ($self) = @_;
- my @acls;
-
- my $schema = $self->{schema};
- my %search = (ae_id => undef);
- my %options = (join => 'acl_entries',
- prefetch => 'acl_entries',
- order_by => [ qw/ac_id/ ],
- select => [ qw/ac_id ac_name/ ]);
-
- eval {
- my @acls_rs = $schema->resultset('Acl')->search (\%search, \%options);
- for my $acl_rs (@acls_rs) {
- push (@acls, [ $acl_rs->ac_id, $acl_rs->ac_name ]);
- }
- };
-
- if ($@) {
- $self->error ("cannot list ACLs: $@");
- return;
- }
- return (@acls);
-}
-
-# Returns the SQL statement and the field required to find ACLs containing the
-# specified entry. The identifier is automatically surrounded by wildcards to
-# do a substring search.
-sub acls_entry {
- my ($self, $type, $identifier) = @_;
- my @acls;
-
- my $schema = $self->{schema};
- my %search = (ae_scheme => $type,
- ae_identifier => { like => '%'.$identifier.'%' });
- my %options = (join => 'acl_entries',
- prefetch => 'acl_entries',
- order_by => [ qw/ac_id/ ],
- select => [ qw/ac_id ac_name/ ],
- distinct => 1);
-
- eval {
- my @acls_rs = $schema->resultset('Acl')->search (\%search, \%options);
- for my $acl_rs (@acls_rs) {
- push (@acls, [ $acl_rs->ac_id, $acl_rs->ac_name ]);
- }
- };
-
- if ($@) {
- $self->error ("cannot list ACLs: $@");
- return;
- }
- return (@acls);
-}
-
-# Returns the SQL statement required to find unused ACLs.
-sub acls_unused {
- my ($self) = @_;
- my @acls;
-
- my $schema = $self->{schema};
- my %search = (
- #'acls_owner.ob_owner' => undef,
- #'acls_get.ob_owner' => undef,
- #'acls_store.ob_owner' => undef,
- #'acls_show.ob_owner' => undef,
- #'acls_destroy.ob_owner' => undef,
- #'acls_flags.ob_owner' => undef,
- );
- my %options = (#join => [ qw/acls_owner acls_get acls_store acls_show acls_destroy acls_flags/ ],
- order_by => [ qw/ac_id/ ],
- select => [ qw/ac_id ac_name/ ]);
-
- eval {
- my @acls_rs = $schema->resultset('Acl')->search (\%search, \%options);
-
- # FIXME: Almost certainly a way of doing this with the search itself.
- for my $acl_rs (@acls_rs) {
- next if $acl_rs->acls_owner->first;
- next if $acl_rs->acls_get->first;
- next if $acl_rs->acls_store->first;
- next if $acl_rs->acls_show->first;
- next if $acl_rs->acls_destroy->first;
- next if $acl_rs->acls_flags->first;
- push (@acls, [ $acl_rs->ac_id, $acl_rs->ac_name ]);
- }
- };
-
- if ($@) {
- $self->error ("cannot list ACLs: $@");
- return;
- }
- return (@acls);
-}
-
-# Obtain a textual representation of the membership of an ACL, returning undef
-# on error and setting the internal error.
-sub acl_membership {
- my ($self, $id) = @_;
- my $acl = eval { Wallet::ACL->new ($id, $self->{schema}) };
- if ($@) {
- $self->error ($@);
- return;
- }
- my @members = map { "$_->[0] $_->[1]" } $acl->list;
- if (!@members && $acl->error) {
- $self->error ($acl->error);
- return;
- }
- return join ("\n", @members);
-}
-
-# Duplicate ACL detection unfortunately needs to do something more complex
-# than just return a SQL statement, so it's handled differently than other
-# reports. All the work is done here and the results returned as a list of
-# sets of duplicates.
-sub acls_duplicate {
- my ($self) = @_;
- my @acls = sort map { $_->[1] } $self->acls;
- return if (!@acls && $self->{error});
- return if @acls < 2;
- my %result;
- for my $i (0 .. ($#acls - 1)) {
- my $members = $self->acl_membership ($acls[$i]);
- return unless defined $members;
- for my $j (($i + 1) .. $#acls) {
- my $check = $self->acl_membership ($acls[$j]);
- return unless defined $check;
- if ($check eq $members) {
- $result{$acls[$i]} ||= [];
- push (@{ $result{$acls[$i]} }, $acls[$j]);
- }
- }
- }
- my @result;
- for my $acl (sort keys %result) {
- push (@result, [ $acl, sort @{ $result{$acl} } ]);
- }
- return @result;
-}
-
-# Returns a list of all ACLs stored in the wallet database as a list of pairs
-# of ACL IDs and ACL names, possibly limited by some criteria. On error and
-# for an empty database, the empty list will be returned. To distinguish
-# between an empty list and an error, call error(), which will return undef if
-# there was no error.
-sub acls {
- my ($self, $type, @args) = @_;
- undef $self->{error};
-
- # Find the ACLs for any given search.
- my @acls;
- if (!defined $type || $type eq '') {
- @acls = $self->acls_all;
- } else {
- if ($type eq 'duplicate') {
- return $self->acls_duplicate;
- } elsif ($type eq 'entry') {
- if (@args == 0) {
- $self->error ('ACL searches require an argument to search');
- return;
- } else {
- @acls = $self->acls_entry (@args);
- }
- } elsif ($type eq 'empty') {
- @acls = $self->acls_empty;
- } elsif ($type eq 'unused') {
- @acls = $self->acls_unused;
- } else {
- $self->error ("unknown search type: $type");
- return;
- }
- }
- return @acls;
-}
-
-# Returns all ACL entries contained in owner ACLs for matching objects.
-# Objects are specified by type and name, which may be SQL wildcard
-# expressions. Each list member will be a pair of ACL scheme and ACL
-# identifier, with duplicates removed. On error and for no matching entries,
-# the empty list will be returned. To distinguish between an empty return and
-# an error, call error(), which will return undef if there was no error.
-sub owners {
- my ($self, $type, $name) = @_;
- undef $self->{error};
- my $schema = $self->{schema};
-
- my @owners;
- eval {
- my %search = (
- 'acls_owner.ob_type' => { like => $type },
- 'acls_owner.ob_name' => { like => $name });
- my %options = (
- join => { 'acls' => 'acls_owner' },
- order_by => [ qw/ae_scheme ae_identifier/ ],
- distinct => 1,
- );
-
- my @acls_rs = $schema->resultset('AclEntry')->search (\%search,
- \%options);
- for my $acl_rs (@acls_rs) {
- my $scheme = $acl_rs->ae_scheme;
- my $identifier = $acl_rs->ae_identifier;
- push (@owners, [ $scheme, $identifier ]);
- }
- };
- if ($@) {
- $self->error ("cannot report on owners: $@");
- return;
- }
- return @owners;
-}
-
-##############################################################################
-# Auditing
-##############################################################################
-
-# Audit the database for violations of local policy. Returns a list of
-# objects (as type and name pairs) or a list of ACLs (as ID and name pairs).
-# On error and for no matching entries, the empty list will be returned. To
-# distinguish between an empty return and an error, call error(), which will
-# return undef if there was no error.
-sub audit {
- my ($self, $type, $audit) = @_;
- undef $self->{error};
- unless (defined ($type) and defined ($audit)) {
- $self->error ("type and audit not specified");
- return;
- }
- if ($type eq 'objects') {
- if ($audit eq 'name') {
- return unless defined &Wallet::Config::verify_name;
- my @objects = $self->objects;
- my @results;
- for my $object (@objects) {
- my ($type, $name) = @$object;
- my $error = Wallet::Config::verify_name ($type, $name);
- push (@results, $object) if $error;
- }
- return @results;
- } else {
- $self->error ("unknown object audit: $audit");
- return;
- }
- } elsif ($type eq 'acls') {
- if ($audit eq 'name') {
- return unless defined &Wallet::Config::verify_acl_name;
- my @acls = $self->acls;
- my @results;
- for my $acl (@acls) {
- my $error = Wallet::Config::verify_acl_name ($acl->[1]);
- push (@results, $acl) if $error;
- }
- return @results;
- } else {
- $self->error ("unknown acl audit: $audit");
- return;
- }
- } else {
- $self->error ("unknown audit type: $type");
- return;
- }
-}
-
-1;
-__DATA__
-
-##############################################################################
-# Documentation
-##############################################################################
-
-=head1 NAME
-
-Wallet::Report - Wallet system reporting interface
-
-=for stopwords
-ACL ACLs wildcard Allbery SQL tuples
-
-=head1 SYNOPSIS
-
- use Wallet::Report;
- my $report = Wallet::Report->new;
- my @objects = $report->objects ('type', 'keytab');
- for my $object (@objects) {
- print "@$object\n";
- }
- @objects = $report->audit ('objects', 'name');
-
-=head1 DESCRIPTION
-
-Wallet::Report provides a mechanism to generate lists and reports on the
-contents of the wallet database. The format of the results returned
-depend on the type of search, but will generally be returned as a list of
-tuples identifying objects, ACLs, or ACL entries.
-
-To use this object, several configuration variables must be set (at least
-the database configuration). For information on those variables and how
-to set them, see L<Wallet::Config>. For more information on the normal
-user interface to the wallet server, see L<Wallet::Server>.
-
-=head1 CLASS METHODS
-
-=over 4
-
-=item new()
-
-Creates a new wallet report object and connects to the database. On any
-error, this method throws an exception.
-
-=back
-
-=head1 INSTANCE METHODS
-
-For all methods that can fail, the caller should call error() after a
-failure to get the error message. For all methods that return lists, if
-they return an empty list, the caller should call error() to distinguish
-between an empty report and an error.
-
-=over 4
-
-=item acls([ TYPE [, SEARCH ... ]])
-
-Returns a list of all ACLs matching a search type and string in the
-database, or all ACLs if no search information is given. There are
-currently four search types. C<duplicate> returns sets of duplicate ACLs
-(ones with exactly the same entries). C<empty> takes no arguments and
-will return only those ACLs that have no entries within them. C<entry>
-takes two arguments, an entry scheme and a (possibly partial) entry
-identifier, and will return any ACLs containing an entry with that scheme
-and with an identifier containing that value. C<unused> returns all ACLs
-that are not referenced by any object.
-
-The return value for everything except C<duplicate> is a list of
-references to pairs of ACL ID and name. For example, if there are two
-ACLs in the database, one with name C<ADMIN> and ID 1 and one with name
-C<group/admins> and ID 3, acls() with no arguments would return:
-
- ([ 1, 'ADMIN' ], [ 3, 'group/admins' ])
-
-The return value for the C<duplicate> search is sets of ACL names that are
-duplicates (have the same entries). For example, if C<d1>, C<d2>, and
-C<d3> are all duplicates, and C<o1> and C<o2> are also duplicates, the
-result would be:
-
- ([ 'd1', 'd2', 'd3' ], [ 'o1', 'o2' ])
-
-Returns the empty list on failure. An error can be distinguished from
-empty search results by calling error(). error() is guaranteed to return
-the error message if there was an error and undef if there was no error.
-
-=item audit(TYPE, AUDIT)
-
-Audits the wallet database for violations of local policy. TYPE is the
-general class of thing to audit, and AUDIT is the specific audit to
-perform. TYPE may be either C<objects> or C<acls>. Currently, the only
-implemented audit is C<name>. This returns a list of all objects, as
-references to pairs of type and name, or ACLs, as references to pairs of
-ID and name, that are not accepted by the verify_name() or
-verify_acl_name() function defined in the wallet configuration. See
-L<Wallet::Config> for more information.
-
-Returns the empty list on failure. An error can be distinguished from
-empty search results by calling error(). error() is guaranteed to return
-the error message if there was an error and undef if there was no error.
-
-=item error()
-
-Returns the error of the last failing operation or undef if no operations
-have failed. Callers should call this function to get the error message
-after an undef return from any other instance method.
-
-=item objects([ TYPE [, SEARCH ... ]])
-
-Returns a list of all objects matching a search type and string in the
-database, or all objects in the database if no search information is
-given.
-
-There are five types of searches currently. C<type>, with a given type,
-will return only those entries where the type matches the given type.
-C<owner>, with a given owner, will only return those objects owned by the
-given ACL name or ID. C<flag>, with a given flag name, will only return
-those items with a flag set to the given value. C<acl> operates like
-C<owner>, but will return only those objects that have the given ACL name
-or ID on any of the possible ACL settings, not just owner. C<unused> will
-return all entries for which a get command has never been issued.
-
-The return value is a list of references to pairs of type and name. For
-example, if two objects existed in the database, both of type C<keytab>
-and with values C<host/example.com> and C<foo>, objects() with no
-arguments would return:
-
- ([ 'keytab', 'host/example.com' ], [ 'keytab', 'foo' ])
-
-Returns the empty list on failure. To distinguish between this and an
-empty search result, the caller should call error(). error() is
-guaranteed to return the error message if there was an error and undef if
-there was no error.
-
-=item owners(TYPE, NAME)
-
-Returns a list of all ACL lines contained in owner ACLs for objects
-matching TYPE and NAME, which are interpreted as SQL patterns using C<%>
-as a wildcard. The return value is a list of references to pairs of
-schema and identifier, with duplicates removed.
-
-Returns the empty list on failure. To distinguish between this and no
-matches, the caller should call error(). error() is guaranteed to return
-the error message if there was an error and undef if there was no error.
-
-=back
-
-=head1 SEE ALSO
-
-Wallet::Config(3), Wallet::Server(3)
-
-This module is part of the wallet system. The current version is
-available from L<http://www.eyrie.org/~eagle/software/wallet/>.
-
-=head1 AUTHOR
-
-Russ Allbery <eagle@eyrie.org> and Jon Robertson <jonrober@stanford.edu>.
-
-=cut