summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS4
-rw-r--r--TODO3
-rw-r--r--client/wallet.pod30
-rw-r--r--perl/Wallet/Server.pm40
-rwxr-xr-xperl/t/server.t10
-rwxr-xr-xserver/wallet-backend31
-rwxr-xr-xtests/server/backend-t30
7 files changed, 106 insertions, 42 deletions
diff --git a/NEWS b/NEWS
index 6f20133..b948d91 100644
--- a/NEWS
+++ b/NEWS
@@ -15,6 +15,10 @@ wallet 1.0 (unreleased)
this ACL type for an existing wallet database, use wallet-admin to
register the new verifier.
+ Add a new acl check command which, given an ACL ID, prints yes if that
+ ACL already exists and no otherwise. This is parallel to the check
+ command for objects.
+
Add a comment field to objects and corresponding commands to
wallet-backend and wallet to set and retrieve it. The comment field
can only be set by the owner or wallet administrators but can be seen
diff --git a/TODO b/TODO
index fd49abc..2fc17b5 100644
--- a/TODO
+++ b/TODO
@@ -29,9 +29,6 @@ Client:
Server Interface:
- * WALLET-12: Add check command for ACLs similar to the check command for
- objects.
-
* WALLET-13: Provide a way to get history for deleted objects and ACLs.
* WALLET-14: Provide an interface to mass-change all instances of one ACL
diff --git a/client/wallet.pod b/client/wallet.pod
index a0785a5..23e4e7c 100644
--- a/client/wallet.pod
+++ b/client/wallet.pod
@@ -151,19 +151,20 @@ options and commands are ignored.
=head1 COMMANDS
As mentioned above, most commands are only available to wallet
-administrators. The exceptions are C<get>, C<store>, C<show>, C<destroy>,
-C<flag clear>, C<flag set>, C<getattr>, C<setattr>, and C<history>. All
-of those commands have their own ACLs except C<getattr> and C<history>,
-which use the C<show> ACL, C<setattr>, which uses the C<store> ACL, and
-C<comment>, which uses the owner or C<show> ACL depending on whether one
-is setting or retrieving the comment. If the appropriate ACL is set, it
-alone is checked to see if the user has access. Otherwise, C<get>,
-C<store>, C<show>, C<getattr>, C<setattr>, C<history>, and C<comment>
-access is permitted if the user is authorized by the owner ACL of the
-object.
+administrators. The exceptions are C<acl check>, C<check>, C<get>,
+C<store>, C<show>, C<destroy>, C<flag clear>, C<flag set>, C<getattr>,
+C<setattr>, and C<history>. C<acl check> and C<check> can be run by
+anyone. All of the rest of those commands have their own ACLs except
+C<getattr> and C<history>, which use the C<show> ACL, C<setattr>, which
+uses the C<store> ACL, and C<comment>, which uses the owner or C<show> ACL
+depending on whether one is setting or retrieving the comment. If the
+appropriate ACL is set, it alone is checked to see if the user has access.
+Otherwise, C<get>, C<store>, C<show>, C<getattr>, C<setattr>, C<history>,
+and C<comment> access is permitted if the user is authorized by the owner
+ACL of the object.
Administrators can run any command on any object or ACL except for C<get>
-and C<store>. For C<get> and C<show>, they must still be authorized by
+and C<store>. For C<get> and C<store>, they must still be authorized by
either the appropriate specific ACL or the owner ACL.
If the locked flag is set on an object, no commands can be run on that
@@ -178,9 +179,14 @@ For more information on attributes, see L<ATTRIBUTES>.
=item acl add <id> <scheme> <identifier>
-Adds an entry with <scheme> and <identifier> to the ACL <id>. <id> may be
+Add an entry with <scheme> and <identifier> to the ACL <id>. <id> may be
either the name of an ACL or its numeric identifier.
+=item acl check <id>
+
+Check whether an ACL with the ID <id> already exists. If it does, prints
+C<yes>; if not, prints C<no>.
+
=item acl create <name>
Create a new, empty ACL with name <name>. When setting an ACL on an
diff --git a/perl/Wallet/Server.pm b/perl/Wallet/Server.pm
index b2bae2c..dfb7dbb 100644
--- a/perl/Wallet/Server.pm
+++ b/perl/Wallet/Server.pm
@@ -275,7 +275,7 @@ sub object_error {
# the internal error message. Note that we do not allow any special access to
# admins for get and store; if they want to do that with objects, they need to
# set the ACL accordingly.
-sub acl_check {
+sub acl_verify {
my ($self, $object, $action) = @_;
my %actions = map { $_ => 1 }
qw(get store show destroy flags setattr getattr comment);
@@ -349,7 +349,7 @@ sub attr {
my $user = $self->{user};
my $host = $self->{host};
if (@values) {
- return unless $self->acl_check ($object, 'setattr');
+ return unless $self->acl_verify ($object, 'setattr');
if (@values == 1 and $values[0] eq '') {
@values = ();
}
@@ -357,7 +357,7 @@ sub attr {
$self->error ($object->error) unless $result;
return $result;
} else {
- return unless $self->acl_check ($object, 'getattr');
+ return unless $self->acl_verify ($object, 'getattr');
my @result = $object->attr ($attr);
if (not @result and $object->error) {
$self->error ($object->error);
@@ -376,10 +376,10 @@ sub comment {
return unless defined $object;
my $result;
if (defined $comment) {
- return unless $self->acl_check ($object, 'comment');
+ return unless $self->acl_verify ($object, 'comment');
$result = $object->comment ($comment, $self->{user}, $self->{host});
} else {
- return unless $self->acl_check ($object, 'show');
+ return unless $self->acl_verify ($object, 'show');
$result = $object->comment;
}
if (not defined ($result) and $object->error) {
@@ -456,7 +456,7 @@ sub get {
my ($self, $type, $name) = @_;
my $object = $self->retrieve ($type, $name);
return unless defined $object;
- return unless $self->acl_check ($object, 'get');
+ return unless $self->acl_verify ($object, 'get');
my $result = $object->get ($self->{user}, $self->{host});
$self->error ($object->error) unless defined $result;
return $result;
@@ -471,7 +471,7 @@ sub store {
my ($self, $type, $name, $data) = @_;
my $object = $self->retrieve ($type, $name);
return unless defined $object;
- return unless $self->acl_check ($object, 'store');
+ return unless $self->acl_verify ($object, 'store');
if (not defined ($data)) {
$self->{error} = "no data supplied to store";
return;
@@ -488,7 +488,7 @@ sub show {
my ($self, $type, $name) = @_;
my $object = $self->retrieve ($type, $name);
return unless defined $object;
- return unless $self->acl_check ($object, 'show');
+ return unless $self->acl_verify ($object, 'show');
my $result = $object->show;
$self->error ($object->error) unless defined $result;
return $result;
@@ -501,7 +501,7 @@ sub history {
my ($self, $type, $name) = @_;
my $object = $self->retrieve ($type, $name);
return unless defined $object;
- return unless $self->acl_check ($object, 'show');
+ return unless $self->acl_verify ($object, 'show');
my $result = $object->history;
$self->error ($object->error) unless defined $result;
return $result;
@@ -513,7 +513,7 @@ sub destroy {
my ($self, $type, $name) = @_;
my $object = $self->retrieve ($type, $name);
return unless defined $object;
- return unless $self->acl_check ($object, 'destroy');
+ return unless $self->acl_verify ($object, 'destroy');
my $result = $object->destroy ($self->{user}, $self->{host});
$self->error ($object->error) unless defined $result;
return $result;
@@ -529,7 +529,7 @@ sub flag_clear {
my ($self, $type, $name, $flag) = @_;
my $object = $self->retrieve ($type, $name);
return unless defined $object;
- return unless $self->acl_check ($object, 'flags');
+ return unless $self->acl_verify ($object, 'flags');
my $result = $object->flag_clear ($flag, $self->{user}, $self->{host});
$self->error ($object->error) unless defined $result;
return $result;
@@ -541,7 +541,7 @@ sub flag_set {
my ($self, $type, $name, $flag) = @_;
my $object = $self->retrieve ($type, $name);
return unless defined $object;
- return unless $self->acl_check ($object, 'flags');
+ return unless $self->acl_verify ($object, 'flags');
my $result = $object->flag_set ($flag, $self->{user}, $self->{host});
$self->error ($object->error) unless defined $result;
return $result;
@@ -551,6 +551,22 @@ sub flag_set {
# ACL methods
##############################################################################
+# Checks for the existence of an ACL. Returns 1 if it does, 0 if it doesn't,
+# and undef if there was an error in checking the existence of the object.
+sub acl_check {
+ my ($self, $id) = @_;
+ my $acl = eval { Wallet::ACL->new ($id, $self->{dbh}) };
+ if ($@) {
+ if ($@ =~ /^ACL .* not found/) {
+ return 0;
+ } else {
+ $self->error ($@);
+ return;
+ }
+ }
+ return 1;
+}
+
# Create a new empty ACL in the database. Returns true on success and undef
# on failure, setting the internal error.
sub acl_create {
diff --git a/perl/t/server.t b/perl/t/server.t
index ad16151..8e0a30d 100755
--- a/perl/t/server.t
+++ b/perl/t/server.t
@@ -3,12 +3,12 @@
# Tests for the wallet server API.
#
# Written by Russ Allbery <rra@stanford.edu>
-# Copyright 2007, 2008, 2010, 2011
+# Copyright 2007, 2008, 2010, 2011, 2012
# The Board of Trustees of the Leland Stanford Junior University
#
# See LICENSE for licensing terms.
-use Test::More tests => 377;
+use Test::More tests => 381;
use POSIX qw(strftime);
use Wallet::Admin;
@@ -66,7 +66,9 @@ is ($result, $history, ' including by number');
is ($server->acl_create (3), undef, 'Cannot create ACL with a numeric name');
is ($server->error, 'ACL name may not be all numbers',
' and returns the right error');
+is ($server->acl_check ('user1'), 0, 'user1 ACL does not exist');
is ($server->acl_create ('user1'), 1, 'Can create regular ACL');
+is ($server->acl_check ('user1'), 1, 'user1 now exists');
is ($server->acl_show ('user1'), "Members of ACL user1 (id: 2) are:\n",
' and show works');
is ($server->acl_create ('user1'), undef, ' but not twice');
@@ -95,8 +97,10 @@ is ($server->acl_history ('test'), undef, ' and history fails');
is ($server->error, 'ACL test not found', ' and returns the right error');
is ($server->acl_destroy ('test'), undef, 'Destroying the old name fails');
is ($server->error, 'ACL test not found', ' and returns the right error');
-is ($server->acl_destroy ('test2'), 1, ' but destroying another one works');
+is ($server->acl_check ('test2'), 1, ' but the other ACL exists');
+is ($server->acl_destroy ('test2'), 1, ' and destroying it works');
is ($server->acl_destroy ('test2'), undef, ' but not twice');
+is ($server->acl_check ('test2'), 0, ' and now it does not exist');
is ($server->error, 'ACL test2 not found', ' and returns the right error');
is ($server->acl_add ('user1', 'krb4', $user1), undef,
'Adding with a bad scheme fails');
diff --git a/server/wallet-backend b/server/wallet-backend
index 9850c0e..948b47c 100755
--- a/server/wallet-backend
+++ b/server/wallet-backend
@@ -3,7 +3,7 @@
# wallet-backend -- Wallet server for storing and retrieving secure data.
#
# Written by Russ Allbery <rra@stanford.edu>
-# Copyright 2007, 2008, 2010, 2011
+# Copyright 2007, 2008, 2010, 2011, 2012
# The Board of Trustees of the Leland Stanford Junior University
#
# See LICENSE for licensing terms.
@@ -150,6 +150,14 @@ sub command {
if ($action eq 'add') {
check_args (3, 3, [3], @args);
$server->acl_add (@args) or failure ($server->error, @_);
+ } elsif ($action eq 'check') {
+ check_args (1, 1, [], @args);
+ my $status = $server->acl_check (@args);
+ if (!defined ($status)) {
+ failure ($server->error, @_);
+ } else {
+ print $status ? "yes\n" : "no\n";
+ }
} elsif ($action eq 'create') {
check_args (1, 1, [], @args);
$server->acl_create (@args) or failure ($server->error, @_);
@@ -376,17 +384,17 @@ syslog.
=head1 COMMANDS
Most commands are only available to wallet administrators (users on the
-C<ADMIN> ACL). The exceptions are C<autocreate>, C<get>, C<store>,
-C<show>, C<destroy>, C<flag clear>, C<flag set>, C<getattr>, C<setattr>,
-and C<history>. All of those commands have their own ACLs except
+C<ADMIN> ACL). The exceptions are C<acl check>, C<check>, C<get>,
+C<store>, C<show>, C<destroy>, C<flag clear>, C<flag set>, C<getattr>,
+C<setattr>, and C<history>. C<acl check> and C<check> can be run by
+anyone. All of the rest of those commands have their own ACLs except
C<getattr> and C<history>, which use the C<show> ACL, C<setattr>, which
-uses the C<store> ACL, and C<comment>, which uses the owner or C<show>
-ACL depending on whether one is setting or retrieving the comment. If the
+uses the C<store> ACL, and C<comment>, which uses the owner or C<show> ACL
+depending on whether one is setting or retrieving the comment. If the
appropriate ACL is set, it alone is checked to see if the user has access.
Otherwise, C<get>, C<store>, C<show>, C<getattr>, C<setattr>, C<history>,
and C<comment> access is permitted if the user is authorized by the owner
-ACL of the object. C<autocreate> is permitted if the user is listed in
-the default ACL for an object for that name.
+ACL of the object.
Administrators can run any command on any object or ACL except for C<get>
and C<store>. For C<get> and C<store>, they must still be authorized by
@@ -404,9 +412,14 @@ For more information on attributes, see L<ATTRIBUTES>.
=item acl add <id> <scheme> <identifier>
-Adds an entry with <scheme> and <identifier> to the ACL <id>. <id> may be
+Add an entry with <scheme> and <identifier> to the ACL <id>. <id> may be
either the name of an ACL or its numeric identifier.
+=item acl check <id>
+
+Check whether an ACL with the ID <id> already exists. If it does, prints
+C<yes>; if not, prints C<no>.
+
=item acl create <name>
Create a new, empty ACL with name <name>. When setting an ACL on an
diff --git a/tests/server/backend-t b/tests/server/backend-t
index 3e377a1..50131b7 100755
--- a/tests/server/backend-t
+++ b/tests/server/backend-t
@@ -3,13 +3,13 @@
# Tests for the wallet-backend dispatch code.
#
# Written by Russ Allbery <rra@stanford.edu>
-# Copyright 2006, 2007, 2008, 2009, 2010, 2011
+# Copyright 2006, 2007, 2008, 2009, 2010, 2011, 2012
# The Board of Trustees of the Leland Stanford Junior University
#
# See LICENSE for licensing terms.
use strict;
-use Test::More tests => 1296;
+use Test::More tests => 1314;
# Create a dummy class for Wallet::Server that prints what method was called
# with its arguments and returns data for testing.
@@ -45,6 +45,18 @@ sub acl_remove
sub acl_rename
{ shift; print "acl_rename @_\n"; ($_[0] eq 'error') ? undef : 1 }
+sub acl_check {
+ shift;
+ print "acl_check @_\n";
+ if ($_[0] eq 'error') {
+ return;
+ } elsif ($_[0] eq 'unknown') {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
sub acl_history {
shift;
print "acl_history @_\n";
@@ -243,6 +255,7 @@ my %commands = (autocreate => [2, 2],
show => [2, 2],
store => [2, 3]);
my %acl_commands = (add => [3, 3],
+ check => [1, 1],
create => [1, 1],
destroy => [1, 1],
history => [1, 1],
@@ -460,7 +473,9 @@ for my $command (sort keys %acl_commands) {
is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n",
' and success logged');
my $expected;
- if ($command eq 'show') {
+ if ($command eq 'check') {
+ $expected = "$new\nacl_$command name$extra\nyes\n";
+ } elsif ($command eq 'show') {
$expected = "$new\nacl_$command name$extra\nacl_show";
} elsif ($command eq 'history') {
$expected = "$new\nacl_$command name$extra\nacl_history";
@@ -476,6 +491,15 @@ for my $command (sort keys %acl_commands) {
is ($out, "$new\nacl_$command error$extra\n",
' and ran the right method');
$error++;
+ if ($command eq 'check') {
+ ($out, $err) = run_backend ('acl', $command, 'unknown');
+ my $ran = "acl $command unknown";
+ is ($err, '', "Command $command ran with no errors (unknown)");
+ is ($OUTPUT, "command $ran from admin (1.2.3.4) succeeded\n",
+ ' and success logged');
+ is ($out, "$new\nacl_$command unknown\nno\n",
+ ' and ran the right method with output');
+ }
}
for my $command (sort keys %flag_commands) {
my @extra = ('foo') x ($flag_commands{$command}[0] - 2);