From 4dbf126b079d87639d0a463770c3e72b5b53d5d1 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Tue, 18 May 2010 16:44:38 -0700 Subject: Add acls duplicate report Add an acls duplicate report to wallet-report and Wallet::Report, returning sets of ACLs that have exactly the same entries. --- perl/Wallet/Report.pm | 81 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 69 insertions(+), 12 deletions(-) (limited to 'perl/Wallet') diff --git a/perl/Wallet/Report.pm b/perl/Wallet/Report.pm index 64418ee..5a8dc52 100644 --- a/perl/Wallet/Report.pm +++ b/perl/Wallet/Report.pm @@ -15,6 +15,7 @@ require 5.006; use strict; use vars qw($VERSION); +use Wallet::ACL; use Wallet::Database; # This version should be increased on any code change to this module. Always @@ -234,6 +235,52 @@ sub acls_unused { return ($sql); } +# 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->{dbh}) }; + 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 @@ -249,7 +296,9 @@ sub acls { if (!defined $type || $type eq '') { ($sql) = $self->acls_all; } else { - if ($type eq 'entry') { + 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; @@ -427,20 +476,28 @@ between an empty report and an error. 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 three search types. C takes no arguments and will return -only those ACLs that have no entries within them. C 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 returns all ACLs that are not -referenced by any object. - -The return value 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 and -ID 1 and one with name C and ID 3, acls() with no arguments -would return: +currently four search types. C returns sets of duplicate ACLs +(ones with exactly the same entries). C takes no arguments and +will return only those ACLs that have no entries within them. C +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 returns all ACLs +that are not referenced by any object. + +The return value for everything except C 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 and ID 1 and one with name +C and ID 3, acls() with no arguments would return: ([ 1, 'ADMIN' ], [ 3, 'group/admins' ]) +The return value for the C search is sets of ACL names that are +duplicates (have the same entries). For example, if C, C, and +C are all duplicates, and C and C 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. -- cgit v1.2.3