summaryrefslogtreecommitdiff
path: root/perl/lib
diff options
context:
space:
mode:
Diffstat (limited to 'perl/lib')
-rw-r--r--perl/lib/Wallet/ACL.pm38
-rw-r--r--perl/lib/Wallet/ACL/Base.pm13
-rw-r--r--perl/lib/Wallet/ACL/Nested.pm195
-rw-r--r--perl/lib/Wallet/Admin.pm1
4 files changed, 245 insertions, 2 deletions
diff --git a/perl/lib/Wallet/ACL.pm b/perl/lib/Wallet/ACL.pm
index 260ff22..6d8005d 100644
--- a/perl/lib/Wallet/ACL.pm
+++ b/perl/lib/Wallet/ACL.pm
@@ -198,6 +198,19 @@ sub rename {
$acls->ac_name ($name);
$acls->update;
$self->log_acl ('rename', undef, undef, $user, $host, $time);
+
+ # Find any references to this being used as a nested verifier and
+ # update the name. This really breaks out of the normal flow, but
+ # it's hard to do otherwise.
+ %search = (ae_scheme => 'nested',
+ ae_identifier => $self->{name},
+ );
+ my @entries = $self->{schema}->resultset('AclEntry')->search(\%search);
+ for my $entry (@entries) {
+ $entry->ae_identifier ($name);
+ $entry->update;
+ }
+
$guard->commit;
};
if ($@) {
@@ -267,6 +280,17 @@ sub destroy {
$entry->delete;
}
+ # Find any references to this being used as a nested verifier and
+ # remove them. This really breaks out of the normal flow, but it's
+ # hard to do otherwise.
+ %search = (ae_scheme => 'nested',
+ ae_identifier => $self->{name},
+ );
+ @entries = $self->{schema}->resultset('AclEntry')->search(\%search);
+ for my $entry (@entries) {
+ $entry->delete;
+ }
+
# There should definitely be an ACL record to delete.
%search = (ac_id => $self->{id});
my $entry = $self->{schema}->resultset('Acl')->find(\%search);
@@ -302,6 +326,18 @@ sub add {
$self->error ("unknown ACL scheme $scheme");
return;
}
+
+ # Check to make sure that this entry has a valid name for the scheme.
+ my $class = $self->scheme_mapping ($scheme);
+ my $object = eval {
+ $class->new ($identifier, $self->{schema});
+ };
+ unless ($object && $object->syntax_check ($identifier)) {
+ $self->error ("invalid ACL identifier $identifier for $scheme");
+ return;
+ };
+
+ # Actually create the scheme.
eval {
my $guard = $self->{schema}->txn_scope_guard;
my %record = (ae_id => $self->{id},
@@ -446,7 +482,7 @@ sub history {
push (@{ $self->{check_errors} }, "unknown scheme $scheme");
return;
}
- $verifier{$scheme} = $class->new;
+ $verifier{$scheme} = $class->new ($identifier, $self->{schema});
unless (defined $verifier{$scheme}) {
push (@{ $self->{check_errors} }, "cannot verify $scheme");
return;
diff --git a/perl/lib/Wallet/ACL/Base.pm b/perl/lib/Wallet/ACL/Base.pm
index a2b07cc..19ca612 100644
--- a/perl/lib/Wallet/ACL/Base.pm
+++ b/perl/lib/Wallet/ACL/Base.pm
@@ -20,7 +20,7 @@ use vars qw($VERSION);
# 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.02';
+$VERSION = '0.03';
##############################################################################
# Interface
@@ -37,6 +37,11 @@ sub new {
return $self;
}
+# The default name check method allows any name.
+sub syntax_check {
+ return 1;
+}
+
# The default check method denies all access.
sub check {
return 0;
@@ -92,6 +97,12 @@ inherit from it. It is not used directly.
Creates a new ACL verifier. The generic function provided here just
creates and blesses an object.
+=item syntax_check(PRINCIPAL, ACL)
+
+This method should be overridden by any child classes that want to
+implement validating the name of an ACL before creation. The default
+implementation allows any name for an ACL.
+
=item check(PRINCIPAL, ACL)
This method should always be overridden by child classes. The default
diff --git a/perl/lib/Wallet/ACL/Nested.pm b/perl/lib/Wallet/ACL/Nested.pm
new file mode 100644
index 0000000..3be84bd
--- /dev/null
+++ b/perl/lib/Wallet/ACL/Nested.pm
@@ -0,0 +1,195 @@
+# Wallet::ACL::Nested - ACL class for nesting ACLs
+#
+# Written by Jon Robertson <jonrober@stanford.edu>
+# Copyright 2015
+# The Board of Trustees of the Leland Stanford Junior University
+#
+# See LICENSE for licensing terms.
+
+##############################################################################
+# Modules and declarations
+##############################################################################
+
+package Wallet::ACL::Nested;
+require 5.006;
+
+use strict;
+use warnings;
+use vars qw($VERSION @ISA);
+
+use Wallet::ACL::Base;
+
+@ISA = qw(Wallet::ACL::Base);
+
+# 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.01';
+
+##############################################################################
+# Interface
+##############################################################################
+
+# Creates a new persistant verifier, taking a database handle. This parent
+# class just creates an empty object and ignores the handle. Child classes
+# should override if there are necessary initialization tasks or if the handle
+# will be used by the verifier.
+sub new {
+ my $type = shift;
+ my ($name, $schema) = @_;
+ my $self = {
+ schema => $schema,
+ expanded => {},
+ };
+ bless ($self, $type);
+ return $self;
+}
+
+# Name checking requires checking that there's an existing ACL already by
+# this name. Try to create the ACL object and use that to determine.
+sub syntax_check {
+ my ($self, $group) = @_;
+
+ my $acl;
+ eval { $acl = Wallet::ACL->new ($group, $self->{schema}) };
+ return 0 if $@;
+ return 0 unless $acl;
+ return 1;
+}
+
+# For checking a nested ACL, we need to expand each entry and then check
+# that entry. We also want to keep track of things already checked in order
+# to avoid any loops.
+sub check {
+ my ($self, $principal, $group) = @_;
+ unless ($principal) {
+ $self->error ('no principal specified');
+ return;
+ }
+ unless ($group) {
+ $self->error ('malformed nested ACL');
+ return;
+ }
+
+ # Make an ACL object just so that we can use it to drop back into the
+ # normal ACL validation after we have expanded the nesting.
+ my $acl;
+ eval { $acl = Wallet::ACL->new ($group, $self->{schema}) };
+
+ # Get the list of all nested acl entries within this entry, and use it
+ # to go through each entry and decide if the given acl has access.
+ my @members = $self->get_membership ($group);
+ for my $entry (@members) {
+ my ($type, $name) = @{ $entry };
+ my $result = $acl->check_line ($principal, $type, $name);
+ return 1 if $result;
+ }
+ return 0;
+}
+
+# Get the membership of a group recursively. The final result will be a list
+# of arrayrefs like that from Wallet::ACL->list, but expanded for full
+# membership.
+sub get_membership {
+ my ($self, $group) = @_;
+
+ # Get the list of members for this nested acl. Consider any missing acls
+ # as empty.
+ my $schema = $self->{schema};
+ my @members;
+ eval {
+ my $acl = Wallet::ACL->new ($group, $schema);
+ @members = $acl->list;
+ };
+
+ # Now go through and expand any other nested groups into their own
+ # memberships.
+ my @expanded;
+ for my $entry (@members) {
+ my ($type, $name) = @{ $entry };
+ if ($type eq 'nested') {
+
+ # Keep track of things we've already expanded and don't look them
+ # up again.
+ next if exists $self->{expanded}{$name};
+ $self->{expanded}{$name} = 1;
+ push (@expanded, $self->get_membership ($name));
+
+ } else {
+ push (@expanded, $entry);
+ }
+ }
+
+ return @expanded;
+}
+
+1;
+__END__
+
+##############################################################################
+# Documentation
+##############################################################################
+
+=for stopwords
+ACL Allbery verifier verifiers
+
+=head1 NAME
+
+Wallet::ACL::Base - Generic parent class for wallet ACL verifiers
+
+=head1 SYNOPSIS
+
+ package Wallet::ACL::Simple
+ @ISA = qw(Wallet::ACL::Base);
+ sub check {
+ my ($self, $principal, $acl) = @_;
+ return ($principal eq $acl) ? 1 : 0;
+ }
+
+=head1 DESCRIPTION
+
+Wallet::ACL::Base is the generic parent class for wallet ACL verifiers.
+It provides default functions and behavior and all ACL verifiers should
+inherit from it. It is not used directly.
+
+=head1 METHODS
+
+=over 4
+
+=item new()
+
+Creates a new ACL verifier. The generic function provided here just
+creates and blesses an object.
+
+=item check(PRINCIPAL, ACL)
+
+This method should always be overridden by child classes. The default
+implementation just declines all access.
+
+=item error([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.
+
+For the convenience of child classes, this method can also be called with
+one or more error strings. If so, those strings are concatenated
+together, trailing newlines are removed, any text of the form S<C< at \S+
+line \d+\.?>> at the end of the message is stripped off, and the result is
+stored as the error. Only child classes should call this method with an
+error string.
+
+=back
+
+=head1 SEE ALSO
+
+Wallet::ACL(3), wallet-backend(8)
+
+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>
+
+=cut
diff --git a/perl/lib/Wallet/Admin.pm b/perl/lib/Wallet/Admin.pm
index b38cc94..f6f1f90 100644
--- a/perl/lib/Wallet/Admin.pm
+++ b/perl/lib/Wallet/Admin.pm
@@ -118,6 +118,7 @@ sub default_data {
[ 'krb5', 'Wallet::ACL::Krb5' ],
[ 'krb5-regex', 'Wallet::ACL::Krb5::Regex' ],
[ 'ldap-attr', 'Wallet::ACL::LDAP::Attribute' ],
+ [ 'nested', 'Wallet::ACL::Nested' ],
[ 'netdb', 'Wallet::ACL::NetDB' ],
[ 'netdb-root', 'Wallet::ACL::NetDB::Root' ],
]);